Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix e2e test failure, add test for local bundle without rekor bundle #2248

Merged
merged 2 commits into from Sep 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 12 additions & 21 deletions cmd/cosign/cli/verify/verify_blob.go
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
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
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
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