@@ -21,7 +21,11 @@ import (
2121 "encoding/base64"
2222 "errors"
2323 "fmt"
24+ "math/big"
25+ "strings"
2426
27+ "github.com/digitorus/pkcs7"
28+ "github.com/digitorus/timestamp"
2529 "github.com/sigstore/sigstore-go/pkg/bundle"
2630 "github.com/sigstore/sigstore-go/pkg/verify"
2731 "github.com/sigstore/timestamp-authority/v2/pkg/verification"
4347 // ErrNoTSARootsConfigured indicates the bundle contains signed timestamps
4448 // but no TSA trust roots are configured on the server.
4549 ErrNoTSARootsConfigured = errors .New ("no TSA trust roots configured" )
50+
51+ // ErrTSASignerNotTrusted indicates the timestamp was signed by a certificate
52+ // that belongs to none of the configured timestamp authorities. Upstream TSAs
53+ // rotate their responder certificates without notice, so this points at a
54+ // pinned chain that has fallen behind, not at a faulty attestation.
55+ ErrTSASignerNotTrusted = errors .New ("TSA response signer is not a configured timestamp authority" )
4656)
4757
58+ // IsTrustConfigError reports whether err is a timestamp verification failure
59+ // attributable to the TSA trust configuration rather than to the attestation
60+ // itself. Such a failure must not reject an incoming attestation: the signature
61+ // is verified independently, and verification is recomputed on every read, so
62+ // the outcome self-heals once the configuration catches up with the upstream TSA.
63+ func IsTrustConfigError (err error ) bool {
64+ return errors .Is (err , ErrNoTSARootsConfigured ) || errors .Is (err , ErrTSASignerNotTrusted )
65+ }
66+
4867func VerifyTimestamps (sb * bundle.Bundle , tr * TrustedRoot ) error {
4968 signedTimestamps , err := sb .Timestamps ()
5069 if err != nil {
@@ -91,11 +110,25 @@ func VerifyTimestamps(sb *bundle.Bundle, tr *TrustedRoot) error {
91110}
92111
93112// verifyTimestamp tries to verify a single signed timestamp against every
94- // configured TSA. Returns the error from the last attempted TSA on failure.
113+ // configured TSA. Returns the error from the last attempted TSA on failure, or
114+ // ErrTSASignerNotTrusted when the response was signed by none of them.
95115func verifyTimestamp (st []byte , sigBytes []byte , vc verify.VerificationContent , tr * TrustedRoot ) error {
116+ // Chainloop's timestamp requests do not ask for certificates, so the response
117+ // carries none and the pinned leaf is injected as the only candidate signer.
118+ // Knowing up front which authority actually signed keeps a rotated upstream
119+ // responder distinguishable from a response we should reject.
120+ signers := tsrSigners (st )
121+
96122 var lastErr error
97- for _ , tsa := range tr .TimestampAuthorities {
123+ var skipped []string
124+ for name , tsa := range tr .TimestampAuthorities {
98125 tsaCert := tsa [0 ]
126+ if ! signedByCert (signers , tsaCert ) {
127+ skipped = append (skipped , fmt .Sprintf ("%q (expected leaf %q, serial %s)" ,
128+ name , tsaCert .Subject .CommonName , tsaCert .SerialNumber ))
129+ continue
130+ }
131+
99132 var roots []* x509.Certificate
100133 var intermediates []* x509.Certificate
101134 if len (tsa ) > 1 {
@@ -127,5 +160,61 @@ func verifyTimestamp(st []byte, sigBytes []byte, vc verify.VerificationContent,
127160
128161 return nil
129162 }
163+
164+ // No authority signed this response: our pinned chains are behind the
165+ // upstream TSA rather than the response being at fault.
166+ if lastErr == nil && len (skipped ) > 0 {
167+ return fmt .Errorf ("%w: tried %s" , ErrTSASignerNotTrusted , strings .Join (skipped , ", " ))
168+ }
169+
130170 return lastErr
131171}
172+
173+ // tsrSignerID identifies the certificate that signed an RFC3161 response, as
174+ // carried in the PKCS#7 SignerInfo.
175+ type tsrSignerID struct {
176+ rawIssuer []byte
177+ serial * big.Int
178+ }
179+
180+ // tsrSigners returns the identities of the certificates that signed the RFC3161
181+ // response. A nil result means the response could not be parsed; callers must
182+ // then treat every candidate as a possible signer so the underlying verifier
183+ // produces the authoritative error.
184+ func tsrSigners (st []byte ) []tsrSignerID {
185+ ts , err := timestamp .ParseResponse (st )
186+ if err != nil {
187+ return nil
188+ }
189+
190+ p7 , err := pkcs7 .Parse (ts .RawToken )
191+ if err != nil || len (p7 .Signers ) == 0 {
192+ return nil
193+ }
194+
195+ signers := make ([]tsrSignerID , 0 , len (p7 .Signers ))
196+ for _ , signer := range p7 .Signers {
197+ signers = append (signers , tsrSignerID {
198+ rawIssuer : signer .IssuerAndSerialNumber .IssuerName .FullBytes ,
199+ serial : signer .IssuerAndSerialNumber .SerialNumber ,
200+ })
201+ }
202+
203+ return signers
204+ }
205+
206+ // signedByCert reports whether cert is one of the given signers. An unknown
207+ // signer set (a response we could not parse) matches every certificate.
208+ func signedByCert (signers []tsrSignerID , cert * x509.Certificate ) bool {
209+ if signers == nil {
210+ return true
211+ }
212+
213+ for _ , signer := range signers {
214+ if signer .serial != nil && cert .SerialNumber .Cmp (signer .serial ) == 0 && bytes .Equal (cert .RawIssuer , signer .rawIssuer ) {
215+ return true
216+ }
217+ }
218+
219+ return false
220+ }
0 commit comments