From ef0f1a7a24c172b25b1c74bdef4e4a4e8a71e1a4 Mon Sep 17 00:00:00 2001 From: asraa Date: Wed, 2 Nov 2022 15:32:39 -0500 Subject: [PATCH] refactor: consolidate verification funcs for GHA (#348) * consolidate verification funcs Signed-off-by: Asra Ali --- .github/workflows/pre-submit.cli.yml | 2 +- cli/slsa-verifier/main_test.go | 4 +- verifiers/internal/gha/provenance.go | 50 +++-- verifiers/internal/gha/rekor.go | 308 ++++++++++----------------- verifiers/internal/gha/rekor_test.go | 2 +- verifiers/internal/gha/verifier.go | 6 +- 6 files changed, 157 insertions(+), 215 deletions(-) diff --git a/.github/workflows/pre-submit.cli.yml b/.github/workflows/pre-submit.cli.yml index 35409daf3..9549c1dce 100644 --- a/.github/workflows/pre-submit.cli.yml +++ b/.github/workflows/pre-submit.cli.yml @@ -46,4 +46,4 @@ jobs: go build -mod=vendor -o service ./cli/experimental/service/ # Tests - go test -mod=vendor -v ./... + go test -mod=vendor -v -timeout=20m ./... diff --git a/cli/slsa-verifier/main_test.go b/cli/slsa-verifier/main_test.go index 99f8e8e58..105b9fadf 100644 --- a/cli/slsa-verifier/main_test.go +++ b/cli/slsa-verifier/main_test.go @@ -401,7 +401,7 @@ func Test_runVerifyGHAArtifactPath(t *testing.T) { name: "rekor upload bypassed", artifact: "binary-linux-amd64-no-tlog-upload", source: "github.com/slsa-framework/example-package", - err: serrors.ErrorNoValidRekorEntries, + err: serrors.ErrorRekorSearch, noversion: true, }, { @@ -415,7 +415,7 @@ func Test_runVerifyGHAArtifactPath(t *testing.T) { name: "malicious: invalid signature expired certificate", artifact: "binary-linux-amd64-expired-cert", source: "github.com/slsa-framework/example-package", - err: serrors.ErrorNoValidRekorEntries, + err: serrors.ErrorRekorSearch, noversion: true, }, // Annotated tags. diff --git a/verifiers/internal/gha/provenance.go b/verifiers/internal/gha/provenance.go index ab26ec885..5b179474e 100644 --- a/verifiers/internal/gha/provenance.go +++ b/verifiers/internal/gha/provenance.go @@ -14,11 +14,24 @@ import ( intoto "github.com/in-toto/in-toto-golang/in_toto" dsselib "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/sigstore/rekor/pkg/generated/client" + "github.com/sigstore/rekor/pkg/generated/models" + "github.com/slsa-framework/slsa-github-generator/signing/envelope" serrors "github.com/slsa-framework/slsa-verifier/errors" "github.com/slsa-framework/slsa-verifier/options" ) +// SignedAttestation contains a signed DSSE envelope +// and its associated signing certificate. +type SignedAttestation struct { + // The signed DSSE envelope + Envelope *dsselib.Envelope + // The signing certificate + SigningCert *x509.Certificate + // The associated verified Rekor entry + RekorEntry *models.LogEntryAnon +} + func EnvelopeFromBytes(payload []byte) (env *dsselib.Envelope, err error) { env = &dsselib.Envelope{} err = json.Unmarshal(payload, env) @@ -150,32 +163,26 @@ func verifySha256Digest(prov *intoto.ProvenanceStatement, expectedHash string) e // VerifyProvenanceSignature returns the verified DSSE envelope containing the provenance // and the signing certificate given the provenance and artifact hash. -func VerifyProvenanceSignature(ctx context.Context, rClient *client.Rekor, provenance []byte, artifactHash string) (*dsselib.Envelope, *x509.Certificate, error) { - // Get Rekor entries corresponding to provenance - env, cert, err := GetRekorEntriesWithCert(rClient, provenance) - if err == nil { - return env, cert, nil +func VerifyProvenanceSignature(ctx context.Context, rClient *client.Rekor, + provenance []byte, artifactHash string) ( + *SignedAttestation, error) { + // There are two cases, either we have an embedded certificate, or we need + // to use the Redis index for searching by artifact SHA. + if hasCertInEnvelope(provenance) { + // Get Rekor entries corresponding to provenance + return GetValidSignedAttestationWithCert(rClient, provenance) } // Fallback on using the redis search index to get matching UUIDs. - fmt.Fprintf(os.Stderr, "Getting rekor entry error %s, trying Redis search index to find entries by subject digest\n", err) - uuids, err := GetRekorEntries(rClient, artifactHash) - if err != nil { - return nil, nil, err - } - - env, err = EnvelopeFromBytes(provenance) - if err != nil { - return nil, nil, err - } + fmt.Fprintf(os.Stderr, "No certificate provided, trying Redis search index to find entries by subject digest\n") // Verify the provenance and return the signing certificate. - cert, err = FindSigningCertificate(ctx, uuids, *env, rClient) + signedAttestation, err := SearchValidSignedAttestation(ctx, artifactHash, provenance, rClient) if err != nil { - return nil, nil, err + return nil, err } - return env, cert, nil + return signedAttestation, nil } func VerifyProvenance(env *dsselib.Envelope, provenanceOpts *options.ProvenanceOpts) error { @@ -556,3 +563,10 @@ func getBranch(prov *intoto.ProvenanceStatement) (string, error) { "unknown ref type", refType) } } + +// hasCertInEnvelope checks if a valid x509 certificate is present in the +// envelope. +func hasCertInEnvelope(provenance []byte) bool { + certPem, err := envelope.GetCertFromEnvelope(provenance) + return err == nil && len(certPem) > 0 +} diff --git a/verifiers/internal/gha/rekor.go b/verifiers/internal/gha/rekor.go index 0a9514a96..941961a0b 100644 --- a/verifiers/internal/gha/rekor.go +++ b/verifiers/internal/gha/rekor.go @@ -4,10 +4,8 @@ import ( "bytes" "context" "crypto" - "crypto/ecdsa" "crypto/x509" "encoding/base64" - "encoding/hex" "errors" "fmt" "os" @@ -18,25 +16,20 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" - dsselib "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/sigstore/cosign/cmd/cosign/cli/fulcio" "github.com/sigstore/cosign/pkg/cosign" - "github.com/sigstore/cosign/pkg/cosign/bundle" "github.com/sigstore/rekor/pkg/generated/client" "github.com/sigstore/rekor/pkg/generated/client/entries" "github.com/sigstore/rekor/pkg/generated/client/index" - "github.com/sigstore/rekor/pkg/generated/client/tlog" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/sharding" "github.com/sigstore/rekor/pkg/types" intotod "github.com/sigstore/rekor/pkg/types/intoto/v0.0.1" - "github.com/sigstore/rekor/pkg/util" + rverify "github.com/sigstore/rekor/pkg/verify" "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/dsse" "github.com/slsa-framework/slsa-github-generator/signing/envelope" - "github.com/transparency-dev/merkle/proof" - "github.com/transparency-dev/merkle/rfc6962" serrors "github.com/slsa-framework/slsa-verifier/errors" ) @@ -45,75 +38,8 @@ const ( defaultRekorAddr = "https://rekor.sigstore.dev" ) -func verifyRootHash(ctx context.Context, rekorClient *client.Rekor, - treeID int64, eproof *models.InclusionProof, pub *ecdsa.PublicKey) error { - treeIDString := fmt.Sprintf("%d", treeID) - infoParams := tlog.NewGetLogInfoParamsWithContext(ctx) - result, err := rekorClient.Tlog.GetLogInfo(infoParams) - if err != nil { - return err - } - - logInfo := result.GetPayload() - - sth := util.SignedCheckpoint{} - if err := sth.UnmarshalText([]byte(*logInfo.SignedTreeHead)); err != nil { - return err - } - for _, inactiveShard := range logInfo.InactiveShards { - if *inactiveShard.TreeID == treeIDString { - if err := sth.UnmarshalText([]byte(*inactiveShard.SignedTreeHead)); err != nil { - return err - } - } - } - - verifier, err := signature.LoadVerifier(pub, crypto.SHA256) - if err != nil { - return err - } - - if !sth.Verify(verifier) { - return errors.New("signature on tree head did not verify") - } - - rootHash, err := hex.DecodeString(*eproof.RootHash) - if err != nil { - return errors.New("error decoding root hash in inclusion proof") - } - - if *eproof.TreeSize == int64(sth.Size) { - if !bytes.Equal(rootHash, sth.Hash) { - return errors.New("root hash returned from server does not match inclusion proof hash") - } - } else if *eproof.TreeSize < int64(sth.Size) { - consistencyParams := tlog.NewGetLogProofParamsWithContext(ctx) - consistencyParams.FirstSize = eproof.TreeSize // Root hash at the time the proof was returned - consistencyParams.LastSize = int64(sth.Size) // Root hash verified with rekor pubkey - - consistencyProof, err := rekorClient.Tlog.GetLogProof(consistencyParams) - if err != nil { - return err - } - var hashes [][]byte - for _, h := range consistencyProof.Payload.Hashes { - b, err := hex.DecodeString(h) - if err != nil { - return errors.New("error decoding consistency proof hashes") - } - hashes = append(hashes, b) - } - if err := proof.VerifyConsistency(rfc6962.DefaultHasher, - uint64(*eproof.TreeSize), sth.Size, hashes, rootHash, sth.Hash); err != nil { - return err - } - } else if *eproof.TreeSize > int64(sth.Size) { - return errors.New("inclusion proof returned a tree size larger than the verified tree size") - } - return nil -} - -func verifyTlogEntryByUUID(ctx context.Context, rekorClient *client.Rekor, entryUUID string) (*models.LogEntryAnon, error) { +func verifyTlogEntryByUUID(ctx context.Context, rekorClient *client.Rekor, entryUUID string) ( + *models.LogEntryAnon, error) { params := entries.NewGetLogEntryByUUIDParamsWithContext(ctx) params.EntryUUID = entryUUID @@ -140,89 +66,33 @@ func verifyTlogEntryByUUID(ctx context.Context, rekorClient *client.Rekor, entry if returnUUID != uuid { return nil, errors.New("expected matching UUID") } - return verifyTlogEntry(ctx, rekorClient, k, entry) + // Validate the entry response. + return verifyTlogEntry(ctx, rekorClient, entry) } return nil, serrors.ErrorRekorSearch } -func verifyTlogEntry(ctx context.Context, rekorClient *client.Rekor, - entryUUID string, e models.LogEntryAnon) (*models.LogEntryAnon, error) { - if e.Verification == nil || e.Verification.InclusionProof == nil { - return nil, errors.New("inclusion proof not provided") - } - - uuid, err := sharding.GetUUIDFromIDString(entryUUID) - if err != nil { - return nil, fmt.Errorf("%w: retrieving uuid from entry uuid", err) - } - treeID, err := sharding.TreeID(entryUUID) - if err != nil { - return nil, fmt.Errorf("%w: retrieving tree ID", err) - } - - var hashes [][]byte - for _, h := range e.Verification.InclusionProof.Hashes { - hb, err := hex.DecodeString(h) - if err != nil { - return nil, errors.New("error decoding inclusion proof hashes") - } - hashes = append(hashes, hb) - } - - rootHash, err := hex.DecodeString(*e.Verification.InclusionProof.RootHash) - if err != nil { - return nil, errors.New("error decoding hex encoded root hash") - } - leafHash, err := hex.DecodeString(uuid) - if err != nil { - return nil, errors.New("error decoding hex encoded leaf hash") - } - +func verifyTlogEntry(ctx context.Context, rekorClient *client.Rekor, e models.LogEntryAnon) ( + *models.LogEntryAnon, error) { // Verify the root hash against the current Signed Entry Tree Head pubs, err := cosign.GetRekorPubs(ctx, nil) if err != nil { return nil, fmt.Errorf("%w: %s", err, "unable to fetch Rekor public keys from TUF repository") } - var entryVerError error - for _, pubKey := range pubs { - // Verify inclusion against the signed tree head - entryVerError = verifyRootHash(ctx, rekorClient, treeID, - e.Verification.InclusionProof, pubKey.PubKey) - if entryVerError == nil { - break - } - } - if entryVerError != nil { - return nil, fmt.Errorf("%w: %s", entryVerError, "error verifying root hash") - } - - // Verify the entry's inclusion - if err := proof.VerifyInclusion(rfc6962.DefaultHasher, - uint64(*e.Verification.InclusionProof.LogIndex), - uint64(*e.Verification.InclusionProof.TreeSize), leafHash, hashes, rootHash); err != nil { - return nil, fmt.Errorf("%w: %s", err, "verifying inclusion proof") - } - - // Verify rekor's signature over the SET. - payload := bundle.RekorPayload{ - Body: e.Body, - IntegratedTime: *e.IntegratedTime, - LogIndex: *e.LogIndex, - LogID: *e.LogID, + verifier, err := signature.LoadECDSAVerifier(pubs[*e.LogID].PubKey, crypto.SHA256) + if err != nil { + return nil, fmt.Errorf("%w: %s", err, "unable to fetch Rekor public keys from TUF repository") } - var setVerError error - for _, pubKey := range pubs { - setVerError = cosign.VerifySET(payload, e.Verification.SignedEntryTimestamp, pubKey.PubKey) - // Return once the SET is verified successfully. - if setVerError == nil { - break - } + // This function verifies the inclusion proof, the signature on the root hash of the + // inclusion proof, and the SignedEntryTimestamp. + if err := rverify.VerifyLogEntry(ctx, &e, verifier); err != nil { + return nil, fmt.Errorf("%w: %s", err, "unable to fetch Rekor public keys from TUF repository") } - return &e, setVerError + return &e, nil } func extractCert(e *models.LogEntryAnon) (*x509.Certificate, error) { @@ -284,8 +154,8 @@ func intotoEntry(certPem []byte, provenance []byte) (*intotod.V001Entry, error) }, nil } -// GetRekorEntries finds all entry UUIDs by the digest of the artifact binary. -func GetRekorEntries(rClient *client.Rekor, artifactHash string) ([]string, error) { +// getUUIDsByArtifactDigest finds all entry UUIDs by the digest of the artifact binary. +func getUUIDsByArtifactDigest(rClient *client.Rekor, artifactHash string) ([]string, error) { // Use search index to find rekor entry UUIDs that match Subject Digest. params := index.NewSearchIndexParams() params.Query = &models.SearchIndex{Hash: fmt.Sprintf("sha256:%v", artifactHash)} @@ -301,20 +171,21 @@ func GetRekorEntries(rClient *client.Rekor, artifactHash string) ([]string, erro return resp.GetPayload(), nil } -// GetRekorEntriesWithCert finds all entry UUIDs with the full intoto attestation. +// GetValidSignedAttestationWithCert finds and validates the matching entry UUIDs with +// the full intoto attestation. // The attestation generated by the slsa-github-generator libraries contain a signing certificate. -func GetRekorEntriesWithCert(rClient *client.Rekor, provenance []byte) (*dsselib.Envelope, *x509.Certificate, error) { +func GetValidSignedAttestationWithCert(rClient *client.Rekor, provenance []byte) (*SignedAttestation, error) { // Use intoto attestation to find rekor entry UUIDs. params := entries.NewSearchLogQueryParams() searchLogQuery := models.SearchLogQuery{} certPem, err := envelope.GetCertFromEnvelope(provenance) if err != nil { - return nil, nil, fmt.Errorf("error getting certificate from provenance: %w", err) + return nil, fmt.Errorf("error getting certificate from provenance: %w", err) } e, err := intotoEntry(certPem, provenance) if err != nil { - return nil, nil, fmt.Errorf("error creating intoto entry: %w", err) + return nil, fmt.Errorf("error creating intoto entry: %w", err) } entry := models.Intoto{ APIVersion: swag.String(e.APIVersion()), @@ -326,41 +197,61 @@ func GetRekorEntriesWithCert(rClient *client.Rekor, provenance []byte) (*dsselib params.SetEntry(&searchLogQuery) resp, err := rClient.Entries.SearchLogQuery(params) if err != nil { - return nil, nil, fmt.Errorf("%w: %s", serrors.ErrorRekorSearch, err.Error()) + return nil, fmt.Errorf("%w: %s", serrors.ErrorRekorSearch, err.Error()) } if len(resp.GetPayload()) != 1 { - return nil, nil, fmt.Errorf("%w: %s", serrors.ErrorRekorSearch, "no matching rekor entries") + return nil, fmt.Errorf("%w: %s", serrors.ErrorRekorSearch, "no matching rekor entries") } logEntry := resp.Payload[0] + var rekorEntry models.LogEntryAnon for uuid, e := range logEntry { - if _, err := verifyTlogEntry(context.Background(), rClient, uuid, e); err != nil { - return nil, nil, fmt.Errorf("error verifying tlog entry: %w", err) + if _, err := verifyTlogEntry(context.Background(), rClient, e); err != nil { + return nil, fmt.Errorf("error verifying tlog entry: %w", err) } + rekorEntry = e url := fmt.Sprintf("%v/%v/%v", defaultRekorAddr, "api/v1/log/entries", uuid) fmt.Fprintf(os.Stderr, "Verified signature against tlog entry index %d at URL: %s\n", *e.LogIndex, url) } - env, err := EnvelopeFromBytes(provenance) + certs, err := cryptoutils.UnmarshalCertificatesFromPEM(certPem) if err != nil { - return nil, nil, err + return nil, err + } + if len(certs) != 1 { + return nil, fmt.Errorf("error unmarshaling certificate from pem") } - certs, err := cryptoutils.UnmarshalCertificatesFromPEM(certPem) + env, err := EnvelopeFromBytes(provenance) if err != nil { - return nil, nil, err + return nil, err } - if len(certs) != 1 { - return nil, nil, fmt.Errorf("error unmarshaling certificate from pem") + + proposedSignedAtt := &SignedAttestation{ + SigningCert: certs[0], + Envelope: env, + RekorEntry: &rekorEntry, } - return env, certs[0], nil + if err := verifySignedAttestation(proposedSignedAtt); err != nil { + return nil, err + } + + return proposedSignedAtt, nil } -// FindSigningCertificate finds and verifies a matching signing certificate from a list of Rekor entry UUIDs. -func FindSigningCertificate(ctx context.Context, uuids []string, dssePayload dsselib.Envelope, rClient *client.Rekor) (*x509.Certificate, error) { - attBytes, err := cjson.MarshalCanonical(dssePayload) +// SearchValidSignedAttestation searches for a valid signing certificate using the Rekor +// Redis search index by using the artifact digest. +func SearchValidSignedAttestation(ctx context.Context, artifactHash string, provenance []byte, + rClient *client.Rekor) (*SignedAttestation, error) { + // Get Rekor UUIDs by artifact digest. + uuids, err := getUUIDsByArtifactDigest(rClient, artifactHash) + if err != nil { + return nil, err + } + + env, err := EnvelopeFromBytes(provenance) if err != nil { return nil, err } @@ -379,6 +270,7 @@ func FindSigningCertificate(ctx context.Context, uuids []string, dssePayload dss errs = append(errs, fmt.Sprintf("%s: verifying tlog entry %s", err, uuid)) continue } + cert, err := extractCert(entry) if err != nil { // this is unexpected, hold on to this error. @@ -386,39 +278,73 @@ func FindSigningCertificate(ctx context.Context, uuids []string, dssePayload dss continue } - roots, err := fulcio.GetRoots() - if err != nil { - // this is unexpected, hold on to this error. - errs = append(errs, fmt.Sprintf("%s: retrieving fulcio root", err)) - continue - } - co := &cosign.CheckOpts{ - RootCerts: roots, - CertOidcIssuer: certOidcIssuer, - } - verifier, err := cosign.ValidateAndUnpackCert(cert, co) - if err != nil { - continue - } - verifier = dsse.WrapVerifier(verifier) - if err := verifier.VerifySignature(bytes.NewReader(attBytes), bytes.NewReader(attBytes)); err != nil { - continue + proposedSignedAtt := &SignedAttestation{ + Envelope: env, + SigningCert: cert, + RekorEntry: entry, } - it := time.Unix(*entry.IntegratedTime, 0) - if err := cosign.CheckExpiry(cert, it); err != nil { - continue - } - uuid, err := cosign.ComputeLeafHash(entry) - if err != nil { - fmt.Fprintf(os.Stderr, "Error computing leaf hash for tlog entry at index: %d\n", *entry.LogIndex) + + err = verifySignedAttestation(proposedSignedAtt) + if errors.Is(err, serrors.ErrorInternal) { + // Return on an internal error + return nil, err + } else if err != nil { + errs = append(errs, err.Error()) continue } // success! - url := fmt.Sprintf("%v/%v/%v", defaultRekorAddr, "api/v1/log/entries", hex.EncodeToString(uuid)) + url := fmt.Sprintf("%v/%v/%v", defaultRekorAddr, "api/v1/log/entries", uuid) fmt.Fprintf(os.Stderr, "Verified signature against tlog entry index %d at URL: %s\n", *entry.LogIndex, url) - return cert, nil + return proposedSignedAtt, nil } return nil, fmt.Errorf("%w: got unexpected errors %s", serrors.ErrorNoValidRekorEntries, strings.Join(errs, ", ")) } + +// verifyAttestationSignature validates the signature on the attestation +// given a certificate and a validated signature time. +// The certificate is verified up to Fulcio, the signature is validated +// using the certificate, and the signature generation time is checked +// to be within the certificate validity period. +func verifySignedAttestation(signedAtt *SignedAttestation) error { + cert := signedAtt.SigningCert + attBytes, err := cjson.MarshalCanonical(signedAtt.Envelope) + if err != nil { + return err + } + signatureTimestamp := time.Unix(*signedAtt.RekorEntry.IntegratedTime, 0) + + // 1. Verify certificate chain. + roots, err := fulcio.GetRoots() + if err != nil { + // this is unexpected, hold on to this error. + return fmt.Errorf("%w: %s", serrors.ErrorInternal, err) + } + intermediates, err := fulcio.GetIntermediates() + if err != nil { + // this is unexpected, hold on to this error. + return fmt.Errorf("%w: %s", serrors.ErrorInternal, err) + } + co := &cosign.CheckOpts{ + RootCerts: roots, + IntermediateCerts: intermediates, + CertOidcIssuer: certOidcIssuer, + } + verifier, err := cosign.ValidateAndUnpackCert(signedAtt.SigningCert, co) + if err != nil { + return fmt.Errorf("%w: %s", serrors.ErrorInvalidSignature, err) + } + + // 2. Verify signature using validated certificate. + verifier = dsse.WrapVerifier(verifier) + if err := verifier.VerifySignature(bytes.NewReader(attBytes), bytes.NewReader(attBytes)); err != nil { + return fmt.Errorf("%w: %s", serrors.ErrorInvalidSignature, err) + } + + // 3. Verify signature was creating during certificate validity period. + if err := cosign.CheckExpiry(cert, signatureTimestamp); err != nil { + return fmt.Errorf("%w: %s", serrors.ErrorInvalidSignature, err) + } + return nil +} diff --git a/verifiers/internal/gha/rekor_test.go b/verifiers/internal/gha/rekor_test.go index 2d8c285e0..2182d202e 100644 --- a/verifiers/internal/gha/rekor_test.go +++ b/verifiers/internal/gha/rekor_test.go @@ -81,7 +81,7 @@ func Test_GetRekorEntries(t *testing.T) { var mClient client.Rekor mClient.Index = &MockIndexClient{result: tt.res} - _, err := GetRekorEntries(&mClient, tt.artifactHash) + _, err := getUUIDsByArtifactDigest(&mClient, tt.artifactHash) if !errCmp(err, tt.expected) { t.Errorf(cmp.Diff(err, tt.expected)) } diff --git a/verifiers/internal/gha/verifier.go b/verifiers/internal/gha/verifier.go index 49f7274be..d6a023060 100644 --- a/verifiers/internal/gha/verifier.go +++ b/verifiers/internal/gha/verifier.go @@ -92,12 +92,14 @@ func (v *GHAVerifier) VerifyArtifact(ctx context.Context, } /* Verify signature on the intoto attestation. */ - env, cert, err := VerifyProvenanceSignature(ctx, rClient, provenance, artifactHash) + // TODO(https://github.com/slsa-framework/slsa-github-generator/issues/716): + // We will also need to support bundles when those are complete. + signedAtt, err := VerifyProvenanceSignature(ctx, rClient, provenance, artifactHash) if err != nil { return nil, nil, err } - return verifyEnvAndCert(env, cert, + return verifyEnvAndCert(signedAtt.Envelope, signedAtt.SigningCert, provenanceOpts, builderOpts, defaultArtifactTrustedReusableWorkflows) }