Skip to content

Commit

Permalink
verify: remove extra calls to rekor for verify and verify-blob (#1694)
Browse files Browse the repository at this point in the history
* remove extra calls to rekor for verify-blob

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

* fix hex output for uuid

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

* fix lint

Signed-off-by: Asra Ali <asraa@google.com>
  • Loading branch information
asraa authored Mar 31, 2022
1 parent 02f5099 commit ac682db
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 66 deletions.
45 changes: 27 additions & 18 deletions cmd/cosign/cli/verify/verify_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
_ "crypto/sha256" // for `crypto.SHA256`
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -176,7 +177,7 @@ func VerifyBlobCmd(ctx context.Context, ko sign.KeyOpts, certRef, certEmail, cer
}

// verify the rekor entry
if err := verifyRekorEntry(ctx, ko, verifier, cert, b64sig, blobBytes); err != nil {
if err := verifyRekorEntry(ctx, ko, nil, verifier, cert, b64sig, blobBytes); err != nil {
return err
}

Expand Down Expand Up @@ -217,7 +218,7 @@ func verifySigByUUID(ctx context.Context, ko sign.KeyOpts, rClient *client.Rekor
}

// verify the rekor entry
if err := verifyRekorEntry(ctx, ko, verifier, cert, b64sig, blobBytes); err != nil {
if err := verifyRekorEntry(ctx, ko, tlogEntry, verifier, cert, b64sig, blobBytes); err != nil {
continue
}
validSigExists = true
Expand Down Expand Up @@ -284,7 +285,7 @@ func payloadBytes(blobRef string) ([]byte, error) {
return blobBytes, nil
}

func verifyRekorEntry(ctx context.Context, ko sign.KeyOpts, pubKey signature.Verifier, cert *x509.Certificate, b64sig string, blobBytes []byte) error {
func verifyRekorEntry(ctx context.Context, ko sign.KeyOpts, e *models.LogEntryAnon, pubKey signature.Verifier, cert *x509.Certificate, b64sig string, blobBytes []byte) error {
// If we have a bundle with a rekor entry, let's first try to verify offline
if ko.BundlePath != "" {
if err := verifyRekorBundle(ctx, ko.BundlePath, cert); err == nil {
Expand All @@ -300,33 +301,41 @@ func verifyRekorEntry(ctx context.Context, ko sign.KeyOpts, pubKey signature.Ver
if err != nil {
return err
}
var pubBytes []byte
if pubKey != nil {
pubBytes, err = sigs.PublicKeyPem(pubKey, signatureoptions.WithContext(ctx))
if err != nil {
return err
// Only fetch from rekor tlog if we don't already have the entry.
if e == nil {
var pubBytes []byte
if pubKey != nil {
pubBytes, err = sigs.PublicKeyPem(pubKey, signatureoptions.WithContext(ctx))
if err != nil {
return err
}
}
}
if cert != nil {
pubBytes, err = cryptoutils.MarshalCertificateToPEM(cert)
if cert != nil {
pubBytes, err = cryptoutils.MarshalCertificateToPEM(cert)
if err != nil {
return err
}
}
e, err = cosign.FindTlogEntry(ctx, rekorClient, b64sig, blobBytes, pubBytes)
if err != nil {
return err
}
}
uuid, index, err := cosign.FindTlogEntry(ctx, rekorClient, b64sig, blobBytes, pubBytes)

if err := cosign.VerifyTLogEntry(ctx, rekorClient, e); err != nil {
return nil
}

uuid, err := cosign.ComputeLeafHash(e)
if err != nil {
return err
}
fmt.Fprintf(os.Stderr, "tlog entry verified with uuid: %q index: %d\n", uuid, index)

fmt.Fprintf(os.Stderr, "tlog entry verified with uuid: %s index: %d\n", hex.EncodeToString(uuid), *e.Verification.InclusionProof.LogIndex)
if cert == nil {
return nil
}
// if we have a cert, we should check expiry
// The IntegratedTime verified in VerifyTlog
e, err := cosign.GetTlogEntry(ctx, rekorClient, uuid)
if err != nil {
return err
}
return cosign.CheckExpiry(cert, time.Unix(*e.IntegratedTime, 0))
}

Expand Down
101 changes: 59 additions & 42 deletions pkg/cosign/tlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,11 @@ func doUpload(ctx context.Context, rekorClient *client.Rekor, pe models.Proposed
fmt.Println("Signature already exists. Displaying proof")
uriSplit := strings.Split(existsErr.Location.String(), "/")
uuid := uriSplit[len(uriSplit)-1]
return verifyTLogEntry(ctx, rekorClient, uuid)
e, err := GetTlogEntry(ctx, rekorClient, uuid)
if err != nil {
return nil, err
}
return e, VerifyTLogEntry(ctx, rekorClient, e)
}
return nil, err
}
Expand Down Expand Up @@ -182,14 +186,43 @@ func rekorEntry(payload, signature, pubKey []byte) hashedrekord_v001.V001Entry {
}
}

func ComputeLeafHash(e *models.LogEntryAnon) ([]byte, error) {
entryBytes, err := base64.StdEncoding.DecodeString(e.Body.(string))
if err != nil {
return nil, err
}
return rfc6962.DefaultHasher.HashLeaf(entryBytes), nil
}

func verifyUUID(uuid string, e models.LogEntryAnon) error {
entryUUID, _ := hex.DecodeString(uuid)

// Verify leaf hash matches hash of the entry body.
computedLeafHash, err := ComputeLeafHash(&e)
if err != nil {
return err
}
if !bytes.Equal(computedLeafHash, entryUUID) {
return fmt.Errorf("computed leaf hash did not match entry UUID")
}
return nil
}

func GetTlogEntry(ctx context.Context, rekorClient *client.Rekor, uuid string) (*models.LogEntryAnon, error) {
params := entries.NewGetLogEntryByUUIDParamsWithContext(ctx)
params.SetEntryUUID(uuid)
resp, err := rekorClient.Entries.GetLogEntryByUUID(params)
if err != nil {
return nil, err
}
for _, e := range resp.Payload {
for k, e := range resp.Payload {
// Check that body hash matches UUID
if k != uuid {
return nil, fmt.Errorf("unexpected entry returned from rekor server")
}
if err := verifyUUID(k, e); err != nil {
return nil, err
}
return &e, nil
}
return nil, errors.New("empty response")
Expand Down Expand Up @@ -222,39 +255,40 @@ func proposedEntry(b64Sig string, payload, pubKey []byte) ([]models.ProposedEntr
return proposedEntry, nil
}

func FindTlogEntry(ctx context.Context, rekorClient *client.Rekor, b64Sig string, payload, pubKey []byte) (uuid string, index int64, err error) {
func FindTlogEntry(ctx context.Context, rekorClient *client.Rekor, b64Sig string, payload, pubKey []byte) (entry *models.LogEntryAnon, err error) {
searchParams := entries.NewSearchLogQueryParamsWithContext(ctx)
searchLogQuery := models.SearchLogQuery{}
proposedEntry, err := proposedEntry(b64Sig, payload, pubKey)
if err != nil {
return "", 0, err
return nil, err
}

searchLogQuery.SetEntries(proposedEntry)

searchParams.SetEntry(&searchLogQuery)
resp, err := rekorClient.Entries.SearchLogQuery(searchParams)
if err != nil {
return "", 0, errors.Wrap(err, "searching log query")
return nil, errors.Wrap(err, "searching log query")
}
if len(resp.Payload) == 0 {
return "", 0, errors.New("signature not found in transparency log")
return nil, errors.New("signature not found in transparency log")
} else if len(resp.Payload) > 1 {
return "", 0, errors.New("multiple entries returned; this should not happen")
return nil, errors.New("multiple entries returned; this should not happen")
}
logEntry := resp.Payload[0]
if len(logEntry) != 1 {
return "", 0, errors.New("UUID value can not be extracted")
return nil, errors.New("UUID value can not be extracted")
}

for k := range logEntry {
uuid = k
}
verifiedEntry, err := verifyTLogEntry(ctx, rekorClient, uuid)
if err != nil {
return "", 0, err
var tlogEntry models.LogEntryAnon
for k, e := range logEntry {
// Check body hash matches uuid
if err := verifyUUID(k, e); err != nil {
return nil, err
}
tlogEntry = e
}
return uuid, *verifiedEntry.Verification.InclusionProof.LogIndex, nil
return &tlogEntry, nil
}

func FindTLogEntriesByPayload(ctx context.Context, rekorClient *client.Rekor, payload []byte) (uuids []string, err error) {
Expand All @@ -272,21 +306,9 @@ func FindTLogEntriesByPayload(ctx context.Context, rekorClient *client.Rekor, pa
return searchIndex.GetPayload(), nil
}

func verifyTLogEntry(ctx context.Context, rekorClient *client.Rekor, uuid string) (*models.LogEntryAnon, error) {
params := entries.NewGetLogEntryByUUIDParamsWithContext(ctx)
params.EntryUUID = uuid

lep, err := rekorClient.Entries.GetLogEntryByUUID(params)
if err != nil {
return nil, err
}

if len(lep.Payload) != 1 {
return nil, errors.New("UUID value can not be extracted")
}
e := lep.Payload[params.EntryUUID]
func VerifyTLogEntry(ctx context.Context, rekorClient *client.Rekor, e *models.LogEntryAnon) error {
if e.Verification == nil || e.Verification.InclusionProof == nil {
return nil, errors.New("inclusion proof not provided")
return errors.New("inclusion proof not provided")
}

hashes := [][]byte{}
Expand All @@ -296,21 +318,16 @@ func verifyTLogEntry(ctx context.Context, rekorClient *client.Rekor, uuid string
}

rootHash, _ := hex.DecodeString(*e.Verification.InclusionProof.RootHash)
leafHash, _ := hex.DecodeString(params.EntryUUID)

// Verify leaf hash matches hash of the entry body.
entryBytes, err := base64.StdEncoding.DecodeString(e.Body.(string))
if err != nil {
return nil, err
}
if !bytes.Equal(rfc6962.DefaultHasher.HashLeaf(entryBytes), leafHash) {
return nil, fmt.Errorf("computed leaf hash did not match entry UUID")
return err
}
leafHash := rfc6962.DefaultHasher.HashLeaf(entryBytes)

// Verify the inclusion proof.
v := logverifier.New(rfc6962.DefaultHasher)
if err := v.VerifyInclusionProof(*e.Verification.InclusionProof.LogIndex, *e.Verification.InclusionProof.TreeSize, hashes, rootHash, leafHash); err != nil {
return nil, errors.Wrap(err, "verifying inclusion proof")
return errors.Wrap(err, "verifying inclusion proof")
}

// Verify rekor's signature over the SET.
Expand All @@ -323,18 +340,18 @@ func verifyTLogEntry(ctx context.Context, rekorClient *client.Rekor, uuid string

rekorPubKeys, err := GetRekorPubs(ctx)
if err != nil {
return nil, errors.Wrap(err, "unable to fetch Rekor public keys from TUF repository")
return errors.Wrap(err, "unable to fetch Rekor public keys from TUF repository")
}

addRekorPublic := os.Getenv(addRekorPublicKeyFromRekor)
if addRekorPublic != "" {
pubOK, err := rekorClient.Pubkey.GetPublicKey(nil)
if err != nil {
return nil, errors.Wrap(err, "unable to fetch rekor public key from rekor")
return errors.Wrap(err, "unable to fetch rekor public key from rekor")
}
pubFromAPI, err := PemToECDSAKey([]byte(pubOK.Payload))
if err != nil {
return nil, errors.Wrap(err, "error converting rekor PEM public key from rekor to ECDSAKey")
return errors.Wrap(err, "error converting rekor PEM public key from rekor to ECDSAKey")
}
rekorPubKeys = append(rekorPubKeys, RekorPubKey{PubKey: pubFromAPI, Status: tuf.Active})
}
Expand All @@ -347,8 +364,8 @@ func verifyTLogEntry(ctx context.Context, rekorClient *client.Rekor, uuid string
if pubKey.Status != tuf.Active {
fmt.Fprintf(os.Stderr, "**Info** Successfully verified Rekor entry using an expired verification key\n")
}
return &e, nil
return nil
}
}
return nil, errors.Wrap(entryVerError, "verifying signedEntryTimestamp")
return errors.Wrap(entryVerError, "verifying signedEntryTimestamp")
}
10 changes: 4 additions & 6 deletions pkg/cosign/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ func tlogValidatePublicKey(ctx context.Context, rekorClient *client.Rekor, pub c
if err != nil {
return err
}
_, _, err = FindTlogEntry(ctx, rekorClient, b64sig, payload, pemBytes)
_, err = FindTlogEntry(ctx, rekorClient, b64sig, payload, pemBytes)
return err
}

Expand All @@ -235,16 +235,14 @@ func tlogValidateCertificate(ctx context.Context, rekorClient *client.Rekor, sig
if err != nil {
return err
}
uuid, _, err := FindTlogEntry(ctx, rekorClient, b64sig, payload, pemBytes)
e, err := FindTlogEntry(ctx, rekorClient, b64sig, payload, pemBytes)
if err != nil {
return err
}
// if we have a cert, we should check expiry
// The IntegratedTime verified in VerifyTlog
e, err := GetTlogEntry(ctx, rekorClient, uuid)
if err != nil {
if err := VerifyTLogEntry(ctx, rekorClient, e); err != nil {
return err
}
// if we have a cert, we should check expiry
return CheckExpiry(cert, time.Unix(*e.IntegratedTime, 0))
}

Expand Down

0 comments on commit ac682db

Please sign in to comment.