Skip to content

Commit

Permalink
Fix e2e test failure, add test for local bundle without rekor bundle (#…
Browse files Browse the repository at this point in the history
…2248)

* Fix e2e test failure, add test for local bundle without rekor bundle

Signed-off-by: Hayden Blauzvern <hblauzvern@google.com>

* fix verify logic when bundle does not contain rekor bundle

Signed-off-by: Asra Ali <asraa@google.com>

Signed-off-by: Hayden Blauzvern <hblauzvern@google.com>
Signed-off-by: Asra Ali <asraa@google.com>
Co-authored-by: Asra Ali <asraa@google.com>
  • Loading branch information
haydentherapper and asraa authored Sep 14, 2022
1 parent 58a96ba commit a780d68
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 71 deletions.
33 changes: 12 additions & 21 deletions cmd/cosign/cli/verify/verify_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func VerifyBlobCmd(ctx context.Context, ko options.KeyOpts, certRef, certEmail,
certGithubWorkflowRepository,
certGithubWorkflowRef string, enforceSCT bool) error {
var cert *x509.Certificate
var bundle *bundle.RekorBundle

if !options.OneOf(ko.KeyRef, ko.Sk, certRef) && !options.EnableExperimental() && ko.BundlePath == "" {
return &options.PubKeyParseError{}
Expand Down Expand Up @@ -178,13 +179,11 @@ func VerifyBlobCmd(ctx context.Context, ko options.KeyOpts, certRef, certEmail,
co.SigVerifier, err = sigs.LoadPublicKeyRaw(certBytes, crypto.SHA256)
} else {
co.SigVerifier, err = cosign.ValidateAndUnpackCert(cert, co)
if err != nil {
return err
}
}
if err != nil {
return err
}
bundle = b.Bundle
// No certificate is provided: search by artifact sha in the TLOG.
case options.EnableExperimental():
uuids, err := cosign.FindTLogEntriesByPayload(ctx, co.RekorClient, blobBytes)
Expand Down Expand Up @@ -219,7 +218,7 @@ func VerifyBlobCmd(ctx context.Context, ko options.KeyOpts, certRef, certEmail,
}

if err := verifyBlob(ctx, co, blobBytes, sig, cert,
ko.BundlePath, tlogEntry); err == nil {
nil, tlogEntry); err == nil {
// We found a succesful Rekor entry!
fmt.Fprintln(os.Stderr, "Verified OK")
return nil
Expand All @@ -237,7 +236,7 @@ We recommend requesting the certificate/signature from the original signer of th
}

// Performs all blob verification.
if err := verifyBlob(ctx, co, blobBytes, sig, cert, ko.BundlePath, nil); err != nil {
if err := verifyBlob(ctx, co, blobBytes, sig, cert, bundle, nil); err != nil {
return err
}

Expand All @@ -258,7 +257,7 @@ We recommend requesting the certificate/signature from the original signer of th
// clean up the args into CheckOpts or use KeyOpts here to resolve different KeyOpts.
func verifyBlob(ctx context.Context, co *cosign.CheckOpts,
blobBytes []byte, sig string, cert *x509.Certificate,
bundlePath string, e *models.LogEntryAnon) error {
bundle *bundle.RekorBundle, e *models.LogEntryAnon) error {
if cert != nil {
// This would have already be done in the main entrypoint, but do this for robustness.
var err error
Expand Down Expand Up @@ -287,7 +286,7 @@ func verifyBlob(ctx context.Context, co *cosign.CheckOpts,
// 2. Checks for transparency log entry presence:
switch {
// a. We have a local bundle.
case bundlePath != "":
case bundle != nil:
var svBytes []byte
var err error
if cert != nil {
Expand All @@ -301,7 +300,7 @@ func verifyBlob(ctx context.Context, co *cosign.CheckOpts,
return fmt.Errorf("marshalling pubkey: %w", err)
}
}
bundle, err := verifyRekorBundle(ctx, bundlePath, co.RekorClient, blobBytes, sig, svBytes)
bundle, err := verifyRekorBundle(ctx, bundle, co.RekorClient, blobBytes, sig, svBytes)
if err != nil {
// Return when the provided bundle fails verification. (Do not fallback).
return err
Expand Down Expand Up @@ -433,17 +432,9 @@ func payloadBytes(blobRef string) ([]byte, error) {

// TODO: RekorClient can be removed when SIGSTORE_TRUST_REKOR_API_PUBLIC_KEY
// is removed.
func verifyRekorBundle(ctx context.Context, bundlePath string, rekorClient *client.Rekor,
func verifyRekorBundle(ctx context.Context, bundle *bundle.RekorBundle, rekorClient *client.Rekor,
blobBytes []byte, sig string, pubKeyBytes []byte) (*bundle.RekorPayload, error) {
b, err := cosign.FetchLocalSignedPayloadFromPath(bundlePath)
if err != nil {
return nil, err
}
if b.Bundle == nil {
return nil, fmt.Errorf("rekor entry could not be extracted from local bundle")
}

if err := verifyBundleMatchesData(ctx, b.Bundle, blobBytes, pubKeyBytes, []byte(sig)); err != nil {
if err := verifyBundleMatchesData(ctx, bundle, blobBytes, pubKeyBytes, []byte(sig)); err != nil {
return nil, err
}

Expand All @@ -452,19 +443,19 @@ func verifyRekorBundle(ctx context.Context, bundlePath string, rekorClient *clie
return nil, fmt.Errorf("retrieving rekor public key: %w", err)
}

pubKey, ok := publicKeys[b.Bundle.Payload.LogID]
pubKey, ok := publicKeys[bundle.Payload.LogID]
if !ok {
return nil, errors.New("rekor log public key not found for payload")
}
err = cosign.VerifySET(b.Bundle.Payload, b.Bundle.SignedEntryTimestamp, pubKey.PubKey)
err = cosign.VerifySET(bundle.Payload, bundle.SignedEntryTimestamp, pubKey.PubKey)
if err != nil {
return nil, err
}
if pubKey.Status != tuf.Active {
fmt.Fprintf(os.Stderr, "**Info** Successfully verified Rekor entry using an expired verification key\n")
}

return &b.Bundle.Payload, nil
return &bundle.Payload, nil
}

func verifyBundleMatchesData(ctx context.Context, bundle *bundle.RekorBundle, blobBytes, certBytes, sigBytes []byte) error {
Expand Down
37 changes: 36 additions & 1 deletion cmd/cosign/cli/verify/verify_blob_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,15 @@ func TestVerifyBlob(t *testing.T) {
pubKeyBytes, true),
shouldErr: false,
},
{
name: "valid signature with public key - bundle without rekor bundle fails",
blob: blobBytes,
signature: blobSignature,
sigVerifier: signer,
experimental: false,
bundlePath: makeLocalBundleWithoutRekorBundle(t, []byte(blobSignature), pubKeyBytes),
shouldErr: false,
},
{
name: "valid signature with public key - bad bundle SET",
blob: blobBytes,
Expand Down Expand Up @@ -532,7 +541,13 @@ func TestVerifyBlob(t *testing.T) {
co.RekorClient = &mClient
}

err := verifyBlob(ctx, co, tt.blob, tt.signature, tt.cert, tt.bundlePath, nil)
var bundle *bundle.RekorBundle
b, err := cosign.FetchLocalSignedPayloadFromPath(tt.bundlePath)
if err == nil && b.Bundle != nil {
bundle = b.Bundle
}

err = verifyBlob(ctx, co, tt.blob, tt.signature, tt.cert, bundle, nil)
if (err != nil) != tt.shouldErr {
t.Fatalf("verifyBlob()= %s, expected shouldErr=%t ", err, tt.shouldErr)
}
Expand Down Expand Up @@ -647,6 +662,26 @@ func makeLocalBundle(t *testing.T, rekorSigner signature.ECDSASignerVerifier,
return bundlePath
}

func makeLocalBundleWithoutRekorBundle(t *testing.T, sig []byte, svBytes []byte) string {
td := t.TempDir()

b := cosign.LocalSignedPayload{
Base64Signature: base64.StdEncoding.EncodeToString(sig),
Cert: string(svBytes),
}

// Write bundle to disk
jsonBundle, err := json.Marshal(b)
if err != nil {
t.Fatal(err)
}
bundlePath := filepath.Join(td, "bundle.sig")
if err := os.WriteFile(bundlePath, jsonBundle, 0644); err != nil {
t.Fatal(err)
}
return bundlePath
}

func TestVerifyBlobCmdWithBundle(t *testing.T) {
keyless := newKeylessStack(t)

Expand Down
95 changes: 47 additions & 48 deletions test/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -644,54 +644,53 @@ func TestSignBlob(t *testing.T) {
mustErr(cliverify.VerifyBlobCmd(ctx, ko2, "" /*certRef*/, "" /*certEmail*/, "" /*certOidcIssuer*/, "" /*certChain*/, string(sig), bp, "", "", "", "", "", false), t)
}

// TODO: Uncomment and fix
// func TestSignBlobBundle(t *testing.T) {
// blob := "someblob"
// td1 := t.TempDir()
// t.Cleanup(func() {
// os.RemoveAll(td1)
// })
// bp := filepath.Join(td1, blob)
// bundlePath := filepath.Join(td1, "bundle.sig")

// if err := os.WriteFile(bp, []byte(blob), 0644); err != nil {
// t.Fatal(err)
// }

// _, privKeyPath1, pubKeyPath1 := keypair(t, td1)

// ctx := context.Background()

// ko1 := options.KeyOpts{
// KeyRef: pubKeyPath1,
// BundlePath: bundlePath,
// }
// // Verify should fail on a bad input
// mustErr(cliverify.VerifyBlobCmd(ctx, ko1, "", "", "", "", "", blob, "", "", "", "", "", false), t)

// // Now sign the blob with one key
// ko := options.KeyOpts{
// KeyRef: privKeyPath1,
// PassFunc: passFunc,
// BundlePath: bundlePath,
// RekorURL: rekorURL,
// }
// if _, err := sign.SignBlobCmd(ro, ko, options.RegistryOptions{}, bp, true, "", ""); err != nil {
// t.Fatal(err)
// }
// // Now verify should work
// must(cliverify.VerifyBlobCmd(ctx, ko1, "", "", "", "", "", bp, "", "", "", "", "", false), t)

// // Now we turn on the tlog and sign again
// defer setenv(t, options.ExperimentalEnv, "1")()
// if _, err := sign.SignBlobCmd(ro, ko, options.RegistryOptions{}, bp, true, "", ""); err != nil {
// t.Fatal(err)
// }

// // Point to a fake rekor server to make sure offline verification of the tlog entry works
// os.Setenv(serverEnv, "notreal")
// must(cliverify.VerifyBlobCmd(ctx, ko1, "", "", "", "", "", bp, "", "", "", "", "", false), t)
// }
func TestSignBlobBundle(t *testing.T) {
blob := "someblob"
td1 := t.TempDir()
t.Cleanup(func() {
os.RemoveAll(td1)
})
bp := filepath.Join(td1, blob)
bundlePath := filepath.Join(td1, "bundle.sig")

if err := os.WriteFile(bp, []byte(blob), 0644); err != nil {
t.Fatal(err)
}

_, privKeyPath1, pubKeyPath1 := keypair(t, td1)

ctx := context.Background()

ko1 := options.KeyOpts{
KeyRef: pubKeyPath1,
BundlePath: bundlePath,
}
// Verify should fail on a bad input
mustErr(cliverify.VerifyBlobCmd(ctx, ko1, "", "", "", "", "", blob, "", "", "", "", "", false), t)

// Now sign the blob with one key
ko := options.KeyOpts{
KeyRef: privKeyPath1,
PassFunc: passFunc,
BundlePath: bundlePath,
RekorURL: rekorURL,
}
if _, err := sign.SignBlobCmd(ro, ko, options.RegistryOptions{}, bp, true, "", ""); err != nil {
t.Fatal(err)
}
// Now verify should work
must(cliverify.VerifyBlobCmd(ctx, ko1, "", "", "", "", "", bp, "", "", "", "", "", false), t)

// Now we turn on the tlog and sign again
defer setenv(t, options.ExperimentalEnv, "1")()
if _, err := sign.SignBlobCmd(ro, ko, options.RegistryOptions{}, bp, true, "", ""); err != nil {
t.Fatal(err)
}

// Point to a fake rekor server to make sure offline verification of the tlog entry works
os.Setenv(serverEnv, "notreal")
must(cliverify.VerifyBlobCmd(ctx, ko1, "", "", "", "", "", bp, "", "", "", "", "", false), t)
}

func TestGenerate(t *testing.T) {
repo, stop := reg(t)
Expand Down
5 changes: 4 additions & 1 deletion test/e2e_test_secrets.sh
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,16 @@ echo "myblob2" > myblob2
./cosign sign-blob --key ${signing_key} myblob2 > myblob2.sig

./cosign verify-blob --key ${verification_key} --signature myblob.sig myblob
# expected to fail because signature mismatch
if (./cosign verify-blob --key ${verification_key} --signature myblob.sig myblob2); then false; fi

# expected to fail because signature mismatch
if (./cosign verify-blob --key ${verification_key} --signature myblob2.sig myblob); then false; fi
./cosign verify-blob --key ${verification_key} --signature myblob2.sig myblob2

./cosign sign-blob --key ${signing_key} --bundle bundle.sig myblob
./cosign verify-blob --key ${verification_key} --bundle bundle.sig myblob
# expected to fail because the local bundle does not contain a rekor bundle
if (./cosign verify-blob --key ${verification_key} --bundle bundle.sig myblob); then false; fi

## sign and verify multiple blobs
./cosign sign-blob --key ${signing_key} myblob myblob2 > sigs
Expand Down

0 comments on commit a780d68

Please sign in to comment.