diff --git a/verifier/testdata/truststore/x509/tsa/test-mismatch/DigiCertTSARootSHA384.cer b/verifier/testdata/truststore/x509/tsa/test-mismatch/DigiCertTSARootSHA384.cer new file mode 100644 index 00000000..99bcc84b Binary files /dev/null and b/verifier/testdata/truststore/x509/tsa/test-mismatch/DigiCertTSARootSHA384.cer differ diff --git a/verifier/testdata/truststore/x509/tsa/test-mismatch/wabbit-networks.io.crt b/verifier/testdata/truststore/x509/tsa/test-nonCA/wabbit-networks.io.crt similarity index 100% rename from verifier/testdata/truststore/x509/tsa/test-mismatch/wabbit-networks.io.crt rename to verifier/testdata/truststore/x509/tsa/test-nonCA/wabbit-networks.io.crt diff --git a/verifier/testdata/truststore/x509/tsa/test-nonSelfIssued/nonSelfIssued.crt b/verifier/testdata/truststore/x509/tsa/test-nonSelfIssued/nonSelfIssued.crt new file mode 100644 index 00000000..6ec50052 Binary files /dev/null and b/verifier/testdata/truststore/x509/tsa/test-nonSelfIssued/nonSelfIssued.crt differ diff --git a/verifier/truststore/truststore.go b/verifier/truststore/truststore.go index 067f5e63..067372c1 100644 --- a/verifier/truststore/truststore.go +++ b/verifier/truststore/truststore.go @@ -15,6 +15,7 @@ package truststore import ( + "bytes" "context" "crypto/x509" "errors" @@ -106,6 +107,14 @@ func (trustStore *x509TrustStore) GetCertificates(ctx context.Context, storeType if err := ValidateCertificates(certs); err != nil { return nil, CertificateError{InnerError: err, Msg: fmt.Sprintf("failed to validate the trusted certificate %s in trust store %s of type %s", certFileName, namedStore, storeType)} } + // we require TSA certificates in trust store to be root CA certificates + if storeType == TypeTSA { + for _, cert := range certs { + if err := isRootCACertificate(cert); err != nil { + return nil, CertificateError{InnerError: err, Msg: fmt.Sprintf("trusted certificate %s in trust store %s of type %s is invalid: %v", certFileName, namedStore, storeType, err.Error())} + } + } + } certificates = append(certificates, certs...) } if len(certificates) < 1 { @@ -137,3 +146,14 @@ func ValidateCertificates(certs []*x509.Certificate) error { func isValidStoreType(storeType Type) bool { return slices.Contains(Types, storeType) } + +// isRootCACertificate returns nil if cert is a root CA certificate +func isRootCACertificate(cert *x509.Certificate) error { + if err := cert.CheckSignatureFrom(cert); err != nil { + return fmt.Errorf("certificate with subject %q is not a root CA certificate: %w", cert.Subject, err) + } + if !bytes.Equal(cert.RawSubject, cert.RawIssuer) { + return fmt.Errorf("certificate with subject %q is not a root CA certificate: issuer (%s) and subject (%s) are not the same", cert.Subject, cert.Issuer, cert.Subject) + } + return nil +} diff --git a/verifier/truststore/truststore_test.go b/verifier/truststore/truststore_test.go index 762553e5..3ee3c740 100644 --- a/verifier/truststore/truststore_test.go +++ b/verifier/truststore/truststore_test.go @@ -98,3 +98,31 @@ func TestValidateCertsWithLeafCert(t *testing.T) { t.Fatalf("leaf cert in a trust store should return error %q, got: %v", expectedErr, err) } } + +func TestGetCertFromValidTsaTrustStore(t *testing.T) { + // testing ../testdata/truststore/x509/tsa/test-nonCA/globalsignRoot.cer + _, err := trustStore.GetCertificates(context.Background(), "tsa", "test-timestamp") + if err != nil { + t.Fatalf("expected nil error, but got %s", err) + } +} + +func TestGetCertFromInvalidTsaTrustStore(t *testing.T) { + t.Run("non CA certificate", func(t *testing.T) { + // testing ../testdata/truststore/x509/tsa/test-nonCA/wabbit-networks.io + expectedErrMsg := `trusted certificate wabbit-networks.io.crt in trust store test-nonCA of type tsa is invalid: certificate with subject "CN=wabbit-networks.io,O=Notary,L=Seattle,ST=WA,C=US" is not a root CA certificate: x509: invalid signature: parent certificate cannot sign this kind of certificate` + _, err := trustStore.GetCertificates(context.Background(), "tsa", "test-nonCA") + if err == nil || err.Error() != expectedErrMsg { + t.Fatalf("expected error: %s, but got %s", expectedErrMsg, err) + } + }) + + t.Run("not self-issued", func(t *testing.T) { + //testing ../testdata/truststore/x509/tsa/test-nonSelfIssued/nonSelfIssued.crt + expectedErrMsg := `trusted certificate nonSelfIssued.crt in trust store test-nonSelfIssued of type tsa is invalid: certificate with subject "CN=Notation Test Revokable RSA Chain Cert 2,O=Notary,L=Seattle,ST=WA,C=US" is not a root CA certificate: issuer (CN=Notation Test Revokable RSA Chain Cert Root,O=Notary,L=Seattle,ST=WA,C=US) and subject (CN=Notation Test Revokable RSA Chain Cert 2,O=Notary,L=Seattle,ST=WA,C=US) are not the same` + _, err := trustStore.GetCertificates(context.Background(), "tsa", "test-nonSelfIssued") + if err == nil || err.Error() != expectedErrMsg { + t.Fatalf("expected error: %s, but got %s", expectedErrMsg, err) + } + }) +}