diff --git a/x509util/extensions.go b/x509util/extensions.go index 3f733a0b..ede5ab64 100644 --- a/x509util/extensions.go +++ b/x509util/extensions.go @@ -340,9 +340,15 @@ func (b BasicConstraints) Set(c *x509.Certificate) { c.MaxPathLenZero = true case b.MaxPathLen < 0: c.MaxPathLen = -1 + c.MaxPathLenZero = false default: c.MaxPathLen = b.MaxPathLen + c.MaxPathLenZero = false } + } else { + c.BasicConstraintsValid = false + c.MaxPathLen = 0 + c.MaxPathLenZero = false } } diff --git a/x509util/extensions_test.go b/x509util/extensions_test.go new file mode 100644 index 00000000..71a00a09 --- /dev/null +++ b/x509util/extensions_test.go @@ -0,0 +1,662 @@ +package x509util + +import ( + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "net" + "net/url" + "reflect" + "testing" +) + +func Test_convertName(t *testing.T) { + type args struct { + s string + } + tests := []struct { + name string + args args + want string + }{ + {"lowerCase", args{"FooBAR"}, "foobar"}, + {"underscore", args{"foo_bar"}, "foobar"}, + {"mixed", args{"FOO_Bar"}, "foobar"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := convertName(tt.args.s); got != tt.want { + t.Errorf("convertName() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_newExtension(t *testing.T) { + type args struct { + e pkix.Extension + } + tests := []struct { + name string + args args + want Extension + }{ + {"ok", args{pkix.Extension{Id: []int{1, 2, 3, 4}, Value: []byte("foo")}}, Extension{ID: []int{1, 2, 3, 4}, Critical: false, Value: []byte("foo")}}, + {"critical", args{pkix.Extension{Id: []int{1, 2, 3, 4}, Critical: true, Value: []byte("foo")}}, Extension{ID: []int{1, 2, 3, 4}, Critical: true, Value: []byte("foo")}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := newExtension(tt.args.e); !reflect.DeepEqual(got, tt.want) { + t.Errorf("newExtension() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_newExtensions(t *testing.T) { + ext1 := pkix.Extension{Id: []int{1, 2, 3, 4}, Value: []byte("foo")} + ext2 := pkix.Extension{Id: []int{4, 3, 2, 1}, Critical: true, Value: []byte("bar")} + + type args struct { + extensions []pkix.Extension + } + tests := []struct { + name string + args args + want []Extension + }{ + {"ok", args{[]pkix.Extension{ext1, ext2}}, []Extension{ + {ID: []int{1, 2, 3, 4}, Critical: false, Value: []byte("foo")}, + {ID: []int{4, 3, 2, 1}, Critical: true, Value: []byte("bar")}, + }}, + {"nil", args{}, nil}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := newExtensions(tt.args.extensions); !reflect.DeepEqual(got, tt.want) { + t.Errorf("newExtensions() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestExtension_Set(t *testing.T) { + type fields struct { + ID ObjectIdentifier + Critical bool + Value []byte + } + type args struct { + c *x509.Certificate + } + tests := []struct { + name string + fields fields + args args + want *x509.Certificate + }{ + {"ok", fields{[]int{1, 2, 3, 4}, true, []byte("foo")}, args{&x509.Certificate{}}, &x509.Certificate{ + ExtraExtensions: []pkix.Extension{{Id: []int{1, 2, 3, 4}, Critical: true, Value: []byte("foo")}}, + }}, + {"existing", fields{[]int{1, 2, 3, 4}, true, []byte("foo")}, args{&x509.Certificate{ + ExtraExtensions: []pkix.Extension{ + {Id: []int{1, 1, 1, 1}, Critical: false, Value: []byte("foo")}, + }, + }}, &x509.Certificate{ + ExtraExtensions: []pkix.Extension{ + {Id: []int{1, 1, 1, 1}, Critical: false, Value: []byte("foo")}, + {Id: []int{1, 2, 3, 4}, Critical: true, Value: []byte("foo")}, + }, + }}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := Extension{ + ID: tt.fields.ID, + Critical: tt.fields.Critical, + Value: tt.fields.Value, + } + e.Set(tt.args.c) + if !reflect.DeepEqual(tt.args.c, tt.want) { + t.Errorf("Extension.Set() = %v, want %v", tt.args.c, tt.want) + } + }) + } +} + +func TestObjectIdentifier_MarshalJSON(t *testing.T) { + tests := []struct { + name string + o ObjectIdentifier + want []byte + wantErr bool + }{ + {"ok", []int{1, 2, 3, 4}, []byte(`"1.2.3.4"`), false}, + {"empty", []int{}, []byte(`""`), false}, + {"nil", nil, []byte(`""`), false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.o.MarshalJSON() + if (err != nil) != tt.wantErr { + t.Errorf("ObjectIdentifier.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ObjectIdentifier.MarshalJSON() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestObjectIdentifier_UnmarshalJSON(t *testing.T) { + type args struct { + data []byte + } + tests := []struct { + name string + args args + want ObjectIdentifier + wantErr bool + }{ + {"ok", args{[]byte(`"1.2.3.4"`)}, []int{1, 2, 3, 4}, false}, + {"empty", args{[]byte(`""`)}, []int{}, false}, + {"null", args{[]byte(`null`)}, []int{}, false}, + {"number", args{[]byte(`123`)}, nil, true}, + {"badFormat", args{[]byte(`"1.2.foo.4`)}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got ObjectIdentifier + if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { + t.Errorf("ObjectIdentifier.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ObjectIdentifier.UnmarshalJSON() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSubjectAlternativeName_Set(t *testing.T) { + panicCount := 0 + type fields struct { + Type string + Value string + } + type args struct { + c *x509.Certificate + } + tests := []struct { + name string + fields fields + args args + want *x509.Certificate + }{ + {"dns", fields{"dns", "foo.com"}, args{&x509.Certificate{}}, &x509.Certificate{DNSNames: []string{"foo.com"}}}, + {"dnsAdd", fields{"DNS", "bar.com"}, args{&x509.Certificate{DNSNames: []string{"foo.com"}}}, &x509.Certificate{DNSNames: []string{"foo.com", "bar.com"}}}, + {"email", fields{"email", "john@doe.com"}, args{&x509.Certificate{}}, &x509.Certificate{EmailAddresses: []string{"john@doe.com"}}}, + {"emailAdd", fields{"EMAIL", "jane@doe.com"}, args{&x509.Certificate{EmailAddresses: []string{"john@doe.com"}}}, &x509.Certificate{EmailAddresses: []string{"john@doe.com", "jane@doe.com"}}}, + {"ip", fields{"ip", "127.0.0.1"}, args{&x509.Certificate{}}, &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}}}, + {"ipAdd", fields{"IP", "::1"}, args{&x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}}}, &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")}}}, + {"ipBad", fields{"IP", "fooo"}, args{&x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}}}, &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}}}, + {"uri", fields{"uri", "https://foo.com"}, args{&x509.Certificate{}}, &x509.Certificate{URIs: []*url.URL{{Scheme: "https", Host: "foo.com"}}}}, + {"uriAdd", fields{"URI", "uri:foo:bar"}, args{&x509.Certificate{URIs: []*url.URL{{Scheme: "https", Host: "foo.com"}}}}, &x509.Certificate{URIs: []*url.URL{{Scheme: "https", Host: "foo.com"}, {Scheme: "uri", Opaque: "foo:bar"}}}}, + {"uriBad", fields{"URI", "::1"}, args{&x509.Certificate{URIs: []*url.URL{{Scheme: "https", Host: "foo.com"}}}}, &x509.Certificate{URIs: []*url.URL{{Scheme: "https", Host: "foo.com"}}}}, + {"AutoDNS", fields{"", "foo.com"}, args{&x509.Certificate{}}, &x509.Certificate{DNSNames: []string{"foo.com"}}}, + {"AutoDNSAdd", fields{"auto", "bar.com"}, args{&x509.Certificate{DNSNames: []string{"foo.com"}}}, &x509.Certificate{DNSNames: []string{"foo.com", "bar.com"}}}, + {"AutoEmail", fields{"AUTO", "john@doe.com"}, args{&x509.Certificate{}}, &x509.Certificate{EmailAddresses: []string{"john@doe.com"}}}, + {"AutoEmailAdd", fields{"", "jane@doe.com"}, args{&x509.Certificate{EmailAddresses: []string{"john@doe.com"}}}, &x509.Certificate{EmailAddresses: []string{"john@doe.com", "jane@doe.com"}}}, + {"IPAutoIP", fields{"AutO", "127.0.0.1"}, args{&x509.Certificate{}}, &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}}}, + {"AutoIPAdd", fields{"", "::1"}, args{&x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}}}, &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")}}}, + {"AutoURI", fields{"Auto", "https://foo.com"}, args{&x509.Certificate{}}, &x509.Certificate{URIs: []*url.URL{{Scheme: "https", Host: "foo.com"}}}}, + {"AutoURIAdd", fields{"", "uri:foo:bar"}, args{&x509.Certificate{URIs: []*url.URL{{Scheme: "https", Host: "foo.com"}}}}, &x509.Certificate{URIs: []*url.URL{{Scheme: "https", Host: "foo.com"}, {Scheme: "uri", Opaque: "foo:bar"}}}}, + {"panic", fields{"panic", "foo.com"}, args{&x509.Certificate{}}, &x509.Certificate{DNSNames: []string{"foo.com"}}}, + {"panicAdd", fields{"panic", "bar.com"}, args{&x509.Certificate{DNSNames: []string{"foo.com"}}}, &x509.Certificate{DNSNames: []string{"foo.com"}}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + panicCount++ + } + }() + s := SubjectAlternativeName{ + Type: tt.fields.Type, + Value: tt.fields.Value, + } + s.Set(tt.args.c) + if !reflect.DeepEqual(tt.args.c, tt.want) { + t.Errorf("SubjectAlternativeName.Set() = %v, want %v", tt.args.c, tt.want) + } + }) + } + + if panicCount != 2 { + t.Errorf("SubjectAlternativeName.Set() number of panics = %d, want 2", panicCount) + } +} + +func TestKeyUsage_Set(t *testing.T) { + type args struct { + c *x509.Certificate + } + tests := []struct { + name string + k KeyUsage + args args + want *x509.Certificate + }{ + {"ok", KeyUsage(x509.KeyUsageDigitalSignature), args{&x509.Certificate{}}, &x509.Certificate{KeyUsage: x509.KeyUsageDigitalSignature}}, + {"overwrite", KeyUsage(x509.KeyUsageCRLSign | x509.KeyUsageCertSign), args{&x509.Certificate{KeyUsage: x509.KeyUsageDigitalSignature}}, &x509.Certificate{KeyUsage: x509.KeyUsageCRLSign | x509.KeyUsageCertSign}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.k.Set(tt.args.c) + if !reflect.DeepEqual(tt.args.c, tt.want) { + t.Errorf("KeyUsage.Set() = %v, want %v", tt.args.c, tt.want) + } + }) + } +} + +func TestKeyUsage_UnmarshalJSON(t *testing.T) { + type args struct { + data []byte + } + tests := []struct { + name string + args args + want KeyUsage + wantErr bool + }{ + // Normalized + {"DigitalSignature", args{[]byte(`"DigitalSignature"`)}, KeyUsage(x509.KeyUsageDigitalSignature), false}, + {"ContentCommitment", args{[]byte(`"ContentCommitment"`)}, KeyUsage(x509.KeyUsageContentCommitment), false}, + {"KeyEncipherment", args{[]byte(`"KeyEncipherment"`)}, KeyUsage(x509.KeyUsageKeyEncipherment), false}, + {"DataEncipherment", args{[]byte(`"DataEncipherment"`)}, KeyUsage(x509.KeyUsageDataEncipherment), false}, + {"KeyAgreement", args{[]byte(`"KeyAgreement"`)}, KeyUsage(x509.KeyUsageKeyAgreement), false}, + {"CertSign", args{[]byte(`"CertSign"`)}, KeyUsage(x509.KeyUsageCertSign), false}, + {"CRLSign", args{[]byte(`"CRLSign"`)}, KeyUsage(x509.KeyUsageCRLSign), false}, + {"EncipherOnly", args{[]byte(`"EncipherOnly"`)}, KeyUsage(x509.KeyUsageEncipherOnly), false}, + {"DecipherOnly", args{[]byte(`"DecipherOnly"`)}, KeyUsage(x509.KeyUsageDecipherOnly), false}, + // Snake case + {"digital_signature", args{[]byte(`"digital_signature"`)}, KeyUsage(x509.KeyUsageDigitalSignature), false}, + {"content_commitment", args{[]byte(`"content_commitment"`)}, KeyUsage(x509.KeyUsageContentCommitment), false}, + {"key_encipherment", args{[]byte(`"key_encipherment"`)}, KeyUsage(x509.KeyUsageKeyEncipherment), false}, + {"data_encipherment", args{[]byte(`"data_encipherment"`)}, KeyUsage(x509.KeyUsageDataEncipherment), false}, + {"key_agreement", args{[]byte(`"key_agreement"`)}, KeyUsage(x509.KeyUsageKeyAgreement), false}, + {"cert_sign", args{[]byte(`"cert_sign"`)}, KeyUsage(x509.KeyUsageCertSign), false}, + {"crl_sign", args{[]byte(`"crl_sign"`)}, KeyUsage(x509.KeyUsageCRLSign), false}, + {"encipher_only", args{[]byte(`"encipher_only"`)}, KeyUsage(x509.KeyUsageEncipherOnly), false}, + {"decipher_only", args{[]byte(`"decipher_only"`)}, KeyUsage(x509.KeyUsageDecipherOnly), false}, + // MultiString + {"DigitalSignatureAsArray", args{[]byte(`["digital_signature"]`)}, KeyUsage(x509.KeyUsageDigitalSignature), false}, + {"DigitalSignature|KeyEncipherment", args{[]byte(`["DigitalSignature", "key_encipherment"]`)}, KeyUsage(x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment), false}, + // Errors + {"invalid", args{[]byte(`"invalid"`)}, KeyUsage(0), true}, + {"number", args{[]byte(`123`)}, KeyUsage(0), true}, + {"object", args{[]byte(`{}`)}, KeyUsage(0), true}, + {"badJSON", args{[]byte(`{`)}, KeyUsage(0), true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got KeyUsage + if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { + t.Errorf("KeyUsage.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("KeyUsage.UnmarshalJSON() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestExtKeyUsage_Set(t *testing.T) { + eku1 := []x509.ExtKeyUsage{ + x509.ExtKeyUsageClientAuth, + x509.ExtKeyUsageServerAuth, + } + eku2 := []x509.ExtKeyUsage{ + x509.ExtKeyUsageCodeSigning, + } + type args struct { + c *x509.Certificate + } + tests := []struct { + name string + k ExtKeyUsage + args args + want *x509.Certificate + }{ + {"ok", ExtKeyUsage(eku1), args{&x509.Certificate{}}, &x509.Certificate{ExtKeyUsage: eku1}}, + {"overwrite", ExtKeyUsage(eku2), args{&x509.Certificate{ExtKeyUsage: eku1}}, &x509.Certificate{ExtKeyUsage: eku2}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.k.Set(tt.args.c) + if !reflect.DeepEqual(tt.args.c, tt.want) { + t.Errorf("ExtKeyUsage.Set() = %v, want %v", tt.args.c, tt.want) + } + }) + } +} + +func TestExtKeyUsage_UnmarshalJSON(t *testing.T) { + type args struct { + data []byte + } + tests := []struct { + name string + args args + want ExtKeyUsage + wantErr bool + }{ + // Normalized + {"Any", args{[]byte(`"Any"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageAny}), false}, + {"ServerAuth", args{[]byte(`"ServerAuth"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}), false}, + {"ClientAuth", args{[]byte(`"ClientAuth"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}), false}, + {"CodeSigning", args{[]byte(`"CodeSigning"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}), false}, + {"EmailProtection", args{[]byte(`"EmailProtection"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection}), false}, + {"IPSECEndSystem", args{[]byte(`"IPSECEndSystem"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageIPSECEndSystem}), false}, + {"IPSECTunnel", args{[]byte(`"IPSECTunnel"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageIPSECTunnel}), false}, + {"IPSECUser", args{[]byte(`"IPSECUser"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageIPSECUser}), false}, + {"TimeStamping", args{[]byte(`"TimeStamping"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping}), false}, + {"OCSPSigning", args{[]byte(`"OCSPSigning"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageOCSPSigning}), false}, + {"MicrosoftServerGatedCrypto", args{[]byte(`"MicrosoftServerGatedCrypto"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageMicrosoftServerGatedCrypto}), false}, + {"NetscapeServerGatedCrypto", args{[]byte(`"NetscapeServerGatedCrypto"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageNetscapeServerGatedCrypto}), false}, + {"MicrosoftCommercialCodeSigning", args{[]byte(`"MicrosoftCommercialCodeSigning"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageMicrosoftCommercialCodeSigning}), false}, + {"MicrosoftKernelCodeSigning", args{[]byte(`"MicrosoftKernelCodeSigning"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageMicrosoftKernelCodeSigning}), false}, + // Snake case + {"any", args{[]byte(`"any"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageAny}), false}, + {"server_auth", args{[]byte(`"server_auth"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}), false}, + {"client_auth", args{[]byte(`"client_auth"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}), false}, + {"code_signing", args{[]byte(`"code_signing"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}), false}, + {"email_protection", args{[]byte(`"email_protection"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection}), false}, + {"ipsec_end_system", args{[]byte(`"ipsec_end_system"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageIPSECEndSystem}), false}, + {"ipsec_tunnel", args{[]byte(`"ipsec_tunnel"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageIPSECTunnel}), false}, + {"ipsec_user", args{[]byte(`"ipsec_user"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageIPSECUser}), false}, + {"time_stamping", args{[]byte(`"time_stamping"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping}), false}, + {"ocsp_signing", args{[]byte(`"ocsp_signing"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageOCSPSigning}), false}, + {"microsoft_server_gated_crypto", args{[]byte(`"microsoft_server_gated_crypto"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageMicrosoftServerGatedCrypto}), false}, + {"netscape_server_gated_crypto", args{[]byte(`"netscape_server_gated_crypto"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageNetscapeServerGatedCrypto}), false}, + {"microsoft_commercial_code_signing", args{[]byte(`"microsoft_commercial_code_signing"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageMicrosoftCommercialCodeSigning}), false}, + {"microsoft_kernel_code_signing", args{[]byte(`"microsoft_kernel_code_signing"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageMicrosoftKernelCodeSigning}), false}, + // Multistring + {"CodeSigningAsArray", args{[]byte(`["code_signing"]`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}), false}, + {"ServerAuth+ClientAuth", args{[]byte(`["ServerAuth","client_auth"]`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}), false}, + // Errors + {"invalid", args{[]byte(`"invalid"`)}, nil, true}, + {"number", args{[]byte(`123`)}, nil, true}, + {"object", args{[]byte(`{}`)}, nil, true}, + {"badJSON", args{[]byte(`{`)}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got ExtKeyUsage + if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { + t.Errorf("ExtKeyUsage.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ExtKeyUsage.UnmarshalJSON() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSubjectKeyID_Set(t *testing.T) { + type args struct { + c *x509.Certificate + } + tests := []struct { + name string + id SubjectKeyID + args args + want *x509.Certificate + }{ + {"ok", []byte("subjectKeyID"), args{&x509.Certificate{}}, &x509.Certificate{SubjectKeyId: []byte("subjectKeyID")}}, + {"overwrite", []byte("newSubjectKeyID"), args{&x509.Certificate{SubjectKeyId: []byte("subjectKeyID")}}, &x509.Certificate{SubjectKeyId: []byte("newSubjectKeyID")}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.id.Set(tt.args.c) + if !reflect.DeepEqual(tt.args.c, tt.want) { + t.Errorf("SubjectKeyID.Set() = %v, want %v", tt.args.c, tt.want) + } + }) + } +} + +func TestAuthorityKeyID_Set(t *testing.T) { + type args struct { + c *x509.Certificate + } + tests := []struct { + name string + id AuthorityKeyID + args args + want *x509.Certificate + }{ + {"ok", []byte("authorityKeyID"), args{&x509.Certificate{}}, &x509.Certificate{AuthorityKeyId: []byte("authorityKeyID")}}, + {"overwrite", []byte("newAuthorityKeyID"), args{&x509.Certificate{AuthorityKeyId: []byte("authorityKeyID")}}, &x509.Certificate{AuthorityKeyId: []byte("newAuthorityKeyID")}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.id.Set(tt.args.c) + if !reflect.DeepEqual(tt.args.c, tt.want) { + t.Errorf("AuthorityKeyID.Set() = %v, want %v", tt.args.c, tt.want) + } + }) + } +} + +func TestOCSPServer_Set(t *testing.T) { + type args struct { + c *x509.Certificate + } + tests := []struct { + name string + o OCSPServer + args args + want *x509.Certificate + }{ + {"ok", []string{"oscp.server"}, args{&x509.Certificate{}}, &x509.Certificate{OCSPServer: []string{"oscp.server"}}}, + {"overwrite", []string{"oscp.server", "oscp.com"}, args{&x509.Certificate{OCSPServer: []string{"oscp.server"}}}, &x509.Certificate{OCSPServer: []string{"oscp.server", "oscp.com"}}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.o.Set(tt.args.c) + if !reflect.DeepEqual(tt.args.c, tt.want) { + t.Errorf("OCSPServer.Set() = %v, want %v", tt.args.c, tt.want) + } + }) + } +} + +func TestIssuingCertificateURL_Set(t *testing.T) { + type args struct { + c *x509.Certificate + } + tests := []struct { + name string + o IssuingCertificateURL + args args + want *x509.Certificate + }{ + {"ok", []string{"issuing.server"}, args{&x509.Certificate{}}, &x509.Certificate{IssuingCertificateURL: []string{"issuing.server"}}}, + {"overwrite", []string{"issuing.server", "issuing.com"}, args{&x509.Certificate{IssuingCertificateURL: []string{"issuing.server"}}}, &x509.Certificate{IssuingCertificateURL: []string{"issuing.server", "issuing.com"}}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.o.Set(tt.args.c) + if !reflect.DeepEqual(tt.args.c, tt.want) { + t.Errorf("IssuingCertificateURL.Set() = %v, want %v", tt.args.c, tt.want) + } + }) + } +} + +func TestCRLDistributionPoints_Set(t *testing.T) { + type args struct { + c *x509.Certificate + } + tests := []struct { + name string + o CRLDistributionPoints + args args + want *x509.Certificate + }{ + {"ok", []string{"crl.server"}, args{&x509.Certificate{}}, &x509.Certificate{CRLDistributionPoints: []string{"crl.server"}}}, + {"overwrite", []string{"crl.server", "crl.com"}, args{&x509.Certificate{CRLDistributionPoints: []string{"crl.server"}}}, &x509.Certificate{CRLDistributionPoints: []string{"crl.server", "crl.com"}}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.o.Set(tt.args.c) + if !reflect.DeepEqual(tt.args.c, tt.want) { + t.Errorf("CRLDistributionPoints.Set() = %v, want %v", tt.args.c, tt.want) + } + }) + } +} + +func TestPolicyIdentifiers_Set(t *testing.T) { + type args struct { + c *x509.Certificate + } + tests := []struct { + name string + o PolicyIdentifiers + args args + want *x509.Certificate + }{ + {"ok", []asn1.ObjectIdentifier{{1, 2, 3, 4}}, args{&x509.Certificate{}}, &x509.Certificate{PolicyIdentifiers: []asn1.ObjectIdentifier{{1, 2, 3, 4}}}}, + {"overwrite", []asn1.ObjectIdentifier{{1, 2, 3, 4}, {4, 3, 2, 1}}, args{&x509.Certificate{PolicyIdentifiers: []asn1.ObjectIdentifier{{1, 2, 3, 4}}}}, &x509.Certificate{PolicyIdentifiers: []asn1.ObjectIdentifier{{1, 2, 3, 4}, {4, 3, 2, 1}}}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.o.Set(tt.args.c) + if !reflect.DeepEqual(tt.args.c, tt.want) { + t.Errorf("PolicyIdentifiers.Set() = %v, want %v", tt.args.c, tt.want) + } + }) + } +} + +func TestBasicConstraints_Set(t *testing.T) { + type fields struct { + IsCA bool + MaxPathLen int + } + type args struct { + c *x509.Certificate + } + tests := []struct { + name string + fields fields + args args + want *x509.Certificate + }{ + {"IsCAFalse", fields{false, 0}, args{&x509.Certificate{}}, &x509.Certificate{}}, + {"IsCAFalseWithPathLen", fields{false, 1}, args{&x509.Certificate{}}, &x509.Certificate{}}, + {"IsCAFalseWithAnyPathLen", fields{false, -1}, args{&x509.Certificate{}}, &x509.Certificate{}}, + {"IsCATrue", fields{true, 0}, args{&x509.Certificate{}}, &x509.Certificate{IsCA: true, MaxPathLen: 0, MaxPathLenZero: true, BasicConstraintsValid: true}}, + {"IsCATrueWithPathLen", fields{true, 1}, args{&x509.Certificate{}}, &x509.Certificate{IsCA: true, MaxPathLen: 1, MaxPathLenZero: false, BasicConstraintsValid: true}}, + {"IsCATrueWithAnyPathLen", fields{true, -1}, args{&x509.Certificate{}}, &x509.Certificate{IsCA: true, MaxPathLen: -1, MaxPathLenZero: false, BasicConstraintsValid: true}}, + {"overwriteToFalse", fields{false, 0}, args{&x509.Certificate{IsCA: true, MaxPathLen: 0, MaxPathLenZero: true, BasicConstraintsValid: true}}, &x509.Certificate{}}, + {"overwriteToTrue", fields{true, -100}, args{&x509.Certificate{IsCA: true, MaxPathLen: 0, MaxPathLenZero: true, BasicConstraintsValid: true}}, &x509.Certificate{IsCA: true, MaxPathLen: -1, MaxPathLenZero: false, BasicConstraintsValid: true}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b := BasicConstraints{ + IsCA: tt.fields.IsCA, + MaxPathLen: tt.fields.MaxPathLen, + } + b.Set(tt.args.c) + if !reflect.DeepEqual(tt.args.c, tt.want) { + t.Errorf("BasicConstraints.Set() = %v, want %v", tt.args.c, tt.want) + } + }) + } +} + +func TestNameConstraints_Set(t *testing.T) { + ipNet := func(s string) *net.IPNet { + _, ipNet, err := net.ParseCIDR(s) + if err != nil { + t.Fatal(err) + } + return ipNet + } + type fields struct { + Critical bool + PermittedDNSDomains MultiString + ExcludedDNSDomains MultiString + PermittedIPRanges MultiIPNet + ExcludedIPRanges MultiIPNet + PermittedEmailAddresses MultiString + ExcludedEmailAddresses MultiString + PermittedURIDomains MultiString + ExcludedURIDomains MultiString + } + type args struct { + c *x509.Certificate + } + tests := []struct { + name string + fields fields + args args + want *x509.Certificate + }{ + {"ok", fields{ + Critical: true, + PermittedDNSDomains: []string{"foo.com", "bar.com"}, + ExcludedDNSDomains: []string{"zar.com"}, + PermittedIPRanges: []*net.IPNet{ipNet("1.2.0.0/16"), ipNet("2.3.4.0/8")}, + ExcludedIPRanges: []*net.IPNet{ipNet("3.0.0.0/24")}, + PermittedEmailAddresses: []string{"root@foo.com"}, + ExcludedEmailAddresses: []string{"admin@foo.com", "root@bar.com", "admin@bar.com"}, + PermittedURIDomains: []string{".foo.com", ".bar.com"}, + ExcludedURIDomains: []string{".zar.com"}, + }, args{&x509.Certificate{}}, &x509.Certificate{ + PermittedDNSDomainsCritical: true, + PermittedDNSDomains: []string{"foo.com", "bar.com"}, + ExcludedDNSDomains: []string{"zar.com"}, + PermittedIPRanges: []*net.IPNet{ipNet("1.2.0.0/16"), ipNet("2.3.4.0/8")}, + ExcludedIPRanges: []*net.IPNet{ipNet("3.0.0.0/24")}, + PermittedEmailAddresses: []string{"root@foo.com"}, + ExcludedEmailAddresses: []string{"admin@foo.com", "root@bar.com", "admin@bar.com"}, + PermittedURIDomains: []string{".foo.com", ".bar.com"}, + ExcludedURIDomains: []string{".zar.com"}, + }}, + {"overwrite", fields{}, args{&x509.Certificate{ + PermittedDNSDomainsCritical: true, + PermittedDNSDomains: []string{"foo.com", "bar.com"}, + ExcludedDNSDomains: []string{"zar.com"}, + PermittedIPRanges: []*net.IPNet{ipNet("1.2.0.0/16"), ipNet("2.3.4.0/8")}, + ExcludedIPRanges: []*net.IPNet{ipNet("3.0.0.0/24")}, + PermittedEmailAddresses: []string{"root@foo.com"}, + ExcludedEmailAddresses: []string{"admin@foo.com", "root@bar.com", "admin@bar.com"}, + PermittedURIDomains: []string{".foo.com", ".bar.com"}, + ExcludedURIDomains: []string{".zar.com"}, + }}, &x509.Certificate{}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := NameConstraints{ + Critical: tt.fields.Critical, + PermittedDNSDomains: tt.fields.PermittedDNSDomains, + ExcludedDNSDomains: tt.fields.ExcludedDNSDomains, + PermittedIPRanges: tt.fields.PermittedIPRanges, + ExcludedIPRanges: tt.fields.ExcludedIPRanges, + PermittedEmailAddresses: tt.fields.PermittedEmailAddresses, + ExcludedEmailAddresses: tt.fields.ExcludedEmailAddresses, + PermittedURIDomains: tt.fields.PermittedURIDomains, + ExcludedURIDomains: tt.fields.ExcludedURIDomains, + } + n.Set(tt.args.c) + if !reflect.DeepEqual(tt.args.c, tt.want) { + t.Errorf("NameConstraints.Set() = %v, want %v", tt.args.c, tt.want) + } + }) + } +} diff --git a/x509util/marshal_utils.go b/x509util/marshal_utils.go index 2195900b..fedc0a1c 100644 --- a/x509util/marshal_utils.go +++ b/x509util/marshal_utils.go @@ -163,6 +163,10 @@ func unmarshalMultiString(data []byte) ([]string, error) { } func parseObjectIdentifier(oid string) (asn1.ObjectIdentifier, error) { + if oid == "" { + return asn1.ObjectIdentifier{}, nil + } + parts := strings.Split(oid, ".") oids := make([]int, len(parts))