diff --git a/x509/codesigning_cert_validations_test.go b/x509/codesigning_cert_validations_test.go index dcb2e230..4fd74d78 100644 --- a/x509/codesigning_cert_validations_test.go +++ b/x509/codesigning_cert_validations_test.go @@ -19,6 +19,7 @@ import ( _ "embed" "errors" "os" + "strings" "testing" "time" @@ -200,6 +201,33 @@ func TestFailEmptyChain(t *testing.T) { assertErrorEqual("certificate chain must contain at least one certificate", err, t) } +func TestInvalidSelfSignedLeaf(t *testing.T) { + cert, err := createSelfSignedCert("valid cert", "invalid cert", false) + if err != nil { + t.Error(err) + } + certChain := []*x509.Certificate{cert} + signingTime := time.Now() + + err = ValidateCodeSigningCertChain(certChain, &signingTime) + assertErrorEqual("invalid self-signed leaf certificate. subject: \"CN=valid cert\". Error: issuer and subject are not the same", err, t) +} + +func TestInvalidCodeSigningCertSigningTime(t *testing.T) { + cert, err := createSelfSignedCert("valid cert", "valid cert", false) + if err != nil { + t.Error(err) + } + certChain := []*x509.Certificate{cert} + signingTime := time.Date(2021, 7, 7, 20, 48, 42, 0, time.UTC) + + expectPrefix := "certificate with subject \"CN=valid cert\" was invalid at signing time of 2021-07-07 20:48:42 +0000 UTC" + err = ValidateCodeSigningCertChain(certChain, &signingTime) + if !strings.HasPrefix(err.Error(), expectPrefix) { + t.Errorf("expected error to start with %q, got %q", expectPrefix, err) + } +} + func TestFailInvalidSigningTime(t *testing.T) { certChain := []*x509.Certificate{codeSigningCert, intermediateCert2, intermediateCert1, rootCert} diff --git a/x509/helper_test.go b/x509/helper_test.go new file mode 100644 index 00000000..c7d703ee --- /dev/null +++ b/x509/helper_test.go @@ -0,0 +1,102 @@ +package x509 + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "math/big" + "testing" + "time" + + "github.com/notaryproject/notation-core-go/internal/oid" +) + +func createSelfSignedCert(subject string, issuer string, isTimestamp bool) (*x509.Certificate, error) { + priv, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, err + } + + template := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{CommonName: subject}, + NotBefore: time.Now(), + NotAfter: time.Now().Add(365 * 24 * time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature, + } + + if isTimestamp { + oids := []asn1.ObjectIdentifier{{1, 3, 6, 1, 5, 5, 7, 3, 8}} + value, err := asn1.Marshal(oids) + if err != nil { + return nil, err + } + template.ExtraExtensions = []pkix.Extension{{ + Id: oid.ExtKeyUsage, + Critical: true, + Value: value, + }} + template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping} + } + + parentTemplate := &x509.Certificate{ + SerialNumber: big.NewInt(2), + Subject: pkix.Name{CommonName: issuer}, + NotBefore: time.Now(), + NotAfter: time.Now().Add(365 * 24 * time.Hour), + KeyUsage: x509.KeyUsageCertSign, + } + + certDER, err := x509.CreateCertificate(rand.Reader, template, parentTemplate, &priv.PublicKey, priv) + if err != nil { + return nil, err + } + + return x509.ParseCertificate(certDER) +} + +func TestValidateSelfSignedLeaf(t *testing.T) { + selfSignedCert, err := createSelfSignedCert("Valid Cert", "Valid Cert", false) + if err != nil { + t.Fatalf("failed to create valid self-signed certificate: %v", err) + } + emptyCert := &x509.Certificate{} + notSelfIssuedCert, err := createSelfSignedCert("Not Self Issued Cert", "Invalid Issuer", false) + if err != nil { + t.Fatalf("failed to create not self-issued certificate: %v", err) + } + + tests := []struct { + name string + cert *x509.Certificate + wantErr bool + }{ + { + name: "Valid Self-Signed Certificate", + cert: selfSignedCert, + wantErr: false, + }, + { + name: "Empty Certificate", + cert: emptyCert, + wantErr: true, + }, + { + name: "Not Self-Issued Certificate", + cert: notSelfIssuedCert, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateSelfSignedLeaf(tt.cert) + if (err != nil) != tt.wantErr { + t.Errorf("validateSelfSignedLeaf() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } + +} diff --git a/x509/timestamp_cert_validations_test.go b/x509/timestamp_cert_validations_test.go index f2a29ff2..2314e03d 100644 --- a/x509/timestamp_cert_validations_test.go +++ b/x509/timestamp_cert_validations_test.go @@ -16,6 +16,7 @@ package x509 import ( "crypto/x509" "crypto/x509/pkix" + "strings" "testing" ) @@ -39,6 +40,33 @@ func TestValidTimestampingChain(t *testing.T) { } } +func TestInvalidTimestampSelfSignedCert(t *testing.T) { + cert, err := createSelfSignedCert("valid cert", "valid cert", false) + if err != nil { + t.Error(err) + } + certChain := []*x509.Certificate{cert} + + expectPrefix := "invalid self-signed certificate. Error: timestamp signing certificate with subject \"CN=valid cert\" must have and only have Timestamping as extended key usage" + err = ValidateTimestampingCertChain(certChain) + if !strings.HasPrefix(err.Error(), expectPrefix) { + t.Errorf("expected error to start with %q, got %q", expectPrefix, err) + } +} + +func TestValidTimestampSelfSignedCert(t *testing.T) { + cert, err := createSelfSignedCert("valid cert", "valid cert", true) + if err != nil { + t.Error(err) + } + certChain := []*x509.Certificate{cert} + + err = ValidateTimestampingCertChain(certChain) + if err != nil { + t.Error(err) + } +} + func TestInvalidTimestampingChain(t *testing.T) { timestamp_leaf, err := readSingleCertificate("testdata/timestamp_leaf.crt") if err != nil {