Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
Signed-off-by: Patrick Zheng <[email protected]>
  • Loading branch information
Two-Hearts committed Aug 6, 2024
1 parent da3704d commit 3ca469d
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 60 deletions.
46 changes: 22 additions & 24 deletions revocation/revocation.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ import (
// Deprecated: Revocation exists for backwards compatibility and should not be used.
// To perform revocation check, use [Validator].
type Revocation interface {
// Validate checks the revocation status for a certificate chain using OCSP
// and returns an array of CertRevocationResults that contain the results
// and any errors that are encountered during the process
// Validate checks the revocation status for a code signing certificate chain
// using OCSP and returns an array of CertRevocationResults that contain
// the results and any errors that are encountered during the process
Validate(certChain []*x509.Certificate, signingTime time.Time) ([]*result.CertRevocationResult, error)
}

Expand All @@ -50,6 +50,11 @@ type ValidateContextOptions struct {
//
// Reference: https://github.com/notaryproject/specifications/blob/v1.0.0/specs/trust-store-trust-policy.md#revocation-checking-with-ocsp
AuthenticSigningTime time.Time

// CertChainPurpose is the purpose of the certificate chain. Supported
// values are x509.ExtKeyUsageCodeSigning and x509.ExtKeyUsageTimeStamping.
// REQUIRED.
CertChainPurpose x509.ExtKeyUsage
}

// Validator is an interface that provides revocation checking with
Expand All @@ -63,8 +68,7 @@ type Validator interface {

// revocation is an internal struct used for revocation checking
type revocation struct {
httpClient *http.Client
certChainPurpose x509.ExtKeyUsage
httpClient *http.Client
}

// New constructs a revocation object for code signing certificate chain.
Expand All @@ -76,8 +80,7 @@ func New(httpClient *http.Client) (Revocation, error) {
return nil, errors.New("invalid input: a non-nil httpClient must be specified")
}
return &revocation{
httpClient: httpClient,
certChainPurpose: x509.ExtKeyUsageCodeSigning,
httpClient: httpClient,
}, nil
}

Expand All @@ -87,11 +90,6 @@ type Options struct {
// a default *http.Client with timeout of 2 seconds will be used.
// OPTIONAL.
OCSPHTTPClient *http.Client

// CertChainPurpose is the purpose of the certificate chain. Supported
// values are x509.ExtKeyUsageCodeSigning and x509.ExtKeyUsageTimeStamping.
// REQUIRED.
CertChainPurpose x509.ExtKeyUsage
}

// NewWithOptions constructs a Validator with the specified options
Expand All @@ -100,28 +98,22 @@ func NewWithOptions(opts Options) (Validator, error) {
opts.OCSPHTTPClient = &http.Client{Timeout: 2 * time.Second}
}

switch opts.CertChainPurpose {
case x509.ExtKeyUsageCodeSigning, x509.ExtKeyUsageTimeStamping:
default:
return nil, fmt.Errorf("unsupported certificate chain purpose %v", opts.CertChainPurpose)
}

return &revocation{
httpClient: opts.OCSPHTTPClient,
certChainPurpose: opts.CertChainPurpose,
httpClient: opts.OCSPHTTPClient,
}, nil
}

// Validate checks the revocation status for a certificate chain using OCSP and
// returns an array of CertRevocationResults that contain the results and any
// errors that are encountered during the process
// Validate checks the revocation status for a code signing certificate chain
// using OCSP and returns an array of CertRevocationResults that contain the
// results and any errors that are encountered during the process.
//
// TODO: add CRL support
// https://github.com/notaryproject/notation-core-go/issues/125
func (r *revocation) Validate(certChain []*x509.Certificate, signingTime time.Time) ([]*result.CertRevocationResult, error) {
return r.ValidateContext(context.Background(), ValidateContextOptions{
CertChain: certChain,
AuthenticSigningTime: signingTime,
CertChainPurpose: x509.ExtKeyUsageCodeSigning,
})
}

Expand All @@ -136,9 +128,15 @@ func (r *revocation) ValidateContext(ctx context.Context, validateContextOpts Va
return nil, result.InvalidChainError{Err: errors.New("chain does not contain any certificates")}
}

switch validateContextOpts.CertChainPurpose {
case x509.ExtKeyUsageCodeSigning, x509.ExtKeyUsageTimeStamping:
default:
return nil, fmt.Errorf("unsupported certificate chain purpose %v", validateContextOpts.CertChainPurpose)
}

