Skip to content

Support Verification in sigstore/cosign with X.509 Certificate Chain - #581

Merged
Hayden-IO merged 11 commits into
sigstore:mainfrom
yangkenneth:yangkenneth/certificate-chain-verification
Jul 6, 2026
Merged

Hayden-IO merged 11 commits into
sigstore:mainfrom
yangkenneth:yangkenneth/certificate-chain-verification

Conversation

@yangkenneth

@yangkenneth yangkenneth commented Feb 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Within sigstore/cosign we had opened a thread with @Hayden-IO around supporting OCI Image validation within the v0.3 bundle using certificate chains.

So tl;dr - it is fine to include the certificate chain in a v0.3 bundle. If there's any constraints in sigstore-go around parsing v0.3 bundles with chains, we can relax those constraints. Implementing support in Cosign is the intended design - see #132 and #298 (Edit: also #253 for more discussion) for some context, where we discussed an interface that allows us to create verifiers for alternative (aka not Sigstore-spec compliant) signing/verification paths for private deployments.

  • For the change in Supporting OCI Signing with X.509 Certificate Chain cosign#4614 if we attempt to create a v0.3 bundle which includes a certificate chain and verify the OCI Image we get the following error. By adding a configurable --allow-certificate-chain flag we load the Intermediate Certificate Authorities that are exist within the Sigstore bundle to establish the Trust Store.
cosign verify \
    --insecure-ignore-tlog \
    --insecure-ignore-sct \
    --check-claims=true \
    --certificate-identity example.com \
    --certificate-oidc-issuer-regexp ".*" \
    --trusted-root trusted-roots.json \
    --new-bundle-format \
    --use-signed-timestamps \
    --experimental-oci11 \
    {OCI IMAGE}
WARNING: Skipping tlog verification is an insecure practice that lacks transparency and auditability verification for the signature.
Error: no signatures found
error during command execution: no signatures found
cosign verify \
    --insecure-ignore-tlog \
    --insecure-ignore-sct \
    --check-claims=true \
    --certificate-identity example.com \
    --certificate-oidc-issuer-regexp ".*" \
    --trusted-root trusted-roots.json \
    --new-bundle-format \
    --use-signed-timestamps \
    --experimental-oci11 \
    --allow-certificate-chain \
    {OCI IMAGE}

The following checks were performed on each of these signatures:
  - The cosign claims were validated
  - Existence of the claims in the transparency log was verified offline
  - The code-signing certificate was verified using trusted certificate authority certificates

Release Note

  • NONE

Documentation

@yangkenneth
yangkenneth force-pushed the yangkenneth/certificate-chain-verification branch from 7fe70e2 to c2a8440 Compare February 1, 2026 04:18
@yangkenneth
yangkenneth marked this pull request as ready for review February 1, 2026 04:34
@yangkenneth
yangkenneth requested a review from a team as a code owner February 1, 2026 04:34
@kommendorkapten

Copy link
Copy Markdown
Member

@Hayden-IO Just removing the check for the certificate chain here seems a bit too permissive. This could potential have effects for clients using a PGI-like deployment (such as GitHub) which operates under the same semantics.

However, I still don't think this would work as IIRC chain building ignores what's in the bundle, it only relies on the intermediates from the bundle:

intermediateCertPool := x509.NewCertPool()
for _, cert := range ca.Intermediates {
intermediateCertPool.AddCert(cert)
}

@Hayden-IO

Copy link
Copy Markdown
Contributor

+1 to @kommendorkapten, I agree this is too permissive. I'd be open to some sort of a policy flag that allows this check to be bypassed when set explicitly by the calling library, though off the top of my head I'm not sure the best way to cleanly do this.

@yangkenneth

Copy link
Copy Markdown
Contributor Author

+1 to @kommendorkapten, I agree this is too permissive. I'd be open to some sort of a policy flag that allows this check to be bypassed when set explicitly by the calling library, though off the top of my head I'm not sure the best way to cleanly do this.

@Hayden-IO and @kommendorkapten pushed an update to support passing verification options through the bundle. If we want this to be opt-in, we’d need a corresponding change in cosign to add a flag through cosign for verification (e.g. --allow-certificate-chain). That way the default behavior stays as-is, and users can explicitly enable validation using certificate chains when using the new bundle format.

cosign verify \
    --insecure-ignore-tlog \
    --insecure-ignore-sct \
    --check-claims=true \
    --certificate-identity example.com \
    --certificate-oidc-issuer-regexp ".*" \
    --trusted-root trusted-roots.json \
    --new-bundle-format \
    --use-signed-timestamps \
    --experimental-oci11 \
    --allow-certificate-chain \
    {OCI IMAGE}

@kommendorkapten

Copy link
Copy Markdown
Member

Sorry for a late response but having an explicit parameter --allow-certificate-chain seems good to me, as that could be implemented in sigstore-go as a WithBundleIntermediate option to be passed to the verifier.

@Hayden-IO

Copy link
Copy Markdown
Contributor

