Skip to content

Commit

Permalink
feat: refactor error messages for cosign verification
Browse files Browse the repository at this point in the history
  • Loading branch information
binbin-li committed Sep 9, 2024
1 parent 4f48d2f commit 8893dac
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 78 deletions.
4 changes: 2 additions & 2 deletions pkg/referrerstore/oras/cosign.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func getCosignReferences(ctx context.Context, subjectReference common.Reference,
return nil, nil
}
evictOnError(ctx, err, subjectReference.Original)
return nil, re.ErrorCodeRepositoryOperationFailure.WithError(err).WithComponentType(re.ReferrerStore)
return nil, re.ErrorCodeRepositoryOperationFailure.WithDetail("Failed to resolve Cosign signature reference to descriptor").WithError(err)
}

references = append(references, ocispecs.ReferenceDescriptor{
Expand All @@ -64,7 +64,7 @@ func getCosignReferences(ctx context.Context, subjectReference common.Reference,
func attachedImageTag(subjectReference common.Reference, tagSuffix string) (string, error) {
// sha256:d34db33f -> sha256-d34db33f.suffix
if subjectReference.Digest.String() == "" {
return "", re.ErrorCodeReferenceInvalid.WithComponentType(re.ReferrerStore).WithDetail("Cosign subject digest is empty")
return "", re.ErrorCodeReferenceInvalid.WithDetail("Cosign subject digest is empty").WithRemediation("Ensure the Cosign signature was generated correctly.")
}
tagStr := strings.ReplaceAll(subjectReference.Digest.String(), ":", "-") + tagSuffix
return fmt.Sprintf("%s:%s", subjectReference.Path, tagStr), nil
Expand Down
8 changes: 4 additions & 4 deletions pkg/referrerstore/oras/oras.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ func (store *orasStore) GetConfig() *config.StoreConfig {
func (store *orasStore) ListReferrers(ctx context.Context, subjectReference common.Reference, _ []string, _ string, subjectDesc *ocispecs.SubjectDescriptor) (referrerstore.ListReferrersResult, error) {
repository, err := store.createRepository(ctx, store, subjectReference)
if err != nil {
return referrerstore.ListReferrersResult{}, re.ErrorCodeCreateRepositoryFailure.WithError(err).WithComponentType(re.ReferrerStore)
return referrerstore.ListReferrersResult{}, re.ErrorCodeRepositoryOperationFailure.WithDetail("Failed to create client to remote registry").WithError(err)
}

// resolve subject descriptor if not provided
Expand Down Expand Up @@ -260,7 +260,7 @@ func (store *orasStore) GetBlobContent(ctx context.Context, subjectReference com

repository, err := store.createRepository(ctx, store, subjectReference)
if err != nil {
return nil, err
return nil, re.ErrorCodeGetBlobContentFailure.WithDetail("Failed to connect to the remote registry").WithError(err)
}

// create a dummy Descriptor to check the local store cache
Expand Down Expand Up @@ -292,10 +292,10 @@ func (store *orasStore) GetBlobContent(ctx context.Context, subjectReference com
blobDesc, rc, err := repository.Blobs().FetchReference(ctx, ref)
if err != nil {
evictOnError(ctx, err, subjectReference.Original)
return nil, err
return nil, re.ErrorCodeRepositoryOperationFailure.WithDetail("Failed to fetch the artifact metadata from the registry").WithError(err)
}
if blobContent, err = io.ReadAll(rc); err != nil {
return nil, re.ErrorCodeGetBlobContentFailure.WithError(err)
return nil, re.ErrorCodeRepositoryOperationFailure.WithDetail("Failed to parse the artifact metadata").WithError(err)
}

// push fetched content to local ORAS cache
Expand Down
40 changes: 20 additions & 20 deletions pkg/verifier/cosign/cosign.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,17 +143,17 @@ func (f *cosignVerifierFactory) Create(_ string, verifierConfig config.VerifierC
logger.GetLogger(context.Background(), logOpt).Debugf("creating cosign verifier with config %v, namespace '%v'", verifierConfig, namespace)
verifierName, hasName := verifierConfig[types.Name].(string)
if !hasName {
return nil, re.ErrorCodeConfigInvalid.WithComponentType(re.Verifier).WithPluginName(verifierName).WithDetail("missing name in verifier config")
return nil, re.ErrorCodeConfigInvalid.WithDetail("The name field is required in the Cosign Verifier configuration")
}

config, err := parseVerifierConfig(verifierConfig)
if err != nil {
return nil, re.ErrorCodeConfigInvalid.WithComponentType(re.Verifier).WithPluginName(verifierName)
return nil, re.ErrorCodeConfigInvalid.WithDetail("Failed to create Cosign Verifier").WithError(err)
}

// if key or rekorURL is provided, trustPolicies should not be provided
if (config.KeyRef != "" || config.RekorURL != "") && len(config.TrustPolicies) > 0 {
return nil, re.ErrorCodeConfigInvalid.WithComponentType(re.Verifier).WithPluginName(verifierName).WithDetail("'key' and 'rekorURL' are part of cosign legacy configuration and cannot be used with `trustPolicies`")
return nil, re.ErrorCodeConfigInvalid.WithDetail("'key' and 'rekorURL' are part of Cosign legacy configuration and cannot be used with `trustPolicies` parameter")
}

var trustPolicies *TrustPolicies
Expand All @@ -163,7 +163,7 @@ func (f *cosignVerifierFactory) Create(_ string, verifierConfig config.VerifierC
logger.GetLogger(context.Background(), logOpt).Debugf("legacy cosign verifier configuration not found, creating trust policies")
trustPolicies, err = CreateTrustPolicies(config.TrustPolicies, verifierName)
if err != nil {
return nil, err
return nil, re.ErrorCodePluginInitFailure.WithDetail("Failed to create Cosign Verifier").WithError(err)
}
legacy = false
}
Expand Down Expand Up @@ -224,18 +224,18 @@ func (v *cosignVerifier) verifyInternal(ctx context.Context, subjectReference co
// get the reference manifest (cosign oci image)
referenceManifest, err := referrerStore.GetReferenceManifest(ctx, subjectReference, referenceDescriptor)
if err != nil {
return errorToVerifyResult(v.name, v.verifierType, fmt.Errorf("failed to get reference manifest: %w", err)), nil
return errorToVerifyResult(v.name, v.verifierType, re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("Failed to get artifact metadata for %s", referenceDescriptor.Digest)).WithError(err)), nil
}

// manifest must be an OCI Image
if referenceManifest.MediaType != imgspec.MediaTypeImageManifest {
return errorToVerifyResult(v.name, v.verifierType, fmt.Errorf("reference manifest is not an image")), nil
return errorToVerifyResult(v.name, v.verifierType, re.ErrorCodeVerifyPluginFailure.WithDetail("The artifact metadata is not an oci image")), nil
}

// get the subject image descriptor
subjectDesc, err := referrerStore.GetSubjectDescriptor(ctx, subjectReference)
if err != nil {
return errorToVerifyResult(v.name, v.verifierType, fmt.Errorf("failed to create subject hash: %w", err)), nil
return errorToVerifyResult(v.name, v.verifierType, re.ErrorCodeGetSubjectDescriptorFailure.WithDetail(fmt.Sprintf("Failed to get the descriptor for subject: %s", subjectReference.Digest)).WithError(err)), nil
}

// create the hash of the subject image descriptor (used as the hashed payload)
Expand All @@ -255,23 +255,23 @@ func (v *cosignVerifier) verifyInternal(ctx context.Context, subjectReference co
// fetch the blob content of the signature from the referrer store
blobBytes, err := referrerStore.GetBlobContent(ctx, subjectReference, blob.Digest)
if err != nil {
return errorToVerifyResult(v.name, v.verifierType, fmt.Errorf("failed to get blob content: %w", err)), nil
return errorToVerifyResult(v.name, v.verifierType, re.ErrorCodeGetBlobContentFailure.WithDetail(fmt.Sprintf("Failed to get Cosign signature with digest %s", blob.Digest)).WithError(err)), nil
}
// convert the blob to a static signature
staticOpts, err := staticLayerOpts(blob)
if err != nil {
return errorToVerifyResult(v.name, v.verifierType, fmt.Errorf("failed to parse static signature opts: %w", err)), nil
return errorToVerifyResult(v.name, v.verifierType, re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("Failed to parse Cosign signature with digest %s", blob.Digest)).WithError(err)), nil
}
sig, err := static.NewSignature(blobBytes, blob.Annotations[static.SignatureAnnotationKey], staticOpts...)
if err != nil {
return errorToVerifyResult(v.name, v.verifierType, fmt.Errorf("failed to generate static signature: %w", err)), nil
return errorToVerifyResult(v.name, v.verifierType, re.ErrorCodeVerifyPluginFailure.WithDetail("Failed to generate static Cosign signature").WithError(err)), nil
}
if len(keysMap) > 0 {
// if keys are found, perform verification with keys
var verifications []cosignExtension
verifications, hasValidSignature, err = verifyWithKeys(ctx, keysMap, sig, blob.Annotations[static.SignatureAnnotationKey], blobBytes, staticOpts, &cosignOpts, subjectDescHash)
if err != nil {
return errorToVerifyResult(v.name, v.verifierType, fmt.Errorf("failed to verify with keys: %w", err)), nil
return errorToVerifyResult(v.name, v.verifierType, re.ErrorCodeVerifyPluginFailure.WithDetail("Failed to verify Cosign signature with keys").WithError(err)), nil
}
extensionListEntry.Verifications = append(extensionListEntry.Verifications, verifications...)
} else {
Expand All @@ -295,7 +295,7 @@ func (v *cosignVerifier) verifyInternal(ctx context.Context, subjectReference co
), nil
}

errorResult := errorToVerifyResult(v.name, v.verifierType, fmt.Errorf("no valid signatures found"))
errorResult := errorToVerifyResult(v.name, v.verifierType, fmt.Errorf("no valid Cosign signatures found"))
errorResult.Extensions = Extension{SignatureExtension: sigExtensions, TrustPolicy: trustPolicy.GetName()}
return errorResult, nil
}
Expand Down Expand Up @@ -485,7 +485,7 @@ func staticLayerOpts(desc imgspec.Descriptor) ([]static.Option, error) {

// ErrorToVerifyResult returns a verifier result with the error message and isSuccess set to false
func errorToVerifyResult(name string, verifierType string, err error) verifier.VerifierResult {
verifierErr := re.ErrorCodeVerifyReferenceFailure.WithDetail("Verification failed").WithError(err)
verifierErr := re.ErrorCodeVerifyReferenceFailure.WithDetail("Cosign signature verification failed").WithError(err)
return verifier.NewVerifierResult(
"",
name,
Expand Down Expand Up @@ -540,14 +540,14 @@ func verifyWithKeys(ctx context.Context, keysMap map[PKKey]keymanagementprovider
if pubKey.ProviderType == azurekeyvault.ProviderName {
hashType, sig, err = processAKVSignature(sigEncoded, sig, pubKey.Key, payload, staticOpts)
if err != nil {
return verifications, false, fmt.Errorf("failed to process AKV signature: %w", err)
return verifications, false, re.ErrorCodeVerifyPluginFailure.WithDetail("Failed to process AKV signature").WithError(err)
}
}

// return the correct verifier based on public key type and bytes
verifier, err := signature.LoadVerifier(pubKey.Key, hashType)
if err != nil {
return verifications, false, fmt.Errorf("failed to load public key from provider [%s] name [%s] version [%s]: %w", mapKey.Provider, mapKey.Name, mapKey.Version, err)
return verifications, false, re.ErrorCodeVerifyPluginFailure.WithDetail(fmt.Sprintf("Failed to load public key from provider [%s] name [%s] version [%s]", mapKey.Provider, mapKey.Name, mapKey.Version)).WithError(err)
}
cosignOpts.SigVerifier = verifier
// verify signature with cosign options + perform bundle verification
Expand Down Expand Up @@ -627,17 +627,17 @@ func processAKVSignature(sigEncoded string, staticSig oci.Signature, publicKey c
// EC verifiers in cosign have built in ASN.1 decoding, but RSA verifiers do not
base64DecodedBytes, err := base64.StdEncoding.DecodeString(sigEncoded)
if err != nil {
return crypto.SHA256, nil, fmt.Errorf("RSA key check: failed to decode base64 signature: %w", err)
return crypto.SHA256, nil, re.ErrorCodeVerifyPluginFailure.WithDetail("RSA key check: failed to decode base64 signature").WithError(err)
}
// decode ASN.1 signature to raw signature if it is ASN.1 encoded
decodedSigBytes, err := decodeASN1Signature(base64DecodedBytes)
if err != nil {
return crypto.SHA256, nil, fmt.Errorf("RSA key check: failed to decode ASN.1 signature: %w", err)
return crypto.SHA256, nil, re.ErrorCodeVerifyPluginFailure.WithDetail("RSA key check: failed to decode ASN.1 signature").WithError(err)
}
encodedBase64SigBytes := base64.StdEncoding.EncodeToString(decodedSigBytes)
staticSig, err = static.NewSignature(payloadBytes, encodedBase64SigBytes, staticOpts...)
if err != nil {
return crypto.SHA256, nil, fmt.Errorf("RSA key check: failed to generate static signature: %w", err)
return crypto.SHA256, nil, re.ErrorCodeVerifyPluginFailure.WithDetail("RSA key check: failed to generate static signature").WithError(err)
}
case *ecdsa.PublicKey:
switch keyType.Curve {
Expand All @@ -648,10 +648,10 @@ func processAKVSignature(sigEncoded string, staticSig oci.Signature, publicKey c
case elliptic.P521():
hashType = crypto.SHA512
default:
return crypto.SHA256, nil, fmt.Errorf("ECDSA key check: unsupported key curve: %s", keyType.Params().Name)
return crypto.SHA256, nil, fmt.Errorf("ECDSA key check: unsupported key curve [%s]", keyType.Params().Name)
}
default:
return crypto.SHA256, nil, fmt.Errorf("unsupported public key type: %T", publicKey)
return crypto.SHA256, nil, fmt.Errorf("unsupported public key type [%T]", publicKey)
}
return hashType, staticSig, nil
}
Expand Down
Loading

0 comments on commit 8893dac

Please sign in to comment.