Add experimental sigstore-go support#819
Open
wlynch wants to merge 12 commits into
Open
Conversation
6634439 to
eeb2f52
Compare
Introduces internal/sigstore/compat, the foundation for refactoring
gitsign onto the sigstore-go libraries without changing the on-disk CMS
signature format.
CMS -> bundle (verification direction):
- SignerInfoToBundle converts a single CMS signer into a sigstore v0.3
bundle, returning a SignerBundle{Bundle, Artifact}. Artifact is the
marshaled SignedAttrs that the bundle signs over (gitsign signs the
SignedAttrs, not the commit body), to be supplied as the verification
artifact.
- SignedDataToBundle converts every signer, returning one SignerBundle
per SignerInfo: a bundle holds a single MessageSignature, and the CMS
format permits multiple signers.
bundle -> CMS attributes (signing direction):
- BundleToAttributes converts each Rekor tlog entry in a bundle into CMS
unsigned attributes (one protocol.Attributes set per entry), encoded
under OIDRekorTransparencyLogEntry as the legacy signing path did. The
caller owns the SignedData mutation.
Refactors internal/rekor/oid to share the HashedRekord canonical body
recompute: extracts ToLogEntryProto (raw canonical body, sigstore
convention) and adds ProtoToLogEntryAnon, while ToLogEntry keeps its
existing base64 behavior.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Billy Lynch <billy@chainguard.dev>
Adds an opt-in verification path (cfg.VerifyBundle, set via gitsign.verifyBundle git config or GITSIGN_VERIFY_BUNDLE) that verifies a gitsign CMS signature by converting it to sigstore bundles and verifying them with sigstore-go. The default behavior is unchanged. internal/gitsign: - bundle.go: verifyBundle converts each signer with compat.SignerInfoToBundle and verifies it with sigstore-go. At least one signer must verify (a failing signer is skipped, not fatal); the first verified signer drives the VerificationSummary. Content binding (hash(object) == SignedAttrs message-digest) is enforced separately since sigstore-go signs over the SignedAttrs, and a Rekor entry is required (no current-time fallback). When a signer carries an RFC3161 timestamp it must verify against the configured TSA; embedded SCTs are verified by default unless IgnoreSCT. - trustedroot.go: builds a sigstore-go root.TrustedMaterial from gitsign's existing trust sources (Fulcio roots, Rekor/CT log keys, cfg.TimestampCert). Supporting changes: - config: VerifyBundle option (git config + env). - fulcioroots: CertsFromConfig returns []*x509.Certificate for the trust material (FulcioCertificateAuthority can't consume a CertPool). - compat: ParseSignaturePEM (PEM-or-DER -> CMS SignedData), now shared by pkg/git, pkg/rekor, and the bundle path; SignerInfoToBundle takes a SignerInfo value. - internal/e2e: bundle parity test (e2e build tag) comparing the legacy and bundle paths on the offline commit fixture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Billy Lynch <billy@chainguard.dev>
Adds an opt-in signing path where sigstore-go produces the signature and Rekor entry as a bundle, which is then converted to a CMS signature for storage. The on-disk CMS format is unchanged. Gated, together with the verification path, by cfg.EnableSigstoreGo (gitsign.enableSigstoreGo / GITSIGN_ENABLE_SIGSTORE_GO), which requires offline Rekor mode. Signing flow (internal/signature): - signBundle builds the CMS signed attributes, has sigstore-go's sign.Bundle sign them (using gitsign's existing identity via Identity.Keypair, so the OIDC/Fulcio flow and credential cache are unchanged) and upload to Rekor, then converts the bundle to CMS via the compat layer. - A validatingRekorClient rejects a Rekor response lacking an inclusion proof, turning a would-be panic in sigstore-go's conversion into an error. - The signing key/cert binding is checked before signing; RFC3161 timestamps are applied to the assembled CMS via the fork. compat: - BuildSignedAttributes / BundleToSignedData (the inverse of SignerInfoToBundle): assemble a CMS SignedData from a bundle, byte-equivalent to the fork's signer. - Keypair adapts a crypto.Signer to sign.Keypair, deriving algorithms from the key; CertificateProvider supplies the identity's existing Fulcio cert. fork/ietf-cms: - SignedAttributes + AddSignerInfoWithSignature assemble a SignerInfo with an externally-computed signature (the non-signing half of AddSignerInfo), so the CMS assembly lives in the CMS layer rather than being duplicated in compat. config: EnableSigstoreGo replaces the separate VerifyBundle/SignBundle options. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Billy Lynch <billy@chainguard.dev>
Adds docs/bundle-cms.md explaining how gitsign's CMS/PKCS7 signature format maps to the Sigstore bundle format, the CMS<->bundle conversions used by the experimental sigstore-go signing and verification paths, and the field mapping (the signed artifact being the marshaled CMS SignedAttrs). Also documents the gitsign.enableSigstoreGo / GITSIGN_ENABLE_SIGSTORE_GO option in the README config tables. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Billy Lynch <billy@chainguard.dev>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Billy Lynch <billy@chainguard.dev>
verifySigner now returns the bundle it verified, and verifyBundle populates a new VerificationSummary.Bundle field with it (nil on the legacy path). Also makes verifySigner's policyOpts variadic. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Billy Lynch <billy@chainguard.dev>
When cfg.EnableSigstoreGo is set, commandSign now prints a message to the user TTY so it is clear the experimental sigstore-go signing path is active. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Billy Lynch <billy@chainguard.dev>
Compare the signing key and certificate public key via the key's own Equal(crypto.PublicKey) bool method (implemented by all stdlib public key types) instead of marshaling both to PKIX and comparing bytes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Billy Lynch <billy@chainguard.dev>
The helper was only called once and added no abstraction over the inline Equal check, so fold it into its single caller. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Billy Lynch <billy@chainguard.dev>
This reverts commit 2763429. Signed-off-by: Billy Lynch <billy@chainguard.dev>
…log entry Older "online" signatures embed no Rekor transparency log entry - their entry lives in Rekor keyed on the commit SHA and is found via online search, which sigstore-go cannot do from the signature alone. The bundle verification path now signals these via errNoEmbeddedRekorEntry, and Verify falls back to the legacy path so they keep verifying under gitsign.enableSigstoreGo. Adds a regression test using a real online-mode signature fixture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Billy Lynch <billy@chainguard.dev>
eeb2f52 to
ac217bd
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds an experimental opt-in path to perform gitsign commit/tag signing and verification using sigstore-go, while preserving gitsign’s existing CMS/PKCS7 on-disk signature format by converting between CMS and Sigstore Bundles.
Changes:
- Introduces
gitsign.enableSigstoreGo/GITSIGN_ENABLE_SIGSTORE_GO(requiresrekorMode=offline) and wires it into signing + verification flows. - Adds a CMS↔Bundle compatibility layer (
internal/sigstore/compat) plus supporting Rekor OID protobuf helpers. - Implements bundle-based verification and bundle-based offline signing (Rekor entry embedded), with tests and documentation.
Reviewed changes
Copilot reviewed 36 out of 36 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Documents the new experimental config/env options and offline requirement. |
| pkg/rekor/rekor.go | Switches signature parsing to shared compat PEM/DER parsing helper. |
| pkg/git/verify.go | Extends verification summary to optionally include the verified bundle. |
| pkg/git/verifier.go | Switches signature parsing to compat helper (PEM/DER). |
| pkg/git/signature_test.go | Adds sigstore-go Keypair adapter usage in tests. |
| internal/sigstore/compat/testdata/tlog.json | Adds Rekor log entry fixture for compat tests. |
| internal/sigstore/compat/testdata/commit.txt | Adds signed commit fixture for compat tests. |
| internal/sigstore/compat/signeddata.go | Adds bundle→CMS assembly helpers for signature storage compatibility. |
| internal/sigstore/compat/signeddata_test.go | Tests byte-for-byte CMS equivalence between legacy and assembled CMS. |
| internal/sigstore/compat/keypair.go | Adapts crypto.Signer to sigstore-go sign.Keypair. |
| internal/sigstore/compat/compat.go | Implements CMS→bundle conversion, artifact definition, and Rekor/timestamp mapping. |
| internal/sigstore/compat/compat_test.go | Tests CMS↔bundle conversions and Rekor attribute round-tripping. |
| internal/sigstore/compat/certificate.go | Provides a sigstore-go CertificateProvider backed by gitsign’s Fulcio cert. |
| internal/sigstore/compat/attrs.go | Extracts RFC3161 timestamp tokens from CMS unsigned attributes. |
| internal/signature/testdata/tlog.json | Adds Rekor log entry fixture for bundle signing tests. |
| internal/signature/sign.go | Adds experimental bundle signing option (sigstore-go signing + conversion). |
| internal/signature/bundlesign.go | Implements sign→bundle→CMS flow and Rekor client wrapper for inclusion-proof enforcement. |
| internal/signature/bundle_test.go | Unit tests for bundle signing path and Rekor wrapper behavior. |
| internal/rekor/oid/pbcompat.go | Adds protobuf→LogEntryAnon conversion for bundle-carried tlog entries. |
| internal/rekor/oid/oid.go | Refactors Rekor body recomputation and adds TransparencyLogEntry proto reconstruction. |
| internal/gitsign/trustedroot.go | Builds sigstore-go TrustedMaterial from existing gitsign trust sources. |
| internal/gitsign/testdata/online.commit | Adds “online signature” fixture to ensure bundle path falls back correctly. |
| internal/gitsign/gitsign.go | Wires cfg gating and fallback logic for bundle-based verification. |
| internal/gitsign/gitsign_test.go | Updates identity test type to support sigstore-go Keypair adapter. |
| internal/gitsign/bundle.go | Adds bundle verification implementation (CMS→bundle conversion + sigstore-go verification + content binding). |
| internal/gitsign/bundle_test.go | Tests fallback behavior when no embedded Rekor entry exists. |
| internal/fulcio/identity.go | Adds Keypair() to Fulcio identity to support bundle signing. |
| internal/fulcio/fulcioroots/fulcioroots.go | Adds raw-certs access for building sigstore-go trust material. |
| internal/fork/ietf-cms/assemble.go | Adds helpers to build signed attributes and assemble CMS with external signatures. |
| internal/fork/ietf-cms/assemble_test.go | Tests external-signature CMS assembly verifies with fork verifier. |
| internal/e2e/bundle_test.go | E2E parity test between legacy and bundle verification paths. |
| internal/config/config.go | Adds config/env parsing + enforces offline Rekor mode when sigstore-go enabled. |
| internal/config/config_test.go | Tests offline requirement enforcement for enableSigstoreGo. |
| internal/commands/root/sign.go | Enables bundle signing path from CLI when config flag is set (and emits a notice). |
| go.mod | Promotes sigstore-go to a direct dependency. |
| docs/bundle-cms.md | Documents the CMS↔bundle mapping and the experimental enablement flow. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Preallocate the claims slice via a composite literal (prealloc). - Drop the unused *models.LogEntryAnon return from loadSignedData and the constant path parameter from parseCommit in the compat tests (unparam). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Billy Lynch <billy@chainguard.dev>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a new config option -
gitsign.enableSigstoreGoif set totrue, then new sigstore-go signer/verifier is used to do the signing and verification of commits.At a high level, this works by adding a compatibility layer between Sigstore Bundles and gitsign's CMS signature layout (which we use to stay compatible with other Git tooling). When signing, we get the bundle then convert it to a CMS signature. When verifying we construct the bundle from the CMS signature, then use sigstore-go to verify.
CAVEATS: For now, signing requires use of offline signatures. If verification encounters an online signature, we fall back to the old non-sigstore-go behavior. I'll probably add online signing/verification support in another PR.
Part of #537
Release Note
gitsign.enableSigstoreGoif set totrue, then new sigstore-go signer/verifier is used to do the signing and verification of commits. This should not have a meaningful visible effect for most users.Documentation
✅