From 7f50741011397cb831a6627ecc97e6c8c6e063d3 Mon Sep 17 00:00:00 2001 From: Asra Ali Date: Thu, 15 Sep 2022 11:25:00 -0500 Subject: [PATCH 1/5] fix: fix cert chain validation Signed-off-by: Asra Ali --- cmd/cosign/cli/verify/verify_blob.go | 22 +++++- cmd/cosign/cli/verify/verify_blob_test.go | 87 ++++++++++++++++++++++- 2 files changed, 105 insertions(+), 4 deletions(-) diff --git a/cmd/cosign/cli/verify/verify_blob.go b/cmd/cosign/cli/verify/verify_blob.go index c41d6d0a72b..a9761bf02fb 100644 --- a/cmd/cosign/cli/verify/verify_blob.go +++ b/cmd/cosign/cli/verify/verify_blob.go @@ -107,7 +107,7 @@ func VerifyBlobCmd(ctx context.Context, ko options.KeyOpts, certRef, certEmail, co.RekorClient = rekorClient } } - if certRef == "" || options.EnableExperimental() { + if options.EnableExperimental() { co.RootCerts, err = fulcio.GetRoots() if err != nil { return fmt.Errorf("getting Fulcio roots: %w", err) @@ -145,6 +145,14 @@ func VerifyBlobCmd(ctx context.Context, ko options.KeyOpts, certRef, certEmail, return err } if certChain == "" { + co.RootCerts, err = fulcio.GetRoots() + if err != nil { + return fmt.Errorf("getting Fulcio roots: %w", err) + } + co.IntermediateCerts, err = fulcio.GetIntermediates() + if err != nil { + return fmt.Errorf("getting Fulcio intermediates: %w", err) + } co.SigVerifier, err = cosign.ValidateAndUnpackCert(cert, co) if err != nil { return err @@ -178,10 +186,20 @@ func VerifyBlobCmd(ctx context.Context, ko options.KeyOpts, certRef, certEmail, // check if cert is actually a public key co.SigVerifier, err = sigs.LoadPublicKeyRaw(certBytes, crypto.SHA256) } else { + if certChain == "" { + co.RootCerts, err = fulcio.GetRoots() + if err != nil { + return fmt.Errorf("getting Fulcio roots: %w", err) + } + co.IntermediateCerts, err = fulcio.GetIntermediates() + if err != nil { + return fmt.Errorf("getting Fulcio intermediates: %w", err) + } + } co.SigVerifier, err = cosign.ValidateAndUnpackCert(cert, co) } if err != nil { - return err + return fmt.Errorf("loading verifier from bundle: %w", err) } bundle = b.Bundle // No certificate is provided: search by artifact sha in the TLOG. diff --git a/cmd/cosign/cli/verify/verify_blob_test.go b/cmd/cosign/cli/verify/verify_blob_test.go index 84f7463907c..fbea749ea31 100644 --- a/cmd/cosign/cli/verify/verify_blob_test.go +++ b/cmd/cosign/cli/verify/verify_blob_test.go @@ -434,7 +434,6 @@ func TestVerifyBlob(t *testing.T) { experimental: false, shouldErr: true, }, - { name: "valid signature with expired certificate - experimental good rekor lookup", blob: blobBytes, @@ -446,7 +445,6 @@ func TestVerifyBlob(t *testing.T) { expiredLeafPem, true), shouldErr: false, }, - { name: "valid signature with expired certificate - experimental bad rekor integrated time", blob: blobBytes, @@ -959,6 +957,91 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) { }) } +func TestVerifyBlobCmdInvalidRootCA(t *testing.T) { + keyless := newKeylessStack(t) + // Change the keyless stack. + _ = newKeylessStack(t) + t.Run("Invalid certificate root explicit certRef", func(t *testing.T) { + // Change the keyless stack. + _ = newKeylessStack(t) + identity := "hello@foo.com" + issuer := "issuer" + leafCert, _, leafPemCert, signer := keyless.genLeafCert(t, identity, issuer) + + // Create blob + blob := "someblob" + + // Sign blob with private key + sig, err := signer.SignMessage(bytes.NewReader([]byte(blob))) + if err != nil { + t.Fatal(err) + } + + // Create bundle + entry := genRekorEntry(t, hashedrekord.KIND, hashedrekord.New().DefaultVersion(), []byte(blob), leafPemCert, sig) + b := createBundle(t, sig, leafPemCert, keyless.rekorLogID, leafCert.NotBefore.Unix()+1, entry) + b.Bundle.SignedEntryTimestamp = keyless.rekorSignPayload(t, b.Bundle.Payload) + bundlePath := writeBundleFile(t, keyless.td, b, "bundle.json") + blobPath := writeBlobFile(t, keyless.td, blob, "blob.txt") + certPath := writeBlobFile(t, keyless.td, string(leafPemCert), "cert.pem") + + // Verify command + err = VerifyBlobCmd(context.Background(), + options.KeyOpts{BundlePath: bundlePath}, + certPath, /*certRef*/ + identity, /*certEmail*/ + issuer, /*certOidcIssuer*/ + "", /*certChain*/ // Chain is fetched from TUF/SIGSTORE_ROOT_FILE + "", /*sigRef*/ // Sig is fetched from bundle + blobPath, /*blobRef*/ + // GitHub identity flags start + "", "", "", "", "", + // GitHub identity flags end + false /*enforceSCT*/) + if err == nil || !strings.Contains(err.Error(), "certificate signed by unknown authority") { + t.Fatalf("expected error with invalid root CA, got %v", err) + } + }) + t.Run("Invalid certificate root explicit bundle", func(t *testing.T) { + identity := "hello@foo.com" + issuer := "issuer" + leafCert, _, leafPemCert, signer := keyless.genLeafCert(t, identity, issuer) + + // Create blob + blob := "someblob" + + // Sign blob with private key + sig, err := signer.SignMessage(bytes.NewReader([]byte(blob))) + if err != nil { + t.Fatal(err) + } + + // Create bundle + entry := genRekorEntry(t, hashedrekord.KIND, hashedrekord.New().DefaultVersion(), []byte(blob), leafPemCert, sig) + b := createBundle(t, sig, leafPemCert, keyless.rekorLogID, leafCert.NotBefore.Unix()+1, entry) + b.Bundle.SignedEntryTimestamp = keyless.rekorSignPayload(t, b.Bundle.Payload) + bundlePath := writeBundleFile(t, keyless.td, b, "bundle.json") + blobPath := writeBlobFile(t, keyless.td, blob, "blob.txt") + + // Verify command + err = VerifyBlobCmd(context.Background(), + options.KeyOpts{BundlePath: bundlePath}, + "", /*certRef*/ // Fetched from bundle + identity, /*certEmail*/ + issuer, /*certOidcIssuer*/ + "", /*certChain*/ // Chain is fetched from TUF/SIGSTORE_ROOT_FILE + "", /*sigRef*/ // Sig is fetched from bundle + blobPath, /*blobRef*/ + // GitHub identity flags start + "", "", "", "", "", + // GitHub identity flags end + false /*enforceSCT*/) + if err == nil || !strings.Contains(err.Error(), "certificate signed by unknown authority") { + t.Fatalf("expected error with invalid root CA, got %v", err) + } + }) +} + type keylessStack struct { rootCert *x509.Certificate rootPriv *ecdsa.PrivateKey From e641243e5dc237cd6843de9d9982cad3da25e62a Mon Sep 17 00:00:00 2001 From: Asra Ali Date: Thu, 15 Sep 2022 11:44:14 -0500 Subject: [PATCH 2/5] one more test Signed-off-by: Asra Ali --- cmd/cosign/cli/verify/verify_blob_test.go | 41 +++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/cmd/cosign/cli/verify/verify_blob_test.go b/cmd/cosign/cli/verify/verify_blob_test.go index fbea749ea31..223330b4511 100644 --- a/cmd/cosign/cli/verify/verify_blob_test.go +++ b/cmd/cosign/cli/verify/verify_blob_test.go @@ -955,6 +955,45 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) { t.Fatalf("expected error with mismatched issuer, got %v", err) } }) + t.Run("Intermediate root not explicit in non-experimental mode", func(t *testing.T) { + identity := "hello@foo.com" + issuer := "issuer" + leafCert, _, leafPemCert, signer := keyless.genLeafCert(t, identity, issuer) + + // Create blob + blob := "someblob" + + // Sign blob with private key + sig, err := signer.SignMessage(bytes.NewReader([]byte(blob))) + if err != nil { + t.Fatal(err) + } + + // Create bundle + entry := genRekorEntry(t, hashedrekord.KIND, hashedrekord.New().DefaultVersion(), []byte(blob), leafPemCert, sig) + b := createBundle(t, sig, leafPemCert, keyless.rekorLogID, leafCert.NotBefore.Unix()+1, entry) + b.Bundle.SignedEntryTimestamp = keyless.rekorSignPayload(t, b.Bundle.Payload) + bundlePath := writeBundleFile(t, keyless.td, b, "bundle.json") + blobPath := writeBlobFile(t, keyless.td, blob, "blob.txt") + certPath := writeBlobFile(t, keyless.td, string(leafPemCert), "cert.pem") + + // Verify command + err = VerifyBlobCmd(context.Background(), + options.KeyOpts{BundlePath: bundlePath}, + certPath, /*certRef*/ + identity, /*certEmail*/ + issuer, /*certOidcIssuer*/ + "", /*certChain*/ // Chain is fetched from TUF/SIGSTORE_ROOT_FILE + "", /*sigRef*/ // Sig is fetched from bundle + blobPath, /*blobRef*/ + // GitHub identity flags start + "", "", "", "", "", + // GitHub identity flags end + false /*enforceSCT*/) + if err != nil { + t.Fatalf("expected success without specifying the intermediates, got %v", err) + } + }) } func TestVerifyBlobCmdInvalidRootCA(t *testing.T) { @@ -962,8 +1001,6 @@ func TestVerifyBlobCmdInvalidRootCA(t *testing.T) { // Change the keyless stack. _ = newKeylessStack(t) t.Run("Invalid certificate root explicit certRef", func(t *testing.T) { - // Change the keyless stack. - _ = newKeylessStack(t) identity := "hello@foo.com" issuer := "issuer" leafCert, _, leafPemCert, signer := keyless.genLeafCert(t, identity, issuer) From 14053558803be2a303e08fa209978eac51008932 Mon Sep 17 00:00:00 2001 From: Asra Ali Date: Thu, 15 Sep 2022 11:50:51 -0500 Subject: [PATCH 3/5] add one more test Signed-off-by: Asra Ali --- cmd/cosign/cli/verify/verify_blob.go | 12 +++++-- cmd/cosign/cli/verify/verify_blob_test.go | 38 +++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/cmd/cosign/cli/verify/verify_blob.go b/cmd/cosign/cli/verify/verify_blob.go index a9761bf02fb..199b2165113 100644 --- a/cmd/cosign/cli/verify/verify_blob.go +++ b/cmd/cosign/cli/verify/verify_blob.go @@ -149,13 +149,14 @@ func VerifyBlobCmd(ctx context.Context, ko options.KeyOpts, certRef, certEmail, if err != nil { return fmt.Errorf("getting Fulcio roots: %w", err) } + co.IntermediateCerts, err = fulcio.GetIntermediates() if err != nil { return fmt.Errorf("getting Fulcio intermediates: %w", err) } co.SigVerifier, err = cosign.ValidateAndUnpackCert(cert, co) if err != nil { - return err + return fmt.Errorf("validating certRef: %w", err) } } else { // Verify certificate with chain @@ -195,8 +196,15 @@ func VerifyBlobCmd(ctx context.Context, ko options.KeyOpts, certRef, certEmail, if err != nil { return fmt.Errorf("getting Fulcio intermediates: %w", err) } + co.SigVerifier, err = cosign.ValidateAndUnpackCert(cert, co) + } else { + // Verify certificate with chain + chain, err := loadCertChainFromFileOrURL(certChain) + if err != nil { + return err + } + co.SigVerifier, err = cosign.ValidateAndUnpackCertWithChain(cert, chain, co) } - co.SigVerifier, err = cosign.ValidateAndUnpackCert(cert, co) } if err != nil { return fmt.Errorf("loading verifier from bundle: %w", err) diff --git a/cmd/cosign/cli/verify/verify_blob_test.go b/cmd/cosign/cli/verify/verify_blob_test.go index 223330b4511..12f76ece3a6 100644 --- a/cmd/cosign/cli/verify/verify_blob_test.go +++ b/cmd/cosign/cli/verify/verify_blob_test.go @@ -994,6 +994,44 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) { t.Fatalf("expected success without specifying the intermediates, got %v", err) } }) + t.Run("Intermediate root explicit in non-experimental mode", func(t *testing.T) { + identity := "hello@foo.com" + issuer := "issuer" + leafCert, _, leafPemCert, signer := keyless.genLeafCert(t, identity, issuer) + + // Create blob + blob := "someblob" + + // Sign blob with private key + sig, err := signer.SignMessage(bytes.NewReader([]byte(blob))) + if err != nil { + t.Fatal(err) + } + + // Create bundle + entry := genRekorEntry(t, hashedrekord.KIND, hashedrekord.New().DefaultVersion(), []byte(blob), leafPemCert, sig) + b := createBundle(t, sig, leafPemCert, keyless.rekorLogID, leafCert.NotBefore.Unix()+1, entry) + b.Bundle.SignedEntryTimestamp = keyless.rekorSignPayload(t, b.Bundle.Payload) + bundlePath := writeBundleFile(t, keyless.td, b, "bundle.json") + blobPath := writeBlobFile(t, keyless.td, blob, "blob.txt") + + // Verify command + err = VerifyBlobCmd(context.Background(), + options.KeyOpts{BundlePath: bundlePath}, + "", /*certRef*/ + identity, /*certEmail*/ + issuer, /*certOidcIssuer*/ + os.Getenv("SIGSTORE_ROOT_FILE"), /*certChain*/ + "", /*sigRef*/ // Sig is fetched from bundle + blobPath, /*blobRef*/ + // GitHub identity flags start + "", "", "", "", "", + // GitHub identity flags end + false /*enforceSCT*/) + if err != nil { + t.Fatalf("expected success specifying the intermediates, got %v", err) + } + }) } func TestVerifyBlobCmdInvalidRootCA(t *testing.T) { From 0a2e6e7421131c4b53450ebc3c40f61f425262c2 Mon Sep 17 00:00:00 2001 From: Asra Ali Date: Thu, 15 Sep 2022 11:55:07 -0500 Subject: [PATCH 4/5] address comments Signed-off-by: Asra Ali --- cmd/cosign/cli/verify/verify_blob_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/cosign/cli/verify/verify_blob_test.go b/cmd/cosign/cli/verify/verify_blob_test.go index 12f76ece3a6..e63e95f833e 100644 --- a/cmd/cosign/cli/verify/verify_blob_test.go +++ b/cmd/cosign/cli/verify/verify_blob_test.go @@ -1038,7 +1038,7 @@ func TestVerifyBlobCmdInvalidRootCA(t *testing.T) { keyless := newKeylessStack(t) // Change the keyless stack. _ = newKeylessStack(t) - t.Run("Invalid certificate root explicit certRef", func(t *testing.T) { + t.Run("Invalid certificate root when specifying cert via certRef", func(t *testing.T) { identity := "hello@foo.com" issuer := "issuer" leafCert, _, leafPemCert, signer := keyless.genLeafCert(t, identity, issuer) @@ -1077,7 +1077,7 @@ func TestVerifyBlobCmdInvalidRootCA(t *testing.T) { t.Fatalf("expected error with invalid root CA, got %v", err) } }) - t.Run("Invalid certificate root explicit bundle", func(t *testing.T) { + t.Run("Invalid certificate root when specifying cert in bundle", func(t *testing.T) { identity := "hello@foo.com" issuer := "issuer" leafCert, _, leafPemCert, signer := keyless.genLeafCert(t, identity, issuer) From 4f1ae75cacf086d7c91fda2d381df9beb4a0ab08 Mon Sep 17 00:00:00 2001 From: Asra Ali Date: Thu, 15 Sep 2022 12:04:11 -0500 Subject: [PATCH 5/5] update Signed-off-by: Asra Ali --- cmd/cosign/cli/verify/verify_blob_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/cosign/cli/verify/verify_blob_test.go b/cmd/cosign/cli/verify/verify_blob_test.go index e63e95f833e..226cfe54143 100644 --- a/cmd/cosign/cli/verify/verify_blob_test.go +++ b/cmd/cosign/cli/verify/verify_blob_test.go @@ -955,7 +955,7 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) { t.Fatalf("expected error with mismatched issuer, got %v", err) } }) - t.Run("Intermediate root not explicit in non-experimental mode", func(t *testing.T) { + t.Run("Implicit Fulcio chain with bundle in non-experimental mode", func(t *testing.T) { identity := "hello@foo.com" issuer := "issuer" leafCert, _, leafPemCert, signer := keyless.genLeafCert(t, identity, issuer) @@ -994,7 +994,7 @@ func TestVerifyBlobCmdWithBundle(t *testing.T) { t.Fatalf("expected success without specifying the intermediates, got %v", err) } }) - t.Run("Intermediate root explicit in non-experimental mode", func(t *testing.T) { + t.Run("Explicit Fulcio chain with bundle in non-experimental mode", func(t *testing.T) { identity := "hello@foo.com" issuer := "issuer" leafCert, _, leafPemCert, signer := keyless.genLeafCert(t, identity, issuer)