From 7f0717d599650b55e03914f134b2c541dca6f82b Mon Sep 17 00:00:00 2001 From: Riccardo Schirone Date: Wed, 17 Jan 2024 18:12:34 +0100 Subject: [PATCH 1/5] Used LoadOptions for more flexibility * Use ED25519ph algorithm with sign/verify-blob commands Signed-off-by: Riccardo Schirone Signed-off-by: William Woodruff --- cmd/cosign/cli/sign/sign.go | 32 ++- cmd/cosign/cli/sign/sign_blob.go | 52 ++++- cmd/cosign/cli/verify/verify.go | 16 +- cmd/cosign/cli/verify/verify_blob.go | 13 +- .../cli/verify/verify_blob_attestation.go | 3 +- go.mod | 14 +- go.sum | 52 ++--- internal/pkg/cosign/common.go | 17 +- internal/pkg/cosign/common_test.go | 3 +- internal/pkg/cosign/rekor/signer.go | 15 +- internal/pkg/cosign/rekor/signer_test.go | 2 +- pkg/cosign/keys.go | 15 +- pkg/cosign/tlog.go | 63 +++++- pkg/cosign/verify.go | 188 +++++++++++++----- pkg/signature/keys.go | 47 ++++- 15 files changed, 382 insertions(+), 150 deletions(-) diff --git a/cmd/cosign/cli/sign/sign.go b/cmd/cosign/cli/sign/sign.go index 122aaa3339b..348a985e03f 100644 --- a/cmd/cosign/cli/sign/sign.go +++ b/cmd/cosign/cli/sign/sign.go @@ -138,7 +138,12 @@ func SignCmd(ro *options.RootOptions, ko options.KeyOpts, signOpts options.SignO ctx, cancel := context.WithTimeout(context.Background(), ro.Timeout) defer cancel() - sv, err := SignerFromKeyOpts(ctx, signOpts.Cert, signOpts.CertChain, ko) + svOptions := []signature.LoadOption{ + signatureoptions.WithHash(crypto.SHA256), + signatureoptions.WithED25519ph(), + } + + sv, err := signerFromKeyOptsWithSVOpts(ctx, signOpts.Cert, signOpts.CertChain, ko, svOptions...) if err != nil { return fmt.Errorf("getting signer: %w", err) } @@ -261,7 +266,12 @@ func signDigest(ctx context.Context, digest name.Digest, payload []byte, ko opti if err != nil { return err } - s = irekor.NewSigner(s, rClient) + + hashAlgorithm, err := getHashAlgorithmFromSignerVerifier(sv) + if err != nil { + return err + } + s = irekor.NewSigner(s, rClient, hashAlgorithm) } ociSig, _, err := s.Sign(ctx, bytes.NewReader(payload)) @@ -391,8 +401,8 @@ func signerFromSecurityKey(ctx context.Context, keySlot string) (*SignerVerifier }, nil } -func signerFromKeyRef(ctx context.Context, certPath, certChainPath, keyRef string, passFunc cosign.PassFunc) (*SignerVerifier, error) { - k, err := sigs.SignerVerifierFromKeyRef(ctx, keyRef, passFunc) +func signerFromKeyRef(ctx context.Context, certPath, certChainPath, keyRef string, passFunc cosign.PassFunc, opts ...signature.LoadOption) (*SignerVerifier, error) { + k, err := sigs.SignerVerifierFromKeyRefWithOpts(ctx, keyRef, passFunc, opts...) if err != nil { return nil, fmt.Errorf("reading key: %w", err) } @@ -521,12 +531,12 @@ func signerFromKeyRef(ctx context.Context, certPath, certChainPath, keyRef strin return certSigner, nil } -func signerFromNewKey() (*SignerVerifier, error) { +func signerFromNewKey(svOpts ...signature.LoadOption) (*SignerVerifier, error) { privKey, err := cosign.GeneratePrivateKey() if err != nil { return nil, fmt.Errorf("generating cert: %w", err) } - sv, err := signature.LoadECDSASignerVerifier(privKey, crypto.SHA256) + sv, err := signature.LoadSignerVerifierWithOpts(privKey, svOpts...) if err != nil { return nil, err } @@ -559,7 +569,7 @@ func keylessSigner(ctx context.Context, ko options.KeyOpts, sv *SignerVerifier) }, nil } -func SignerFromKeyOpts(ctx context.Context, certPath string, certChainPath string, ko options.KeyOpts) (*SignerVerifier, error) { +func signerFromKeyOptsWithSVOpts(ctx context.Context, certPath string, certChainPath string, ko options.KeyOpts, svOpts ...signature.LoadOption) (*SignerVerifier, error) { var sv *SignerVerifier var err error genKey := false @@ -567,11 +577,11 @@ func SignerFromKeyOpts(ctx context.Context, certPath string, certChainPath strin case ko.Sk: sv, err = signerFromSecurityKey(ctx, ko.Slot) case ko.KeyRef != "": - sv, err = signerFromKeyRef(ctx, certPath, certChainPath, ko.KeyRef, ko.PassFunc) + sv, err = signerFromKeyRef(ctx, certPath, certChainPath, ko.KeyRef, ko.PassFunc, svOpts...) default: genKey = true ui.Infof(ctx, "Generating ephemeral keys...") - sv, err = signerFromNewKey() + sv, err = signerFromNewKey(svOpts...) } if err != nil { return nil, err @@ -584,6 +594,10 @@ func SignerFromKeyOpts(ctx context.Context, certPath string, certChainPath strin return sv, nil } +func SignerFromKeyOpts(ctx context.Context, certPath string, certChainPath string, ko options.KeyOpts) (*SignerVerifier, error) { + return signerFromKeyOptsWithSVOpts(ctx, certPath, certChainPath, ko) +} + type SignerVerifier struct { Cert []byte Chain []byte diff --git a/cmd/cosign/cli/sign/sign_blob.go b/cmd/cosign/cli/sign/sign_blob.go index b60db5b7a28..e7a4467daad 100644 --- a/cmd/cosign/cli/sign/sign_blob.go +++ b/cmd/cosign/cli/sign/sign_blob.go @@ -17,7 +17,10 @@ package sign import ( "context" - "crypto/sha256" + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rsa" "encoding/base64" "encoding/json" "fmt" @@ -33,9 +36,28 @@ import ( "github.com/sigstore/cosign/v2/pkg/cosign" cbundle "github.com/sigstore/cosign/v2/pkg/cosign/bundle" "github.com/sigstore/sigstore/pkg/cryptoutils" + "github.com/sigstore/sigstore/pkg/signature" signatureoptions "github.com/sigstore/sigstore/pkg/signature/options" ) +func getHashAlgorithmFromSignerVerifier(sv *SignerVerifier) (crypto.Hash, error) { + publicKey, err := sv.SignerVerifier.PublicKey() + if err != nil { + return crypto.Hash(0), err + } + + switch publicKey.(type) { + case *ecdsa.PublicKey: + return crypto.SHA256, nil + case *rsa.PublicKey: + return crypto.SHA256, nil + case ed25519.PublicKey: + return crypto.SHA512, nil + default: + return crypto.Hash(0), fmt.Errorf("unsupported public key type") + } +} + // nolint func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string, b64 bool, outputSignature string, outputCertificate string, tlogUpload bool) ([]byte, error) { var payload internal.HashReader @@ -44,26 +66,36 @@ func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string ctx, cancel := context.WithTimeout(context.Background(), ro.Timeout) defer cancel() + svOptions := []signature.LoadOption{ + signatureoptions.WithHash(crypto.SHA256), + signatureoptions.WithED25519ph(), + } + + sv, err := signerFromKeyOptsWithSVOpts(ctx, "", "", ko, svOptions...) + if err != nil { + return nil, err + } + defer sv.Close() + + hashAlgorithm, err := getHashAlgorithmFromSignerVerifier(sv) + if err != nil { + return nil, err + } + if payloadPath == "-" { - payload = internal.NewHashReader(os.Stdin, sha256.New()) + payload = internal.NewHashReader(os.Stdin, hashAlgorithm) } else { ui.Infof(ctx, "Using payload from: %s", payloadPath) f, err := os.Open(filepath.Clean(payloadPath)) if err != nil { return nil, err } - payload = internal.NewHashReader(f, sha256.New()) + payload = internal.NewHashReader(f, hashAlgorithm) } if err != nil { return nil, err } - sv, err := SignerFromKeyOpts(ctx, "", "", ko) - if err != nil { - return nil, err - } - defer sv.Close() - sig, err := sv.SignMessage(&payload, signatureoptions.WithContext(ctx)) if err != nil { return nil, fmt.Errorf("signing blob: %w", err) @@ -123,7 +155,7 @@ func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string if err != nil { return nil, err } - entry, err := cosign.TLogUpload(ctx, rekorClient, sig, &payload, rekorBytes) + entry, err := cosign.TLogUploadWithCustomHash(ctx, rekorClient, sig, &payload, rekorBytes) if err != nil { return nil, err } diff --git a/cmd/cosign/cli/verify/verify.go b/cmd/cosign/cli/verify/verify.go index de91d9229a8..6b8ef256ab6 100644 --- a/cmd/cosign/cli/verify/verify.go +++ b/cmd/cosign/cli/verify/verify.go @@ -44,6 +44,7 @@ import ( sigs "github.com/sigstore/cosign/v2/pkg/signature" "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/signature" + signatureoptions "github.com/sigstore/sigstore/pkg/signature/options" "github.com/sigstore/sigstore/pkg/signature/payload" ) @@ -214,11 +215,16 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { } } + svOpts := []signature.LoadOption{ + signatureoptions.WithHash(crypto.SHA256), + signatureoptions.WithED25519ph(), + } + // Keys are optional! var pubKey signature.Verifier switch { case keyRef != "": - pubKey, err = sigs.PublicKeyFromKeyRefWithHashAlgo(ctx, keyRef, c.HashAlgorithm) + pubKey, err = sigs.PublicKeyFromKeyRefWithOpts(ctx, keyRef, svOpts...) if err != nil { return fmt.Errorf("loading public key: %w", err) } @@ -251,7 +257,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { if err != nil { return fmt.Errorf("getting Fulcio intermediates: %w", err) } - pubKey, err = cosign.ValidateAndUnpackCert(cert, co) + pubKey, err = cosign.ValidateAndUnpackCertWithOpts(cert, co, cosign.WithSignerVerifierOptions(svOpts...)) if err != nil { return err } @@ -261,7 +267,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { if err != nil { return err } - pubKey, err = cosign.ValidateAndUnpackCertWithChain(cert, chain, co) + pubKey, err = cosign.ValidateAndUnpackCertWithOpts(cert, co, cosign.WithChain(chain), cosign.WithSignerVerifierOptions(svOpts...)) if err != nil { return err } @@ -286,7 +292,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { for _, img := range images { if c.LocalImage { - verified, bundleVerified, err := cosign.VerifyLocalImageSignatures(ctx, img, co) + verified, bundleVerified, err := cosign.VerifyLocalImageSignaturesWithOpts(ctx, img, co, svOpts...) if err != nil { return err } @@ -302,7 +308,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { return fmt.Errorf("resolving attachment type %s for image %s: %w", c.Attachment, img, err) } - verified, bundleVerified, err := cosign.VerifyImageSignatures(ctx, ref, co) + verified, bundleVerified, err := cosign.VerifyImageSignaturesWithOpts(ctx, ref, co, svOpts...) if err != nil { return cosignError.WrapError(err) } diff --git a/cmd/cosign/cli/verify/verify_blob.go b/cmd/cosign/cli/verify/verify_blob.go index bd172a3aae1..c6eba84eb22 100644 --- a/cmd/cosign/cli/verify/verify_blob.go +++ b/cmd/cosign/cli/verify/verify_blob.go @@ -42,6 +42,8 @@ import ( sigs "github.com/sigstore/cosign/v2/pkg/signature" "github.com/sigstore/sigstore/pkg/cryptoutils" + "github.com/sigstore/sigstore/pkg/signature" + signatureoptions "github.com/sigstore/sigstore/pkg/signature/options" ) func isb64(data []byte) bool { @@ -171,10 +173,15 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error { } } + svOpts := []signature.LoadOption{ + signatureoptions.WithHash(crypto.SHA256), + signatureoptions.WithED25519ph(), + } + // Keys are optional! switch { case c.KeyRef != "": - co.SigVerifier, err = sigs.PublicKeyFromKeyRef(ctx, c.KeyRef) + co.SigVerifier, err = sigs.PublicKeyFromKeyRefWithOpts(ctx, c.KeyRef, svOpts...) if err != nil { return fmt.Errorf("loading public key: %w", err) } @@ -219,7 +226,7 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error { bundleCert, err := loadCertFromPEM(certBytes) if err != nil { // check if cert is actually a public key - co.SigVerifier, err = sigs.LoadPublicKeyRaw(certBytes, crypto.SHA256) + co.SigVerifier, err = sigs.LoadPublicKeyRawWithOpts(certBytes, svOpts...) if err != nil { return fmt.Errorf("loading verifier from bundle: %w", err) } @@ -298,7 +305,7 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error { if err != nil { return err } - if _, err = cosign.VerifyBlobSignature(ctx, signature, co); err != nil { + if _, err = cosign.VerifyBlobSignatureWithOpts(ctx, signature, co, svOpts...); err != nil { return err } diff --git a/cmd/cosign/cli/verify/verify_blob_attestation.go b/cmd/cosign/cli/verify/verify_blob_attestation.go index 0d9c9b77a61..50e29287362 100644 --- a/cmd/cosign/cli/verify/verify_blob_attestation.go +++ b/cmd/cosign/cli/verify/verify_blob_attestation.go @@ -18,7 +18,6 @@ package verify import ( "context" "crypto" - "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/hex" @@ -118,7 +117,7 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st } defer f.Close() - payload = internal.NewHashReader(f, sha256.New()) + payload = internal.NewHashReader(f, crypto.SHA256) if _, err := io.ReadAll(&payload); err != nil { return err } diff --git a/go.mod b/go.mod index 7ea1c6f94b0..9ecda5b9640 100644 --- a/go.mod +++ b/go.mod @@ -147,13 +147,13 @@ require ( github.com/go-jose/go-jose/v3 v3.0.2 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/analysis v0.22.0 // indirect + github.com/go-openapi/analysis v0.22.2 // indirect github.com/go-openapi/errors v0.21.0 // indirect github.com/go-openapi/jsonpointer v0.20.2 // indirect github.com/go-openapi/jsonreference v0.20.4 // indirect github.com/go-openapi/loads v0.21.5 // indirect github.com/go-openapi/spec v0.20.14 // indirect - github.com/go-openapi/validate v0.22.6 // indirect + github.com/go-openapi/validate v0.23.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect @@ -188,7 +188,7 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.2 // indirect + github.com/klauspost/compress v1.17.4 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/letsencrypt/boulder v0.0.0-20231026200631-000cd05d5491 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -251,9 +251,9 @@ require ( go.opentelemetry.io/otel/sdk v1.22.0 // indirect go.opentelemetry.io/otel/trace v1.23.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.26.0 // indirect + go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 // indirect - golang.org/x/mod v0.14.0 // indirect + golang.org/x/mod v0.15.0 // indirect golang.org/x/net v0.21.0 // indirect golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect @@ -263,7 +263,7 @@ require ( google.golang.org/genproto v0.0.0-20240205150955-31a09d347014 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 // indirect - google.golang.org/grpc v1.61.1 // indirect + google.golang.org/grpc v1.62.0 // indirect google.golang.org/protobuf v1.32.0 // indirect gopkg.in/go-jose/go-jose.v2 v2.6.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -276,3 +276,5 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) + +replace github.com/sigstore/rekor => github.com/trail-of-forks/rekor v0.0.0-20240228223424-d5141b63a1f2 diff --git a/go.sum b/go.sum index 15f7758b80b..7317e0c84b7 100644 --- a/go.sum +++ b/go.sum @@ -200,8 +200,8 @@ github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUK github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY= -github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= @@ -261,8 +261,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= -github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= +github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= @@ -290,8 +290,8 @@ github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/analysis v0.22.0 h1:wQ/d07nf78HNj4u+KiSY0sT234IAyePPbMgpUjUJQR0= -github.com/go-openapi/analysis v0.22.0/go.mod h1:acDnkkCI2QxIo8sSIPgmp1wUlRohV7vfGtAIVae73b0= +github.com/go-openapi/analysis v0.22.2 h1:ZBmNoP2h5omLKr/srIC9bfqrUGzT6g6gNv03HE9Vpj0= +github.com/go-openapi/analysis v0.22.2/go.mod h1:pDF4UbZsQTo/oNuRfAWWd4dAh4yuYf//LYorPTjrpvo= github.com/go-openapi/errors v0.21.0 h1:FhChC/duCnfoLj1gZ0BgaBmzhJC2SL/sJr8a2vAobSY= github.com/go-openapi/errors v0.21.0/go.mod h1:jxNTMUxRCKj65yb/okJGEtahVd7uvWnuWfj53bse4ho= github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= @@ -308,8 +308,8 @@ github.com/go-openapi/strfmt v0.22.0 h1:Ew9PnEYc246TwrEspvBdDHS4BVKXy/AOVsfqGDgA github.com/go-openapi/strfmt v0.22.0/go.mod h1:HzJ9kokGIju3/K6ap8jL+OlGAbjpSv27135Yr9OivU4= github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= -github.com/go-openapi/validate v0.22.6 h1:+NhuwcEYpWdO5Nm4bmvhGLW0rt1Fcc532Mu3wpypXfo= -github.com/go-openapi/validate v0.22.6/go.mod h1:eaddXSqKeTg5XpSmj1dYyFTK/95n/XHwcOY+BMxKMyM= +github.com/go-openapi/validate v0.23.0 h1:2l7PJLzCis4YUGEoW6eoQw3WhyM65WSIcjX6SQnlfDw= +github.com/go-openapi/validate v0.23.0/go.mod h1:EeiAZ5bmpSIOJV1WLfyYF9qp/B1ZgSaEpHTJHtN5cbE= github.com/go-piv/piv-go v1.11.0 h1:5vAaCdRTFSIW4PeqMbnsDlUZ7odMYWnHBDGdmtU/Zhg= github.com/go-piv/piv-go v1.11.0/go.mod h1:NZ2zmjVkfFaL/CF8cVQ/pXdXtuj110zEKGdJM6fJZZM= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= @@ -334,8 +334,8 @@ github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= -github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -467,8 +467,8 @@ github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= -github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -598,8 +598,8 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A= github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk= -github.com/sassoftware/relic/v7 v7.6.1 h1:O5s8ewCgq5QYNpv45dK4u6IpBmDM9RIcsbf/G1uXepQ= -github.com/sassoftware/relic/v7 v7.6.1/go.mod h1:NxwtWxWxlUa9as2qZi635Ye6bBT/tGnMALLq7dSfOOU= +github.com/sassoftware/relic/v7 v7.6.2 h1:rS44Lbv9G9eXsukknS4mSjIAuuX+lMq/FnStgmZlUv4= +github.com/sassoftware/relic/v7 v7.6.2/go.mod h1:kjmP0IBVkJZ6gXeAu35/KCEfca//+PKM6vTAsyDPY+k= github.com/secure-systems-lab/go-securesystemslib v0.8.0 h1:mr5An6X45Kb2nddcFlbmfHkLguCE9laoZCUzEEpIZXA= github.com/secure-systems-lab/go-securesystemslib v0.8.0/go.mod h1:UH2VZVuJfCYR8WgMlCU1uFsOUU+KeyrTWcSS73NBOzU= github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= @@ -608,8 +608,6 @@ github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= github.com/sigstore/fulcio v1.4.3 h1:9JcUCZjjVhRF9fmhVuz6i1RyhCc/EGCD7MOl+iqCJLQ= github.com/sigstore/fulcio v1.4.3/go.mod h1:BQPWo7cfxmJwgaHlphUHUpFkp5+YxeJes82oo39m5og= -github.com/sigstore/rekor v1.3.5 h1:QoVXcS7NppKY+rpbEFVHr4evGDZBBSh65X0g8PXoUkQ= -github.com/sigstore/rekor v1.3.5/go.mod h1:CWqOk/fmnPwORQmm7SyDgB54GTJizqobbZ7yOP1lvw8= github.com/sigstore/sigstore v1.8.2 h1:0Ttjcn3V0fVQXlYq7+oHaaHkGFIt3ywm7SF4JTU/l8c= github.com/sigstore/sigstore v1.8.2/go.mod h1:CHVcSyknCcjI4K2ZhS1SI28r0tcQyBlwtALG536x1DY= github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.2 h1:e0EtUcE7cqWBxxME7h6upA3EA0IR3EOE3F1t+WHOdTc= @@ -676,6 +674,8 @@ github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHT github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= +github.com/trail-of-forks/rekor v0.0.0-20240228223424-d5141b63a1f2 h1:+wqZFR+AeWfbOoOBt9n9aZsehtWEW8ZRjvhOH/0loBw= +github.com/trail-of-forks/rekor v0.0.0-20240228223424-d5141b63a1f2/go.mod h1:IJfmXO9Wku50wJ1DVvNNxOGHw4OU04oewxpvpXm00Ig= github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4= github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A= github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= @@ -710,8 +710,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zalando/go-keyring v0.2.2 h1:f0xmpYiSrHtSNAVgwip93Cg8tuF45HJM6rHq/A5RI/4= -github.com/zalando/go-keyring v0.2.2/go.mod h1:sI3evg9Wvpw3+n4SqplGSJUMwtDeROfD4nsFz4z9PG0= +github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms= +github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs= github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk= @@ -742,8 +742,8 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -771,8 +771,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -911,8 +911,8 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY= -google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= +google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -973,5 +973,5 @@ sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6Lv sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= -software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE= -software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ= +software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= +software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= diff --git a/internal/pkg/cosign/common.go b/internal/pkg/cosign/common.go index a1aa8ebc35f..44027402e24 100644 --- a/internal/pkg/cosign/common.go +++ b/internal/pkg/cosign/common.go @@ -15,6 +15,7 @@ package cosign import ( + "crypto" "errors" "hash" "io" @@ -39,14 +40,17 @@ func FileExists(filename string) (bool, error) { // HashReader hashes while it reads. type HashReader struct { - r io.Reader - h hash.Hash + r io.Reader + h hash.Hash + ch crypto.Hash } -func NewHashReader(r io.Reader, h hash.Hash) HashReader { +func NewHashReader(r io.Reader, ch crypto.Hash) HashReader { + h := ch.New() return HashReader{ - r: io.TeeReader(r, h), - h: h, + r: io.TeeReader(r, h), + h: h, + ch: ch, } } @@ -67,3 +71,6 @@ func (h *HashReader) BlockSize() int { return h.h.BlockSize() } // Write implements hash.Hash func (h *HashReader) Write(p []byte) (int, error) { return 0, errors.New("not implemented") } //nolint: revive + +// HashFunc implements cosign.NamedHash +func (h *HashReader) HashFunc() crypto.Hash { return h.ch } diff --git a/internal/pkg/cosign/common_test.go b/internal/pkg/cosign/common_test.go index 4a54109e435..4f38e07865c 100644 --- a/internal/pkg/cosign/common_test.go +++ b/internal/pkg/cosign/common_test.go @@ -17,6 +17,7 @@ package cosign import ( "bytes" + "crypto" "crypto/sha256" "io" "os" @@ -55,7 +56,7 @@ func Test_FileExists(t *testing.T) { func Test_HashReader(t *testing.T) { input := []byte("hello world") - r := NewHashReader(bytes.NewReader(input), sha256.New()) + r := NewHashReader(bytes.NewReader(input), crypto.SHA256) got, err := io.ReadAll(&r) if err != nil { diff --git a/internal/pkg/cosign/rekor/signer.go b/internal/pkg/cosign/rekor/signer.go index 2fa5e2595ef..612f65c6710 100644 --- a/internal/pkg/cosign/rekor/signer.go +++ b/internal/pkg/cosign/rekor/signer.go @@ -17,7 +17,6 @@ package rekor import ( "context" "crypto" - "crypto/sha256" "encoding/base64" "fmt" "io" @@ -49,7 +48,8 @@ func uploadToTlog(rekorBytes []byte, rClient *client.Rekor, upload tlogUploadFn) type signerWrapper struct { inner cosign.Signer - rClient *client.Rekor + rClient *client.Rekor + hashAlgorithm crypto.Hash } var _ cosign.Signer = (*signerWrapper)(nil) @@ -91,11 +91,11 @@ func (rs *signerWrapper) Sign(ctx context.Context, payload io.Reader) (oci.Signa } bundle, err := uploadToTlog(rekorBytes, rs.rClient, func(r *client.Rekor, b []byte) (*models.LogEntryAnon, error) { - checkSum := sha256.New() + checkSum := cosignv1.NewCryptoNamedHash(rs.hashAlgorithm) if _, err := checkSum.Write(payloadBytes); err != nil { return nil, err } - return cosignv1.TLogUpload(ctx, r, sigBytes, checkSum, b) + return cosignv1.TLogUploadWithCustomHash(ctx, r, sigBytes, checkSum, b) }) if err != nil { return nil, nil, err @@ -110,9 +110,10 @@ func (rs *signerWrapper) Sign(ctx context.Context, payload io.Reader) (oci.Signa } // NewSigner returns a `cosign.Signer` which uploads the signature to Rekor -func NewSigner(inner cosign.Signer, rClient *client.Rekor) cosign.Signer { +func NewSigner(inner cosign.Signer, rClient *client.Rekor, hashAlgorithm crypto.Hash) cosign.Signer { return &signerWrapper{ - inner: inner, - rClient: rClient, + inner: inner, + rClient: rClient, + hashAlgorithm: hashAlgorithm, } } diff --git a/internal/pkg/cosign/rekor/signer_test.go b/internal/pkg/cosign/rekor/signer_test.go index 5f3dfa02351..7d6adbd0d9a 100644 --- a/internal/pkg/cosign/rekor/signer_test.go +++ b/internal/pkg/cosign/rekor/signer_test.go @@ -56,7 +56,7 @@ func TestSigner(t *testing.T) { }}}, } - testSigner := NewSigner(payloadSigner, &mClient) + testSigner := NewSigner(payloadSigner, &mClient, crypto.SHA256) testPayload := "test payload" diff --git a/pkg/cosign/keys.go b/pkg/cosign/keys.go index 9adc22525f8..fb6debfe396 100644 --- a/pkg/cosign/keys.go +++ b/pkg/cosign/keys.go @@ -206,6 +206,10 @@ func PemToECDSAKey(pemBytes []byte) (*ecdsa.PublicKey, error) { // TODO(jason): Move this to pkg/signature, the only place it's used, and unimport it. func LoadPrivateKey(key []byte, pass []byte) (signature.SignerVerifier, error) { + return LoadPrivateKeyWithOpts(key, pass) +} + +func LoadPrivateKeyWithOpts(key []byte, pass []byte, opts ...signature.LoadOption) (signature.SignerVerifier, error) { // Decrypt first p, _ := pem.Decode(key) if p == nil { @@ -224,14 +228,5 @@ func LoadPrivateKey(key []byte, pass []byte) (signature.SignerVerifier, error) { if err != nil { return nil, fmt.Errorf("parsing private key: %w", err) } - switch pk := pk.(type) { - case *rsa.PrivateKey: - return signature.LoadRSAPKCS1v15SignerVerifier(pk, crypto.SHA256) - case *ecdsa.PrivateKey: - return signature.LoadECDSASignerVerifier(pk, crypto.SHA256) - case ed25519.PrivateKey: - return signature.LoadED25519SignerVerifier(pk) - default: - return nil, errors.New("unsupported key type") - } + return signature.LoadSignerVerifierWithOpts(pk, opts...) } diff --git a/pkg/cosign/tlog.go b/pkg/cosign/tlog.go index 83d6f61f179..fd445837082 100644 --- a/pkg/cosign/tlog.go +++ b/pkg/cosign/tlog.go @@ -54,6 +54,24 @@ import ( // This is the rekor transparency log public key target name var rekorTargetStr = `rekor.pub` +type NamedHash interface { + hash.Hash + crypto.SignerOpts +} + +type CryptoNamedHash struct { + hash.Hash + hashType crypto.Hash +} + +func (h CryptoNamedHash) HashFunc() crypto.Hash { + return h.hashType +} + +func NewCryptoNamedHash(hashType crypto.Hash) NamedHash { + return CryptoNamedHash{Hash: hashType.New(), hashType: hashType} +} + // TransparencyLogPubKey contains the ECDSA verification key and the current status // of the key according to TUF metadata, whether it's active or expired. type TransparencyLogPubKey struct { @@ -169,9 +187,28 @@ func rekorPubsFromClient(rekorClient *client.Rekor) (*TrustedTransparencyLogPubK return &publicKeys, nil } +type SHA256NamedHash struct { + hash.Hash +} + +func (h SHA256NamedHash) HashFunc() crypto.Hash { + return crypto.SHA256 +} + +func WrapSHA256Hash(hash hash.Hash) NamedHash { + return SHA256NamedHash{Hash: hash} +} + // TLogUpload will upload the signature, public key and payload to the transparency log. func TLogUpload(ctx context.Context, rekorClient *client.Rekor, signature []byte, sha256CheckSum hash.Hash, pemBytes []byte) (*models.LogEntryAnon, error) { - re := rekorEntry(sha256CheckSum, signature, pemBytes) + cryptoChecksum := WrapSHA256Hash(sha256CheckSum) + return TLogUploadWithCustomHash(ctx, rekorClient, signature, cryptoChecksum, pemBytes) +} + +// TLogUploadWithCustomHash will upload the signature, public key and payload to +// the transparency log. Clients can use this to specify a custom hash function. +func TLogUploadWithCustomHash(ctx context.Context, rekorClient *client.Rekor, signature []byte, checksum NamedHash, pemBytes []byte) (*models.LogEntryAnon, error) { + re := rekorEntry(checksum, signature, pemBytes) returnVal := models.Hashedrekord{ APIVersion: swag.String(re.APIVersion()), Spec: re.HashedRekordObj, @@ -230,16 +267,26 @@ func doUpload(ctx context.Context, rekorClient *client.Rekor, pe models.Proposed return nil, errors.New("bad response from server") } -func rekorEntry(sha256CheckSum hash.Hash, signature, pubKey []byte) hashedrekord_v001.V001Entry { - // TODO: Signatures created on a digest using a hash algorithm other than SHA256 will fail - // upload right now. Plumb information on the hash algorithm used when signing from the - // SignerVerifier to use for the HashedRekordObj.Data.Hash.Algorithm. +func rekorEntryHashAlgorithm(checksum crypto.SignerOpts) string { + switch checksum.HashFunc() { + case crypto.SHA256: + return models.HashedrekordV001SchemaDataHashAlgorithmSha256 + case crypto.SHA384: + return models.HashedrekordV001SchemaDataHashAlgorithmSha384 + case crypto.SHA512: + return models.HashedrekordV001SchemaDataHashAlgorithmSha512 + default: + return models.HashedrekordV001SchemaDataHashAlgorithmSha256 + } +} + +func rekorEntry(checksum NamedHash, signature, pubKey []byte) hashedrekord_v001.V001Entry { return hashedrekord_v001.V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Data: &models.HashedrekordV001SchemaData{ Hash: &models.HashedrekordV001SchemaDataHash{ - Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256), - Value: swag.String(hex.EncodeToString(sha256CheckSum.Sum(nil))), + Algorithm: swag.String(rekorEntryHashAlgorithm(checksum)), + Value: swag.String(hex.EncodeToString(checksum.Sum(nil))), }, }, Signature: &models.HashedrekordV001SchemaSignature{ @@ -392,7 +439,7 @@ func proposedEntries(b64Sig string, payload, pubKey []byte) ([]models.ProposedEn } proposedEntry = []models.ProposedEntry{dsseEntry, intotoEntry} } else { - sha256CheckSum := sha256.New() + sha256CheckSum := NewCryptoNamedHash(crypto.SHA256) if _, err := sha256CheckSum.Write(payload); err != nil { return nil, err } diff --git a/pkg/cosign/verify.go b/pkg/cosign/verify.go index abd551d8028..613b6ea3d34 100644 --- a/pkg/cosign/verify.go +++ b/pkg/cosign/verify.go @@ -20,6 +20,7 @@ import ( "crypto" "crypto/ecdsa" "crypto/sha256" + "crypto/sha512" "crypto/x509" "encoding/asn1" "encoding/base64" @@ -165,6 +166,60 @@ type CheckOpts struct { ExperimentalOCI11 bool } +type validateCertOpts struct { + chain []*x509.Certificate + svOpts []signature.LoadOption + pool *x509.CertPool +} + +type ValidateAndUnpackCertOption func(*validateCertOpts) + +func WithChain(chain []*x509.Certificate) ValidateAndUnpackCertOption { + return func(o *validateCertOpts) { + o.chain = chain + } +} + +func WithPool(pool *x509.CertPool) ValidateAndUnpackCertOption { + return func(o *validateCertOpts) { + o.pool = pool + } +} + +func WithSignerVerifierOptions(svOpts ...signature.LoadOption) ValidateAndUnpackCertOption { + return func(o *validateCertOpts) { + o.svOpts = svOpts + } +} + +func makeValidateAndUnpackCertOpts(co *CheckOpts, opts ...ValidateAndUnpackCertOption) (*validateCertOpts, error) { + o := &validateCertOpts{} + for _, opt := range opts { + opt(o) + } + + // Pool Option has precedence over chain option + if o.pool == nil && o.chain != nil { + if len(o.chain) == 0 { + return nil, errors.New("no chain provided to validate certificate") + } + rootPool := x509.NewCertPool() + rootPool.AddCert(o.chain[len(o.chain)-1]) + co.RootCerts = rootPool + + subPool := x509.NewCertPool() + for _, c := range o.chain[:len(o.chain)-1] { + subPool.AddCert(c) + } + o.pool = subPool + } + // If no pool or chain is provided, use the one from the CheckOpts + if o.pool == nil { + o.pool = co.IntermediateCerts + } + return o, nil +} + // This is a substitutable signature verification function that can be used for verifying // attestations of blobs. type signatureVerificationFn func( @@ -221,14 +276,33 @@ func verifyOCISignature(ctx context.Context, verifier signature.Verifier, sig pa // certificate chains up to a trusted root using intermediate certificate chain coming from CheckOpts. // Optionally verifies the subject and issuer of the certificate. func ValidateAndUnpackCert(cert *x509.Certificate, co *CheckOpts) (signature.Verifier, error) { - return ValidateAndUnpackCertWithIntermediates(cert, co, co.IntermediateCerts) + return ValidateAndUnpackCertWithOpts(cert, co) } // ValidateAndUnpackCertWithIntermediates creates a Verifier from a certificate. Verifies that the // certificate chains up to a trusted root using intermediate cert passed as separate argument. // Optionally verifies the subject and issuer of the certificate. func ValidateAndUnpackCertWithIntermediates(cert *x509.Certificate, co *CheckOpts, intermediateCerts *x509.CertPool) (signature.Verifier, error) { - verifier, err := signature.LoadVerifier(cert.PublicKey, crypto.SHA256) + return ValidateAndUnpackCertWithOpts(cert, co, WithPool(intermediateCerts)) +} + +// ValidateAndUnpackCertWithChain creates a Verifier from a certificate. Verifies that the certificate +// chains up to the provided root. Chain should start with the parent of the certificate and end with the root. +// Optionally verifies the subject and issuer of the certificate. +func ValidateAndUnpackCertWithChain(cert *x509.Certificate, chain []*x509.Certificate, co *CheckOpts) (signature.Verifier, error) { + return ValidateAndUnpackCertWithOpts(cert, co, WithChain(chain)) +} + +// ValidateAndUnpackCertWithOpts creates a Verifier from a certificate. Verifies that the certificate +// chains up to a trusted root. Optionally verifies the subject and issuer of the certificate. +// Accept chain and signerVerifierOptions as optional parameters. +func ValidateAndUnpackCertWithOpts(cert *x509.Certificate, co *CheckOpts, opts ...ValidateAndUnpackCertOption) (signature.Verifier, error) { + o, err := makeValidateAndUnpackCertOpts(co, opts...) + if err != nil { + return nil, err + } + + verifier, err := signature.LoadVerifierWithOpts(cert.PublicKey, o.svOpts...) if err != nil { return nil, fmt.Errorf("invalid certificate found on signature: %w", err) } @@ -248,7 +322,7 @@ func ValidateAndUnpackCertWithIntermediates(cert *x509.Certificate, co *CheckOpt } // Now verify the cert, then the signature. - chains, err := TrustedCert(cert, co.RootCerts, intermediateCerts) + chains, err := TrustedCert(cert, co.RootCerts, o.pool) if err != nil { return nil, err @@ -413,26 +487,6 @@ func validateCertExtensions(ce CertExtensions, co *CheckOpts) error { return nil } -// ValidateAndUnpackCertWithChain creates a Verifier from a certificate. Verifies that the certificate -// chains up to the provided root. Chain should start with the parent of the certificate and end with the root. -// Optionally verifies the subject and issuer of the certificate. -func ValidateAndUnpackCertWithChain(cert *x509.Certificate, chain []*x509.Certificate, co *CheckOpts) (signature.Verifier, error) { - if len(chain) == 0 { - return nil, errors.New("no chain provided to validate certificate") - } - rootPool := x509.NewCertPool() - rootPool.AddCert(chain[len(chain)-1]) - co.RootCerts = rootPool - - subPool := x509.NewCertPool() - for _, c := range chain[:len(chain)-1] { - subPool.AddCert(c) - } - co.IntermediateCerts = subPool - - return ValidateAndUnpackCert(cert, co) -} - func tlogValidateEntry(ctx context.Context, client *client.Rekor, rekorPubKeys *TrustedTransparencyLogPubKeys, sig oci.Signature, pem []byte) (*models.LogEntryAnon, error) { b64sig, err := sig.Base64Signature() @@ -487,9 +541,13 @@ func (fos *fakeOCISignatures) Get() ([]oci.Signature, error) { // Note that if co.ExperimentlOCI11 is set, we will attempt to verify // signatures using the experimental OCI 1.1 behavior. func VerifyImageSignatures(ctx context.Context, signedImgRef name.Reference, co *CheckOpts) (checkedSignatures []oci.Signature, bundleVerified bool, err error) { + return VerifyImageSignaturesWithOpts(ctx, signedImgRef, co) +} + +func VerifyImageSignaturesWithOpts(ctx context.Context, signedImgRef name.Reference, co *CheckOpts, svOpts ...signature.LoadOption) (checkedSignatures []oci.Signature, bundleVerified bool, err error) { // Try first using OCI 1.1 behavior if experimental flag is set. if co.ExperimentalOCI11 { - verified, bundleVerified, err := verifyImageSignaturesExperimentalOCI(ctx, signedImgRef, co) + verified, bundleVerified, err := verifyImageSignaturesExperimentalOCI(ctx, signedImgRef, co, svOpts...) if err == nil { return verified, bundleVerified, nil } @@ -534,12 +592,18 @@ func VerifyImageSignatures(ctx context.Context, signedImgRef name.Reference, co } } - return verifySignatures(ctx, sigs, h, co) + return verifySignatures(ctx, sigs, h, co, svOpts...) } // VerifyLocalImageSignatures verifies signatures from a saved, local image, without any network calls, returning the verified signatures. // If there were no valid signatures, we return an error. func VerifyLocalImageSignatures(ctx context.Context, path string, co *CheckOpts) (checkedSignatures []oci.Signature, bundleVerified bool, err error) { + return VerifyLocalImageSignaturesWithOpts(ctx, path, co) +} + +// VerifyLocalImageSignaturesWithOpts verifies signatures from a saved, local image, without any network calls, returning the verified signatures. +// If there were no valid signatures, we return an error. +func VerifyLocalImageSignaturesWithOpts(ctx context.Context, path string, co *CheckOpts, svOpts ...signature.LoadOption) (checkedSignatures []oci.Signature, bundleVerified bool, err error) { // Enforce this up front. if co.RootCerts == nil && co.SigVerifier == nil { return nil, false, errors.New("one of verifier or root certs is required") @@ -583,10 +647,10 @@ func VerifyLocalImageSignatures(ctx context.Context, path string, co *CheckOpts) return nil, false, fmt.Errorf("no signatures associated with the image saved in %s", path) } - return verifySignatures(ctx, sigs, h, co) + return verifySignatures(ctx, sigs, h, co, svOpts...) } -func verifySignatures(ctx context.Context, sigs oci.Signatures, h v1.Hash, co *CheckOpts) (checkedSignatures []oci.Signature, bundleVerified bool, err error) { +func verifySignatures(ctx context.Context, sigs oci.Signatures, h v1.Hash, co *CheckOpts, svOpts ...signature.LoadOption) (checkedSignatures []oci.Signature, bundleVerified bool, err error) { sl, err := sigs.Get() if err != nil { return nil, false, err @@ -614,7 +678,7 @@ func verifySignatures(ctx context.Context, sigs oci.Signatures, h v1.Hash, co *C return } - verified, err := VerifyImageSignature(ctx, sig, h, co) + verified, err := VerifyImageSignatureWithOpts(ctx, sig, h, co, svOpts...) bundlesVerified[index] = verified if err != nil { t.Done(err) @@ -662,7 +726,7 @@ func verifySignatures(ctx context.Context, sigs oci.Signatures, h v1.Hash, co *C // we are in experimental mode). // 3. If a certificate is provided, check it's expiration using the transparency log timestamp. func verifyInternal(ctx context.Context, sig oci.Signature, h v1.Hash, - verifyFn signatureVerificationFn, co *CheckOpts) ( + verifyFn signatureVerificationFn, co *CheckOpts, svOpts ...signature.LoadOption) ( bundleVerified bool, err error) { var acceptableRFC3161Time, acceptableRekorBundleTime *time.Time // Timestamps for the signature we accept, or nil if not applicable. @@ -745,7 +809,7 @@ func verifyInternal(ctx context.Context, sig oci.Signature, h v1.Hash, if pool == nil { pool = co.IntermediateCerts } - verifier, err = ValidateAndUnpackCertWithIntermediates(cert, co, pool) + verifier, err = ValidateAndUnpackCertWithOpts(cert, co, WithPool(pool), WithSignerVerifierOptions(svOpts...)) if err != nil { return false, err } @@ -822,13 +886,23 @@ func keyBytes(sig oci.Signature, co *CheckOpts) ([]byte, error) { // VerifyBlobSignature verifies a blob signature. func VerifyBlobSignature(ctx context.Context, sig oci.Signature, co *CheckOpts) (bundleVerified bool, err error) { + return VerifyBlobSignatureWithOpts(ctx, sig, co) +} + +// VerifyBlobSignature verifies a blob signature. +func VerifyBlobSignatureWithOpts(ctx context.Context, sig oci.Signature, co *CheckOpts, svOpts ...signature.LoadOption) (bundleVerified bool, err error) { // The hash of the artifact is unused. - return verifyInternal(ctx, sig, v1.Hash{}, verifyOCISignature, co) + return verifyInternal(ctx, sig, v1.Hash{}, verifyOCISignature, co, svOpts...) } // VerifyImageSignature verifies a signature func VerifyImageSignature(ctx context.Context, sig oci.Signature, h v1.Hash, co *CheckOpts) (bundleVerified bool, err error) { - return verifyInternal(ctx, sig, h, verifyOCISignature, co) + return VerifyImageSignatureWithOpts(ctx, sig, h, co) +} + +// VerifyImageSignature verifies a signature +func VerifyImageSignatureWithOpts(ctx context.Context, sig oci.Signature, h v1.Hash, co *CheckOpts, svOpts ...signature.LoadOption) (bundleVerified bool, err error) { + return verifyInternal(ctx, sig, h, verifyOCISignature, co, svOpts...) } func loadSignatureFromFile(ctx context.Context, sigRef string, signedImgRef name.Reference, co *CheckOpts) (oci.Signatures, error) { @@ -1104,12 +1178,23 @@ func VerifyBundle(sig oci.Signature, co *CheckOpts) (bool, error) { if err != nil { return false, fmt.Errorf("computing bundle hash: %w", err) } - h := sha256.Sum256(payload) - payloadHash := hex.EncodeToString(h[:]) - if alg != "sha256" { + var payloadHash string + switch alg.HashFunc() { + case crypto.SHA256: + h := sha256.Sum256(payload) + payloadHash = hex.EncodeToString(h[:]) + case crypto.SHA384: + h := sha512.Sum384(payload) + payloadHash = hex.EncodeToString(h[:]) + case crypto.SHA512: + h := sha512.Sum512(payload) + payloadHash = hex.EncodeToString(h[:]) + default: return false, fmt.Errorf("unexpected algorithm: %q", alg) - } else if bundlehash != payloadHash { + } + + if bundlehash != payloadHash { return false, fmt.Errorf("matching bundle to payload: bundle=%q, payload=%q", bundlehash, payloadHash) } return true, nil @@ -1230,25 +1315,36 @@ func extractEntryImpl(bundleBody string) (rekor_types.EntryImpl, error) { return rekor_types.UnmarshalEntry(pe) } -func bundleHash(bundleBody, _ string) (string, string, error) { +func HashAlgorithmToCryptoHash(hashAlgorithm string) crypto.Hash { + switch hashAlgorithm { + case "sha384": + return crypto.SHA384 + case "sha512": + return crypto.SHA512 + default: + return crypto.SHA256 + } +} + +func bundleHash(bundleBody, _ string) (crypto.Hash, string, error) { ei, err := extractEntryImpl(bundleBody) if err != nil { - return "", "", err + return crypto.Hash(0), "", err } switch entry := ei.(type) { case *dsse_v001.V001Entry: - return *entry.DSSEObj.EnvelopeHash.Algorithm, *entry.DSSEObj.EnvelopeHash.Value, nil + return HashAlgorithmToCryptoHash(*entry.DSSEObj.EnvelopeHash.Algorithm), *entry.DSSEObj.EnvelopeHash.Value, nil case *hashedrekord_v001.V001Entry: - return *entry.HashedRekordObj.Data.Hash.Algorithm, *entry.HashedRekordObj.Data.Hash.Value, nil + return HashAlgorithmToCryptoHash(*entry.HashedRekordObj.Data.Hash.Algorithm), *entry.HashedRekordObj.Data.Hash.Value, nil case *intoto_v001.V001Entry: - return *entry.IntotoObj.Content.Hash.Algorithm, *entry.IntotoObj.Content.Hash.Value, nil + return HashAlgorithmToCryptoHash(*entry.IntotoObj.Content.Hash.Algorithm), *entry.IntotoObj.Content.Hash.Value, nil case *intoto_v002.V002Entry: - return *entry.IntotoObj.Content.Hash.Algorithm, *entry.IntotoObj.Content.Hash.Value, nil + return HashAlgorithmToCryptoHash(*entry.IntotoObj.Content.Hash.Algorithm), *entry.IntotoObj.Content.Hash.Value, nil case *rekord_v001.V001Entry: - return *entry.RekordObj.Data.Hash.Algorithm, *entry.RekordObj.Data.Hash.Value, nil + return HashAlgorithmToCryptoHash(*entry.RekordObj.Data.Hash.Algorithm), *entry.RekordObj.Data.Hash.Value, nil default: - return "", "", errors.New("unsupported type") + return crypto.Hash(0), "", errors.New("unsupported type") } } @@ -1357,7 +1453,7 @@ func correctAnnotations(wanted, have map[string]interface{}) bool { // verifyImageSignaturesExperimentalOCI does all the main cosign checks in a loop, returning the verified signatures. // If there were no valid signatures, we return an error, using OCI 1.1+ behavior. -func verifyImageSignaturesExperimentalOCI(ctx context.Context, signedImgRef name.Reference, co *CheckOpts) (checkedSignatures []oci.Signature, bundleVerified bool, err error) { +func verifyImageSignaturesExperimentalOCI(ctx context.Context, signedImgRef name.Reference, co *CheckOpts, svOpts ...signature.LoadOption) (checkedSignatures []oci.Signature, bundleVerified bool, err error) { // Enforce this up front. if co.RootCerts == nil && co.SigVerifier == nil { return nil, false, errors.New("one of verifier or root certs is required") @@ -1410,5 +1506,5 @@ func verifyImageSignaturesExperimentalOCI(ctx context.Context, signedImgRef name } } - return verifySignatures(ctx, sigs, h, co) + return verifySignatures(ctx, sigs, h, co, svOpts...) } diff --git a/pkg/signature/keys.go b/pkg/signature/keys.go index dfac964725d..3be333a90c9 100644 --- a/pkg/signature/keys.go +++ b/pkg/signature/keys.go @@ -31,6 +31,7 @@ import ( "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/kms" + "github.com/sigstore/sigstore/pkg/signature/options" ) // LoadPublicKey is a wrapper for VerifierForKeyRef, hardcoding SHA256 as the hash algorithm @@ -41,7 +42,18 @@ func LoadPublicKey(ctx context.Context, keyRef string) (verifier signature.Verif // VerifierForKeyRef parses the given keyRef, loads the key and returns an appropriate // verifier using the provided hash algorithm func VerifierForKeyRef(ctx context.Context, keyRef string, hashAlgorithm crypto.Hash) (verifier signature.Verifier, err error) { + return VerifierForKeyRefWithOpts(ctx, keyRef, options.WithHash(hashAlgorithm)) +} + +// VerifierForKeyRefWithOpts parses the given keyRef, loads the key and returns an appropriate +// verifier using the provided hash algorithm and options +func VerifierForKeyRefWithOpts(ctx context.Context, keyRef string, opts ...signature.LoadOption) (verifier signature.Verifier, err error) { // The key could be plaintext, in a file, at a URL, or in KMS. + hashAlgorithm := crypto.SHA256 + for _, o := range opts { + o.ApplyHash(&hashAlgorithm) + } + var perr *kms.ProviderNotFoundError kmsKey, err := kms.Get(ctx, keyRef, hashAlgorithm) switch { @@ -69,10 +81,10 @@ func VerifierForKeyRef(ctx context.Context, keyRef string, hashAlgorithm crypto. return nil, fmt.Errorf("pem to public key: %w", err) } - return signature.LoadVerifier(pubKey, hashAlgorithm) + return signature.LoadVerifierWithOpts(pubKey, opts...) } -func loadKey(keyPath string, pf cosign.PassFunc) (signature.SignerVerifier, error) { +func loadKey(keyPath string, pf cosign.PassFunc, opts ...signature.LoadOption) (signature.SignerVerifier, error) { kb, err := blob.LoadFileOrURL(keyPath) if err != nil { return nil, err @@ -84,16 +96,21 @@ func loadKey(keyPath string, pf cosign.PassFunc) (signature.SignerVerifier, erro return nil, err } } - return cosign.LoadPrivateKey(kb, pass) + return cosign.LoadPrivateKeyWithOpts(kb, pass, opts...) } // LoadPublicKeyRaw loads a verifier from a PEM-encoded public key func LoadPublicKeyRaw(raw []byte, hashAlgorithm crypto.Hash) (signature.Verifier, error) { + return LoadPublicKeyRawWithOpts(raw, options.WithHash(hashAlgorithm)) +} + +// LoadPublicKeyRawWithOpts loads a verifier from a PEM-encoded public key with options +func LoadPublicKeyRawWithOpts(raw []byte, opts ...signature.LoadOption) (signature.Verifier, error) { pub, err := cryptoutils.UnmarshalPEMToPublicKey(raw) if err != nil { return nil, err } - return signature.LoadVerifier(pub, hashAlgorithm) + return signature.LoadVerifierWithOpts(pub, opts...) } func SignerFromKeyRef(ctx context.Context, keyRef string, pf cosign.PassFunc) (signature.Signer, error) { @@ -101,6 +118,10 @@ func SignerFromKeyRef(ctx context.Context, keyRef string, pf cosign.PassFunc) (s } func SignerVerifierFromKeyRef(ctx context.Context, keyRef string, pf cosign.PassFunc) (signature.SignerVerifier, error) { + return SignerVerifierFromKeyRefWithOpts(ctx, keyRef, pf) +} + +func SignerVerifierFromKeyRefWithOpts(ctx context.Context, keyRef string, pf cosign.PassFunc, opts ...signature.LoadOption) (signature.SignerVerifier, error) { switch { case strings.HasPrefix(keyRef, pkcs11key.ReferenceScheme): pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig() @@ -129,7 +150,7 @@ func SignerVerifierFromKeyRef(ctx context.Context, keyRef string, pf cosign.Pass } if len(s.Data) > 0 { - return cosign.LoadPrivateKey(s.Data["cosign.key"], s.Data["cosign.password"]) + return cosign.LoadPrivateKeyWithOpts(s.Data["cosign.key"], s.Data["cosign.password"], opts...) } case strings.HasPrefix(keyRef, gitlab.ReferenceScheme): split := strings.Split(keyRef, "://") @@ -150,7 +171,7 @@ func SignerVerifierFromKeyRef(ctx context.Context, keyRef string, pf cosign.Pass return nil, err } - return cosign.LoadPrivateKey([]byte(pk), []byte(pass)) + return cosign.LoadPrivateKeyWithOpts([]byte(pk), []byte(pass), opts...) } if strings.Contains(keyRef, "://") { @@ -165,14 +186,18 @@ func SignerVerifierFromKeyRef(ctx context.Context, keyRef string, pf cosign.Pass // ProviderNotFoundError is okay; loadKey handles other URL schemes } - return loadKey(keyRef, pf) + return loadKey(keyRef, pf, opts...) } func PublicKeyFromKeyRef(ctx context.Context, keyRef string) (signature.Verifier, error) { - return PublicKeyFromKeyRefWithHashAlgo(ctx, keyRef, crypto.SHA256) + return PublicKeyFromKeyRefWithOpts(ctx, keyRef, options.WithHash(crypto.SHA256)) } func PublicKeyFromKeyRefWithHashAlgo(ctx context.Context, keyRef string, hashAlgorithm crypto.Hash) (signature.Verifier, error) { + return PublicKeyFromKeyRefWithOpts(ctx, keyRef, options.WithHash(hashAlgorithm)) +} + +func PublicKeyFromKeyRefWithOpts(ctx context.Context, keyRef string, opts ...signature.LoadOption) (signature.Verifier, error) { if strings.HasPrefix(keyRef, kubernetes.KeyReference) { s, err := kubernetes.GetKeyPairSecret(ctx, keyRef) if err != nil { @@ -180,7 +205,7 @@ func PublicKeyFromKeyRefWithHashAlgo(ctx context.Context, keyRef string, hashAlg } if len(s.Data) > 0 { - return LoadPublicKeyRaw(s.Data["cosign.pub"], hashAlgorithm) + return LoadPublicKeyRawWithOpts(s.Data["cosign.pub"], opts...) } } @@ -219,11 +244,11 @@ func PublicKeyFromKeyRefWithHashAlgo(ctx context.Context, keyRef string, hashAlg } if len(pubKey) > 0 { - return LoadPublicKeyRaw([]byte(pubKey), hashAlgorithm) + return LoadPublicKeyRawWithOpts([]byte(pubKey), opts...) } } - return VerifierForKeyRef(ctx, keyRef, hashAlgorithm) + return VerifierForKeyRefWithOpts(ctx, keyRef, opts...) } func PublicKeyPem(key signature.PublicKeyProvider, pkOpts ...signature.PublicKeyOption) ([]byte, error) { From 260978c10c581b14e7940f4c6959cdeca3ef4287 Mon Sep 17 00:00:00 2001 From: Riccardo Schirone Date: Fri, 19 Jan 2024 12:37:46 +0100 Subject: [PATCH 2/5] Attempt verification with ED25519 if ED25519ph is used for compatibility Signed-off-by: Riccardo Schirone --- pkg/cosign/verify.go | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/pkg/cosign/verify.go b/pkg/cosign/verify.go index 613b6ea3d34..9c9e44e972b 100644 --- a/pkg/cosign/verify.go +++ b/pkg/cosign/verify.go @@ -19,6 +19,7 @@ import ( "context" "crypto" "crypto/ecdsa" + "crypto/ed25519" "crypto/sha256" "crypto/sha512" "crypto/x509" @@ -261,7 +262,7 @@ func verifyOCISignature(ctx context.Context, verifier signature.Verifier, sig pa if err != nil { return err } - signature, err := base64.StdEncoding.DecodeString(b64sig) + decodedSignature, err := base64.StdEncoding.DecodeString(b64sig) if err != nil { return err } @@ -269,7 +270,33 @@ func verifyOCISignature(ctx context.Context, verifier signature.Verifier, sig pa if err != nil { return err } - return verifier.VerifySignature(bytes.NewReader(signature), bytes.NewReader(payload), options.WithContext(ctx)) + // For compatibility reasons, if ED25519ph is used, we try both ED25519 and ED25519ph. + // Refusing to verify ED25519 signatures (used e.g. by rekord entries) would break compatibility. + // The signature algorithm to use should be uniquely determined before this point. + verificationErr := verifier.VerifySignature(bytes.NewReader(decodedSignature), bytes.NewReader(payload), options.WithContext(ctx)) + if verificationErr == nil { + return nil + } + + switch verifier.(type) { + case *signature.ED25519phVerifier: + publicKey, err := verifier.PublicKey() + if err != nil { + return err + } + + if edPublicKey, ok := publicKey.(ed25519.PublicKey); ok { + altVerifier, err := signature.LoadED25519Verifier(edPublicKey) + if err != nil { + return err + } + + fmt.Fprintf(os.Stderr, "Failed to verify signature with ED25519ph, falling back to ED25519 for backward compatibility.\n") + verificationErr = altVerifier.VerifySignature(bytes.NewReader(decodedSignature), bytes.NewReader(payload), options.WithContext(ctx)) + } + } + + return verificationErr } // ValidateAndUnpackCert creates a Verifier from a certificate. Verifies that the From 1046eea436dbbdcecd0689155342bb8b92482e8d Mon Sep 17 00:00:00 2001 From: Riccardo Schirone Date: Tue, 30 Jan 2024 17:47:39 +0100 Subject: [PATCH 3/5] Use a diffrent Signer when interacting with Fulcio ED25519-ph is not widely supported and it is not an accepted option in x509 Certificates/CSR, so Fulcio does not accept them. Instead, clients are supposed to use PureED25519 when interacting with Fulcio. This commit provides to the Fulcio code a separate SignerVerifier created from the one loaded from the private key. This SignerVerifier is usually of the same type, except when dealing with ED25519ph. Signed-off-by: Riccardo Schirone --- cmd/cosign/cli/fulcio/fulcio.go | 8 +++-- .../fulcio/fulcioverifier/fulcioverifier.go | 8 +++-- cmd/cosign/cli/sign/sign.go | 30 +++++++++++++++++-- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/cmd/cosign/cli/fulcio/fulcio.go b/cmd/cosign/cli/fulcio/fulcio.go index de555d47b27..d9fe79f1ccc 100644 --- a/cmd/cosign/cli/fulcio/fulcio.go +++ b/cmd/cosign/cli/fulcio/fulcio.go @@ -109,7 +109,7 @@ type Signer struct { signature.SignerVerifier } -func NewSigner(ctx context.Context, ko options.KeyOpts, signer signature.SignerVerifier) (*Signer, error) { +func NewSignerWithAdapter(ctx context.Context, ko options.KeyOpts, signer signature.SignerVerifier, fulcioSigner signature.SignerVerifier) (*Signer, error) { fClient, err := NewClient(ko.FulcioURL) if err != nil { return nil, fmt.Errorf("creating Fulcio client: %w", err) @@ -164,7 +164,7 @@ func NewSigner(ctx context.Context, ko options.KeyOpts, signer signature.SignerV } flow = flowNormal } - Resp, err := GetCert(ctx, signer, idToken, flow, ko.OIDCIssuer, ko.OIDCClientID, ko.OIDCClientSecret, ko.OIDCRedirectURL, fClient) // TODO, use the chain. + Resp, err := GetCert(ctx, fulcioSigner, idToken, flow, ko.OIDCIssuer, ko.OIDCClientID, ko.OIDCClientSecret, ko.OIDCRedirectURL, fClient) // TODO, use the chain. if err != nil { return nil, fmt.Errorf("retrieving cert: %w", err) } @@ -179,6 +179,10 @@ func NewSigner(ctx context.Context, ko options.KeyOpts, signer signature.SignerV return f, nil } +func NewSigner(ctx context.Context, ko options.KeyOpts, signer signature.SignerVerifier) (*Signer, error) { + return NewSignerWithAdapter(ctx, ko, signer, signer) +} + func (f *Signer) PublicKey(opts ...signature.PublicKeyOption) (crypto.PublicKey, error) { //nolint: revive return f.SignerVerifier.PublicKey() } diff --git a/cmd/cosign/cli/fulcio/fulcioverifier/fulcioverifier.go b/cmd/cosign/cli/fulcio/fulcioverifier/fulcioverifier.go index 8646bb298bd..1760b888625 100644 --- a/cmd/cosign/cli/fulcio/fulcioverifier/fulcioverifier.go +++ b/cmd/cosign/cli/fulcio/fulcioverifier/fulcioverifier.go @@ -26,8 +26,8 @@ import ( "github.com/sigstore/sigstore/pkg/signature" ) -func NewSigner(ctx context.Context, ko options.KeyOpts, signer signature.SignerVerifier) (*fulcio.Signer, error) { - fs, err := fulcio.NewSigner(ctx, ko, signer) +func NewSignerWithAdapter(ctx context.Context, ko options.KeyOpts, signer signature.SignerVerifier, fulcioSigner signature.SignerVerifier) (*fulcio.Signer, error) { + fs, err := fulcio.NewSignerWithAdapter(ctx, ko, signer, fulcioSigner) if err != nil { return nil, err } @@ -46,3 +46,7 @@ func NewSigner(ctx context.Context, ko options.KeyOpts, signer signature.SignerV return fs, nil } + +func NewSigner(ctx context.Context, ko options.KeyOpts, signer signature.SignerVerifier) (*fulcio.Signer, error) { + return NewSignerWithAdapter(ctx, ko, signer, signer) +} diff --git a/cmd/cosign/cli/sign/sign.go b/cmd/cosign/cli/sign/sign.go index 348a985e03f..2f9c5aa5410 100644 --- a/cmd/cosign/cli/sign/sign.go +++ b/cmd/cosign/cli/sign/sign.go @@ -552,12 +552,17 @@ func keylessSigner(ctx context.Context, ko options.KeyOpts, sv *SignerVerifier) err error ) + fulcioSV, err := adaptSignerVerifierToFulcio(sv) + if err != nil { + return nil, fmt.Errorf("adapting signer verifier to Fulcio: %w", err) + } + if ko.InsecureSkipFulcioVerify { - if k, err = fulcio.NewSigner(ctx, ko, sv); err != nil { + if k, err = fulcio.NewSignerWithAdapter(ctx, ko, sv, fulcioSV); err != nil { return nil, fmt.Errorf("getting key from Fulcio: %w", err) } } else { - if k, err = fulcioverifier.NewSigner(ctx, ko, sv); err != nil { + if k, err = fulcioverifier.NewSignerWithAdapter(ctx, ko, sv, fulcioSV); err != nil { return nil, fmt.Errorf("getting key from Fulcio: %w", err) } } @@ -624,6 +629,27 @@ func (c *SignerVerifier) Bytes(ctx context.Context) ([]byte, error) { return pemBytes, nil } +// adaptSignerVerifierToFulcio adapts, if necessary, the SignerVerifier to be used to interact with Fulcio. +// +// This is needed in particular for ED25519 keys with the pre-hashed version of +// the algorithm, which is not supported by Fulcio. This function creates a +// ED25519 SignerVerifier based on that instead. +func adaptSignerVerifierToFulcio(sv *SignerVerifier) (*SignerVerifier, error) { + if ed25519phSV, ok := sv.SignerVerifier.(*signature.ED25519phSignerVerifier); ok { + signerVerifier, err := ed25519phSV.ToED25519SignerVerifier() + if err != nil { + return nil, err + } + + return &SignerVerifier{ + SignerVerifier: signerVerifier, + Cert: sv.Cert, + Chain: sv.Chain, + }, nil + } + return sv, nil +} + func fetchLocalSignedPayload(sig oci.Signature) (*cosign.LocalSignedPayload, error) { signedPayload := &cosign.LocalSignedPayload{} var err error From e01c68ca8204b1b4cf5ce47d3fc6d1f0041798b9 Mon Sep 17 00:00:00 2001 From: Riccardo Schirone Date: Thu, 29 Feb 2024 10:54:50 +0100 Subject: [PATCH 4/5] Use ED25519ph only when uploading to TLog Signed-off-by: Riccardo Schirone <562321+ret2libc@users.noreply.github.com> --- cmd/cosign/cli/sign/sign_blob.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cmd/cosign/cli/sign/sign_blob.go b/cmd/cosign/cli/sign/sign_blob.go index e7a4467daad..1e1aa7ea2d1 100644 --- a/cmd/cosign/cli/sign/sign_blob.go +++ b/cmd/cosign/cli/sign/sign_blob.go @@ -68,7 +68,13 @@ func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string svOptions := []signature.LoadOption{ signatureoptions.WithHash(crypto.SHA256), - signatureoptions.WithED25519ph(), + } + // Use ED25519 pre-hashed version only when uploading to tlog to maintain + // backwards compatibility. When self-managed keys are used this keeps the + // behavior consistent with older cosign clients, which will still be able + // to verify the newer signatures. + if tlogUpload { + svOptions = append(svOptions, signatureoptions.WithED25519ph()) } sv, err := signerFromKeyOptsWithSVOpts(ctx, "", "", ko, svOptions...) From 7a48924f7b7210d06ca9813e41f4e7325fe3c6e1 Mon Sep 17 00:00:00 2001 From: Riccardo Schirone Date: Thu, 6 Feb 2025 11:06:24 +0100 Subject: [PATCH 5/5] update rekor code Signed-off-by: Riccardo Schirone --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 27e7394ed2d..1f962c80947 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/sigstore/fulcio v1.6.6 github.com/sigstore/protobuf-specs v0.4.0 github.com/sigstore/rekor v1.3.9 - github.com/sigstore/sigstore v1.8.12 + github.com/sigstore/sigstore v1.8.13-0.20250204232249-4b62818325b7 github.com/sigstore/sigstore-go v0.7.0 github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.12 github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.12 @@ -69,7 +69,7 @@ require ( cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect cloud.google.com/go/iam v1.2.2 // indirect - cloud.google.com/go/kms v1.20.4 // indirect + cloud.google.com/go/kms v1.20.5 // indirect cloud.google.com/go/longrunning v0.6.2 // indirect cuelabs.dev/go/oci/ociregistry v0.0.0-20241125120445-2c00c104c6e1 // indirect filippo.io/edwards25519 v1.1.0 // indirect @@ -255,7 +255,7 @@ require ( go.opentelemetry.io/otel/metric v1.34.0 // indirect go.opentelemetry.io/otel/sdk v1.34.0 // indirect go.opentelemetry.io/otel/trace v1.34.0 // indirect - go.step.sm/crypto v0.57.0 // indirect + go.step.sm/crypto v0.57.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect @@ -280,4 +280,4 @@ require ( sigs.k8s.io/yaml v1.4.0 // indirect ) -replace github.com/sigstore/rekor => github.com/trail-of-forks/rekor v0.0.0-20250120134836-ee92b4c7487c +replace github.com/sigstore/rekor => github.com/trail-of-forks/rekor v0.0.0-20250205163412-8fe29f9a710f diff --git a/go.sum b/go.sum index 68afd40a2a7..53c9dfcc1ae 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,8 @@ cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4 cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA= cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY= -cloud.google.com/go/kms v1.20.4 h1:CJ0hMpOg1ANN9tx/a/GPJ+Uxudy8k6f3fvGFuTHiE5A= -cloud.google.com/go/kms v1.20.4/go.mod h1:gPLsp1r4FblUgBYPOcvI/bUPpdMg2Jm1ZVKU4tQUfcc= +cloud.google.com/go/kms v1.20.5 h1:aQQ8esAIVZ1atdJRxihhdxGQ64/zEbJoJnCz/ydSmKg= +cloud.google.com/go/kms v1.20.5/go.mod h1:C5A8M1sv2YWYy1AE6iSrnddSG9lRGdJq5XEdBy28Lmw= cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc= cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI= cuelabs.dev/go/oci/ociregistry v0.0.0-20241125120445-2c00c104c6e1 h1:mRwydyTyhtRX2wXS3mqYWzR2qlv6KsmoKXmlz5vInjg= @@ -622,8 +622,8 @@ github.com/sigstore/fulcio v1.6.6 h1:XaMYX6TNT+8n7Npe8D94nyZ7/ERjEsNGFC+REdi/wzw github.com/sigstore/fulcio v1.6.6/go.mod h1:BhQ22lwaebDgIxVBEYOOqLRcN5+xOV+C9bh/GUXRhOk= github.com/sigstore/protobuf-specs v0.4.0 h1:yoZbdh0kZYKOSiVbYyA8J3f2wLh5aUk2SQB7LgAfIdU= github.com/sigstore/protobuf-specs v0.4.0/go.mod h1:FKW5NYhnnFQ/Vb9RKtQk91iYd0MKJ9AxyqInEwU6+OI= -github.com/sigstore/sigstore v1.8.12 h1:S8xMVZbE2z9ZBuQUEG737pxdLjnbOIcFi5v9UFfkJFc= -github.com/sigstore/sigstore v1.8.12/go.mod h1:+PYQAa8rfw0QdPpBcT+Gl3egKD9c+TUgAlF12H3Nmjo= +github.com/sigstore/sigstore v1.8.13-0.20250204232249-4b62818325b7 h1:dKgngAj90pDWGKB/yBt/AmSvNFzLOPvYGfEgEKRQWc4= +github.com/sigstore/sigstore v1.8.13-0.20250204232249-4b62818325b7/go.mod h1:2lXojNsjZjkqu1//FWxq7qUcPB8Lq1KsR5hc+GkcC/4= github.com/sigstore/sigstore-go v0.7.0 h1:bIGPc2IbnbxnzlqQcKlh1o96bxVJ4yRElpP1gHrOH48= github.com/sigstore/sigstore-go v0.7.0/go.mod h1:4RrCK+i+jhx7lyOG2Vgef0/kFLbKlDI1hrioUYvkxxA= github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.12 h1:EC3UmIaa7nV9sCgSpVevmvgvTYTkMqyrRbj5ojPp7tE= @@ -696,8 +696,8 @@ github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHT github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= -github.com/trail-of-forks/rekor v0.0.0-20250120134836-ee92b4c7487c h1:E574zPXWLi0lcyXhvv7zP4MxCQtLMciVzYguHq5zOvY= -github.com/trail-of-forks/rekor v0.0.0-20250120134836-ee92b4c7487c/go.mod h1:yHzffk2CLofDDv5Q/LWWXd8NjpRREG9gixmdmFG4Y4A= +github.com/trail-of-forks/rekor v0.0.0-20250205163412-8fe29f9a710f h1:YQB4Uk5B6CSMEHUvN9+R8G26gccayvZLnnF74b3SstY= +github.com/trail-of-forks/rekor v0.0.0-20250205163412-8fe29f9a710f/go.mod h1:bt8ddrKNlDb/Ub7XYuJPx/2Ro2OJwVesMhBHxQ81OC0= github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4= github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A= github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= @@ -758,8 +758,8 @@ go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= -go.step.sm/crypto v0.57.0 h1:YjoRQDaJYAxHLVwjst0Bl0xcnoKzVwuHCJtEo2VSHYU= -go.step.sm/crypto v0.57.0/go.mod h1:+Lwp5gOVPaTa3H/Ul/TzGbxQPXZZcKIUGMS0lG6n9Go= +go.step.sm/crypto v0.57.1 h1:bt7ugfc0m2/nJ9/uhQOtXRW3xQr8zJwL087FLQk9mvc= +go.step.sm/crypto v0.57.1/go.mod h1:wL25/Mh7edmo36AA93hf9agP493Zt3y4QBzB1wzwOjc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=