diff --git a/cmd/cosign/cli/attest/attest.go b/cmd/cosign/cli/attest/attest.go index 7c38f7efb92..011646d8305 100644 --- a/cmd/cosign/cli/attest/attest.go +++ b/cmd/cosign/cli/attest/attest.go @@ -46,6 +46,7 @@ import ( "github.com/sigstore/rekor/pkg/generated/client" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/sigstore-go/pkg/sign" + "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/dsse" signatureoptions "github.com/sigstore/sigstore/pkg/signature/options" ) @@ -164,11 +165,14 @@ func (c *AttestCommand) Exec(ctx context.Context, imageRef string) error { var err error if c.Sk || c.Slot != "" || c.KeyRef != "" || c.CertPath != "" { + // Set no load options so that Ed25519 is preferred over Ed25519ph, required for signing DSSEs + var signOpts []signature.LoadOption + c.KeyOpts.DefaultLoadOptions = &signOpts sv, _, err = cosign_sign.SignerFromKeyOpts(ctx, c.CertPath, c.CertChainPath, c.KeyOpts) if err != nil { return fmt.Errorf("getting signer: %w", err) } - keypair, err = key.NewSignerVerifierKeypair(sv, c.DefaultLoadOptions) + keypair, err = key.NewSignerVerifierKeypair(sv, &signOpts) if err != nil { return fmt.Errorf("creating signerverifier keypair: %w", err) } diff --git a/cmd/cosign/cli/attest/attest_blob.go b/cmd/cosign/cli/attest/attest_blob.go index bd369b91acc..11b417b9e66 100644 --- a/cmd/cosign/cli/attest/attest_blob.go +++ b/cmd/cosign/cli/attest/attest_blob.go @@ -165,11 +165,14 @@ func (c *AttestBlobCommand) Exec(ctx context.Context, artifactPath string) error var err error if c.Sk || c.Slot != "" || c.KeyRef != "" || c.CertPath != "" { + // Set no load options so that Ed25519 is preferred over Ed25519ph, required for signing DSSEs + var signOpts []signature.LoadOption + c.KeyOpts.DefaultLoadOptions = &signOpts sv, _, err = cosign_sign.SignerFromKeyOpts(ctx, c.CertPath, c.CertChainPath, c.KeyOpts) if err != nil { return fmt.Errorf("getting signer: %w", err) } - keypair, err = key.NewSignerVerifierKeypair(sv, c.DefaultLoadOptions) + keypair, err = key.NewSignerVerifierKeypair(sv, &signOpts) if err != nil { return fmt.Errorf("creating signerverifier keypair: %w", err) } diff --git a/cmd/cosign/cli/sign/sign.go b/cmd/cosign/cli/sign/sign.go index 51dcedd885e..1eca1b00293 100644 --- a/cmd/cosign/cli/sign/sign.go +++ b/cmd/cosign/cli/sign/sign.go @@ -256,11 +256,14 @@ func signDigestBundle(ctx context.Context, digest name.Digest, ko options.KeyOpt var err error if ko.Sk || ko.Slot != "" || ko.KeyRef != "" || signOpts.Cert != "" { + // Set no load options so that Ed25519 is preferred over Ed25519ph, required for signing DSSEs + var signLoadOpts []signature.LoadOption + ko.DefaultLoadOptions = &signLoadOpts sv, _, err = SignerFromKeyOpts(ctx, signOpts.Cert, signOpts.CertChain, ko) if err != nil { return fmt.Errorf("getting signer: %w", err) } - keypair, err = key.NewSignerVerifierKeypair(sv, ko.DefaultLoadOptions) + keypair, err = key.NewSignerVerifierKeypair(sv, &signLoadOpts) if err != nil { return fmt.Errorf("creating signerverifier keypair: %w", err) } diff --git a/cmd/cosign/cli/sign/sign_blob.go b/cmd/cosign/cli/sign/sign_blob.go index c04d830b313..bad25d176ac 100644 --- a/cmd/cosign/cli/sign/sign_blob.go +++ b/cmd/cosign/cli/sign/sign_blob.go @@ -87,6 +87,7 @@ func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string var err error if ko.Sk || ko.Slot != "" || ko.KeyRef != "" { + // Default load options prefer Ed25519ph over Ed25519, required for blobs with hashedrekord sv, _, err = SignerFromKeyOpts(ctx, "", "", ko) if err != nil { return nil, fmt.Errorf("getting signer: %w", err) diff --git a/cmd/cosign/cli/verify/verify.go b/cmd/cosign/cli/verify/verify.go index aef668a2270..24c129f1e91 100644 --- a/cmd/cosign/cli/verify/verify.go +++ b/cmd/cosign/cli/verify/verify.go @@ -240,7 +240,9 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { var pubKey signature.Verifier switch { case keyRef != "": - pubKey, err = sigs.PublicKeyFromKeyRefWithHashAlgo(ctx, keyRef, c.HashAlgorithm) + // Set no load options so that Ed25519 is preferred over Ed25519ph, required for verifying DSSEs + var signOpts []signature.LoadOption + pubKey, err = sigs.PublicKeyFromKeyRefWithHashAlgo(ctx, keyRef, c.HashAlgorithm, &signOpts) if err != nil { return fmt.Errorf("loading public key: %w", err) } diff --git a/cmd/cosign/cli/verify/verify_attestation.go b/cmd/cosign/cli/verify/verify_attestation.go index 4536321c91c..25ed67eb3c3 100644 --- a/cmd/cosign/cli/verify/verify_attestation.go +++ b/cmd/cosign/cli/verify/verify_attestation.go @@ -40,6 +40,7 @@ import ( "github.com/sigstore/cosign/v2/pkg/policy" sigs "github.com/sigstore/cosign/v2/pkg/signature" "github.com/sigstore/sigstore-go/pkg/root" + "github.com/sigstore/sigstore/pkg/signature" ) // VerifyAttestationCommand verifies a signature on a supplied container image @@ -210,7 +211,9 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e // Keys are optional! switch { case keyRef != "": - co.SigVerifier, err = sigs.PublicKeyFromKeyRefWithHashAlgo(ctx, keyRef, c.HashAlgorithm) + // Set no load options so that Ed25519 is preferred over Ed25519ph, required for verifying DSSEs + var signOpts []signature.LoadOption + co.SigVerifier, err = sigs.PublicKeyFromKeyRefWithHashAlgo(ctx, keyRef, c.HashAlgorithm, &signOpts) if err != nil { return fmt.Errorf("loading public key: %w", err) } diff --git a/cmd/cosign/cli/verify/verify_blob.go b/cmd/cosign/cli/verify/verify_blob.go index fb0bd081868..3d67e21457a 100644 --- a/cmd/cosign/cli/verify/verify_blob.go +++ b/cmd/cosign/cli/verify/verify_blob.go @@ -47,6 +47,7 @@ import ( sgverify "github.com/sigstore/sigstore-go/pkg/verify" "github.com/sigstore/sigstore/pkg/cryptoutils" + "github.com/sigstore/sigstore/pkg/signature" ) func isb64(data []byte) bool { @@ -117,12 +118,19 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error { NewBundleFormat: c.KeyOpts.NewBundleFormat && checkNewBundle(c.BundlePath), } + if !c.IgnoreTlog { + // To maintain backwards compatibility with older cosign versions, + // we do not use ed25519ph for ed25519 keys when the signatures are not + // uploaded to the Tlog. + c.DefaultLoadOptions = &[]signature.LoadOption{} + } + // Keys are optional! var cert *x509.Certificate opts := make([]static.Option, 0) switch { case c.KeyRef != "": - co.SigVerifier, err = sigs.PublicKeyFromKeyRefWithHashAlgo(ctx, c.KeyRef, c.HashAlgorithm) + co.SigVerifier, err = sigs.PublicKeyFromKeyRefWithHashAlgo(ctx, c.KeyRef, c.HashAlgorithm, c.DefaultLoadOptions) if err != nil { return fmt.Errorf("loading public key: %w", err) } @@ -265,7 +273,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.LoadPublicKeyRaw(certBytes, crypto.SHA256, c.DefaultLoadOptions) if err != nil { return fmt.Errorf("loading verifier from bundle: %w", err) } diff --git a/cmd/cosign/cli/verify/verify_blob_attestation.go b/cmd/cosign/cli/verify/verify_blob_attestation.go index a84f4f3b2c4..f400ce280ba 100644 --- a/cmd/cosign/cli/verify/verify_blob_attestation.go +++ b/cmd/cosign/cli/verify/verify_blob_attestation.go @@ -47,6 +47,7 @@ import ( "github.com/sigstore/sigstore-go/pkg/root" sgverify "github.com/sigstore/sigstore-go/pkg/verify" "github.com/sigstore/sigstore/pkg/cryptoutils" + "github.com/sigstore/sigstore/pkg/signature" ) // VerifyBlobAttestationCommand verifies an attestation on a supplied blob @@ -127,12 +128,16 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st NewBundleFormat: c.NewBundleFormat && checkNewBundle(c.BundlePath), } + // Set no load options so that Ed25519 is preferred over Ed25519ph, required for verifying DSSEs + var signOpts []signature.LoadOption + c.DefaultLoadOptions = &signOpts + // Keys are optional! var cert *x509.Certificate opts := make([]static.Option, 0) switch { case c.KeyRef != "": - co.SigVerifier, err = sigs.PublicKeyFromKeyRefWithHashAlgo(ctx, c.KeyRef, c.HashAlgorithm) + co.SigVerifier, err = sigs.PublicKeyFromKeyRefWithHashAlgo(ctx, c.KeyRef, c.HashAlgorithm, c.DefaultLoadOptions) if err != nil { return fmt.Errorf("loading public key: %w", err) } @@ -329,7 +334,7 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st 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.LoadPublicKeyRaw(certBytes, crypto.SHA256, c.DefaultLoadOptions) if err != nil { return fmt.Errorf("loading verifier from bundle: %w", err) } diff --git a/internal/key/svkeypair.go b/internal/key/svkeypair.go index 5bbf822d49e..51b8c9c7ed0 100644 --- a/internal/key/svkeypair.go +++ b/internal/key/svkeypair.go @@ -125,13 +125,20 @@ func (k *SignerVerifierKeypair) GetPublicKeyPem() (string, error) { // SignData signs the given data with the SignerVerifier. func (k *SignerVerifierKeypair) SignData(ctx context.Context, data []byte) ([]byte, []byte, error) { - h := k.sigAlg.GetHashType().New() - h.Write(data) - digest := h.Sum(nil) - sOpts := []signature.SignOption{signatureoptions.WithContext(ctx), signatureoptions.WithDigest(digest)} + sOpts := []signature.SignOption{signatureoptions.WithContext(ctx)} + + hf := k.sigAlg.GetHashType() + dataToSign := data + // RSA, ECDSA, and Ed25519ph sign a digest, while pure Ed25519's interface takes data and hashes during signing + if hf != crypto.Hash(0) { + hasher := hf.New() + hasher.Write(data) + dataToSign = hasher.Sum(nil) + sOpts = append(sOpts, signatureoptions.WithDigest(dataToSign)) + } sig, err := k.sv.SignMessage(bytes.NewReader(data), sOpts...) if err != nil { return nil, nil, err } - return sig, digest, nil + return sig, dataToSign, nil } diff --git a/internal/key/svkeypair_test.go b/internal/key/svkeypair_test.go index 15422b70dfa..37dfc66592a 100644 --- a/internal/key/svkeypair_test.go +++ b/internal/key/svkeypair_test.go @@ -34,6 +34,7 @@ import ( protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/signature" + "github.com/sigstore/sigstore/pkg/signature/options" ) // mockSignerVerifier is a mock implementation of signature.SignerVerifier for testing. @@ -61,7 +62,7 @@ func (m *mockSignerVerifier) VerifySignature(_, _ io.Reader, _ ...signature.Veri return errors.New("not implemented") } -func TestNewKMSKeypair(t *testing.T) { +func TestNewSignerVerifierKeypair(t *testing.T) { ecdsaPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatalf("failed to generate ecdsa key: %v", err) @@ -78,6 +79,7 @@ func TestNewKMSKeypair(t *testing.T) { testCases := []struct { name string sv signature.SignerVerifier + prehash bool expectErr bool errMsg string }{ @@ -102,6 +104,14 @@ func TestNewKMSKeypair(t *testing.T) { }, expectErr: false, }, + { + name: "ED25519ph key", + sv: &mockSignerVerifier{ + pubKey: ed25519Priv.Public(), + }, + prehash: true, + expectErr: false, + }, { name: "Unsupported key type", sv: &mockSignerVerifier{ @@ -122,7 +132,11 @@ func TestNewKMSKeypair(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - kp, err := NewSignerVerifierKeypair(tc.sv, nil) + var loadOpts []signature.LoadOption + if tc.prehash { + loadOpts = []signature.LoadOption{options.WithED25519ph()} + } + kp, err := NewSignerVerifierKeypair(tc.sv, &loadOpts) if tc.expectErr { if err == nil { t.Errorf("expected an error, but got none") @@ -137,6 +151,11 @@ func TestNewKMSKeypair(t *testing.T) { t.Error("expected a keypair, but got nil") } } + if !tc.expectErr { + if _, _, err := kp.SignData(context.Background(), []byte("data")); err != nil { + t.Errorf("unexpected error: %v", err) + } + } }) } } diff --git a/pkg/cosign/bundle/sign.go b/pkg/cosign/bundle/sign.go index 45a91aab981..c57730f0bb9 100644 --- a/pkg/cosign/bundle/sign.go +++ b/pkg/cosign/bundle/sign.go @@ -26,6 +26,7 @@ import ( "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore-go/pkg/sign" "github.com/sigstore/sigstore/pkg/signature" + "github.com/sigstore/sigstore/pkg/signature/options" "google.golang.org/protobuf/encoding/protojson" ) @@ -63,7 +64,11 @@ func SignData(ctx context.Context, content sign.Content, keypair sign.Keypair, i if err != nil { log.Fatal(err) } - verifier, err := signature.LoadDefaultVerifier(pubKey) + var verifierOpts []signature.LoadOption + if _, ok := content.(*sign.PlainData); ok { + verifierOpts = append(verifierOpts, options.WithED25519ph()) + } + verifier, err := signature.LoadDefaultVerifier(pubKey, verifierOpts...) if err != nil { log.Fatal(err) } diff --git a/pkg/signature/keys.go b/pkg/signature/keys.go index a396d096865..cb6cbc29713 100644 --- a/pkg/signature/keys.go +++ b/pkg/signature/keys.go @@ -31,16 +31,17 @@ 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 func LoadPublicKey(ctx context.Context, keyRef string) (verifier signature.Verifier, err error) { - return VerifierForKeyRef(ctx, keyRef, crypto.SHA256) + return VerifierForKeyRef(ctx, keyRef, crypto.SHA256, nil) } // 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) { +func VerifierForKeyRef(ctx context.Context, keyRef string, hashAlgorithm crypto.Hash, defaultLoadOptions *[]signature.LoadOption) (verifier signature.Verifier, err error) { // The key could be plaintext, in a file, at a URL, or in KMS. var perr *kms.ProviderNotFoundError kmsKey, err := kms.Get(ctx, keyRef, hashAlgorithm) @@ -69,7 +70,9 @@ func VerifierForKeyRef(ctx context.Context, keyRef string, hashAlgorithm crypto. return nil, fmt.Errorf("pem to public key: %w", err) } - return signature.LoadVerifier(pubKey, hashAlgorithm) + opts := *cosign.GetDefaultLoadOptions(defaultLoadOptions) + opts = append(opts, options.WithHash(hashAlgorithm)) + return signature.LoadVerifierWithOpts(pubKey, opts...) } func loadKey(keyPath string, pf cosign.PassFunc, defaultLoadOptions *[]signature.LoadOption) (signature.SignerVerifier, error) { @@ -88,12 +91,14 @@ func loadKey(keyPath string, pf cosign.PassFunc, defaultLoadOptions *[]signature } // LoadPublicKeyRaw loads a verifier from a PEM-encoded public key -func LoadPublicKeyRaw(raw []byte, hashAlgorithm crypto.Hash) (signature.Verifier, error) { +func LoadPublicKeyRaw(raw []byte, hashAlgorithm crypto.Hash, defaultLoadOptions *[]signature.LoadOption) (signature.Verifier, error) { pub, err := cryptoutils.UnmarshalPEMToPublicKey(raw) if err != nil { return nil, err } - return signature.LoadVerifier(pub, hashAlgorithm) + opts := *cosign.GetDefaultLoadOptions(defaultLoadOptions) + opts = append(opts, options.WithHash(hashAlgorithm)) + return signature.LoadVerifierWithOpts(pub, opts...) } func SignerFromKeyRef(ctx context.Context, keyRef string, pf cosign.PassFunc) (signature.Signer, error) { @@ -169,10 +174,10 @@ func SignerVerifierFromKeyRef(ctx context.Context, keyRef string, pf cosign.Pass } func PublicKeyFromKeyRef(ctx context.Context, keyRef string) (signature.Verifier, error) { - return PublicKeyFromKeyRefWithHashAlgo(ctx, keyRef, crypto.SHA256) + return PublicKeyFromKeyRefWithHashAlgo(ctx, keyRef, crypto.SHA256, nil) } -func PublicKeyFromKeyRefWithHashAlgo(ctx context.Context, keyRef string, hashAlgorithm crypto.Hash) (signature.Verifier, error) { +func PublicKeyFromKeyRefWithHashAlgo(ctx context.Context, keyRef string, hashAlgorithm crypto.Hash, defaultLoadOptions *[]signature.LoadOption) (signature.Verifier, error) { if strings.HasPrefix(keyRef, kubernetes.KeyReference) { s, err := kubernetes.GetKeyPairSecret(ctx, keyRef) if err != nil { @@ -180,7 +185,7 @@ func PublicKeyFromKeyRefWithHashAlgo(ctx context.Context, keyRef string, hashAlg } if len(s.Data) > 0 { - return LoadPublicKeyRaw(s.Data["cosign.pub"], hashAlgorithm) + return LoadPublicKeyRaw(s.Data["cosign.pub"], hashAlgorithm, defaultLoadOptions) } } @@ -219,11 +224,11 @@ func PublicKeyFromKeyRefWithHashAlgo(ctx context.Context, keyRef string, hashAlg } if len(pubKey) > 0 { - return LoadPublicKeyRaw([]byte(pubKey), hashAlgorithm) + return LoadPublicKeyRaw([]byte(pubKey), hashAlgorithm, defaultLoadOptions) } } - return VerifierForKeyRef(ctx, keyRef, hashAlgorithm) + return VerifierForKeyRef(ctx, keyRef, hashAlgorithm, defaultLoadOptions) } func PublicKeyPem(key signature.PublicKeyProvider, pkOpts ...signature.PublicKeyOption) ([]byte, error) { diff --git a/test/e2e_test.go b/test/e2e_test.go index ce40f6ba1d7..ca8e2fce54f 100644 --- a/test/e2e_test.go +++ b/test/e2e_test.go @@ -847,7 +847,7 @@ func TestSignVerifyWithTUFMirror(t *testing.T) { } } -func prepareSigningConfig(t *testing.T, fulcioURL, rekorURL, oidcURL, tsaURL string) string { +func prepareSigningConfig(t *testing.T, fulcioURL, rekorURL, oidcURL, tsaURL string) string { //nolint: unparam startTime := "2024-01-01T00:00:00Z" fulcioSpec := fmt.Sprintf("url=%s,api-version=1,operator=fulcio-op,start-time=%s", fulcioURL, startTime) rekorSpec := fmt.Sprintf("url=%s,api-version=1,operator=rekor-op,start-time=%s", rekorURL, startTime) @@ -2804,7 +2804,7 @@ func TestSignBlobNewBundle(t *testing.T) { must(verifyBlobCmd.Exec(ctx, blobPath), t) } -func TestSignBlobNewBundleNonSHA256(t *testing.T) { +func TestSignBlobNewBundleManagedKeyNonDefaultAlgorithm(t *testing.T) { td1 := t.TempDir() blob := "someblob" @@ -2817,33 +2817,205 @@ func TestSignBlobNewBundleNonSHA256(t *testing.T) { ctx := context.Background() - // Generate ecdsa-p521 key - _, privKeyPath, pubKeyPath := keypairWithAlgorithm(t, td1, v1.PublicKeyDetails_PKIX_ECDSA_P521_SHA_512) + tts := []struct { + algo v1.PublicKeyDetails + }{ + {v1.PublicKeyDetails_PKIX_ECDSA_P384_SHA_384}, + {v1.PublicKeyDetails_PKIX_ECDSA_P521_SHA_512}, + {v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_2048_SHA256}, + {v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_3072_SHA256}, + {v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_4096_SHA256}, + {v1.PublicKeyDetails_PKIX_ED25519}, // When Rekor isn't used, sign with the pure variant + } + for _, tt := range tts { + _, privKeyPath, pubKeyPath := keypairWithAlgorithm(t, td1, tt.algo) + tlogUpload := false - ko := options.KeyOpts{ - KeyRef: privKeyPath, - PassFunc: passFunc, - BundlePath: bundlePath, - NewBundleFormat: true, + ko := options.KeyOpts{ + KeyRef: privKeyPath, + PassFunc: passFunc, + BundlePath: bundlePath, + NewBundleFormat: true, + } + if _, err := sign.SignBlobCmd(ro, ko, blobPath, true, "", "", tlogUpload); err != nil { + t.Fatal(err) + } + algDetails, err := signature.GetAlgorithmDetails(tt.algo) + if err != nil { + t.Fatal(err) + } + + ko1 := options.KeyOpts{ + KeyRef: pubKeyPath, + BundlePath: bundlePath, + NewBundleFormat: true, + } + verifyBlobCmd := cliverify.VerifyBlobCmd{ + KeyOpts: ko1, + IgnoreTlog: true, + HashAlgorithm: algDetails.GetHashType(), + } + must(verifyBlobCmd.Exec(ctx, blobPath), t) } - if _, err := sign.SignBlobCmd(ro, ko, blobPath, true, "", "", false); err != nil { +} + +func TestSignBlobNewBundleManagedKeyRekorNonDefaultAlgorithm(t *testing.T) { + td1 := t.TempDir() + + tufLocalCache := t.TempDir() + t.Setenv("TUF_ROOT", tufLocalCache) + tufMirror := t.TempDir() + viper.Set("timestamp-signer", "memory") + viper.Set("timestamp-signer-hash", "sha256") + tsaAPIServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, false, 10*time.Second, 10*time.Second) + tsaServer := httptest.NewServer(tsaAPIServer.GetHandler()) + t.Cleanup(tsaServer.Close) + tufServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.FileServer(http.Dir(tufMirror)).ServeHTTP(w, r) + })) + mirror := tufServer.URL + trustedRoot := prepareTrustedRoot(t, tsaServer.URL) + signingConfigStr := prepareSigningConfig(t, fulcioURL, rekorURL, "unused", tsaServer.URL+"/api/v1/timestamp") + _, err := newTUF(tufMirror, []targetInfo{ + { + name: "trusted_root.json", + source: trustedRoot, + }, + { + name: "signing_config.v0.2.json", + source: signingConfigStr, + }, + }) + must(err, t) + + ctx := context.Background() + + rootPath := filepath.Join(tufMirror, "1.root.json") + must(initialize.DoInitialize(ctx, rootPath, mirror), t) + + blob := "someblob" + blobPath := filepath.Join(td1, blob) + if err := os.WriteFile(blobPath, []byte(blob), 0644); err != nil { t.Fatal(err) } - ko1 := options.KeyOpts{ - KeyRef: pubKeyPath, - BundlePath: bundlePath, - NewBundleFormat: true, + bundlePath := filepath.Join(td1, "bundle.sigstore.json") + + tts := []struct { + algo v1.PublicKeyDetails + }{ + {v1.PublicKeyDetails_PKIX_ECDSA_P384_SHA_384}, + {v1.PublicKeyDetails_PKIX_ECDSA_P521_SHA_512}, + {v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_2048_SHA256}, + {v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_3072_SHA256}, + {v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_4096_SHA256}, + {v1.PublicKeyDetails_PKIX_ED25519_PH}, // Prehash variant used with Rekor } - verifyBlobCmd := cliverify.VerifyBlobCmd{ - KeyOpts: ko1, - IgnoreTlog: true, - HashAlgorithm: crypto.SHA512, + for _, tt := range tts { + _, privKeyPath, pubKeyPath := keypairWithAlgorithm(t, td1, tt.algo) + tlogUpload := true + + ko := options.KeyOpts{ + KeyRef: privKeyPath, + PassFunc: passFunc, + BundlePath: bundlePath, + NewBundleFormat: true, + } + + trustedMaterial, err := cosign.TrustedRoot() + must(err, t) + ko.TrustedMaterial = trustedMaterial + signingConfig, err := cosign.SigningConfig() + must(err, t) + ko.SigningConfig = signingConfig + + if _, err := sign.SignBlobCmd(ro, ko, blobPath, true, "", "", tlogUpload); err != nil { + t.Fatal(err) + } + algDetails, err := signature.GetAlgorithmDetails(tt.algo) + if err != nil { + t.Fatal(err) + } + + ko1 := options.KeyOpts{ + KeyRef: pubKeyPath, + BundlePath: bundlePath, + NewBundleFormat: true, + } + verifyBlobCmd := cliverify.VerifyBlobCmd{ + KeyOpts: ko1, + IgnoreTlog: false, + HashAlgorithm: algDetails.GetHashType(), + } + must(verifyBlobCmd.Exec(ctx, blobPath), t) + } +} + +func TestAttestBlobNewBundleManagedKeyNonDefaultAlgorithm(t *testing.T) { + td := t.TempDir() + blob := "someblob" + bp := filepath.Join(td, blob) + if err := os.WriteFile(bp, []byte(blob), 0600); err != nil { + t.Fatal(err) + } + // Sign an attestation + statement := `{"_type":"https://in-toto.io/Statement/v1","subject":[{"name":"someblob","digest":{"alg":"7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3"}}],"predicateType":"something","predicate":{}}` + attestDir := t.TempDir() + statementPath := filepath.Join(attestDir, "statement") + if err := os.WriteFile(statementPath, []byte(statement), 0644); err != nil { + t.Fatal(err) + } + attBundlePath := filepath.Join(attestDir, "attest.bundle.json") + + ctx := context.Background() + + tts := []struct { + algo v1.PublicKeyDetails + }{ + {v1.PublicKeyDetails_PKIX_ECDSA_P384_SHA_384}, + {v1.PublicKeyDetails_PKIX_ECDSA_P521_SHA_512}, + {v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_2048_SHA256}, + {v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_3072_SHA256}, + {v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_4096_SHA256}, + {v1.PublicKeyDetails_PKIX_ED25519}, // Only pure variant is supported + } + for _, tt := range tts { + _, privKeyPath, pubKeyPath := keypairWithAlgorithm(t, td, tt.algo) + + ko := options.KeyOpts{ + KeyRef: privKeyPath, + PassFunc: passFunc, + BundlePath: attBundlePath, + NewBundleFormat: true, + } + + algDetails, err := signature.GetAlgorithmDetails(tt.algo) + if err != nil { + t.Fatal(err) + } + + attestBlobCmd := attest.AttestBlobCommand{ + KeyOpts: ko, + RekorEntryType: "dsse", + StatementPath: statementPath, + } + must(attestBlobCmd.Exec(ctx, bp), t) + + // Verify an attestation + ko.KeyRef = pubKeyPath + verifyBlobAttestationCmd := cliverify.VerifyBlobAttestationCommand{ + KeyOpts: ko, + UseSignedTimestamps: true, + Digest: "7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3", + DigestAlg: "alg", + CheckClaims: true, + HashAlgorithm: algDetails.GetHashType(), + } + must(verifyBlobAttestationCmd.Exec(ctx, ""), t) } - must(verifyBlobCmd.Exec(ctx, blobPath), t) } -func TestSignBlobNewBundleNonDefaultAlgorithm(t *testing.T) { +func TestSignBlobNewBundleFulcioNonDefaultAlgorithm(t *testing.T) { tts := []struct { algo v1.PublicKeyDetails }{