diff --git a/cmd/cosign/cli/fulcio/fulcioverifier/fulcioverifier.go b/cmd/cosign/cli/fulcio/fulcioverifier/fulcioverifier.go index d66dc4a5918..1f6025ef77e 100644 --- a/cmd/cosign/cli/fulcio/fulcioverifier/fulcioverifier.go +++ b/cmd/cosign/cli/fulcio/fulcioverifier/fulcioverifier.go @@ -52,7 +52,7 @@ const altCTLogPublicKeyLocation = "SIGSTORE_CT_LOG_PUBLIC_KEY_FILE" // the certificate issued by Fulcio was also added to the public CT log within // some defined time period func verifySCT(ctx context.Context, certPEM, rawSCT []byte) error { - var pubKeys []crypto.PublicKey + pubKeys := make(map[crypto.PublicKey]tuf.StatusKind) rootEnv := os.Getenv(altCTLogPublicKeyLocation) if rootEnv == "" { tufClient, err := tuf.NewFromEnv(ctx) @@ -70,7 +70,7 @@ func verifySCT(ctx context.Context, certPEM, rawSCT []byte) error { if err != nil { return errors.Wrap(err, "converting Public CT to ECDSAKey") } - pubKeys = append(pubKeys, ctPub) + pubKeys[ctPub] = t.Status } } else { fmt.Fprintf(os.Stderr, "**Warning** Using a non-standard public key for verifying SCT: %s\n", rootEnv) @@ -82,7 +82,7 @@ func verifySCT(ctx context.Context, certPEM, rawSCT []byte) error { if err != nil { return errors.Wrap(err, "error parsing alternate public key from the file") } - pubKeys = append(pubKeys, pubKey) + pubKeys[pubKey] = tuf.Active } if len(pubKeys) == 0 { return errors.New("none of the CTFE keys have been found") @@ -96,10 +96,13 @@ func verifySCT(ctx context.Context, certPEM, rawSCT []byte) error { return errors.Wrap(err, "unmarshal") } var verifySctErr error - for _, pubKey := range pubKeys { + for pubKey, status := range pubKeys { verifySctErr = ctutil.VerifySCT(pubKey, []*ctx509.Certificate{cert}, &sct, false) // Exit after successful verification of the SCT if verifySctErr == nil { + if status != tuf.Active { + fmt.Fprintf(os.Stderr, "**Info** Successfully verified SCT using an expired verification key\n") + } return nil } } diff --git a/cmd/cosign/cli/verify/verify_blob.go b/cmd/cosign/cli/verify/verify_blob.go index 61ed0839c52..78e5ade6e38 100644 --- a/cmd/cosign/cli/verify/verify_blob.go +++ b/cmd/cosign/cli/verify/verify_blob.go @@ -40,6 +40,7 @@ import ( "github.com/sigstore/cosign/pkg/cosign" "github.com/sigstore/cosign/pkg/cosign/pivkey" "github.com/sigstore/cosign/pkg/cosign/pkcs11key" + "github.com/sigstore/cosign/pkg/cosign/tuf" sigs "github.com/sigstore/cosign/pkg/signature" ctypes "github.com/sigstore/cosign/pkg/types" @@ -297,9 +298,12 @@ func verifyRekorBundle(ctx context.Context, bundlePath string, cert *x509.Certif var entryVerError error for _, pubKey := range publicKeys { - entryVerError = cosign.VerifySET(b.Bundle.Payload, b.Bundle.SignedEntryTimestamp, pubKey) + entryVerError = cosign.VerifySET(b.Bundle.Payload, b.Bundle.SignedEntryTimestamp, pubKey.PubKey) // Exit early with successful verification if entryVerError == nil { + if pubKey.Status != tuf.Active { + fmt.Fprintf(os.Stderr, "**Info** Successfully verified Rekor entry using an expired verification key\n") + } break } } diff --git a/pkg/cosign/tlog.go b/pkg/cosign/tlog.go index bb380b7c921..285c07fc49c 100644 --- a/pkg/cosign/tlog.go +++ b/pkg/cosign/tlog.go @@ -21,6 +21,7 @@ import ( "encoding/base64" "encoding/hex" "fmt" + "os" "strings" "github.com/go-openapi/strfmt" @@ -34,7 +35,6 @@ import ( "github.com/sigstore/rekor/pkg/generated/client" "github.com/sigstore/rekor/pkg/generated/client/entries" - "github.com/sigstore/rekor/pkg/generated/client/pubkey" "github.com/sigstore/rekor/pkg/generated/models" hashedrekord_v001 "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1" intoto_v001 "github.com/sigstore/rekor/pkg/types/intoto/v0.0.1" @@ -43,9 +43,16 @@ import ( // This is the rekor public key target name var rekorTargetStr = `rekor.pub` +// RekorPubKey contains the ECDSA verification key and the current status +// of the key according to TUF metadata, whether it's active or expired. +type RekorPubKey struct { + PubKey *ecdsa.PublicKey + Status tuf.StatusKind +} + // GetRekorPubs retrieves trusted Rekor public keys from the embedded or cached // TUF root. If expired, makes a network call to retrieve the updated targets. -func GetRekorPubs(ctx context.Context) ([]*ecdsa.PublicKey, error) { +func GetRekorPubs(ctx context.Context) ([]RekorPubKey, error) { tufClient, err := tuf.NewFromEnv(ctx) if err != nil { return nil, err @@ -55,13 +62,13 @@ func GetRekorPubs(ctx context.Context) ([]*ecdsa.PublicKey, error) { if err != nil { return nil, err } - publicKeys := make([]*ecdsa.PublicKey, 0, len(targets)) + publicKeys := make([]RekorPubKey, 0, len(targets)) for _, t := range targets { rekorPubKey, err := PemToECDSAKey(t.Target) if err != nil { return nil, errors.Wrap(err, "pem to ecdsa") } - publicKeys = append(publicKeys, rekorPubKey) + publicKeys = append(publicKeys, RekorPubKey{PubKey: rekorPubKey, Status: t.Status}) } if len(publicKeys) == 0 { return nil, errors.New("none of the Rekor public keys have been found") @@ -269,24 +276,27 @@ func verifyTLogEntry(ctx context.Context, rekorClient *client.Rekor, uuid string } // Verify rekor's signature over the SET. - resp, err := rekorClient.Pubkey.GetPublicKey(pubkey.NewGetPublicKeyParamsWithContext(ctx)) - if err != nil { - return nil, errors.Wrap(err, "rekor public key") - } - rekorPubKey, err := PemToECDSAKey([]byte(resp.Payload)) - if err != nil { - return nil, errors.Wrap(err, "rekor public key pem to ecdsa") - } - payload := bundle.RekorPayload{ Body: e.Body, IntegratedTime: *e.IntegratedTime, LogIndex: *e.LogIndex, LogID: *e.LogID, } - if err := VerifySET(payload, []byte(e.Verification.SignedEntryTimestamp), rekorPubKey); err != nil { - return nil, errors.Wrap(err, "verifying signedEntryTimestamp") - } - return &e, nil + rekorPubKeys, err := GetRekorPubs(ctx) + if err != nil { + return nil, errors.Wrap(err, "unable to fetch Rekor public keys from TUF repository") + } + var entryVerError error + for _, pubKey := range rekorPubKeys { + entryVerError = VerifySET(payload, []byte(e.Verification.SignedEntryTimestamp), pubKey.PubKey) + // Return once the SET is verified successfully. + if entryVerError == nil { + 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, errors.Wrap(entryVerError, "verifying signedEntryTimestamp") } diff --git a/pkg/cosign/verify.go b/pkg/cosign/verify.go index d06b85602f3..40ea2136d12 100644 --- a/pkg/cosign/verify.go +++ b/pkg/cosign/verify.go @@ -31,6 +31,7 @@ import ( "time" cbundle "github.com/sigstore/cosign/pkg/cosign/bundle" + "github.com/sigstore/cosign/pkg/cosign/tuf" "github.com/sigstore/cosign/pkg/blob" "github.com/sigstore/cosign/pkg/oci/static" @@ -592,9 +593,12 @@ func VerifyBundle(ctx context.Context, sig oci.Signature) (bool, error) { var entryVerError error for _, pubKey := range publicKeys { - entryVerError = VerifySET(bundle.Payload, bundle.SignedEntryTimestamp, pubKey) + entryVerError = VerifySET(bundle.Payload, bundle.SignedEntryTimestamp, pubKey.PubKey) // Exit early with successful verification if entryVerError == nil { + if pubKey.Status != tuf.Active { + fmt.Fprintf(os.Stderr, "**Info** Successfully verified Rekor entry using an expired verification key\n") + } break } }