From 23abb23263ddc17823eed7800fc71e8dc8def69f Mon Sep 17 00:00:00 2001 From: Andrew Harding Date: Mon, 28 Feb 2022 14:09:59 -0700 Subject: [PATCH] Add x509 svid verify options (#176) Signed-off-by: Andrew Harding --- v2/svid/x509svid/verify.go | 36 ++++++++++++++++++++++++++++++--- v2/svid/x509svid/verify_test.go | 14 +++++++++++-- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/v2/svid/x509svid/verify.go b/v2/svid/x509svid/verify.go index 3e2185a1..681d2844 100644 --- a/v2/svid/x509svid/verify.go +++ b/v2/svid/x509svid/verify.go @@ -2,6 +2,7 @@ package x509svid import ( "crypto/x509" + "time" "github.com/spiffe/go-spiffe/v2/bundle/x509bundle" "github.com/spiffe/go-spiffe/v2/internal/x509util" @@ -11,10 +12,28 @@ import ( var x509svidErr = errs.Class("x509svid") +// VerifyOption is an option used when verifying X509-SVIDs. +type VerifyOption interface { + apply(config *verifyConfig) +} + +// WithTime sets the time used when verifying validity periods on the X509-SVID. +// If not used, the current time will be used. +func WithTime(now time.Time) VerifyOption { + return verifyOption(func(config *verifyConfig) { + config.now = now + }) +} + // Verify verifies an X509-SVID chain using the X.509 bundle source. It // returns the SPIFFE ID of the X509-SVID and one or more chains back to a root // in the bundle. -func Verify(certs []*x509.Certificate, bundleSource x509bundle.Source) (spiffeid.ID, [][]*x509.Certificate, error) { +func Verify(certs []*x509.Certificate, bundleSource x509bundle.Source, opts ...VerifyOption) (spiffeid.ID, [][]*x509.Certificate, error) { + config := &verifyConfig{} + for _, opt := range opts { + opt.apply(config) + } + switch { case len(certs) == 0: return spiffeid.ID{}, nil, x509svidErr.New("empty certificates chain") @@ -46,6 +65,7 @@ func Verify(certs []*x509.Certificate, bundleSource x509bundle.Source) (spiffeid Roots: x509util.NewCertPool(bundle.X509Authorities()), Intermediates: x509util.NewCertPool(certs[1:]), KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, + CurrentTime: config.now, }) if err != nil { return id, nil, x509svidErr.New("could not verify leaf certificate: %w", err) @@ -57,7 +77,7 @@ func Verify(certs []*x509.Certificate, bundleSource x509bundle.Source) (spiffeid // ParseAndVerify parses and verifies an X509-SVID chain using the X.509 // bundle source. It returns the SPIFFE ID of the X509-SVID and one or more // chains back to a root in the bundle. -func ParseAndVerify(rawCerts [][]byte, bundleSource x509bundle.Source) (spiffeid.ID, [][]*x509.Certificate, error) { +func ParseAndVerify(rawCerts [][]byte, bundleSource x509bundle.Source, opts ...VerifyOption) (spiffeid.ID, [][]*x509.Certificate, error) { var certs []*x509.Certificate for _, rawCert := range rawCerts { cert, err := x509.ParseCertificate(rawCert) @@ -66,7 +86,7 @@ func ParseAndVerify(rawCerts [][]byte, bundleSource x509bundle.Source) (spiffeid } certs = append(certs, cert) } - return Verify(certs, bundleSource) + return Verify(certs, bundleSource, opts...) } // IDFromCert extracts the SPIFFE ID from the URI SAN of the provided @@ -81,3 +101,13 @@ func IDFromCert(cert *x509.Certificate) (spiffeid.ID, error) { } return spiffeid.FromURI(cert.URIs[0]) } + +type verifyConfig struct { + now time.Time +} + +type verifyOption func(config *verifyConfig) + +func (fn verifyOption) apply(config *verifyConfig) { + fn(config) +} diff --git a/v2/svid/x509svid/verify_test.go b/v2/svid/x509svid/verify_test.go index 54e97268..ae071440 100644 --- a/v2/svid/x509svid/verify_test.go +++ b/v2/svid/x509svid/verify_test.go @@ -4,6 +4,7 @@ import ( "crypto/x509" "net/url" "testing" + "time" "github.com/spiffe/go-spiffe/v2/bundle/spiffebundle" "github.com/spiffe/go-spiffe/v2/bundle/x509bundle" @@ -37,6 +38,7 @@ func TestVerify(t *testing.T) { name string chain []*x509.Certificate bundle x509bundle.Source + opts []x509svid.VerifyOption expectedID spiffeid.ID err string }{ @@ -110,6 +112,13 @@ func TestVerify(t *testing.T) { bundle: bundle1, err: "x509svid: leaf certificate with KeyCrlSign key usage", }, + { + name: "with time", + chain: leaf1, + bundle: bundle1, + opts: []x509svid.VerifyOption{x509svid.WithTime(leaf1[0].NotAfter.Add(time.Second))}, + err: "x509svid: could not verify leaf certificate: x509: certificate has expired", + }, { name: "success", chain: leaf1, @@ -120,9 +129,10 @@ func TestVerify(t *testing.T) { for _, testCase := range testCases { testCase := testCase // alias loop var as it is used in the closure t.Run(testCase.name, func(t *testing.T) { - _, verifiedChains, err := x509svid.Verify(testCase.chain, testCase.bundle) + _, verifiedChains, err := x509svid.Verify(testCase.chain, testCase.bundle, testCase.opts...) if testCase.err != "" { - require.EqualError(t, err, testCase.err) + require.Error(t, err) + require.Contains(t, err.Error(), testCase.err) return } require.NoError(t, err)