LGTM to this approach as well. I've asked @yangkenneth to file an issue on sigstore/sigstore-conformance to start the discussion with other clients about whether or not we want this standardized across clients.

Comment thread pkg/bundle/bundle.go

@woodruffw woodruffw left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a maintainer of sigstore-go so my opinion isn't binding here, but IMO this would be a problematic addition: it effectively punches a hole in the v2/v3 bundle distinction for a use case that is best solved by using the v2 bundle format directly (since it isn't deprecated or obsolete, and is intended for this kind of X.509 topology).

(xref sigstore/sigstore-conformance#342 (comment) for related)

@yangkenneth

Copy link
Copy Markdown
Contributor Author

I'm not a maintainer of sigstore-go so my opinion isn't binding here, but IMO this would be a problematic addition: it effectively punches a hole in the v2/v3 bundle distinction for a use case that is best solved by using the v2 bundle format directly (since it isn't deprecated or obsolete, and is intended for this kind of X.509 topology).

(xref sigstore/sigstore-conformance#342 (comment) for related)

Appreciate the insight @woodruffw; the blocker that I originally ran into was because the current release for cosign does not allow bundle versions under v0.3.

This seems to leave us with a few potential paths forward:

  1. Modify sigstore/cosign to accept v0.2 bundles; however this raises the question about long-term trajectory. If there are protobuf changes for the Sigstore bundle this becomes a breaking change where implementations using certificate chains would not be able to leverage the latest spec.
  2. Allow v0.3 bundles to optionally contain certificate chains through an explicit opt-in flag. An comment would need to be made within the protobuf spec to allow certificate chains in v0.3.

Would appreciate hearing both your and @Hayden-IO's opinions here on what the best path forward is.

@woodruffw

Copy link
Copy Markdown
Member

Yeah, I think option 1 probably makes the most sense.

Allow v0.3 bundles to optionally contain certificate chains through an explicit opt-in flag. An comment would need to be made within the protobuf spec to allow certificate chains in v0.3.

This would effectively be a significant semantic change, which IMO would mean it would need to be v4 instead of v3. The protobuf-specs themselves also don't proscribe things like opt-in flags, so I think this would be a bit of a mismatch.

@Hayden-IO

Copy link
Copy Markdown
Contributor

I'm going to move the conversation back over to conformance to make sure we can get some of the other clients to chime in as well, we'll hold off on making changes to sigstore-go until we have some consensus.

@Hayden-IO

Copy link
Copy Markdown
Contributor

@yangkenneth Would you be able to make the update to sigstore/cosign#4614 that demonstrates how this will be used? It'd be helpful to review both this PR and Cosign at the same time. You can temporarily replace the go.mod package reference for sigstore-go to your fork so it compiles.

As discussed in the client meeting, we'll allow this with the proposed flag.

@yangkenneth
yangkenneth force-pushed the yangkenneth/certificate-chain-verification branch from f514db4 to 698232f Compare May 1, 2026 10:05
@yangkenneth
yangkenneth marked this pull request as draft May 1, 2026 10:06
@Hayden-IO

Copy link
Copy Markdown
Contributor

@yangkenneth just let me know when this PR and the Cosign PR are ready for review!

Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
@yangkenneth
yangkenneth force-pushed the yangkenneth/certificate-chain-verification branch from f0661d0 to 80344ca Compare June 2, 2026 01:21
@yangkenneth
yangkenneth marked this pull request as ready for review June 2, 2026 04:46
@yangkenneth

Copy link
Copy Markdown
Contributor Author

@yangkenneth just let me know when this PR and the Cosign PR are ready for review!

@Hayden-IO ready for a review whenever you have capacity; have the associated changes in sigstore/cosign#4614 which should work if you replace the sigstore-go package with this build. Let me know if you have any questions but thanks again for the help here!

Comment thread pkg/bundle/bundle.go Outdated
Comment thread pkg/bundle/bundle_test.go Outdated
Comment thread pkg/bundle/bundle.go Outdated
if err != nil {
return nil, ErrValidationError(err)
}
if intermediate.IsCA {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have a check here to not include root certificates (self signed) as that's prohibited by the spec. Possibly even reject the bundle. What do you think @Hayden-IO ?

https://github.com/sigstore/protobuf-specs/blob/4a31a816c74309e66a4c037c7e20f2500a588f8a/protos/sigstore_bundle.proto#L71

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to rejecting the bundle if it includes self-signed certificates.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made some changes where during the generation of the bundle we fail if a self-signed certificate exists but during verification we ignore based on the protobuf-spec to maintain backwards compatibility.

Comment thread pkg/sign/signer.go
if opts.CertificateProvider != nil {
switch {
case opts.CertificateChainProvider != nil:
certificateChain, err := opts.CertificateChainProvider.GetCertificateChain(opts.Context, keypair, opts.CertificateProviderOptions)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure here that the root cert is not included?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provider returns the full certificate chain, but based on the protobuf-spec and the points you and @Hayden-IO raised where we should not include any self-signed CA within the bundle I've added a check to support this pattern.

Comment thread pkg/verify/certificate.go
chains, err := ca.Verify(leafCert, observerTimestamp)
caToVerify := ca
if fca, ok := ca.(*root.FulcioCertificateAuthority); ok && len(intermediates) > 0 {
withIntermediates := *fca

@kommendorkapten kommendorkapten Jun 2, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit but this assignment feels unnecessary?

Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
@yangkenneth

Copy link
Copy Markdown
Contributor Author

@Hayden-IO @kommendorkapten made some updates based on the feedback, seeing if you al have another change to review so we can get this pushed through. Thanks!

kommendorkapten
kommendorkapten previously approved these changes Jun 8, 2026

@kommendorkapten kommendorkapten left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks! Waiting for @Hayden-IO to take a look as well.

@yangkenneth

Copy link
Copy Markdown
Contributor Author

Looks good, thanks! Waiting for @Hayden-IO to take a look as well.

Hey @Hayden-IO 👋 just bumping this if you have some time to review, thanks!

@Hayden-IO

Copy link
Copy Markdown
Contributor

yep, it's on my plate, sorry for the delay! will look before eow

@Hayden-IO Hayden-IO left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appreciate the patience in getting this reviewed! Just a few small comments, otherwise LGTM. Can you also rebase?

Comment thread pkg/bundle/bundle.go Outdated
if err != nil {
return nil, ErrValidationError(err)
}
if intermediate.IsCA && !verify.IsSelfSigned(intermediate) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this check throw an error rather than silently skip the certificate? Otherwise, an invalid chain might be constructed.

Comment thread pkg/bundle/options.go Outdated
@@ -0,0 +1,27 @@
// Copyright 2023 The Sigstore Authors.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Copyright 2023 The Sigstore Authors.
// Copyright 2026 The Sigstore Authors.

Comment thread pkg/verify/certificate.go Outdated
return nil, errors.New("leaf certificate verification failed")
}

func IsSelfSigned(certificate *x509.Certificate) bool {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this function be under internal in a new package? Want to avoid exporting any additional functions.

Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>
@yangkenneth

Copy link
Copy Markdown
Contributor Author

Appreciate the patience in getting this reviewed! Just a few small comments, otherwise LGTM. Can you also rebase?

Thanks for the review @Hayden-IO! Addressed your comments (08626ba) and tested the changes as well.

@yangkenneth
yangkenneth requested a review from Hayden-IO July 6, 2026 02:48
Comment thread pkg/bundle/bundle.go
}
// protobuf-specs does not allow signers to include self-signed certificates,
// but allows verifiers to tolerate non-compliant bundles for backwards compatibility.
if certificate.IsSelfSigned(intermediate) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why should this not return an error as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I recall correctly v0.2 and older bundles can contain self-signed certificates within the protobuf bundle. Only within v0.3 did the requirements tighten and self-signed certificates cannot be included within the bundle during the signing process.

If we have an enforcement where allowCertificateChain can only be used for v0.3+ then throwing an error here should be fine, otherwise I added this for backwards compatibility in case older versions can use this path as well. Let me know if you want me to add an explicit error here since the expectation should be that we use this verification path for v0.3+ bundle versions.

https://github.com/sigstore/protobuf-specs/blob/4a31a816c74309e66a4c037c7e20f2500a588f8a/protos/sigstore_bundle.proto#L75-L79

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-signed would be fine but only if it were at the end of the chain, which is what the comment was referring to iirc. One of the related issues is that the trust root chain really shouldn't be a chain, it should be a bundle of certificates with chain building being the responsibility of the verification library, not the client's responsibility, at which point we could just skip over superfluous certs.

Anywho, let's constrain this just for v0.3+, since that is the current bundle revision and what the current library will only produce. We can revisit this if it comes up that someone wants to use an older version, though I doubt it will.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good! Added the enforcement and validation check c5fd166 👍

Signed-off-by: Kenneth Yang <kenneth.yang@coinbase.com>

@Hayden-IO Hayden-IO left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all your work on this! Will get the Cosign change in much faster.

@Hayden-IO

Copy link
Copy Markdown
Contributor

@woodruffw Did you have anything you wanted to add, since you had left a review?

@Hayden-IO
Hayden-IO requested a review from kommendorkapten July 6, 2026 05:06

@kommendorkapten kommendorkapten left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@Hayden-IO
Hayden-IO merged commit cbf26e4 into sigstore:main Jul 6, 2026
12 of 13 checks passed
@yangkenneth

Copy link
Copy Markdown
Contributor Author

@Hayden-IO @kommendorkapten thank you both for the reviews and getting this through! Would it be possible to cut out a new release so I can reference this version within the follow-up sigstore/cosign changes?

@Hayden-IO

Copy link
Copy Markdown
Contributor

yep, will cut v1.2.2 momentarily. feel free to use the latest commit hash for now in the pr

@Hayden-IO

Copy link
Copy Markdown
Contributor

https://github.com/sigstore/sigstore-go/releases/tag/v1.2.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants