diff --git a/go.mod b/go.mod index 29e957b1..784e4520 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,9 @@ go 1.21 require ( github.com/go-ldap/ldap/v3 v3.4.8 - github.com/notaryproject/notation-core-go v1.1.0-beta.1 + github.com/notaryproject/notation-core-go v1.1.0-rc.1 github.com/notaryproject/notation-plugin-framework-go v1.0.0 - github.com/notaryproject/tspclient-go v0.1.1-0.20240715235637-df25ef8d2172 + github.com/notaryproject/tspclient-go v0.2.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0 github.com/veraison/go-cose v1.1.0 diff --git a/go.sum b/go.sum index 34b798e3..fb01ee43 100644 --- a/go.sum +++ b/go.sum @@ -32,12 +32,12 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6 github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= -github.com/notaryproject/notation-core-go v1.1.0-beta.1 h1:Rpg8qhoEo4nIdiAWz597lbEcvEmjKwJLUtvJqXa8M4c= -github.com/notaryproject/notation-core-go v1.1.0-beta.1/go.mod h1:MdxSbL9F5h63EmtXWfYMWy7hEmGmOmsfN4B6KM2WyhY= +github.com/notaryproject/notation-core-go v1.1.0-rc.1 h1:6cxfVUuc4rTqYu0u7vOmgXfqw1zZabSLJNo8KvkDEzU= +github.com/notaryproject/notation-core-go v1.1.0-rc.1/go.mod h1:j6NELapik2bE1DcrL5otTfXWuW5PR/JLLfREZ4ggmYY= github.com/notaryproject/notation-plugin-framework-go v1.0.0 h1:6Qzr7DGXoCgXEQN+1gTZWuJAZvxh3p8Lryjn5FaLzi4= github.com/notaryproject/notation-plugin-framework-go v1.0.0/go.mod h1:RqWSrTOtEASCrGOEffq0n8pSg2KOgKYiWqFWczRSics= -github.com/notaryproject/tspclient-go v0.1.1-0.20240715235637-df25ef8d2172 h1:Q8UsmeFMzyFuMMq4dlbIRJUi7khEKXKUe2H2Hm3W92Y= -github.com/notaryproject/tspclient-go v0.1.1-0.20240715235637-df25ef8d2172/go.mod h1:LGyA/6Kwd2FlM0uk8Vc5il3j0CddbWSHBj/4kxQDbjs= +github.com/notaryproject/tspclient-go v0.2.0 h1:g/KpQGmyk/h7j60irIRG1mfWnibNOzJ8WhLqAzuiQAQ= +github.com/notaryproject/tspclient-go v0.2.0/go.mod h1:LGyA/6Kwd2FlM0uk8Vc5il3j0CddbWSHBj/4kxQDbjs= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= diff --git a/verifier/timestamp_test.go b/verifier/timestamp_test.go index 288014de..f7eb90ec 100644 --- a/verifier/timestamp_test.go +++ b/verifier/timestamp_test.go @@ -22,6 +22,7 @@ import ( "time" "github.com/notaryproject/notation-core-go/revocation" + "github.com/notaryproject/notation-core-go/revocation/purpose" "github.com/notaryproject/notation-core-go/signature" "github.com/notaryproject/notation-core-go/signature/cose" "github.com/notaryproject/notation-core-go/signature/jws" @@ -44,7 +45,10 @@ func TestAuthenticTimestamp(t *testing.T) { TrustStores: []string{"ca:valid-trust-store", "tsa:test-timestamp"}, TrustedIdentities: []string{"*"}, } - revocationTimestsampClient, err := revocation.NewTimestamp(&http.Client{Timeout: 5 * time.Second}) + revocationTimestampingValidator, err := revocation.NewWithOptions(revocation.Options{ + OCSPHTTPClient: &http.Client{Timeout: 2 * time.Second}, + CertChainPurpose: purpose.Timestamping, + }) if err != nil { t.Fatalf("failed to get revocation timestamp client: %v", err) } @@ -65,7 +69,7 @@ func TestAuthenticTimestamp(t *testing.T) { EnvelopeContent: jwsEnvContent, VerificationLevel: trustpolicy.LevelStrict, } - authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestsampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestampingValidator, outcome) if err := authenticTimestampResult.Error; err != nil { t.Fatalf("expected nil error, but got %s", err) } @@ -76,7 +80,7 @@ func TestAuthenticTimestamp(t *testing.T) { EnvelopeContent: coseEnvContent, VerificationLevel: trustpolicy.LevelStrict, } - authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestsampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestampingValidator, outcome) if err := authenticTimestampResult.Error; err != nil { t.Fatalf("expected nil error, but got %s", err) } @@ -91,7 +95,7 @@ func TestAuthenticTimestamp(t *testing.T) { EnvelopeContent: jwsEnvContent, VerificationLevel: trustpolicy.LevelStrict, } - authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestsampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestampingValidator, outcome) if err := authenticTimestampResult.Error; err != nil { t.Fatalf("expected nil error, but got %s", err) } @@ -106,7 +110,7 @@ func TestAuthenticTimestamp(t *testing.T) { EnvelopeContent: coseEnvContent, VerificationLevel: trustpolicy.LevelStrict, } - authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestsampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestampingValidator, outcome) if err := authenticTimestampResult.Error; err != nil { t.Fatalf("expected nil error, but got %s", err) } @@ -127,7 +131,7 @@ func TestAuthenticTimestamp(t *testing.T) { EnvelopeContent: coseEnvContent, VerificationLevel: trustpolicy.LevelStrict, } - authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestsampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestampingValidator, outcome) if err := authenticTimestampResult.Error; err != nil { t.Fatalf("expected nil error, but got %s", err) } @@ -148,7 +152,7 @@ func TestAuthenticTimestamp(t *testing.T) { EnvelopeContent: jwsEnvContent, VerificationLevel: trustpolicy.LevelStrict, } - authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestsampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestampingValidator, outcome) expectedErrMsg := "failed to check tsa trust store configuration in turst policy with error: invalid trust policy statement: \"test-timestamp\" is missing separator in trust store value \"tsa\". The required format is :" if err := authenticTimestampResult.Error; err == nil || err.Error() != expectedErrMsg { t.Fatalf("expected %s, but got %s", expectedErrMsg, err) @@ -174,7 +178,7 @@ func TestAuthenticTimestamp(t *testing.T) { EnvelopeContent: coseEnvContent, VerificationLevel: trustpolicy.LevelStrict, } - authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestsampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestampingValidator, outcome) expectedErrMsg := "verification time is after certificate \"CN=testTSA,O=Notary,L=Seattle,ST=WA,C=US\" validity period, it was expired at \"Tue, 18 Jun 2024 07:30:31 +0000\"" if err := authenticTimestampResult.Error; err == nil || err.Error() != expectedErrMsg { t.Fatalf("expected %s, but got %s", expectedErrMsg, err) @@ -190,7 +194,7 @@ func TestAuthenticTimestamp(t *testing.T) { EnvelopeContent: envContent, VerificationLevel: trustpolicy.LevelStrict, } - authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestsampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestampingValidator, outcome) expectedErrMsg := "no timestamp countersignature was found in the signature envelope" if err := authenticTimestampResult.Error; err == nil || err.Error() != expectedErrMsg { t.Fatalf("expected %s, but got %s", expectedErrMsg, err) @@ -211,7 +215,7 @@ func TestAuthenticTimestamp(t *testing.T) { EnvelopeContent: envContent, VerificationLevel: trustpolicy.LevelStrict, } - authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestsampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestampingValidator, outcome) expectedErrMsg := "failed to parse timestamp countersignature with error: unexpected content type: 1.2.840.113549.1.7.1" if err := authenticTimestampResult.Error; err == nil || err.Error() != expectedErrMsg { t.Fatalf("expected %s, but got %s", expectedErrMsg, err) @@ -232,7 +236,7 @@ func TestAuthenticTimestamp(t *testing.T) { EnvelopeContent: envContent, VerificationLevel: trustpolicy.LevelStrict, } - authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestsampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestampingValidator, outcome) expectedErrMsg := "failed to get the timestamp TSTInfo with error: cannot unmarshal TSTInfo from timestamp token: asn1: structure error: tags don't match (23 vs {class:0 tag:16 length:3 isCompound:true}) {optional:false explicit:false application:false private:false defaultValue: tag: stringType:0 timeType:24 set:false omitEmpty:false} Time @89" if err := authenticTimestampResult.Error; err == nil || err.Error() != expectedErrMsg { t.Fatalf("expected %s, but got %s", expectedErrMsg, err) @@ -254,7 +258,7 @@ func TestAuthenticTimestamp(t *testing.T) { EnvelopeContent: envContent, VerificationLevel: trustpolicy.LevelStrict, } - authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestsampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestampingValidator, outcome) expectedErrMsg := "failed to get timestamp from timestamp countersignature with error: invalid TSTInfo: mismatched message" if err := authenticTimestampResult.Error; err == nil || err.Error() != expectedErrMsg { t.Fatalf("expected %s, but got %s", expectedErrMsg, err) @@ -276,7 +280,7 @@ func TestAuthenticTimestamp(t *testing.T) { EnvelopeContent: envContent, VerificationLevel: trustpolicy.LevelStrict, } - authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestsampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestampingValidator, outcome) expectedErrMsg := "failed to verify the timestamp countersignature with error: failed to verify signed token: signing certificate not found in the timestamp token" if err := authenticTimestampResult.Error; err == nil || err.Error() != expectedErrMsg { t.Fatalf("expected %s, but got %s", expectedErrMsg, err) @@ -298,7 +302,7 @@ func TestAuthenticTimestamp(t *testing.T) { EnvelopeContent: coseEnvContent, VerificationLevel: trustpolicy.LevelStrict, } - authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestsampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestampingValidator, outcome) expectedErrMsg := "failed to load tsa trust store with error: the trust store \"does-not-exist\" of type \"tsa\" does not exist" if err := authenticTimestampResult.Error; err == nil || err.Error() != expectedErrMsg { t.Fatalf("expected %s, but got %s", expectedErrMsg, err) @@ -320,7 +324,7 @@ func TestAuthenticTimestamp(t *testing.T) { EnvelopeContent: coseEnvContent, VerificationLevel: trustpolicy.LevelStrict, } - authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, dummyTrustStore{}, revocationTimestsampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, dummyTrustStore{}, revocationTimestampingValidator, outcome) expectedErrMsg := "no trusted TSA certificate found in trust store" if err := authenticTimestampResult.Error; err == nil || err.Error() != expectedErrMsg { t.Fatalf("expected %s, but got %s", expectedErrMsg, err) @@ -342,7 +346,7 @@ func TestAuthenticTimestamp(t *testing.T) { EnvelopeContent: coseEnvContent, VerificationLevel: trustpolicy.LevelStrict, } - authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestsampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestampingValidator, outcome) expectedErrMsg := "failed to verify the timestamp countersignature with error: failed to verify signed token: cms verification failure: x509: certificate signed by unknown authority" if err := authenticTimestampResult.Error; err == nil || err.Error() != expectedErrMsg { t.Fatalf("expected %s, but got %s", expectedErrMsg, err) @@ -368,7 +372,7 @@ func TestAuthenticTimestamp(t *testing.T) { EnvelopeContent: envContent, VerificationLevel: trustpolicy.LevelStrict, } - authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestsampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestampingValidator, outcome) expectedErrMsg := "timestamp can be before certificate \"CN=testTSA,O=Notary,L=Seattle,ST=WA,C=US\" validity period, it will be valid from \"Fri, 18 Sep 2099 11:54:34 +0000\"" if err := authenticTimestampResult.Error; err == nil || err.Error() != expectedErrMsg { t.Fatalf("expected %s, but got %s", expectedErrMsg, err) @@ -394,7 +398,7 @@ func TestAuthenticTimestamp(t *testing.T) { EnvelopeContent: envContent, VerificationLevel: trustpolicy.LevelStrict, } - authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestsampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(context.Background(), dummyTrustPolicy.Name, dummyTrustPolicy.TrustStores, dummyTrustPolicy.SignatureVerification, trustStore, revocationTimestampingValidator, outcome) expectedErrMsg := "timestamp can be after certificate \"CN=testTSA,O=Notary,L=Seattle,ST=WA,C=US\" validity period, it was expired at \"Tue, 18 Sep 2001 11:54:34 +0000\"" if err := authenticTimestampResult.Error; err == nil || err.Error() != expectedErrMsg { t.Fatalf("expected %s, but got %s", expectedErrMsg, err) diff --git a/verifier/verifier.go b/verifier/verifier.go index 30cc1ef5..90d00c95 100644 --- a/verifier/verifier.go +++ b/verifier/verifier.go @@ -30,6 +30,7 @@ import ( "oras.land/oras-go/v2/content" "github.com/notaryproject/notation-core-go/revocation" + "github.com/notaryproject/notation-core-go/revocation/purpose" revocationresult "github.com/notaryproject/notation-core-go/revocation/result" "github.com/notaryproject/notation-core-go/signature" nx509 "github.com/notaryproject/notation-core-go/x509" @@ -58,12 +59,13 @@ var algorithms = map[crypto.Hash]digest.Algorithm{ // verifier implements notation.Verifier, notation.BlobVerifier and notation.verifySkipper type verifier struct { - ociTrustPolicyDoc *trustpolicy.OCIDocument - blobTrustPolicyDoc *trustpolicy.BlobDocument - trustStore truststore.X509TrustStore - pluginManager plugin.Manager - revocationClient revocation.Revocation - revocationTimestampClient revocation.Revocation + ociTrustPolicyDoc *trustpolicy.OCIDocument + blobTrustPolicyDoc *trustpolicy.BlobDocument + trustStore truststore.X509TrustStore + pluginManager plugin.Manager + revocationClient revocation.Revocation + revocationCodeSigningValidator revocation.Validator + revocationTimestampingValidator revocation.Validator } // VerifierOptions specifies additional parameters that can be set when using @@ -71,11 +73,19 @@ type verifier struct { type VerifierOptions struct { // RevocationClient is an implementation of revocation.Revocation to use for // verifying revocation of code signing certificate chain + // + // Deprecated: RevocationClient exists for backwards compatibility and + // should not be used. To perform code signing certificate chain revocation + // check, use [RevocationCodeSigningValidator]. RevocationClient revocation.Revocation - // RevocationTimestampClient is an implementaion of evocation.Revocation to - // use for verifying revocation of timestamping certificate chain - RevocationTimestampClient revocation.Revocation + // RevocationCodeSigningValidator is used for verifying revocation of + // code signing certificate chain with context. + RevocationCodeSigningValidator revocation.Validator + + // RevocationTimestampingValidator is used for verifying revocation of + // timestamping certificate chain with context. + RevocationTimestampingValidator revocation.Validator } // NewOCIVerifierFromConfig returns a OCI verifier based on local file system @@ -105,7 +115,8 @@ func NewBlobVerifierFromConfig() (*verifier, error) { } // NewWithOptions creates a new verifier given ociTrustPolicy, trustStore, -// pluginManager, and VerifierOptions +// pluginManager, and VerifierOptions. +// // Deprecated: NewWithOptions function exists for historical compatibility and should not be used. // To create verifier, use NewVerifierWithOptions function. func NewWithOptions(ociTrustPolicy *trustpolicy.OCIDocument, trustStore truststore.X509TrustStore, pluginManager plugin.Manager, opts VerifierOptions) (notation.Verifier, error) { @@ -120,68 +131,91 @@ func NewVerifier(ociTrustPolicy *trustpolicy.OCIDocument, blobTrustPolicy *trust // NewVerifierWithOptions creates a new verifier given ociTrustPolicy, blobTrustPolicy, // trustStore, pluginManager, and verifierOptions func NewVerifierWithOptions(ociTrustPolicy *trustpolicy.OCIDocument, blobTrustPolicy *trustpolicy.BlobDocument, trustStore truststore.X509TrustStore, pluginManager plugin.Manager, verifierOptions VerifierOptions) (*verifier, error) { - revocationClient := verifierOptions.RevocationClient - if revocationClient == nil { - var err error - revocationClient, err = revocation.New(&http.Client{Timeout: 2 * time.Second}) - if err != nil { - return nil, err - } - } - - revocationTimestampClient := verifierOptions.RevocationTimestampClient - if revocationTimestampClient == nil { - var err error - revocationTimestampClient, err = revocation.NewTimestamp(&http.Client{Timeout: 2 * time.Second}) - if err != nil { - return nil, err - } - } - if trustStore == nil { return nil, errors.New("trustStore cannot be nil") } - if ociTrustPolicy == nil && blobTrustPolicy == nil { return nil, errors.New("ociTrustPolicy and blobTrustPolicy both cannot be nil") } - if ociTrustPolicy != nil { if err := ociTrustPolicy.Validate(); err != nil { return nil, err } } - if blobTrustPolicy != nil { if err := blobTrustPolicy.Validate(); err != nil { return nil, err } } + v := &verifier{ + ociTrustPolicyDoc: ociTrustPolicy, + blobTrustPolicyDoc: blobTrustPolicy, + trustStore: trustStore, + pluginManager: pluginManager, + } - return &verifier{ - ociTrustPolicyDoc: ociTrustPolicy, - blobTrustPolicyDoc: blobTrustPolicy, - trustStore: trustStore, - pluginManager: pluginManager, - revocationClient: revocationClient, - revocationTimestampClient: revocationTimestampClient, - }, nil + if err := v.setRevocation(verifierOptions); err != nil { + return nil, err + } + return v, nil } -// NewFromConfig returns a OCI verifier based on local file system +// NewFromConfig returns a OCI verifier based on local file system. +// // Deprecated: NewFromConfig function exists for historical compatibility and should not be used. // To create an OCI verifier, use NewOCIVerifierFromConfig function. func NewFromConfig() (notation.Verifier, error) { return NewOCIVerifierFromConfig() } -// New creates a new verifier given ociTrustPolicy, trustStore and pluginManager +// New creates a new verifier given ociTrustPolicy, trustStore and pluginManager. +// // Deprecated: New function exists for historical compatibility and should not be used. // To create verifier, use NewVerifier function. func New(ociTrustPolicy *trustpolicy.OCIDocument, trustStore truststore.X509TrustStore, pluginManager plugin.Manager) (notation.Verifier, error) { return NewVerifier(ociTrustPolicy, nil, trustStore, pluginManager) } +// setRevocation sets revocation validators of v +func (v *verifier) setRevocation(verifierOptions VerifierOptions) error { + // timestamping validator + revocationTimestampingValidator := verifierOptions.RevocationTimestampingValidator + var err error + if revocationTimestampingValidator == nil { + revocationTimestampingValidator, err = revocation.NewWithOptions(revocation.Options{ + OCSPHTTPClient: &http.Client{Timeout: 2 * time.Second}, + CertChainPurpose: purpose.Timestamping, + }) + if err != nil { + return err + } + } + v.revocationTimestampingValidator = revocationTimestampingValidator + + // code signing validator + revocationCodeSigningValidator := verifierOptions.RevocationCodeSigningValidator + if revocationCodeSigningValidator != nil { + v.revocationCodeSigningValidator = revocationCodeSigningValidator + return nil + } + revocationClient := verifierOptions.RevocationClient + if revocationClient != nil { + v.revocationClient = revocationClient + return nil + } + + // both RevocationCodeSigningValidator and RevocationClient are nil + revocationCodeSigningValidator, err = revocation.NewWithOptions(revocation.Options{ + OCSPHTTPClient: &http.Client{Timeout: 2 * time.Second}, + CertChainPurpose: purpose.CodeSigning, + }) + if err != nil { + return err + } + v.revocationCodeSigningValidator = revocationCodeSigningValidator + return nil +} + // SkipVerify validates whether the verification level is skip. func (v *verifier) SkipVerify(ctx context.Context, opts notation.VerifierVerifyOptions) (bool, *trustpolicy.VerificationLevel, error) { logger := log.GetLogger(ctx) @@ -461,7 +495,7 @@ func (v *verifier) processSignature(ctx context.Context, sigBlob []byte, envelop // verify authentic timestamp logger.Debug("Validating authentic timestamp") - authenticTimestampResult := verifyAuthenticTimestamp(ctx, policyName, trustStores, signatureVerification, v.trustStore, v.revocationTimestampClient, outcome) + authenticTimestampResult := verifyAuthenticTimestamp(ctx, policyName, trustStores, signatureVerification, v.trustStore, v.revocationTimestampingValidator, outcome) outcome.VerificationResults = append(outcome.VerificationResults, authenticTimestampResult) logVerificationResult(logger, authenticTimestampResult) if isCriticalFailure(authenticTimestampResult) { @@ -475,7 +509,7 @@ func (v *verifier) processSignature(ctx context.Context, sigBlob []byte, envelop !slices.Contains(pluginCapabilities, pluginframework.CapabilityRevocationCheckVerifier) { logger.Debug("Validating revocation") - revocationResult := verifyRevocation(outcome, v.revocationClient, logger) + revocationResult := v.verifyRevocation(ctx, outcome) outcome.VerificationResults = append(outcome.VerificationResults, revocationResult) logVerificationResult(logger, revocationResult) if isCriticalFailure(revocationResult) { @@ -509,6 +543,59 @@ func (v *verifier) processSignature(ctx context.Context, sigBlob []byte, envelop return nil } +func (v *verifier) verifyRevocation(ctx context.Context, outcome *notation.VerificationOutcome) *notation.ValidationResult { + logger := log.GetLogger(ctx) + + if v.revocationCodeSigningValidator == nil && v.revocationClient == nil { + return ¬ation.ValidationResult{ + Type: trustpolicy.TypeRevocation, + Action: outcome.VerificationLevel.Enforcement[trustpolicy.TypeRevocation], + Error: fmt.Errorf("unable to check revocation status, code signing revocation validator cannot be nil"), + } + } + + var authenticSigningTime time.Time + if outcome.EnvelopeContent.SignerInfo.SignedAttributes.SigningScheme == signature.SigningSchemeX509SigningAuthority { + authenticSigningTime, _ = outcome.EnvelopeContent.SignerInfo.AuthenticSigningTime() + } + + var certResults []*revocationresult.CertRevocationResult + var err error + if v.revocationCodeSigningValidator != nil { + certResults, err = v.revocationCodeSigningValidator.ValidateContext(ctx, revocation.ValidateContextOptions{ + CertChain: outcome.EnvelopeContent.SignerInfo.CertificateChain, + AuthenticSigningTime: authenticSigningTime, + }) + } else { + certResults, err = v.revocationClient.Validate(outcome.EnvelopeContent.SignerInfo.CertificateChain, authenticSigningTime) + } + if err != nil { + logger.Debug("Error while checking revocation status, err: %s", err.Error()) + return ¬ation.ValidationResult{ + Type: trustpolicy.TypeRevocation, + Action: outcome.VerificationLevel.Enforcement[trustpolicy.TypeRevocation], + Error: fmt.Errorf("unable to check revocation status, err: %s", err.Error()), + } + } + + result := ¬ation.ValidationResult{ + Type: trustpolicy.TypeRevocation, + Action: outcome.VerificationLevel.Enforcement[trustpolicy.TypeRevocation], + } + finalResult, problematicCertSubject := revocationFinalResult(certResults, outcome.EnvelopeContent.SignerInfo.CertificateChain, logger) + switch finalResult { + case revocationresult.ResultOK: + logger.Debug("No verification impacting errors encountered while checking revocation, status is OK") + case revocationresult.ResultRevoked: + result.Error = fmt.Errorf("signing certificate with subject %q is revoked", problematicCertSubject) + default: + // revocationresult.ResultUnknown + result.Error = fmt.Errorf("signing certificate with subject %q revocation status is unknown", problematicCertSubject) + } + + return result +} + func processPluginResponse(capabilitiesToVerify []pluginframework.Capability, response *pluginframework.VerifySignatureResponse, outcome *notation.VerificationOutcome) error { verificationPluginName, err := getVerificationPlugin(&outcome.EnvelopeContent.SignerInfo) if err != nil { @@ -678,7 +765,7 @@ func verifyExpiry(outcome *notation.VerificationOutcome) *notation.ValidationRes } } -func verifyAuthenticTimestamp(ctx context.Context, policyName string, trustStores []string, signatureVerification trustpolicy.SignatureVerification, x509TrustStore truststore.X509TrustStore, r revocation.Revocation, outcome *notation.VerificationOutcome) *notation.ValidationResult { +func verifyAuthenticTimestamp(ctx context.Context, policyName string, trustStores []string, signatureVerification trustpolicy.SignatureVerification, x509TrustStore truststore.X509TrustStore, r revocation.Validator, outcome *notation.VerificationOutcome) *notation.ValidationResult { logger := log.GetLogger(ctx) signerInfo := outcome.EnvelopeContent.SignerInfo @@ -712,48 +799,6 @@ func verifyAuthenticTimestamp(ctx context.Context, policyName string, trustStore } } -func verifyRevocation(outcome *notation.VerificationOutcome, r revocation.Revocation, logger log.Logger) *notation.ValidationResult { - if r == nil { - return ¬ation.ValidationResult{ - Type: trustpolicy.TypeRevocation, - Action: outcome.VerificationLevel.Enforcement[trustpolicy.TypeRevocation], - Error: fmt.Errorf("unable to check revocation status, revocation client cannot be nil"), - } - } - - var authenticSigningTime time.Time - if outcome.EnvelopeContent.SignerInfo.SignedAttributes.SigningScheme == signature.SigningSchemeX509SigningAuthority { - authenticSigningTime, _ = outcome.EnvelopeContent.SignerInfo.AuthenticSigningTime() - } - - certResults, err := r.Validate(outcome.EnvelopeContent.SignerInfo.CertificateChain, authenticSigningTime) - if err != nil { - logger.Debug("Error while checking revocation status, err: %s", err.Error()) - return ¬ation.ValidationResult{ - Type: trustpolicy.TypeRevocation, - Action: outcome.VerificationLevel.Enforcement[trustpolicy.TypeRevocation], - Error: fmt.Errorf("unable to check revocation status, err: %s", err.Error()), - } - } - - result := ¬ation.ValidationResult{ - Type: trustpolicy.TypeRevocation, - Action: outcome.VerificationLevel.Enforcement[trustpolicy.TypeRevocation], - } - finalResult, problematicCertSubject := revocationFinalResult(certResults, outcome.EnvelopeContent.SignerInfo.CertificateChain, logger) - switch finalResult { - case revocationresult.ResultOK: - logger.Debug("No verification impacting errors encountered while checking revocation, status is OK") - case revocationresult.ResultRevoked: - result.Error = fmt.Errorf("signing certificate with subject %q is revoked", problematicCertSubject) - default: - // revocationresult.ResultUnknown - result.Error = fmt.Errorf("signing certificate with subject %q revocation status is unknown", problematicCertSubject) - } - - return result -} - // revocationFinalResult returns the final revocation result and problematic // certificate subject if the final result is not ResultOK func revocationFinalResult(certResults []*revocationresult.CertRevocationResult, certChain []*x509.Certificate, logger log.Logger) (revocationresult.Result, string) { @@ -906,7 +951,7 @@ func isRequiredVerificationPluginVer(pluginVer string, minPluginVer string) bool // verifyTimestamp provides core verification logic of authentic timestamp under // signing scheme `notary.x509`. -func verifyTimestamp(ctx context.Context, policyName string, trustStores []string, signatureVerification trustpolicy.SignatureVerification, x509TrustStore truststore.X509TrustStore, r revocation.Revocation, outcome *notation.VerificationOutcome) error { +func verifyTimestamp(ctx context.Context, policyName string, trustStores []string, signatureVerification trustpolicy.SignatureVerification, x509TrustStore truststore.X509TrustStore, r revocation.Validator, outcome *notation.VerificationOutcome) error { logger := log.GetLogger(ctx) signerInfo := outcome.EnvelopeContent.SignerInfo @@ -1019,7 +1064,9 @@ func verifyTimestamp(ctx context.Context, policyName string, trustStores []strin // 5. Perform the timestamping certificate chain revocation check logger.Debug("Checking timestamping certificate chain revocation...") - certResults, err := r.Validate(tsaCertChain, time.Time{}) + certResults, err := r.ValidateContext(ctx, revocation.ValidateContextOptions{ + CertChain: tsaCertChain, + }) if err != nil { return fmt.Errorf("failed to check timestamping certificate chain revocation with error: %w", err) } diff --git a/verifier/verifier_test.go b/verifier/verifier_test.go index 9489dfae..1038e260 100644 --- a/verifier/verifier_test.go +++ b/verifier/verifier_test.go @@ -29,6 +29,7 @@ import ( "golang.org/x/crypto/ocsp" "github.com/notaryproject/notation-core-go/revocation" + "github.com/notaryproject/notation-core-go/revocation/purpose" "github.com/notaryproject/notation-core-go/signature" _ "github.com/notaryproject/notation-core-go/signature/cose" "github.com/notaryproject/notation-core-go/signature/jws" @@ -38,7 +39,6 @@ import ( "github.com/notaryproject/notation-go/dir" "github.com/notaryproject/notation-go/internal/envelope" "github.com/notaryproject/notation-go/internal/mock" - "github.com/notaryproject/notation-go/log" "github.com/notaryproject/notation-go/plugin/proto" "github.com/notaryproject/notation-go/signer" "github.com/notaryproject/notation-go/verifier/trustpolicy" @@ -491,7 +491,6 @@ func createMockOutcome(certChain []*x509.Certificate, signingTime time.Time) *no } func TestVerifyRevocation(t *testing.T) { - logger := log.GetLogger(context.Background()) zeroTime := time.Time{} revokableTuples := testhelper.GetRevokableRSAChain(3) @@ -514,10 +513,12 @@ func TestVerifyRevocation(t *testing.T) { unknownMsg := fmt.Sprintf("signing certificate with subject %q revocation status is unknown", revokableChain[0].Subject.String()) revokedMsg := fmt.Sprintf("signing certificate with subject %q is revoked", revokableChain[0].Subject.String()) multiMsg := fmt.Sprintf("signing certificate with subject %q is revoked", revokableChain[1].Subject.String()) + ctx := context.Background() t.Run("verifyRevocation nil client", func(t *testing.T) { - result := verifyRevocation(createMockOutcome(revokableChain, time.Now()), nil, logger) - expectedErrMsg := "unable to check revocation status, revocation client cannot be nil" + v := &verifier{} + result := v.verifyRevocation(ctx, createMockOutcome(revokableChain, time.Now())) + expectedErrMsg := "unable to check revocation status, code signing revocation validator cannot be nil" if result.Error == nil || result.Error.Error() != expectedErrMsg { t.Fatalf("expected verifyRevocation to fail with %s, but got %v", expectedErrMsg, result.Error) } @@ -527,7 +528,10 @@ func TestVerifyRevocation(t *testing.T) { if err != nil { t.Fatalf("unexpected error while creating revocation object: %v", err) } - result := verifyRevocation(createMockOutcome(invalidChain, time.Now()), revocationClient, logger) + v := &verifier{ + revocationClient: revocationClient, + } + result := v.verifyRevocation(ctx, createMockOutcome(invalidChain, time.Now())) expectedErrMsg := "unable to check revocation status, err: invalid chain: expected chain to be correct and complete: invalid certificates or certificate with subject \"CN=Notation Test Revokable RSA Chain Cert 2,O=Notary,L=Seattle,ST=WA,C=US\" is not issued by \"CN=Notation Test Revokable RSA Chain Cert 3,O=Notary,L=Seattle,ST=WA,C=US\". Error: x509: invalid signature: parent certificate cannot sign this kind of certificate" if result.Error == nil || result.Error.Error() != expectedErrMsg { t.Fatalf("expected verifyRevocation to fail with %s, but got %v", expectedErrMsg, result.Error) @@ -538,7 +542,10 @@ func TestVerifyRevocation(t *testing.T) { if err != nil { t.Fatalf("unexpected error while creating revocation object: %v", err) } - result := verifyRevocation(createMockOutcome(revokableChain, time.Now()), revocationClient, logger) + v := &verifier{ + revocationClient: revocationClient, + } + result := v.verifyRevocation(ctx, createMockOutcome(revokableChain, time.Now())) if result.Error != nil { t.Fatalf("expected verifyRevocation to succeed, but got %v", result.Error) } @@ -548,7 +555,10 @@ func TestVerifyRevocation(t *testing.T) { if err != nil { t.Fatalf("unexpected error while creating revocation object: %v", err) } - result := verifyRevocation(createMockOutcome(revokableChain, time.Now()), revocationClient, logger) + v := &verifier{ + revocationClient: revocationClient, + } + result := v.verifyRevocation(ctx, createMockOutcome(revokableChain, time.Now())) if result.Error == nil || result.Error.Error() != revokedMsg { t.Fatalf("expected verifyRevocation to fail with %s, but got %v", revokedMsg, result.Error) } @@ -558,7 +568,10 @@ func TestVerifyRevocation(t *testing.T) { if err != nil { t.Fatalf("unexpected error while creating revocation object: %v", err) } - result := verifyRevocation(createMockOutcome(revokableChain, time.Now()), revocationClient, logger) + v := &verifier{ + revocationClient: revocationClient, + } + result := v.verifyRevocation(ctx, createMockOutcome(revokableChain, time.Now())) if result.Error == nil || result.Error.Error() != revokedMsg { t.Fatalf("expected verifyRevocation to fail with %s, but got %v", revokedMsg, result.Error) } @@ -568,7 +581,10 @@ func TestVerifyRevocation(t *testing.T) { if err != nil { t.Fatalf("unexpected error while creating revocation object: %v", err) } - result := verifyRevocation(createMockOutcome(revokableChain, time.Now()), revocationClient, logger) + v := &verifier{ + revocationClient: revocationClient, + } + result := v.verifyRevocation(ctx, createMockOutcome(revokableChain, time.Now())) if result.Error == nil || result.Error.Error() != unknownMsg { t.Fatalf("expected verifyRevocation to fail with %s, but got %v", unknownMsg, result.Error) } @@ -578,7 +594,10 @@ func TestVerifyRevocation(t *testing.T) { if err != nil { t.Fatalf("unexpected error while creating revocation object: %v", err) } - result := verifyRevocation(createMockOutcome(revokableChain, time.Now()), revocationClient, logger) + v := &verifier{ + revocationClient: revocationClient, + } + result := v.verifyRevocation(ctx, createMockOutcome(revokableChain, time.Now())) if result.Error == nil || result.Error.Error() != multiMsg { t.Fatalf("expected verifyRevocation to fail with %s, but got %v", multiMsg, result.Error) } @@ -588,7 +607,10 @@ func TestVerifyRevocation(t *testing.T) { if err != nil { t.Fatalf("unexpected error while creating revocation object: %v", err) } - result := verifyRevocation(createMockOutcome(revokableChain, time.Now()), revocationClient, logger) + v := &verifier{ + revocationClient: revocationClient, + } + result := v.verifyRevocation(ctx, createMockOutcome(revokableChain, time.Now())) if result.Error == nil || result.Error.Error() != revokedMsg { t.Fatalf("expected verifyRevocation to fail with %s, but got %v", revokedMsg, result.Error) } @@ -598,7 +620,10 @@ func TestVerifyRevocation(t *testing.T) { if err != nil { t.Fatalf("unexpected error while creating revocation object: %v", err) } - result := verifyRevocation(createMockOutcome(revokableChain, time.Now()), revocationClient, logger) + v := &verifier{ + revocationClient: revocationClient, + } + result := v.verifyRevocation(ctx, createMockOutcome(revokableChain, time.Now())) if result.Error != nil { t.Fatalf("expected verifyRevocation to succeed, but got %v", result.Error) } @@ -608,7 +633,10 @@ func TestVerifyRevocation(t *testing.T) { if err != nil { t.Fatalf("unexpected error while creating revocation object: %v", err) } - result := verifyRevocation(createMockOutcome(revokableChain, time.Now()), revocationClient, logger) + v := &verifier{ + revocationClient: revocationClient, + } + result := v.verifyRevocation(ctx, createMockOutcome(revokableChain, time.Now())) if result.Error == nil || result.Error.Error() != unknownMsg { t.Fatalf("expected verifyRevocation to fail with %s, but got %v", unknownMsg, result.Error) } @@ -618,7 +646,10 @@ func TestVerifyRevocation(t *testing.T) { if err != nil { t.Fatalf("unexpected error while creating revocation object: %v", err) } - result := verifyRevocation(createMockOutcome(revokableChain, time.Now().Add(-4*time.Hour)), revocationClient, logger) + v := &verifier{ + revocationClient: revocationClient, + } + result := v.verifyRevocation(ctx, createMockOutcome(revokableChain, time.Now().Add(-4*time.Hour))) if result.Error == nil || result.Error.Error() != revokedMsg { t.Fatalf("expected verifyRevocation to fail with %s, but got %v", revokedMsg, result.Error) } @@ -629,7 +660,10 @@ func TestVerifyRevocation(t *testing.T) { t.Fatalf("unexpected error while creating revocation object: %v", err) } expectedErrMsg := "signing certificate with subject \"CN=Notation Test Revokable RSA Chain Cert 3,O=Notary,L=Seattle,ST=WA,C=US\" is revoked" - result := verifyRevocation(createMockOutcome(revokableChain, zeroTime), revocationClient, logger) + v := &verifier{ + revocationClient: revocationClient, + } + result := v.verifyRevocation(ctx, createMockOutcome(revokableChain, zeroTime)) if result.Error == nil || result.Error.Error() != expectedErrMsg { t.Fatalf("expected verifyRevocation to fail with %s, but got %v", expectedErrMsg, result.Error) } @@ -642,7 +676,10 @@ func TestVerifyRevocation(t *testing.T) { if err != nil { t.Fatalf("unexpected error while creating revocation object: %v", err) } - result := verifyRevocation(createMockOutcome(revokableChain, time.Now().Add(-4*time.Hour)), revocationClient, logger) + v := &verifier{ + revocationClient: revocationClient, + } + result := v.verifyRevocation(ctx, createMockOutcome(revokableChain, time.Now().Add(-4*time.Hour))) if result.Error != nil { t.Fatalf("expected verifyRevocation to succeed, but got %v", result.Error) } @@ -660,7 +697,10 @@ func TestVerifyRevocation(t *testing.T) { if !authenticSigningTime.IsZero() || err == nil || err.Error() != expectedErr.Error() { t.Fatalf("expected AuthenticSigningTime to fail with %v, but got %v", expectedErr, err) } - result := verifyRevocation(outcome, revocationClient, logger) + v := &verifier{ + revocationClient: revocationClient, + } + result := v.verifyRevocation(ctx, outcome) if result.Error == nil || result.Error.Error() != revokedMsg { t.Fatalf("expected verifyRevocation to fail with %s, but got %v", revokedMsg, result.Error) } @@ -702,6 +742,9 @@ func TestNewVerifierWithOptions(t *testing.T) { if v.revocationClient == nil { t.Fatal("expected nonnil revocationClient") } + if v.revocationCodeSigningValidator != nil { + t.Fatal("expected nil revocationCodeSigningValidator") + } _, err = NewVerifierWithOptions(nil, &blobPolicy, store, pm, opts) if err != nil { @@ -718,6 +761,30 @@ func TestNewVerifierWithOptions(t *testing.T) { if err != nil { t.Fatalf("expected NewVerifierWithOptions constructor to succeed, but got %v", err) } + + csValidator, err := revocation.NewWithOptions(revocation.Options{}) + if err != nil { + t.Fatal(err) + } + opts = VerifierOptions{ + RevocationCodeSigningValidator: csValidator, + } + v, err = NewVerifierWithOptions(&ociPolicy, nil, store, pm, opts) + if err != nil { + t.Fatalf("expected NewVerifierWithOptions constructor to succeed, but got %v", err) + } + if v.revocationCodeSigningValidator == nil { + t.Fatal("expected v.revocationCodeSigningValidator to be non-nil") + } + + opts = VerifierOptions{} + v, err = NewVerifierWithOptions(&ociPolicy, nil, store, pm, opts) + if err != nil { + t.Fatalf("expected NewVerifierWithOptions constructor to succeed, but got %v", err) + } + if v.revocationCodeSigningValidator == nil { + t.Fatal("expected v.revocationCodeSigningValidator to be non-nil") + } } func TestNewVerifierWithOptionsError(t *testing.T) { @@ -725,13 +792,16 @@ func TestNewVerifierWithOptionsError(t *testing.T) { if err != nil { t.Fatalf("unexpected error while creating revocation object: %v", err) } - rt, err := revocation.NewTimestamp(&http.Client{}) + rt, err := revocation.NewWithOptions(revocation.Options{ + OCSPHTTPClient: &http.Client{}, + CertChainPurpose: purpose.Timestamping, + }) if err != nil { t.Fatalf("unexpected error while creating revocation timestamp object: %v", err) } opts := VerifierOptions{ - RevocationClient: r, - RevocationTimestampClient: rt, + RevocationClient: r, + RevocationTimestampingValidator: rt, } _, err = NewVerifierWithOptions(nil, nil, store, pm, opts)