diff --git a/pkg/cryptoutil/verifier.go b/pkg/cryptoutil/verifier.go index 82e4acb2..b243e286 100644 --- a/pkg/cryptoutil/verifier.go +++ b/pkg/cryptoutil/verifier.go @@ -22,6 +22,7 @@ import ( "crypto/x509" "fmt" "io" + "time" ) type Verifier interface { @@ -36,6 +37,7 @@ type verifierOptions struct { roots []*x509.Certificate intermediates []*x509.Certificate hash crypto.Hash + trustedTime time.Time } func VerifyWithRoots(roots []*x509.Certificate) VerifierOption { @@ -56,6 +58,12 @@ func VerifyWithHash(h crypto.Hash) VerifierOption { } } +func VerifyWithTrustedTime(t time.Time) VerifierOption { + return func(vo *verifierOptions) { + vo.trustedTime = t + } +} + func NewVerifier(pub interface{}, opts ...VerifierOption) (Verifier, error) { options := &verifierOptions{ hash: crypto.SHA256, @@ -73,7 +81,7 @@ func NewVerifier(pub interface{}, opts ...VerifierOption) (Verifier, error) { case ed25519.PublicKey: return NewED25519Verifier(key), nil case *x509.Certificate: - return NewX509Verifier(key, options.intermediates, options.roots) + return NewX509Verifier(key, options.intermediates, options.roots, options.trustedTime) default: return nil, ErrUnsupportedKeyType{ t: fmt.Sprintf("%T", pub), diff --git a/pkg/cryptoutil/x509.go b/pkg/cryptoutil/x509.go index c2b89baf..c9a3d28c 100644 --- a/pkg/cryptoutil/x509.go +++ b/pkg/cryptoutil/x509.go @@ -18,6 +18,7 @@ import ( "crypto/x509" "encoding/pem" "io" + "time" ) type X509Verifier struct { @@ -25,9 +26,10 @@ type X509Verifier struct { roots []*x509.Certificate intermediates []*x509.Certificate verifier Verifier + trustedTime time.Time } -func NewX509Verifier(cert *x509.Certificate, intermediates, roots []*x509.Certificate) (*X509Verifier, error) { +func NewX509Verifier(cert *x509.Certificate, intermediates, roots []*x509.Certificate, trustedTime time.Time) (*X509Verifier, error) { verifier, err := NewVerifier(cert.PublicKey) if err != nil { return nil, err @@ -38,6 +40,7 @@ func NewX509Verifier(cert *x509.Certificate, intermediates, roots []*x509.Certif roots: roots, intermediates: intermediates, verifier: verifier, + trustedTime: trustedTime, }, nil } @@ -49,6 +52,7 @@ func (v *X509Verifier) Verify(body io.Reader, sig []byte) error { rootPool := certificatesToPool(v.roots) intermediatePool := certificatesToPool(v.intermediates) if _, err := v.cert.Verify(x509.VerifyOptions{ + CurrentTime: v.trustedTime, Roots: rootPool, Intermediates: intermediatePool, }); err != nil { diff --git a/pkg/cryptoutil/x509_test.go b/pkg/cryptoutil/x509_test.go index b43c319b..b8d9d676 100644 --- a/pkg/cryptoutil/x509_test.go +++ b/pkg/cryptoutil/x509_test.go @@ -143,16 +143,23 @@ func TestX509(t *testing.T) { sig, err := signer.Sign(bytes.NewReader(data)) require.NoError(t, err) - verifier, err := NewX509Verifier(leaf, []*x509.Certificate{intermediate}, []*x509.Certificate{root}) + // test success and failure with expected and unexpected data + verifier, err := NewX509Verifier(leaf, []*x509.Certificate{intermediate}, []*x509.Certificate{root}, time.Time{}) require.NoError(t, err) err = verifier.Verify(bytes.NewReader(data), sig) assert.NoError(t, err) err = verifier.Verify(bytes.NewReader([]byte("this is not the signed data")), sig) assert.Error(t, err) - verifier, err = NewX509Verifier(leaf, []*x509.Certificate{intermediate}, nil) + // test without roots + verifier, err = NewX509Verifier(leaf, []*x509.Certificate{intermediate}, nil, time.Time{}) require.NoError(t, err) err = verifier.Verify(bytes.NewReader(data), sig) assert.Error(t, err) + // test invalid time, certificate didn't exist at the "trusted time" + verifier, err = NewX509Verifier(leaf, []*x509.Certificate{intermediate}, []*x509.Certificate{root}, time.Now().Add(-1*time.Hour)) + require.NoError(t, err) + err = verifier.Verify(bytes.NewReader(data), sig) + assert.Error(t, err) } diff --git a/pkg/dsse/dsse.go b/pkg/dsse/dsse.go index 4ffde80c..1aef0869 100644 --- a/pkg/dsse/dsse.go +++ b/pkg/dsse/dsse.go @@ -20,6 +20,7 @@ import ( "encoding/pem" "fmt" "io" + "time" "github.com/testifysec/witness/pkg/cryptoutil" ) @@ -111,6 +112,7 @@ type verificationOptions struct { roots []*x509.Certificate intermediates []*x509.Certificate verifiers []cryptoutil.Verifier + trustedTime time.Time } func WithRoots(roots []*x509.Certificate) VerificationOption { @@ -131,6 +133,12 @@ func WithVerifiers(verifiers []cryptoutil.Verifier) VerificationOption { } } +func WithTrustedTime(time time.Time) VerificationOption { + return func(vo *verificationOptions) { + vo.trustedTime = time + } +} + func (e Envelope) Verify(opts ...VerificationOption) ([]cryptoutil.Verifier, error) { options := &verificationOptions{} for _, opt := range opts { @@ -156,7 +164,7 @@ func (e Envelope) Verify(opts ...VerificationOption) ([]cryptoutil.Verifier, err continue } - verifier, err := cryptoutil.NewX509Verifier(cert, options.intermediates, options.roots) + verifier, err := cryptoutil.NewX509Verifier(cert, options.intermediates, options.roots, options.trustedTime) if err != nil { return nil, err } diff --git a/pkg/rekor/rekor.go b/pkg/rekor/rekor.go index 63a66d82..aac023c7 100644 --- a/pkg/rekor/rekor.go +++ b/pkg/rekor/rekor.go @@ -21,6 +21,7 @@ import ( "encoding/base64" "errors" "fmt" + "time" "github.com/go-openapi/runtime" "github.com/sigstore/rekor/pkg/client" @@ -160,7 +161,7 @@ func ParseEnvelopeFromEntry(entry *models.LogEntryAnon) (dsse.Envelope, error) { return env, fmt.Errorf("failed to decode signature: %w", err) } - verifier, err := cryptoutil.NewVerifierFromReader(bytes.NewReader(sig.PublicKey)) + verifier, err := cryptoutil.NewVerifierFromReader(bytes.NewReader(sig.PublicKey), cryptoutil.VerifyWithTrustedTime(time.Unix(*entry.IntegratedTime, 0))) if err != nil { return env, fmt.Errorf("failed to create verifier from public key on rekor entry: %w", err) }