Skip to content

Commit 9c851da

Browse files
committed
pkg/verify: try to verify some key types with multiple algos
Signed-off-by: Riccardo Schirone <[email protected]>
1 parent 16c9a0b commit 9c851da

File tree

1 file changed

+81
-7
lines changed

1 file changed

+81
-7
lines changed

pkg/verify/signature.go

+81-7
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,20 @@ import (
1818
"bytes"
1919
"context"
2020
"crypto"
21+
"crypto/ecdsa"
22+
"crypto/elliptic"
23+
"crypto/x509"
2124
"encoding/hex"
2225
"errors"
2326
"fmt"
2427
"hash"
2528
"io"
29+
"os"
2630
"slices"
2731

2832
in_toto "github.com/in-toto/attestation/go/v1"
2933
"github.com/secure-systems-lab/go-securesystemslib/dsse"
34+
v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1"
3035
"github.com/sigstore/sigstore-go/pkg/root"
3136
"github.com/sigstore/sigstore/pkg/signature"
3237
sigdsse "github.com/sigstore/sigstore/pkg/signature/dsse"
@@ -104,15 +109,84 @@ func VerifySignatureWithArtifactDigests(sigContent SignatureContent, verificatio
104109
return verifyEnvelopeWithArtifactDigests(verifier, envelope, digests)
105110
}
106111

112+
// compatVerifier is a signature.Verifier that tries multiple verifiers
113+
// and returns nil if any of them verify the signature. This is used to
114+
// verify signatures that were generated with old clients that used SHA256
115+
// for ECDSA P384/P521 keys.
116+
type compatVerifier struct {
117+
verifiers []signature.Verifier
118+
}
119+
120+
func (v *compatVerifier) VerifySignature(signature, message io.Reader, opts ...signature.VerifyOption) error {
121+
for idx, verifier := range v.verifiers {
122+
if idx != 0 {
123+
fmt.Fprint(os.Stderr, "Failed to verify signature with default verifier, trying compatibility verifier\n")
124+
// Reset the signature and message readers to the beginning so they can be reused
125+
seeker, ok := signature.(io.Seeker)
126+
if ok {
127+
seeker.Seek(0, 0)
128+
}
129+
seeker, ok = message.(io.Seeker)
130+
if ok {
131+
seeker.Seek(0, 0)
132+
}
133+
}
134+
err := verifier.VerifySignature(signature, message, opts...)
135+
if err == nil {
136+
return nil
137+
}
138+
}
139+
return fmt.Errorf("no compatible verifier found")
140+
}
141+
142+
func (v *compatVerifier) PublicKey(opts ...signature.PublicKeyOption) (crypto.PublicKey, error) {
143+
return v.verifiers[0].PublicKey(opts...)
144+
}
145+
146+
func compatSignatureVerifier(leafCert *x509.Certificate) (signature.Verifier, error) {
147+
// LoadDefaultSigner/Verifier functions accept a few options to select
148+
// the default signer/verifier when there are ambiguities, like for
149+
// ED25519 keys, which could be used with PureEd25519 or Ed25519ph.
150+
//
151+
// Pass `WithED25519ph()` to select Ed25519ph by default, when ED25519
152+
// key is found, because for hashedrekord entries this is the only option.
153+
defaultOpts := []signature.LoadOption{options.WithED25519ph()}
154+
155+
verifiers := make([]signature.Verifier, 0)
156+
verifier, err := signature.LoadDefaultVerifier(leafCert.PublicKey, defaultOpts...)
157+
if err != nil {
158+
return nil, err
159+
}
160+
verifiers = append(verifiers, verifier)
161+
162+
// Add a compatibility verifier for ECDSA P384/P521, because we still want
163+
// to verify signatures generated with old clients that used SHA256
164+
switch leafCert.PublicKey.(type) {
165+
case *ecdsa.PublicKey:
166+
var algorithmDetails signature.AlgorithmDetails
167+
switch leafCert.PublicKey.(*ecdsa.PublicKey).Curve {
168+
case elliptic.P384():
169+
algorithmDetails, err = signature.GetAlgorithmDetails(v1.PublicKeyDetails_PKIX_ECDSA_P384_SHA_256)
170+
case elliptic.P521():
171+
algorithmDetails, err = signature.GetAlgorithmDetails(v1.PublicKeyDetails_PKIX_ECDSA_P521_SHA_256)
172+
default:
173+
return verifier, nil
174+
}
175+
if err != nil {
176+
return nil, err
177+
}
178+
verifier, err = signature.LoadVerifierFromAlgorithmDetails(leafCert.PublicKey, algorithmDetails, defaultOpts...)
179+
if err != nil {
180+
return nil, err
181+
}
182+
verifiers = append(verifiers, verifier)
183+
}
184+
return &compatVerifier{verifiers: verifiers}, nil
185+
}
186+
107187
func getSignatureVerifier(verificationContent VerificationContent, tm root.TrustedMaterial) (signature.Verifier, error) {
108188
if leafCert := verificationContent.Certificate(); leafCert != nil {
109-
// LoadDefaultSigner/Verifier functions accept a few options to select
110-
// the default signer/verifier when there are ambiguities, like for
111-
// ED25519 keys, which could be used with PureEd25519 or Ed25519ph.
112-
//
113-
// Pass `WithED25519ph()` to select Ed25519ph by default, when ED25519
114-
// key is found, because for hashedrekord entries this is the only option.
115-
return signature.LoadDefaultVerifier(leafCert.PublicKey, options.WithED25519ph())
189+
return compatSignatureVerifier(leafCert)
116190
} else if pk := verificationContent.PublicKey(); pk != nil {
117191
return tm.PublicKeyVerifier(pk.Hint())
118192
}

0 commit comments

Comments
 (0)