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 Aug 23, 2024
1 parent aaba189 commit 0e31b2f
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 54 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 attached correctly.")
}
tagStr := strings.ReplaceAll(subjectReference.Digest.String(), ":", "-") + tagSuffix
return fmt.Sprintf("%s:%s", subjectReference.Path, tagStr), nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/referrerstore/oras/oras.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ func (store *orasStore) GetBlobContent(ctx context.Context, subjectReference com
var err error
repository, err := store.createRepository(ctx, store, subjectReference)
if err != nil {
return nil, err
return nil, re.ErrorCodeGetBlobContentFailure.WithDetail("Failed to create client to remote registry").WithError(err)
}

// create a dummy Descriptor to check the local store cache
Expand Down
38 changes: 19 additions & 19 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("Missing name in Cosign verifier config")
}

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`")
}

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("Failed to get reference manifest").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("Reference manifest 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 blob content for %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("Failed to parse static signature options").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 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 @@ -651,7 +651,7 @@ func processAKVSignature(sigEncoded string, staticSig oci.Signature, publicKey c
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
20 changes: 10 additions & 10 deletions pkg/verifier/cosign/trustpolicies.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ var validScopeRegex = regexp.MustCompile(`^[a-z0-9\.\-:@\/]*\*?$`)
// CreateTrustPolicies creates a set of trust policies from the given configuration
func CreateTrustPolicies(configs []TrustPolicyConfig, verifierName string) (*TrustPolicies, error) {
if len(configs) == 0 {
return nil, re.ErrorCodeConfigInvalid.WithComponentType(re.Verifier).WithPluginName(verifierName).WithDetail("failed to create trust policies: no policies found")
return nil, re.ErrorCodeConfigInvalid.WithDetail("Failed to create trust policies: no policies found").WithRemediation("Ensure that the trust policy configuration is correct.")
}

policies := make([]TrustPolicy, 0, len(configs))
names := make(map[string]struct{})
for _, policyConfig := range configs {
if _, ok := names[policyConfig.Name]; ok {
return nil, re.ErrorCodeConfigInvalid.WithComponentType(re.Verifier).WithPluginName(verifierName).WithDetail(fmt.Sprintf("failed to create trust policies: duplicate policy name %s", policyConfig.Name))
return nil, re.ErrorCodeConfigInvalid.WithDetail(fmt.Sprintf("Failed to create trust policies: duplicate policy name %s", policyConfig.Name)).WithRemediation("Ensure that trust policy names are unique.")
}
names[policyConfig.Name] = struct{}{}
policy, err := CreateTrustPolicy(policyConfig, verifierName)
Expand Down Expand Up @@ -86,7 +86,7 @@ func (tps *TrustPolicies) GetScopedPolicy(reference string) (TrustPolicy, error)
if globalPolicy != nil {
return globalPolicy, nil
}
return nil, re.ErrorCodeConfigInvalid.WithComponentType(re.Verifier).WithDetail(fmt.Sprintf("failed to get trust policy: no policy found for reference %s", reference))
return nil, re.ErrorCodeConfigInvalid.WithDetail(fmt.Sprintf("Failed to get trust policy: no policy found for reference %s", reference))
}

// validateScopes validates the scopes in the trust policies
Expand All @@ -97,32 +97,32 @@ func validateScopes(policies []TrustPolicy) error {
policyName := policy.GetName()
scopes := policy.GetScopes()
if len(scopes) == 0 {
return re.ErrorCodeConfigInvalid.WithComponentType(re.Verifier).WithDetail(fmt.Sprintf("failed to create trust policies: no scopes defined for trust policy %s", policyName))
return re.ErrorCodeConfigInvalid.WithDetail(fmt.Sprintf("Failed to create trust policies: no scopes defined for trust policy %s", policyName))
}
// check for global wildcard character along with other scopes in the same policy
if len(scopes) > 1 && slices.Contains(scopes, string(GlobalWildcardCharacter)) {
return re.ErrorCodeConfigInvalid.WithComponentType(re.Verifier).WithDetail(fmt.Sprintf("failed to create trust policies: global wildcard character %c cannot be used with other scopes within the same trust policy %s", GlobalWildcardCharacter, policyName))
return re.ErrorCodeConfigInvalid.WithDetail(fmt.Sprintf("Failed to create trust policies: global wildcard character %c cannot be used with other scopes within the same trust policy %s", GlobalWildcardCharacter, policyName))
}
// check for duplicate global wildcard characters across policies
if slices.Contains(scopes, string(GlobalWildcardCharacter)) {
if hasGlobalWildcard {
return re.ErrorCodeConfigInvalid.WithComponentType(re.Verifier).WithDetail(fmt.Sprintf("failed to create trust policies: global wildcard character %c can only be used once", GlobalWildcardCharacter))
return re.ErrorCodeConfigInvalid.WithDetail(fmt.Sprintf("Failed to create trust policies: global wildcard character %c can only be used once", GlobalWildcardCharacter))
}
hasGlobalWildcard = true
continue
}
for _, scope := range scopes {
// check for empty scope
if scope == "" {
return re.ErrorCodeConfigInvalid.WithComponentType(re.Verifier).WithDetail(fmt.Sprintf("failed to create trust policies: scope defined is empty for trust policy %s", policyName))
return re.ErrorCodeConfigInvalid.WithDetail(fmt.Sprintf("Failed to create trust policies: scope defined is empty for trust policy %s", policyName))
}
// check scope is formatted correctly
if !validScopeRegex.MatchString(scope) {
return re.ErrorCodeConfigInvalid.WithComponentType(re.Verifier).WithDetail(fmt.Sprintf("failed to create trust policies: invalid scope %s for trust policy %s", scope, policyName))
return re.ErrorCodeConfigInvalid.WithDetail(fmt.Sprintf("Failed to create trust policies: invalid scope %s for trust policy %s", scope, policyName))
}
// check for duplicate scopes
if _, ok := scopesMap[scope]; ok {
return re.ErrorCodeConfigInvalid.WithComponentType(re.Verifier).WithDetail(fmt.Sprintf("failed to create trust policies: duplicate scope %s for trust policy %s", scope, policyName))
return re.ErrorCodeConfigInvalid.WithDetail(fmt.Sprintf("Failed to create trust policies: duplicate scope %s for trust policy %s", scope, policyName))
}
// check wildcard overlaps
for existingScope := range scopesMap {
Expand All @@ -144,7 +144,7 @@ func validateScopes(policies []TrustPolicy) error {
isConflict = strings.HasPrefix(existingScope, trimmedScope)
}
if isConflict {
return re.ErrorCodeConfigInvalid.WithComponentType(re.Verifier).WithDetail(fmt.Sprintf("failed to create trust policies: overlapping scopes %s and %s for trust policy %s", scope, existingScope, policyName))
return re.ErrorCodeConfigInvalid.WithDetail(fmt.Sprintf("Failed to create trust policies: overlapping scopes %s and %s for trust policy %s", scope, existingScope, policyName))
}
}
scopesMap[scope] = struct{}{}
Expand Down
Loading

0 comments on commit 0e31b2f

Please sign in to comment.