From 527b84a2b66d2e61f4453ef7c786a9717adf6d6f Mon Sep 17 00:00:00 2001 From: Patrick Zheng Date: Tue, 16 Apr 2024 16:41:07 +0800 Subject: [PATCH] added timestamp trust policy Signed-off-by: Patrick Zheng --- notation.go | 17 +++-------------- verifier/trustpolicy/trustpolicy.go | 17 +++++++++++++++-- verifier/verifier.go | 18 +++++++++++++----- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/notation.go b/notation.go index 429c4c29..34ddda88 100644 --- a/notation.go +++ b/notation.go @@ -319,11 +319,6 @@ type VerifierVerifyOptions struct { // UserMetadata contains key-value pairs that must be present in the // signature. UserMetadata map[string]string - - // VerifyAtTimestampedTime verifies the timestamp countersignature at the - // time point been stamped. This time point MUST be within timestamp - // certificate chain's validity period. - VerifyAtTimestampedTime bool } // Verifier is a generic interface for verifying an artifact. @@ -358,11 +353,6 @@ type VerifyOptions struct { // UserMetadata contains key-value pairs that must be present in the // signature UserMetadata map[string]string - - // VerifyAtTimestampedTime verifies the timestamp countersignature at the - // time point been stamped. This time point MUST be within timestamp - // certificate chain's validity period. - VerifyAtTimestampedTime bool } // Verify performs signature verification on each of the notation supported @@ -386,10 +376,9 @@ func Verify(ctx context.Context, verifier Verifier, repo registry.Repository, ve // opts to be passed in verifier.Verify() opts := VerifierVerifyOptions{ - ArtifactReference: verifyOpts.ArtifactReference, - PluginConfig: verifyOpts.PluginConfig, - UserMetadata: verifyOpts.UserMetadata, - VerifyAtTimestampedTime: verifyOpts.VerifyAtTimestampedTime, + ArtifactReference: verifyOpts.ArtifactReference, + PluginConfig: verifyOpts.PluginConfig, + UserMetadata: verifyOpts.UserMetadata, } if skipChecker, ok := verifier.(verifySkipper); ok { diff --git a/verifier/trustpolicy/trustpolicy.go b/verifier/trustpolicy/trustpolicy.go index b0aaede5..b28ec9dd 100644 --- a/verifier/trustpolicy/trustpolicy.go +++ b/verifier/trustpolicy/trustpolicy.go @@ -134,7 +134,7 @@ var ( } ) -var supportedPolicyVersions = []string{"1.0"} +var supportedPolicyVersions = []string{"1.0", "1.1"} // Document represents a trustPolicy.json document type Document struct { @@ -156,6 +156,9 @@ type TrustPolicy struct { // SignatureVerification setting for this policy statement SignatureVerification SignatureVerification `json:"signatureVerification"` + // TimestampVerification setting for this policy statement + TimestampVerification *TimestampVerification `json:"timestampVerification,omitempty"` + // TrustStores this policy statement uses TrustStores []string `json:"trustStores,omitempty"` @@ -169,6 +172,12 @@ type SignatureVerification struct { Override map[ValidationType]ValidationAction `json:"override,omitempty"` } +// TimestampVerification represents timestamp countersignature verification +// configuration in a trust policy +type TimestampVerification struct { + AtTimestampedTime bool `json:"atTimestampedTime"` +} + // Validate validates a policy document according to its version's rule set. // if any rule is violated, returns an error func (policyDoc *Document) Validate() error { @@ -185,7 +194,6 @@ func (policyDoc *Document) Validate() error { return fmt.Errorf("trust policy document uses unsupported version %q", policyDoc.Version) } - // Validate the policy according to 1.0 rules if len(policyDoc.TrustPolicies) == 0 { return errors.New("trust policy document can not have zero trust policy statements") } @@ -206,6 +214,11 @@ func (policyDoc *Document) Validate() error { return fmt.Errorf("trust policy statement %q has invalid signatureVerification: %w", statement.Name, err) } + // Verify timestamp verification is valid + if statement.TimestampVerification != nil && policyDoc.Version != "1.1" { + return fmt.Errorf("trust policy document version must be 1.1 to support timestamp verification, but got %q", policyDoc.Version) + } + // Any signature verification other than "skip" needs a trust store and // trusted identities if verificationLevel.Name == "skip" { diff --git a/verifier/verifier.go b/verifier/verifier.go index 453d10e4..a07bfd0c 100644 --- a/verifier/verifier.go +++ b/verifier/verifier.go @@ -157,7 +157,7 @@ func (v *verifier) Verify(ctx context.Context, desc ocispec.Descriptor, signatur logger.Debug("Skipping signature verification") return outcome, nil } - err = v.processSignature(ctx, signature, envelopeMediaType, trustPolicy, opts.VerifyAtTimestampedTime, pluginConfig, outcome) + err = v.processSignature(ctx, signature, envelopeMediaType, trustPolicy, pluginConfig, outcome) if err != nil { outcome.Error = err @@ -188,7 +188,7 @@ func (v *verifier) Verify(ctx context.Context, desc ocispec.Descriptor, signatur return outcome, outcome.Error } -func (v *verifier) processSignature(ctx context.Context, sigBlob []byte, envelopeMediaType string, trustPolicy *trustpolicy.TrustPolicy, verifyAtTimestampedTime bool, pluginConfig map[string]string, outcome *notation.VerificationOutcome) error { +func (v *verifier) processSignature(ctx context.Context, sigBlob []byte, envelopeMediaType string, trustPolicy *trustpolicy.TrustPolicy, pluginConfig map[string]string, outcome *notation.VerificationOutcome) error { logger := log.GetLogger(ctx) // verify integrity first. notation will always verify integrity no matter @@ -288,7 +288,7 @@ func (v *verifier) processSignature(ctx context.Context, sigBlob []byte, envelop // verify authentic timestamp logger.Debug("Validating authentic timestamp") - authenticTimestampResult := verifyAuthenticTimestamp(ctx, trustPolicy, v.trustStore, verifyAtTimestampedTime, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(ctx, trustPolicy, v.trustStore, outcome) outcome.VerificationResults = append(outcome.VerificationResults, authenticTimestampResult) logVerificationResult(logger, authenticTimestampResult) if isCriticalFailure(authenticTimestampResult) { @@ -516,7 +516,7 @@ func verifyExpiry(outcome *notation.VerificationOutcome) *notation.ValidationRes } } -func verifyAuthenticTimestamp(ctx context.Context, trustPolicy *trustpolicy.TrustPolicy, x509TrustStore truststore.X509TrustStore, verifyAtTimestampedTime bool, outcome *notation.VerificationOutcome) *notation.ValidationResult { +func verifyAuthenticTimestamp(ctx context.Context, trustPolicy *trustpolicy.TrustPolicy, x509TrustStore truststore.X509TrustStore, outcome *notation.VerificationOutcome) *notation.ValidationResult { logger := log.GetLogger(ctx) // under signing scheme notary.x509 @@ -544,6 +544,14 @@ func verifyAuthenticTimestamp(ctx context.Context, trustPolicy *trustpolicy.Trus Action: outcome.VerificationLevel.Enforcement[trustpolicy.TypeAuthenticTimestamp], } } + if trustPolicy.TimestampVerification == nil { + // if there is no timestamp verification configuration in trust policy + return ¬ation.ValidationResult{ + Error: errors.New("current time is not in certificate chain validity period and no timestamp verification configuration was found in trust policy"), + Type: trustpolicy.TypeAuthenticTimestamp, + Action: outcome.VerificationLevel.Enforcement[trustpolicy.TypeAuthenticTimestamp], + } + } trustTSACerts, err := loadX509TSATrustStores(ctx, outcome.EnvelopeContent.SignerInfo.SignedAttributes.SigningScheme, trustPolicy, x509TrustStore) if err != nil { return ¬ation.ValidationResult{ @@ -592,7 +600,7 @@ func verifyAuthenticTimestamp(ctx context.Context, trustPolicy *trustpolicy.Trus Action: outcome.VerificationLevel.Enforcement[trustpolicy.TypeAuthenticTimestamp], } } - if verifyAtTimestampedTime { + if trustPolicy.TimestampVerification.AtTimestampedTime { timestampVerifyOpts.CurrentTime = ts } // verify the timestamp countersignature