return ocsp.CheckStatus(ocsp.Options{
CertChain: validateContextOpts.CertChain,
CertChainPurpose: r.certChainPurpose,
CertChainPurpose: validateContextOpts.CertChainPurpose,
SigningTime: validateContextOpts.AuthenticSigningTime,
HTTPClient: r.httpClient,
})
Expand Down
78 changes: 42 additions & 36 deletions revocation/revocation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,25 +102,11 @@ func TestNew(t *testing.T) {

func TestNewWithOptions(t *testing.T) {
t.Run("nil OCSP HTTP Client", func(t *testing.T) {
_, err := NewWithOptions(Options{
CertChainPurpose: x509.ExtKeyUsageCodeSigning,
})
_, err := NewWithOptions(Options{})
if err != nil {
t.Fatal(err)
}
})

t.Run("invalid CertChainPurpose", func(t *testing.T) {
_, err := NewWithOptions(Options{
OCSPHTTPClient: &http.Client{},
CertChainPurpose: -1,
})
expectedErrMsg := "unsupported certificate chain purpose -1"
if err == nil || err.Error() != expectedErrMsg {
t.Fatalf("expected %s, but got %s", expectedErrMsg, err.Error())
}
})

}

func TestCheckRevocationStatusForSingleCert(t *testing.T) {
Expand Down Expand Up @@ -494,15 +480,15 @@ func TestCheckRevocationStatusForTimestampChain(t *testing.T) {

t.Run("empty chain", func(t *testing.T) {
r, err := NewWithOptions(Options{
OCSPHTTPClient: &http.Client{Timeout: 5 * time.Second},
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
OCSPHTTPClient: &http.Client{Timeout: 5 * time.Second},
})
if err != nil {
t.Errorf("Expected successful creation of revocation, but received error: %v", err)
}
certResults, err := r.ValidateContext(context.Background(), ValidateContextOptions{
CertChain: []*x509.Certificate{},
AuthenticSigningTime: time.Now(),
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
})
expectedErr := result.InvalidChainError{Err: errors.New("chain does not contain any certificates")}
if err == nil || err.Error() != expectedErr.Error() {
Expand All @@ -515,15 +501,15 @@ func TestCheckRevocationStatusForTimestampChain(t *testing.T) {
t.Run("check non-revoked chain", func(t *testing.T) {
client := testhelper.MockClient(testChain, []ocsp.ResponseStatus{ocsp.Good}, nil, true)
r, err := NewWithOptions(Options{
OCSPHTTPClient: client,
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
OCSPHTTPClient: client,
})
if err != nil {
t.Errorf("Expected successful creation of revocation, but received error: %v", err)
}
certResults, err := r.ValidateContext(context.Background(), ValidateContextOptions{
CertChain: revokableChain,
AuthenticSigningTime: time.Now(),
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
})
if err != nil {
t.Errorf("Expected CheckStatus to succeed, but got error: %v", err)
Expand All @@ -538,19 +524,38 @@ func TestCheckRevocationStatusForTimestampChain(t *testing.T) {
}
validateEquivalentCertResults(certResults, expectedCertResults, t)
})

t.Run("invalid CertChainPurpose", func(t *testing.T) {
r, err := NewWithOptions(Options{
OCSPHTTPClient: &http.Client{Timeout: 5 * time.Second},
})
if err != nil {
t.Errorf("Expected successful creation of revocation, but received error: %v", err)
}
_, err = r.ValidateContext(context.Background(), ValidateContextOptions{
CertChain: revokableChain,
AuthenticSigningTime: time.Now(),
CertChainPurpose: -1,
})
expectedErrMsg := "unsupported certificate chain purpose -1"
if err == nil || err.Error() != expectedErrMsg {
t.Fatalf("expected %s, but got %s", expectedErrMsg, err.Error())
}
})

t.Run("check chain with 1 Unknown cert", func(t *testing.T) {
// 3rd cert will be unknown, the rest will be good
client := testhelper.MockClient(testChain, []ocsp.ResponseStatus{ocsp.Good, ocsp.Good, ocsp.Unknown, ocsp.Good}, nil, true)
r, err := NewWithOptions(Options{
OCSPHTTPClient: client,
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
OCSPHTTPClient: client,
})
if err != nil {
t.Errorf("Expected successful creation of revocation, but received error: %v", err)
}
certResults, err := r.ValidateContext(context.Background(), ValidateContextOptions{
CertChain: revokableChain,
AuthenticSigningTime: time.Now(),
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
})
if err != nil {
t.Errorf("Expected CheckStatus to succeed, but got error: %v", err)
Expand All @@ -574,15 +579,15 @@ func TestCheckRevocationStatusForTimestampChain(t *testing.T) {
// 3rd cert will be revoked, the rest will be good
client := testhelper.MockClient(testChain, []ocsp.ResponseStatus{ocsp.Good, ocsp.Good, ocsp.Revoked, ocsp.Good}, nil, true)
r, err := NewWithOptions(Options{
OCSPHTTPClient: client,
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
OCSPHTTPClient: client,
})
if err != nil {
t.Errorf("Expected successful creation of revocation, but received error: %v", err)
}
certResults, err := r.ValidateContext(context.Background(), ValidateContextOptions{
CertChain: revokableChain,
AuthenticSigningTime: time.Now(),
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
})
if err != nil {
t.Errorf("Expected CheckStatus to succeed, but got error: %v", err)
Expand All @@ -606,15 +611,15 @@ func TestCheckRevocationStatusForTimestampChain(t *testing.T) {
// 3rd cert will be unknown, 5th will be revoked, the rest will be good
client := testhelper.MockClient(testChain, []ocsp.ResponseStatus{ocsp.Good, ocsp.Good, ocsp.Unknown, ocsp.Good, ocsp.Revoked, ocsp.Good}, nil, true)
r, err := NewWithOptions(Options{
OCSPHTTPClient: client,
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
OCSPHTTPClient: client,
})
if err != nil {
t.Errorf("Expected successful creation of revocation, but received error: %v", err)
}
certResults, err := r.ValidateContext(context.Background(), ValidateContextOptions{
CertChain: revokableChain,
AuthenticSigningTime: time.Now(),
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
})
if err != nil {
t.Errorf("Expected CheckStatus to succeed, but got error: %v", err)
Expand Down Expand Up @@ -644,15 +649,15 @@ func TestCheckRevocationStatusForTimestampChain(t *testing.T) {
// 3rd cert will be future revoked, the rest will be good
client := testhelper.MockClient(testChain, []ocsp.ResponseStatus{ocsp.Good, ocsp.Good, ocsp.Revoked, ocsp.Good}, &revokedTime, true)
r, err := NewWithOptions(Options{
OCSPHTTPClient: client,
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
OCSPHTTPClient: client,
})
if err != nil {
t.Errorf("Expected successful creation of revocation, but received error: %v", err)
}
certResults, err := r.ValidateContext(context.Background(), ValidateContextOptions{
CertChain: revokableChain,
AuthenticSigningTime: time.Now(),
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
})
if err != nil {
t.Errorf("Expected CheckStatus to succeed, but got error: %v", err)
Expand All @@ -672,15 +677,15 @@ func TestCheckRevocationStatusForTimestampChain(t *testing.T) {
// 3rd cert will be unknown, 5th will be future revoked, the rest will be good
client := testhelper.MockClient(testChain, []ocsp.ResponseStatus{ocsp.Good, ocsp.Good, ocsp.Unknown, ocsp.Good, ocsp.Revoked, ocsp.Good}, &revokedTime, true)
r, err := NewWithOptions(Options{
OCSPHTTPClient: client,
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
OCSPHTTPClient: client,
})
if err != nil {
t.Errorf("Expected successful creation of revocation, but received error: %v", err)
}
certResults, err := r.ValidateContext(context.Background(), ValidateContextOptions{
CertChain: revokableChain,
AuthenticSigningTime: time.Now(),
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
})
if err != nil {
t.Errorf("Expected CheckStatus to succeed, but got error: %v", err)
Expand All @@ -704,15 +709,15 @@ func TestCheckRevocationStatusForTimestampChain(t *testing.T) {
// 3rd cert will be revoked, the rest will be good
client := testhelper.MockClient(testChain, []ocsp.ResponseStatus{ocsp.Good, ocsp.Good, ocsp.Revoked, ocsp.Good}, nil, true)
r, err := NewWithOptions(Options{
OCSPHTTPClient: client,
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
OCSPHTTPClient: client,
})
if err != nil {
t.Errorf("Expected successful creation of revocation, but received error: %v", err)
}
certResults, err := r.ValidateContext(context.Background(), ValidateContextOptions{
CertChain: revokableChain,
AuthenticSigningTime: time.Now().Add(time.Hour),
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
})
if err != nil {
t.Errorf("Expected CheckStatus to succeed, but got error: %v", err)
Expand Down Expand Up @@ -740,15 +745,15 @@ func TestCheckRevocationStatusForTimestampChain(t *testing.T) {
t.Errorf("exected zeroTime.IsZero() to be true")
}
r, err := NewWithOptions(Options{
OCSPHTTPClient: client,
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
OCSPHTTPClient: client,
})
if err != nil {
t.Errorf("Expected successful creation of revocation, but received error: %v", err)
}
certResults, err := r.ValidateContext(context.Background(), ValidateContextOptions{
CertChain: revokableChain,
AuthenticSigningTime: time.Now().Add(time.Hour),
CertChainPurpose: x509.ExtKeyUsageTimeStamping,
})
if err != nil {
t.Errorf("Expected CheckStatus to succeed, but got error: %v", err)
Expand Down Expand Up @@ -990,14 +995,15 @@ func TestCheckRevocationInvalidChain(t *testing.T) {

func TestValidateContext(t *testing.T) {
r, err := NewWithOptions(Options{
OCSPHTTPClient: &http.Client{},
CertChainPurpose: x509.ExtKeyUsageCodeSigning,
OCSPHTTPClient: &http.Client{},
})
if err != nil {
t.Fatal(err)
}
expectedErrMsg := "invalid chain: expected chain to be correct and complete: chain does not contain any certificates"
_, err = r.ValidateContext(context.Background(), ValidateContextOptions{})
_, err = r.ValidateContext(context.Background(), ValidateContextOptions{
CertChainPurpose: x509.ExtKeyUsageCodeSigning,
})
if err == nil || err.Error() != expectedErrMsg {
t.Fatalf("expected %s, but got %s", expectedErrMsg, err)
}
Expand Down

0 comments on commit 3ca469d

Please sign in to comment.