Source file
src/crypto/x509/parser_test.go
1
2
3
4
5 package x509
6
7 import (
8 "bytes"
9 "crypto/ecdsa"
10 "crypto/elliptic"
11 "crypto/rand"
12 "crypto/x509/pkix"
13 "encoding/asn1"
14 "encoding/pem"
15 "os"
16 "reflect"
17 "strings"
18 "testing"
19 "time"
20
21 cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
22 )
23
24 func TestParseASN1String(t *testing.T) {
25 tests := []struct {
26 name string
27 tag cryptobyte_asn1.Tag
28 value []byte
29 expected string
30 expectedErr string
31 }{
32 {
33 name: "T61String",
34 tag: cryptobyte_asn1.T61String,
35 value: []byte{0xbf, 0x61, 0x3f},
36 expected: string("¿a?"),
37 },
38 {
39 name: "PrintableString",
40 tag: cryptobyte_asn1.PrintableString,
41 value: []byte{80, 81, 82},
42 expected: string("PQR"),
43 },
44 {
45 name: "PrintableString (invalid)",
46 tag: cryptobyte_asn1.PrintableString,
47 value: []byte{1, 2, 3},
48 expectedErr: "invalid PrintableString",
49 },
50 {
51 name: "UTF8String",
52 tag: cryptobyte_asn1.UTF8String,
53 value: []byte{80, 81, 82},
54 expected: string("PQR"),
55 },
56 {
57 name: "UTF8String (invalid)",
58 tag: cryptobyte_asn1.UTF8String,
59 value: []byte{255},
60 expectedErr: "invalid UTF-8 string",
61 },
62 {
63 name: "BMPString",
64 tag: cryptobyte_asn1.Tag(asn1.TagBMPString),
65 value: []byte{80, 81},
66 expected: string("偑"),
67 },
68 {
69 name: "BMPString (invalid length)",
70 tag: cryptobyte_asn1.Tag(asn1.TagBMPString),
71 value: []byte{255},
72 expectedErr: "invalid BMPString",
73 },
74 {
75 name: "BMPString (invalid surrogate)",
76 tag: cryptobyte_asn1.Tag(asn1.TagBMPString),
77 value: []byte{80, 81, 216, 1},
78 expectedErr: "invalid BMPString",
79 },
80 {
81 name: "BMPString (invalid noncharacter 0xfdd1)",
82 tag: cryptobyte_asn1.Tag(asn1.TagBMPString),
83 value: []byte{80, 81, 253, 209},
84 expectedErr: "invalid BMPString",
85 },
86 {
87 name: "BMPString (invalid noncharacter 0xffff)",
88 tag: cryptobyte_asn1.Tag(asn1.TagBMPString),
89 value: []byte{80, 81, 255, 255},
90 expectedErr: "invalid BMPString",
91 },
92 {
93 name: "BMPString (invalid noncharacter 0xfffe)",
94 tag: cryptobyte_asn1.Tag(asn1.TagBMPString),
95 value: []byte{80, 81, 255, 254},
96 expectedErr: "invalid BMPString",
97 },
98 {
99 name: "IA5String",
100 tag: cryptobyte_asn1.IA5String,
101 value: []byte{80, 81},
102 expected: string("PQ"),
103 },
104 {
105 name: "IA5String (invalid)",
106 tag: cryptobyte_asn1.IA5String,
107 value: []byte{255},
108 expectedErr: "invalid IA5String",
109 },
110 {
111 name: "NumericString",
112 tag: cryptobyte_asn1.Tag(asn1.TagNumericString),
113 value: []byte{49, 50},
114 expected: string("12"),
115 },
116 {
117 name: "NumericString (invalid)",
118 tag: cryptobyte_asn1.Tag(asn1.TagNumericString),
119 value: []byte{80},
120 expectedErr: "invalid NumericString",
121 },
122 }
123
124 for _, tc := range tests {
125 t.Run(tc.name, func(t *testing.T) {
126 out, err := parseASN1String(tc.tag, tc.value)
127 if err != nil && err.Error() != tc.expectedErr {
128 t.Fatalf("parseASN1String returned unexpected error: got %q, want %q", err, tc.expectedErr)
129 } else if err == nil && tc.expectedErr != "" {
130 t.Fatalf("parseASN1String didn't fail, expected: %s", tc.expectedErr)
131 }
132 if out != tc.expected {
133 t.Fatalf("parseASN1String returned unexpected value: got %q, want %q", out, tc.expected)
134 }
135 })
136 }
137 }
138
139 const policyPEM = `-----BEGIN CERTIFICATE-----
140 MIIGeDCCBWCgAwIBAgIUED9KQBi0ScBDoufB2mgAJ63G5uIwDQYJKoZIhvcNAQEL
141 BQAwVTELMAkGA1UEBhMCVVMxGDAWBgNVBAoTD1UuUy4gR292ZXJubWVudDENMAsG
142 A1UECxMERlBLSTEdMBsGA1UEAxMURmVkZXJhbCBCcmlkZ2UgQ0EgRzQwHhcNMjAx
143 MDIyMTcwNDE5WhcNMjMxMDIyMTcwNDE5WjCBgTELMAkGA1UEBhMCVVMxHTAbBgNV
144 BAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVz
145 dCBOZXR3b3JrMTIwMAYDVQQDEylTeW1hbnRlYyBDbGFzcyAzIFNTUCBJbnRlcm1l
146 ZGlhdGUgQ0EgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL2p
147 75cMpx86sS2aH4r+0o8r+m/KTrPrknWP0RA9Kp6sewAzkNa7BVwg0jOhyamiv1iP
148 Cns10usoH93nxYbXLWF54vOLRdYU/53KEPNmgkj2ipMaTLuaReBghNibikWSnAmy
149 S8RItaDMs8tdF2goKPI4xWiamNwqe92VC+pic2tq0Nva3Y4kvMDJjtyje3uduTtL
150 oyoaaHkrX7i7gE67psnMKj1THUtre1JV1ohl9+oOuyot4p3eSxVlrMWiiwb11bnk
151 CakecOz/mP2DHMGg6pZ/BeJ+ThaLUylAXECARIqHc9UwRPKC9BfLaCX4edIoeYiB
152 loRs4KdqLdg/I9eTwKkCAwEAAaOCAxEwggMNMB0GA1UdDgQWBBQ1Jn1QleGhwb0F
153 1cOdd0LHDBOWjDAfBgNVHSMEGDAWgBR58ABJ6393wl1BAmU0ipAjmx4HbzAOBgNV
154 HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zCBiAYDVR0gBIGAMH4wDAYKYIZI
155 AWUDAgEDAzAMBgpghkgBZQMCAQMMMAwGCmCGSAFlAwIBAw4wDAYKYIZIAWUDAgED
156 DzAMBgpghkgBZQMCAQMSMAwGCmCGSAFlAwIBAxMwDAYKYIZIAWUDAgEDFDAMBgpg
157 hkgBZQMCAQMlMAwGCmCGSAFlAwIBAyYwggESBgNVHSEEggEJMIIBBTAbBgpghkgB
158 ZQMCAQMDBg1ghkgBhvhFAQcXAwEGMBsGCmCGSAFlAwIBAwwGDWCGSAGG+EUBBxcD
159 AQcwGwYKYIZIAWUDAgEDDgYNYIZIAYb4RQEHFwMBDjAbBgpghkgBZQMCAQMPBg1g
160 hkgBhvhFAQcXAwEPMBsGCmCGSAFlAwIBAxIGDWCGSAGG+EUBBxcDARIwGwYKYIZI
161 AWUDAgEDEwYNYIZIAYb4RQEHFwMBETAbBgpghkgBZQMCAQMUBg1ghkgBhvhFAQcX
162 AwEUMBsGCmCGSAFlAwIBAyUGDWCGSAGG+EUBBxcDAQgwGwYKYIZIAWUDAgEDJgYN
163 YIZIAYb4RQEHFwMBJDBgBggrBgEFBQcBCwRUMFIwUAYIKwYBBQUHMAWGRGh0dHA6
164 Ly9zc3Atc2lhLnN5bWF1dGguY29tL1NUTlNTUC9DZXJ0c19Jc3N1ZWRfYnlfQ2xh
165 c3MzU1NQQ0EtRzMucDdjMA8GA1UdJAQIMAaAAQCBAQAwCgYDVR02BAMCAQAwUQYI
166 KwYBBQUHAQEERTBDMEEGCCsGAQUFBzAChjVodHRwOi8vcmVwby5mcGtpLmdvdi9i
167 cmlkZ2UvY2FDZXJ0c0lzc3VlZFRvZmJjYWc0LnA3YzA3BgNVHR8EMDAuMCygKqAo
168 hiZodHRwOi8vcmVwby5mcGtpLmdvdi9icmlkZ2UvZmJjYWc0LmNybDANBgkqhkiG
169 9w0BAQsFAAOCAQEAA751TycC1f/WTkHmedF9ZWxP58Jstmwvkyo8bKueJ0eF7LTG
170 BgQlzE2B9vke4sFhd4V+BdgOPGE1dsGzllYKCWg0BhkCBs5kIJ7F6Ay6G1TBuGU1
171 Ie8247GL+P9pcC5TVvXHC/62R2w3DuD/vAPLbYEbSQjobXlsqt8Kmtd6yK/jVuDV
172 BTZMdZmvoNtjemqmgcBXHsf0ctVm0m6tH5uYqyVxu8tfyUis6Cf303PHj+spWP1k
173 gc5PYnVF0ot7qAmNFENIpbKg3BdusBkF9rGxLaDSUBvSc7+s9iQz9d/iRuAebrYu
174 +eqUlJ2lsjS1U8qyPmlH+spfPNbAEQEsuP32Aw==
175 -----END CERTIFICATE-----
176 `
177
178 func TestPolicyParse(t *testing.T) {
179 b, _ := pem.Decode([]byte(policyPEM))
180 c, err := ParseCertificate(b.Bytes)
181 if err != nil {
182 t.Fatal(err)
183 }
184 if len(c.Policies) != 9 {
185 t.Errorf("unexpected number of policies: got %d, want %d", len(c.Policies), 9)
186 }
187 if len(c.PolicyMappings) != 9 {
188 t.Errorf("unexpected number of policy mappings: got %d, want %d", len(c.PolicyMappings), 9)
189 }
190 if !c.RequireExplicitPolicyZero {
191 t.Error("expected RequireExplicitPolicyZero to be set")
192 }
193 if !c.InhibitPolicyMappingZero {
194 t.Error("expected InhibitPolicyMappingZero to be set")
195 }
196 if !c.InhibitAnyPolicyZero {
197 t.Error("expected InhibitAnyPolicyZero to be set")
198 }
199 }
200
201 func TestParsePolicies(t *testing.T) {
202 for _, tc := range []string{
203 "testdata/policy_leaf_duplicate.pem",
204 "testdata/policy_leaf_invalid.pem",
205 } {
206 t.Run(tc, func(t *testing.T) {
207 b, err := os.ReadFile(tc)
208 if err != nil {
209 t.Fatal(err)
210 }
211 p, _ := pem.Decode(b)
212 _, err = ParseCertificate(p.Bytes)
213 if err == nil {
214 t.Error("parsing should've failed")
215 }
216 })
217 }
218 }
219
220 func TestParseCertificateNegativeMaxPathLength(t *testing.T) {
221 certs := []string{
222
223 `
224 -----BEGIN CERTIFICATE-----
225 MIIByTCCATKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDEwRURVNU
226 MB4XDTcwMDEwMTAwMTY0MFoXDTcwMDEwMjAzNDY0MFowDzENMAsGA1UEAxMEVEVT
227 VDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsaHglFuSicTT8TKfipgsSi3N
228 Wb/TcvuAhanFF1VGB+vS95kO7yFqyfRgX3GgOwT0KlJVsVjPjghEGR9RGTSLqkTD
229 UFbiBgm8+VEPMOrUtIHIHXhl+ye44AkOEStxfz7gjN/EAS2h8ffPKhvDTHOlShKw
230 Y3LQlxR0LdeJXq3eSqUCAwEAAaM1MDMwEgYDVR0TAQH/BAgwBgEB/wIB/zAdBgNV
231 HQ4EFgQUrbrk0tqQAEsce8uYifP0BIVhuFAwDQYJKoZIhvcNAQELBQADgYEAIkhV
232 ZBj1ThT+eyh50XsoU570NUysTg3Nj/3lbkEolzdcE+wu0CPXvgxLRM6Y62u1ey82
233 8d5VQHstzF4dXgc3W+O9UySa+CKdcHx/q7o7seOGXdysT0IJtAY3w66mFkuF7PIn
234 y9b7M5t6pmWjb7N0QqGuWeNqi4ZvS8gLKmVEgGY=
235 -----END CERTIFICATE-----
236 `,
237
238 `
239 -----BEGIN CERTIFICATE-----
240 MIIByTCCATKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDEwRURVNU
241 MB4XDTcwMDEwMTAwMTY0MFoXDTcwMDEwMjAzNDY0MFowDzENMAsGA1UEAxMEVEVT
242 VDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsaHglFuSicTT8TKfipgsSi3N
243 Wb/TcvuAhanFF1VGB+vS95kO7yFqyfRgX3GgOwT0KlJVsVjPjghEGR9RGTSLqkTD
244 UFbiBgm8+VEPMOrUtIHIHXhl+ye44AkOEStxfz7gjN/EAS2h8ffPKhvDTHOlShKw
245 Y3LQlxR0LdeJXq3eSqUCAwEAAaM1MDMwEgYDVR0TAQH/BAgwBgEB/wIB/jAdBgNV
246 HQ4EFgQUrbrk0tqQAEsce8uYifP0BIVhuFAwDQYJKoZIhvcNAQELBQADgYEAGjIr
247 YGQc7Ods+BuKck7p+vpAMONM8SLEuUtKorCP3ecsO51MoA4/niLbgMHaOGNHwzMp
248 ajg0zLbY0Dj6Ml0VZ+lS3rjgTEhYXc626eZkoQqgUzL1jhe3S0ZbSxxmHMBKjJFl
249 d5l1tRhScKu2NBgm74nYmJxJYgvuTA38wGhRrGU=
250 -----END CERTIFICATE-----
251 `,
252 }
253
254 for _, cert := range certs {
255 b, _ := pem.Decode([]byte(cert))
256 _, err := ParseCertificate(b.Bytes)
257 if err == nil || err.Error() != "x509: invalid basic constraints" {
258 t.Errorf(`ParseCertificate() = %v; want = "x509: invalid basic constraints"`, err)
259 }
260 }
261 }
262
263 func TestDomainNameValid(t *testing.T) {
264 for _, tc := range []struct {
265 name string
266 dnsName string
267 constraint bool
268 valid bool
269 }{
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285 {"254 char label, name", strings.Repeat("a.a", 84) + "aaa", false, true},
286 {"254 char label, constraint", strings.Repeat("a.a", 84) + "aaa", true, true},
287 {"253 char label, name", strings.Repeat("a.a", 84) + "aa", false, true},
288 {"253 char label, constraint", strings.Repeat("a.a", 84) + "aa", true, true},
289 {"64 char single label, name", strings.Repeat("a", 64), false, true},
290 {"64 char single label, constraint", strings.Repeat("a", 64), true, true},
291 {"64 char label, name", "a." + strings.Repeat("a", 64), false, true},
292 {"64 char label, constraint", "a." + strings.Repeat("a", 64), true, true},
293
294
295 {"empty name, constraint", "", true, true},
296 {"empty label, name", "a..a", false, false},
297 {"empty label, constraint", "a..a", true, false},
298 {"period, name", ".", false, false},
299 {"period, constraint", ".", true, false},
300 {"valid, name", "a.b.c", false, true},
301 {"valid, constraint", "a.b.c", true, true},
302 {"leading period, name", ".a.b.c", false, false},
303 {"leading period, constraint", ".a.b.c", true, true},
304 {"trailing period, name", "a.", false, false},
305 {"trailing period, constraint", "a.", true, false},
306 {"bare label, name", "a", false, true},
307 {"bare label, constraint", "a", true, true},
308 {"63 char single label, name", strings.Repeat("a", 63), false, true},
309 {"63 char single label, constraint", strings.Repeat("a", 63), true, true},
310 {"63 char label, name", "a." + strings.Repeat("a", 63), false, true},
311 {"63 char label, constraint", "a." + strings.Repeat("a", 63), true, true},
312 } {
313 t.Run(tc.name, func(t *testing.T) {
314 valid := domainNameValid(tc.dnsName, tc.constraint)
315 if tc.valid != valid {
316 t.Errorf("domainNameValid(%q, %t) = %v; want %v", tc.dnsName, tc.constraint, !tc.valid, tc.valid)
317 }
318
319 trimmedName := tc.dnsName
320 if tc.constraint && len(trimmedName) > 1 && trimmedName[0] == '.' {
321 trimmedName = trimmedName[1:]
322 }
323 _, revValid := domainToReverseLabels(trimmedName)
324 if valid != revValid {
325 t.Errorf("domainNameValid(%q, %t) = %t != domainToReverseLabels(%q) = %t", tc.dnsName, tc.constraint, valid, trimmedName, revValid)
326 }
327 })
328 }
329 }
330
331 func TestRoundtripWeirdSANs(t *testing.T) {
332
333
334
335 k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
336 if err != nil {
337 t.Fatal(err)
338 }
339 badNames := []string{
340 "baredomain",
341 "baredomain.",
342 strings.Repeat("a", 255),
343 strings.Repeat("a", 65) + ".com",
344 }
345 tmpl := &Certificate{
346 EmailAddresses: badNames,
347 DNSNames: badNames,
348 }
349 b, err := CreateCertificate(rand.Reader, tmpl, tmpl, &k.PublicKey, k)
350 if err != nil {
351 t.Fatal(err)
352 }
353 _, err = ParseCertificate(b)
354 if err != nil {
355 t.Fatalf("Couldn't roundtrip certificate: %v", err)
356 }
357 }
358
359 func FuzzDomainNameValid(f *testing.F) {
360 f.Fuzz(func(t *testing.T, data string) {
361 domainNameValid(data, false)
362 domainNameValid(data, true)
363 })
364 }
365
366 func TestParseNameTypes(t *testing.T) {
367 block, _ := pem.Decode([]byte(`
368 -----BEGIN CERTIFICATE-----
369 MIICGzCCAcGgAwIBAgIJAIZft7jy3RcpMAoGCCqGSM49BAMCMA8xDTALBgNVBAMM
370 BFRlc3QwHhcNMjUwOTAyMTg0MzE3WhcNMjUxMDAyMTg0MzE3WjCCARUxIjAgBg0q
371 hkiG9xIEAYS3CQIBDA91dGY4LXN0cmluZ/CfpooxHzAdBg0qhkiG9xIEAYS3CQIC
372 BAxvY3RldC1zdHJpbmcxGDAWBg0qhkiG9xIEAYS3CQIDDQUBAgMEBTETMBEGDSqG
373 SIb3EgQBhLcJAgQFADEVMBMGDSqGSIb3EgQBhLcJAgUwAgECMRQwEgYNKoZIhvcS
374 BAGEtwkCBgIBKjEgMB4GDSqGSIb3EgQBhLcJAgcGDSqGSIb3EgQBhLcJAgcxGDAW
375 Bg0qhkiG9xIEAYS3CQIIAwUAqrvM3TEgMB4GDSqGSIb3EgQBhLcJAgkXDTI1MDkw
376 MjE4NDMxN1oxFDASBg0qhkiG9xIEAYS3CQIKAQH/MFkwEwYHKoZIzj0CAQYIKoZI
377 zj0DAQcDQgAE7M4zoqQtXbvGsudKaM5gd8emxyk68AFTjIYU4PO1AtiYX3wyL89k
378 wbHjxgvmh/9aBg1LOj6kfsJIxULUmUpdzTAKBggqhkjOPQQDAgNIADBFAiBvKz3o
379 ALhCqKrRFLUbax6+tI1s1B14IPVk2ZHbBEou5gIhAOpvJRNj5qluPXKLXmZvIK8u
380 OjUhiZoowYvborSS1EBK
381 -----END CERTIFICATE-----
382 `))
383 expected := map[string]any{
384 "1.2.840.113554.4.1.72585.2.1": "utf8-string🦊",
385 "1.2.840.113554.4.1.72585.2.2": []byte("octet-string"),
386 "1.2.840.113554.4.1.72585.2.3": asn1.RawValue{Tag: 13,
387 Bytes: []byte{1, 2, 3, 4, 5}, FullBytes: []byte{13, 5, 1, 2, 3, 4, 5}},
388 "1.2.840.113554.4.1.72585.2.4": nil,
389 "1.2.840.113554.4.1.72585.2.5": asn1.RawValue{Tag: asn1.TagSequence, IsCompound: true,
390 Bytes: []byte{1, 2}, FullBytes: []byte{0x30, 2, 1, 2}},
391 "1.2.840.113554.4.1.72585.2.6": int64(42),
392 "1.2.840.113554.4.1.72585.2.7": asn1.ObjectIdentifier{1, 2, 840, 113554, 4, 1, 72585, 2, 7},
393 "1.2.840.113554.4.1.72585.2.8": asn1.BitString{BitLength: 32,
394 Bytes: []byte{0xaa, 0xbb, 0xcc, 0xdd}},
395 "1.2.840.113554.4.1.72585.2.9": time.Date(2025, 9, 2, 18, 43, 17, 0, time.UTC),
396 "1.2.840.113554.4.1.72585.2.10": true,
397 }
398 cert, err := ParseCertificate(block.Bytes)
399 if err != nil {
400 t.Fatalf("ParseCertificate failed: %v", err)
401 }
402 for _, attr := range cert.Subject.Names {
403 if val, ok := expected[attr.Type.String()]; ok {
404 if !reflect.DeepEqual(attr.Value, val) {
405 t.Errorf("unexpected value for %s: got %v (%T), want %v (%T)", attr.Type, attr.Value, attr.Value, val, val)
406 }
407 } else {
408 t.Errorf("unexpected attribute type: %s", attr.Type)
409 }
410 }
411 extra := pkix.Name{ExtraNames: cert.Subject.Names}
412
413 for i := range extra.ExtraNames {
414 if extra.ExtraNames[i].Value == nil {
415 extra.ExtraNames[i].Value = asn1.NullRawValue
416 }
417 }
418 got, err := asn1.Marshal(extra.ToRDNSequence())
419 if err != nil {
420 t.Fatalf("asn1.Marshal failed: %v", err)
421 }
422 if !bytes.Equal(got, cert.RawSubject) {
423 t.Errorf("unexpected marshaled RDNSequence: got %x, want %x", got, cert.RawSubject)
424 }
425 }
426
View as plain text