1// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package x509
6
7import (
8	"crypto"
9	"crypto/ecdsa"
10	"crypto/elliptic"
11	"crypto/rand"
12	"crypto/x509/pkix"
13	"encoding/asn1"
14	"encoding/pem"
15	"errors"
16	"fmt"
17	"internal/testenv"
18	"math/big"
19	"os/exec"
20	"reflect"
21	"runtime"
22	"slices"
23	"strconv"
24	"strings"
25	"testing"
26	"time"
27)
28
29type verifyTest struct {
30	name          string
31	leaf          string
32	intermediates []string
33	roots         []string
34	currentTime   int64
35	dnsName       string
36	systemSkip    bool
37	systemLax     bool
38	keyUsages     []ExtKeyUsage
39
40	errorCallback  func(*testing.T, error)
41	expectedChains [][]string
42}
43
44var verifyTests = []verifyTest{
45	{
46		name:          "Valid",
47		leaf:          googleLeaf,
48		intermediates: []string{gtsIntermediate},
49		roots:         []string{gtsRoot},
50		currentTime:   1677615892,
51		dnsName:       "www.google.com",
52
53		expectedChains: [][]string{
54			{"www.google.com", "GTS CA 1C3", "GTS Root R1"},
55		},
56	},
57	{
58		name:          "Valid (fqdn)",
59		leaf:          googleLeaf,
60		intermediates: []string{gtsIntermediate},
61		roots:         []string{gtsRoot},
62		currentTime:   1677615892,
63		dnsName:       "www.google.com.",
64
65		expectedChains: [][]string{
66			{"www.google.com", "GTS CA 1C3", "GTS Root R1"},
67		},
68	},
69	{
70		name:          "MixedCase",
71		leaf:          googleLeaf,
72		intermediates: []string{gtsIntermediate},
73		roots:         []string{gtsRoot},
74		currentTime:   1677615892,
75		dnsName:       "WwW.GooGLE.coM",
76
77		expectedChains: [][]string{
78			{"www.google.com", "GTS CA 1C3", "GTS Root R1"},
79		},
80	},
81	{
82		name:          "HostnameMismatch",
83		leaf:          googleLeaf,
84		intermediates: []string{gtsIntermediate},
85		roots:         []string{gtsRoot},
86		currentTime:   1677615892,
87		dnsName:       "www.example.com",
88
89		errorCallback: expectHostnameError("certificate is valid for"),
90	},
91	{
92		name:          "IPMissing",
93		leaf:          googleLeaf,
94		intermediates: []string{gtsIntermediate},
95		roots:         []string{gtsRoot},
96		currentTime:   1677615892,
97		dnsName:       "1.2.3.4",
98
99		errorCallback: expectHostnameError("doesn't contain any IP SANs"),
100	},
101	{
102		name:          "Expired",
103		leaf:          googleLeaf,
104		intermediates: []string{gtsIntermediate},
105		roots:         []string{gtsRoot},
106		currentTime:   1,
107		dnsName:       "www.example.com",
108
109		errorCallback: expectExpired,
110	},
111	{
112		name:        "MissingIntermediate",
113		leaf:        googleLeaf,
114		roots:       []string{gtsRoot},
115		currentTime: 1677615892,
116		dnsName:     "www.google.com",
117
118		// Skip when using systemVerify, since Windows
119		// *will* find the missing intermediate cert.
120		systemSkip:    true,
121		errorCallback: expectAuthorityUnknown,
122	},
123	{
124		name:          "RootInIntermediates",
125		leaf:          googleLeaf,
126		intermediates: []string{gtsRoot, gtsIntermediate},
127		roots:         []string{gtsRoot},
128		currentTime:   1677615892,
129		dnsName:       "www.google.com",
130
131		expectedChains: [][]string{
132			{"www.google.com", "GTS CA 1C3", "GTS Root R1"},
133		},
134		// CAPI doesn't build the chain with the duplicated GeoTrust
135		// entry so the results don't match.
136		systemLax: true,
137	},
138	{
139		name:          "dnssec-exp",
140		leaf:          dnssecExpLeaf,
141		intermediates: []string{startComIntermediate},
142		roots:         []string{startComRoot},
143		currentTime:   1302726541,
144
145		// The StartCom root is not trusted by Windows when the default
146		// ServerAuth EKU is requested.
147		systemSkip: true,
148
149		expectedChains: [][]string{
150			{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
151		},
152	},
153	{
154		name:          "dnssec-exp/AnyEKU",
155		leaf:          dnssecExpLeaf,
156		intermediates: []string{startComIntermediate},
157		roots:         []string{startComRoot},
158		currentTime:   1302726541,
159		keyUsages:     []ExtKeyUsage{ExtKeyUsageAny},
160
161		expectedChains: [][]string{
162			{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
163		},
164	},
165	{
166		name:          "dnssec-exp/RootInIntermediates",
167		leaf:          dnssecExpLeaf,
168		intermediates: []string{startComIntermediate, startComRoot},
169		roots:         []string{startComRoot},
170		currentTime:   1302726541,
171		systemSkip:    true, // see dnssec-exp test
172
173		expectedChains: [][]string{
174			{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
175		},
176	},
177	{
178		name:          "InvalidHash",
179		leaf:          googleLeafWithInvalidHash,
180		intermediates: []string{gtsIntermediate},
181		roots:         []string{gtsRoot},
182		currentTime:   1677615892,
183		dnsName:       "www.google.com",
184
185		// The specific error message may not occur when using system
186		// verification.
187		systemLax:     true,
188		errorCallback: expectHashError,
189	},
190	// EKULeaf tests use an unconstrained chain leading to a leaf certificate
191	// with an E-mail Protection EKU but not a Server Auth one, checking that
192	// the EKUs on the leaf are enforced.
193	{
194		name:          "EKULeaf",
195		leaf:          smimeLeaf,
196		intermediates: []string{smimeIntermediate},
197		roots:         []string{smimeRoot},
198		currentTime:   1594673418,
199
200		errorCallback: expectUsageError,
201	},
202	{
203		name:          "EKULeafExplicit",
204		leaf:          smimeLeaf,
205		intermediates: []string{smimeIntermediate},
206		roots:         []string{smimeRoot},
207		currentTime:   1594673418,
208		keyUsages:     []ExtKeyUsage{ExtKeyUsageServerAuth},
209
210		errorCallback: expectUsageError,
211	},
212	{
213		name:          "EKULeafValid",
214		leaf:          smimeLeaf,
215		intermediates: []string{smimeIntermediate},
216		roots:         []string{smimeRoot},
217		currentTime:   1594673418,
218		keyUsages:     []ExtKeyUsage{ExtKeyUsageEmailProtection},
219
220		expectedChains: [][]string{
221			{"CORPORATIVO FICTICIO ACTIVO", "EAEko Herri Administrazioen CA - CA AAPP Vascas (2)", "IZENPE S.A."},
222		},
223	},
224	{
225		// Check that a name constrained intermediate works even when
226		// it lists multiple constraints.
227		name:          "MultipleConstraints",
228		leaf:          nameConstraintsLeaf,
229		intermediates: []string{nameConstraintsIntermediate1, nameConstraintsIntermediate2},
230		roots:         []string{globalSignRoot},
231		currentTime:   1382387896,
232		dnsName:       "secure.iddl.vt.edu",
233
234		expectedChains: [][]string{
235			{
236				"Technology-enhanced Learning and Online Strategies",
237				"Virginia Tech Global Qualified Server CA",
238				"Trusted Root CA G2",
239				"GlobalSign Root CA",
240			},
241		},
242	},
243	{
244		// Check that SHA-384 intermediates (which are popping up)
245		// work.
246		name:          "SHA-384",
247		leaf:          trustAsiaLeaf,
248		intermediates: []string{trustAsiaSHA384Intermediate},
249		roots:         []string{digicertRoot},
250		currentTime:   1558051200,
251		dnsName:       "tm.cn",
252
253		// CryptoAPI can find alternative validation paths.
254		systemLax: true,
255
256		expectedChains: [][]string{
257			{
258				"tm.cn",
259				"TrustAsia ECC OV TLS Pro CA",
260				"DigiCert Global Root CA",
261			},
262		},
263	},
264	{
265		// Putting a certificate as a root directly should work as a
266		// way of saying “exactly this”.
267		name:        "LeafInRoots",
268		leaf:        selfSigned,
269		roots:       []string{selfSigned},
270		currentTime: 1471624472,
271		dnsName:     "foo.example",
272		systemSkip:  true, // does not chain to a system root
273
274		expectedChains: [][]string{
275			{"Acme Co"},
276		},
277	},
278	{
279		// Putting a certificate as a root directly should not skip
280		// other checks however.
281		name:        "LeafInRootsInvalid",
282		leaf:        selfSigned,
283		roots:       []string{selfSigned},
284		currentTime: 1471624472,
285		dnsName:     "notfoo.example",
286		systemSkip:  true, // does not chain to a system root
287
288		errorCallback: expectHostnameError("certificate is valid for"),
289	},
290	{
291		// An X.509 v1 certificate should not be accepted as an
292		// intermediate.
293		name:          "X509v1Intermediate",
294		leaf:          x509v1TestLeaf,
295		intermediates: []string{x509v1TestIntermediate},
296		roots:         []string{x509v1TestRoot},
297		currentTime:   1481753183,
298		systemSkip:    true, // does not chain to a system root
299
300		errorCallback: expectNotAuthorizedError,
301	},
302	{
303		name:        "IgnoreCNWithSANs",
304		leaf:        ignoreCNWithSANLeaf,
305		dnsName:     "foo.example.com",
306		roots:       []string{ignoreCNWithSANRoot},
307		currentTime: 1486684488,
308		systemSkip:  true, // does not chain to a system root
309
310		errorCallback: expectHostnameError("certificate is not valid for any names"),
311	},
312	{
313		// Test that excluded names are respected.
314		name:          "ExcludedNames",
315		leaf:          excludedNamesLeaf,
316		dnsName:       "bender.local",
317		intermediates: []string{excludedNamesIntermediate},
318		roots:         []string{excludedNamesRoot},
319		currentTime:   1486684488,
320		systemSkip:    true, // does not chain to a system root
321
322		errorCallback: expectNameConstraintsError,
323	},
324	{
325		// Test that unknown critical extensions in a leaf cause a
326		// verify error.
327		name:          "CriticalExtLeaf",
328		leaf:          criticalExtLeafWithExt,
329		intermediates: []string{criticalExtIntermediate},
330		roots:         []string{criticalExtRoot},
331		currentTime:   1486684488,
332		systemSkip:    true, // does not chain to a system root
333
334		errorCallback: expectUnhandledCriticalExtension,
335	},
336	{
337		// Test that unknown critical extensions in an intermediate
338		// cause a verify error.
339		name:          "CriticalExtIntermediate",
340		leaf:          criticalExtLeaf,
341		intermediates: []string{criticalExtIntermediateWithExt},
342		roots:         []string{criticalExtRoot},
343		currentTime:   1486684488,
344		systemSkip:    true, // does not chain to a system root
345
346		errorCallback: expectUnhandledCriticalExtension,
347	},
348	{
349		name:        "ValidCN",
350		leaf:        validCNWithoutSAN,
351		dnsName:     "foo.example.com",
352		roots:       []string{invalidCNRoot},
353		currentTime: 1540000000,
354		systemSkip:  true, // does not chain to a system root
355
356		errorCallback: expectHostnameError("certificate relies on legacy Common Name field"),
357	},
358	{
359		// A certificate with an AKID should still chain to a parent without SKID.
360		// See Issue 30079.
361		name:        "AKIDNoSKID",
362		leaf:        leafWithAKID,
363		roots:       []string{rootWithoutSKID},
364		currentTime: 1550000000,
365		dnsName:     "example",
366		systemSkip:  true, // does not chain to a system root
367
368		expectedChains: [][]string{
369			{"Acme LLC", "Acme Co"},
370		},
371	},
372	{
373		// When there are two parents, one with an incorrect subject but matching SKID
374		// and one with a correct subject but missing SKID, the latter should be
375		// considered as a possible parent.
376		leaf:        leafMatchingAKIDMatchingIssuer,
377		roots:       []string{rootMatchingSKIDMismatchingSubject, rootMismatchingSKIDMatchingSubject},
378		currentTime: 1550000000,
379		dnsName:     "example",
380		systemSkip:  true,
381
382		expectedChains: [][]string{
383			{"Leaf", "Root B"},
384		},
385	},
386}
387
388func expectHostnameError(msg string) func(*testing.T, error) {
389	return func(t *testing.T, err error) {
390		if _, ok := err.(HostnameError); !ok {
391			t.Fatalf("error was not a HostnameError: %v", err)
392		}
393		if !strings.Contains(err.Error(), msg) {
394			t.Fatalf("HostnameError did not contain %q: %v", msg, err)
395		}
396	}
397}
398
399func expectExpired(t *testing.T, err error) {
400	if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != Expired {
401		t.Fatalf("error was not Expired: %v", err)
402	}
403}
404
405func expectUsageError(t *testing.T, err error) {
406	if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != IncompatibleUsage {
407		t.Fatalf("error was not IncompatibleUsage: %v", err)
408	}
409}
410
411func expectAuthorityUnknown(t *testing.T, err error) {
412	e, ok := err.(UnknownAuthorityError)
413	if !ok {
414		t.Fatalf("error was not UnknownAuthorityError: %v", err)
415	}
416	if e.Cert == nil {
417		t.Fatalf("error was UnknownAuthorityError, but missing Cert: %v", err)
418	}
419}
420
421func expectHashError(t *testing.T, err error) {
422	if err == nil {
423		t.Fatalf("no error resulted from invalid hash")
424	}
425	if expected := "algorithm unimplemented"; !strings.Contains(err.Error(), expected) {
426		t.Fatalf("error resulting from invalid hash didn't contain '%s', rather it was: %v", expected, err)
427	}
428}
429
430func expectNameConstraintsError(t *testing.T, err error) {
431	if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != CANotAuthorizedForThisName {
432		t.Fatalf("error was not a CANotAuthorizedForThisName: %v", err)
433	}
434}
435
436func expectNotAuthorizedError(t *testing.T, err error) {
437	if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != NotAuthorizedToSign {
438		t.Fatalf("error was not a NotAuthorizedToSign: %v", err)
439	}
440}
441
442func expectUnhandledCriticalExtension(t *testing.T, err error) {
443	if _, ok := err.(UnhandledCriticalExtension); !ok {
444		t.Fatalf("error was not an UnhandledCriticalExtension: %v", err)
445	}
446}
447
448func certificateFromPEM(pemBytes string) (*Certificate, error) {
449	block, _ := pem.Decode([]byte(pemBytes))
450	if block == nil {
451		return nil, errors.New("failed to decode PEM")
452	}
453	return ParseCertificate(block.Bytes)
454}
455
456func testVerify(t *testing.T, test verifyTest, useSystemRoots bool) {
457	opts := VerifyOptions{
458		Intermediates: NewCertPool(),
459		DNSName:       test.dnsName,
460		CurrentTime:   time.Unix(test.currentTime, 0),
461		KeyUsages:     test.keyUsages,
462	}
463
464	if !useSystemRoots {
465		opts.Roots = NewCertPool()
466		for j, root := range test.roots {
467			ok := opts.Roots.AppendCertsFromPEM([]byte(root))
468			if !ok {
469				t.Fatalf("failed to parse root #%d", j)
470			}
471		}
472	}
473
474	for j, intermediate := range test.intermediates {
475		ok := opts.Intermediates.AppendCertsFromPEM([]byte(intermediate))
476		if !ok {
477			t.Fatalf("failed to parse intermediate #%d", j)
478		}
479	}
480
481	leaf, err := certificateFromPEM(test.leaf)
482	if err != nil {
483		t.Fatalf("failed to parse leaf: %v", err)
484	}
485
486	chains, err := leaf.Verify(opts)
487
488	if test.errorCallback == nil && err != nil {
489		if runtime.GOOS == "windows" && strings.HasSuffix(testenv.Builder(), "-2008") && err.Error() == "x509: certificate signed by unknown authority" {
490			testenv.SkipFlaky(t, 19564)
491		}
492		t.Fatalf("unexpected error: %v", err)
493	}
494	if test.errorCallback != nil {
495		if useSystemRoots && test.systemLax {
496			if err == nil {
497				t.Fatalf("expected error")
498			}
499		} else {
500			test.errorCallback(t, err)
501		}
502	}
503
504	doesMatch := func(expectedChain []string, chain []*Certificate) bool {
505		if len(chain) != len(expectedChain) {
506			return false
507		}
508
509		for k, cert := range chain {
510			if !strings.Contains(nameToKey(&cert.Subject), expectedChain[k]) {
511				return false
512			}
513		}
514		return true
515	}
516
517	// Every expected chain should match one (or more) returned chain. We tolerate multiple
518	// matches, as due to root store semantics it is plausible that (at least on the system
519	// verifiers) multiple identical (looking) chains may be returned when two roots with the
520	// same subject are present.
521	for _, expectedChain := range test.expectedChains {
522		var match bool
523		for _, chain := range chains {
524			if doesMatch(expectedChain, chain) {
525				match = true
526				break
527			}
528		}
529
530		if !match {
531			t.Errorf("No match found for %v", expectedChain)
532		}
533	}
534
535	// Every returned chain should match 1 expected chain (or <2 if testing against the system)
536	for _, chain := range chains {
537		nMatched := 0
538		for _, expectedChain := range test.expectedChains {
539			if doesMatch(expectedChain, chain) {
540				nMatched++
541			}
542		}
543		// Allow additional unknown chains if systemLax is set
544		if nMatched == 0 && test.systemLax == false || nMatched > 1 {
545			t.Errorf("Got %v matches for chain %v", nMatched, chainToDebugString(chain))
546			for _, expectedChain := range test.expectedChains {
547				if doesMatch(expectedChain, chain) {
548					t.Errorf("\t matched %v", expectedChain)
549				}
550			}
551		}
552	}
553}
554
555func TestGoVerify(t *testing.T) {
556	// Temporarily enable SHA-1 verification since a number of test chains
557	// require it. TODO(filippo): regenerate test chains.
558	t.Setenv("GODEBUG", "x509sha1=1")
559
560	for _, test := range verifyTests {
561		t.Run(test.name, func(t *testing.T) {
562			testVerify(t, test, false)
563		})
564	}
565}
566
567func TestSystemVerify(t *testing.T) {
568	if runtime.GOOS != "windows" {
569		t.Skipf("skipping verify test using system APIs on %q", runtime.GOOS)
570	}
571
572	for _, test := range verifyTests {
573		t.Run(test.name, func(t *testing.T) {
574			if test.systemSkip {
575				t.SkipNow()
576			}
577			testVerify(t, test, true)
578		})
579	}
580}
581
582func chainToDebugString(chain []*Certificate) string {
583	var chainStr string
584	for _, cert := range chain {
585		if len(chainStr) > 0 {
586			chainStr += " -> "
587		}
588		chainStr += nameToKey(&cert.Subject)
589	}
590	return chainStr
591}
592
593func nameToKey(name *pkix.Name) string {
594	return strings.Join(name.Country, ",") + "/" + strings.Join(name.Organization, ",") + "/" + strings.Join(name.OrganizationalUnit, ",") + "/" + name.CommonName
595}
596
597const gtsIntermediate = `-----BEGIN CERTIFICATE-----
598MIIFljCCA36gAwIBAgINAgO8U1lrNMcY9QFQZjANBgkqhkiG9w0BAQsFADBHMQsw
599CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
600MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMjAwODEzMDAwMDQyWhcNMjcwOTMwMDAw
601MDQyWjBGMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
602Y2VzIExMQzETMBEGA1UEAxMKR1RTIENBIDFDMzCCASIwDQYJKoZIhvcNAQEBBQAD
603ggEPADCCAQoCggEBAPWI3+dijB43+DdCkH9sh9D7ZYIl/ejLa6T/belaI+KZ9hzp
604kgOZE3wJCor6QtZeViSqejOEH9Hpabu5dOxXTGZok3c3VVP+ORBNtzS7XyV3NzsX
605lOo85Z3VvMO0Q+sup0fvsEQRY9i0QYXdQTBIkxu/t/bgRQIh4JZCF8/ZK2VWNAcm
606BA2o/X3KLu/qSHw3TT8An4Pf73WELnlXXPxXbhqW//yMmqaZviXZf5YsBvcRKgKA
607gOtjGDxQSYflispfGStZloEAoPtR28p3CwvJlk/vcEnHXG0g/Zm0tOLKLnf9LdwL
608tmsTDIwZKxeWmLnwi/agJ7u2441Rj72ux5uxiZ0CAwEAAaOCAYAwggF8MA4GA1Ud
609DwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0T
610AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUinR/r4XN7pXNPZzQ4kYU83E1HScwHwYD
611VR0jBBgwFoAU5K8rJnEaK0gnhS9SZizv8IkTcT4waAYIKwYBBQUHAQEEXDBaMCYG
612CCsGAQUFBzABhhpodHRwOi8vb2NzcC5wa2kuZ29vZy9ndHNyMTAwBggrBgEFBQcw
613AoYkaHR0cDovL3BraS5nb29nL3JlcG8vY2VydHMvZ3RzcjEuZGVyMDQGA1UdHwQt
614MCswKaAnoCWGI2h0dHA6Ly9jcmwucGtpLmdvb2cvZ3RzcjEvZ3RzcjEuY3JsMFcG
615A1UdIARQME4wOAYKKwYBBAHWeQIFAzAqMCgGCCsGAQUFBwIBFhxodHRwczovL3Br
616aS5nb29nL3JlcG9zaXRvcnkvMAgGBmeBDAECATAIBgZngQwBAgIwDQYJKoZIhvcN
617AQELBQADggIBAIl9rCBcDDy+mqhXlRu0rvqrpXJxtDaV/d9AEQNMwkYUuxQkq/BQ
618cSLbrcRuf8/xam/IgxvYzolfh2yHuKkMo5uhYpSTld9brmYZCwKWnvy15xBpPnrL
619RklfRuFBsdeYTWU0AIAaP0+fbH9JAIFTQaSSIYKCGvGjRFsqUBITTcFTNvNCCK9U
620+o53UxtkOCcXCb1YyRt8OS1b887U7ZfbFAO/CVMkH8IMBHmYJvJh8VNS/UKMG2Yr
621PxWhu//2m+OBmgEGcYk1KCTd4b3rGS3hSMs9WYNRtHTGnXzGsYZbr8w0xNPM1IER
622lQCh9BIiAfq0g3GvjLeMcySsN1PCAJA/Ef5c7TaUEDu9Ka7ixzpiO2xj2YC/WXGs
623Yye5TBeg2vZzFb8q3o/zpWwygTMD0IZRcZk0upONXbVRWPeyk+gB9lm+cZv9TSjO
624z23HFtz30dZGm6fKa+l3D/2gthsjgx0QGtkJAITgRNOidSOzNIb2ILCkXhAd4FJG
625AJ2xDx8hcFH1mt0G/FX0Kw4zd8NLQsLxdxP8c4CU6x+7Nz/OAipmsHMdMqUybDKw
626juDEI/9bfU1lcKwrmz3O2+BtjjKAvpafkmO8l7tdufThcV4q5O8DIrGKZTqPwJNl
6271IXNDw9bg1kWRxYtnCQ6yICmJhSFm/Y3m6xv+cXDBlHz4n/FsRC6UfTd
628-----END CERTIFICATE-----`
629
630const gtsRoot = `-----BEGIN CERTIFICATE-----
631MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw
632CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
633MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
634MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
635Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA
636A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo
63727xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w
638Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw
639TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl
640qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH
641szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8
642Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk
643MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92
644wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p
645aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN
646VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID
647AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
648FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb
649C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe
650QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy
651h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4
6527HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J
653ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef
654MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/
655Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT
6566u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ
6570E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm
6582tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb
659bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c
660-----END CERTIFICATE-----`
661
662const googleLeaf = `-----BEGIN CERTIFICATE-----
663MIIFUjCCBDqgAwIBAgIQERmRWTzVoz0SMeozw2RM3DANBgkqhkiG9w0BAQsFADBG
664MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM
665QzETMBEGA1UEAxMKR1RTIENBIDFDMzAeFw0yMzAxMDIwODE5MTlaFw0yMzAzMjcw
666ODE5MThaMBkxFzAVBgNVBAMTDnd3dy5nb29nbGUuY29tMIIBIjANBgkqhkiG9w0B
667AQEFAAOCAQ8AMIIBCgKCAQEAq30odrKMT54TJikMKL8S+lwoCMT5geP0u9pWjk6a
668wdB6i3kO+UE4ijCAmhbcZKeKaLnGJ38weZNwB1ayabCYyX7hDiC/nRcZU49LX5+o
66955kDVaNn14YKkg2kCeX25HDxSwaOsNAIXKPTqiQL5LPvc4Twhl8HY51hhNWQrTEr
670N775eYbixEULvyVLq5BLbCOpPo8n0/MTjQ32ku1jQq3GIYMJC/Rf2VW5doF6t9zs
671KleflAN8OdKp0ME9OHg0T1P3yyb67T7n0SpisHbeG06AmQcKJF9g/9VPJtRf4l1Q
672WRPDC+6JUqzXCxAGmIRGZ7TNMxPMBW/7DRX6w8oLKVNb0wIDAQABo4ICZzCCAmMw
673DgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQC
674MAAwHQYDVR0OBBYEFBnboj3lf9+Xat4oEgo6ZtIMr8ZuMB8GA1UdIwQYMBaAFIp0
675f6+Fze6VzT2c0OJGFPNxNR0nMGoGCCsGAQUFBwEBBF4wXDAnBggrBgEFBQcwAYYb
676aHR0cDovL29jc3AucGtpLmdvb2cvZ3RzMWMzMDEGCCsGAQUFBzAChiVodHRwOi8v
677cGtpLmdvb2cvcmVwby9jZXJ0cy9ndHMxYzMuZGVyMBkGA1UdEQQSMBCCDnd3dy5n
678b29nbGUuY29tMCEGA1UdIAQaMBgwCAYGZ4EMAQIBMAwGCisGAQQB1nkCBQMwPAYD
679VR0fBDUwMzAxoC+gLYYraHR0cDovL2NybHMucGtpLmdvb2cvZ3RzMWMzL1FPdkow
680TjFzVDJBLmNybDCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2AHoyjFTYty22IOo4
6814FIe6YQWcDIThU070ivBOlejUutSAAABhXHHOiUAAAQDAEcwRQIgBUkikUIXdo+S
6823T8PP0/cvokhUlumRE3GRWGL4WRMLpcCIQDY+bwK384mZxyXGZ5lwNRTAPNzT8Fx
6831+//nbaGK3BQMAB2AOg+0No+9QY1MudXKLyJa8kD08vREWvs62nhd31tBr1uAAAB
684hXHHOfQAAAQDAEcwRQIgLoVydNfMFKV9IoZR+M0UuJ2zOqbxIRum7Sn9RMPOBGMC
685IQD1/BgzCSDTvYvco6kpB6ifKSbg5gcb5KTnYxQYwRW14TANBgkqhkiG9w0BAQsF
686AAOCAQEA2bQQu30e3OFu0bmvQHmcqYvXBu6tF6e5b5b+hj4O+Rn7BXTTmaYX3M6p
687MsfRH4YVJJMB/dc3PROR2VtnKFC6gAZX+RKM6nXnZhIlOdmQnonS1ecOL19PliUd
688VXbwKjXqAO0Ljd9y9oXaXnyPyHmUJNI5YXAcxE+XXiOZhcZuMYyWmoEKJQ/XlSga
689zWfTn1IcKhA3IC7A1n/5bkkWD1Xi1mdWFQ6DQDMp//667zz7pKOgFMlB93aPDjvI
690c78zEqNswn6xGKXpWF5xVwdFcsx9HKhJ6UAi2bQ/KQ1yb7LPUOR6wXXWrG1cLnNP
691i8eNLnKL9PXQ+5SwJFCzfEhcIZuhzg==
692-----END CERTIFICATE-----`
693
694// googleLeafWithInvalidHash is the same as googleLeaf, but the signature
695// algorithm in the certificate contains a nonsense OID.
696const googleLeafWithInvalidHash = `-----BEGIN CERTIFICATE-----
697MIIFUjCCBDqgAwIBAgIQERmRWTzVoz0SMeozw2RM3DANBgkqhkiG9w0BAQ4FADBG
698MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM
699QzETMBEGA1UEAxMKR1RTIENBIDFDMzAeFw0yMzAxMDIwODE5MTlaFw0yMzAzMjcw
700ODE5MThaMBkxFzAVBgNVBAMTDnd3dy5nb29nbGUuY29tMIIBIjANBgkqhkiG9w0B
701AQEFAAOCAQ8AMIIBCgKCAQEAq30odrKMT54TJikMKL8S+lwoCMT5geP0u9pWjk6a
702wdB6i3kO+UE4ijCAmhbcZKeKaLnGJ38weZNwB1ayabCYyX7hDiC/nRcZU49LX5+o
70355kDVaNn14YKkg2kCeX25HDxSwaOsNAIXKPTqiQL5LPvc4Twhl8HY51hhNWQrTEr
704N775eYbixEULvyVLq5BLbCOpPo8n0/MTjQ32ku1jQq3GIYMJC/Rf2VW5doF6t9zs
705KleflAN8OdKp0ME9OHg0T1P3yyb67T7n0SpisHbeG06AmQcKJF9g/9VPJtRf4l1Q
706WRPDC+6JUqzXCxAGmIRGZ7TNMxPMBW/7DRX6w8oLKVNb0wIDAQABo4ICZzCCAmMw
707DgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQC
708MAAwHQYDVR0OBBYEFBnboj3lf9+Xat4oEgo6ZtIMr8ZuMB8GA1UdIwQYMBaAFIp0
709f6+Fze6VzT2c0OJGFPNxNR0nMGoGCCsGAQUFBwEBBF4wXDAnBggrBgEFBQcwAYYb
710aHR0cDovL29jc3AucGtpLmdvb2cvZ3RzMWMzMDEGCCsGAQUFBzAChiVodHRwOi8v
711cGtpLmdvb2cvcmVwby9jZXJ0cy9ndHMxYzMuZGVyMBkGA1UdEQQSMBCCDnd3dy5n
712b29nbGUuY29tMCEGA1UdIAQaMBgwCAYGZ4EMAQIBMAwGCisGAQQB1nkCBQMwPAYD
713VR0fBDUwMzAxoC+gLYYraHR0cDovL2NybHMucGtpLmdvb2cvZ3RzMWMzL1FPdkow
714TjFzVDJBLmNybDCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2AHoyjFTYty22IOo4
7154FIe6YQWcDIThU070ivBOlejUutSAAABhXHHOiUAAAQDAEcwRQIgBUkikUIXdo+S
7163T8PP0/cvokhUlumRE3GRWGL4WRMLpcCIQDY+bwK384mZxyXGZ5lwNRTAPNzT8Fx
7171+//nbaGK3BQMAB2AOg+0No+9QY1MudXKLyJa8kD08vREWvs62nhd31tBr1uAAAB
718hXHHOfQAAAQDAEcwRQIgLoVydNfMFKV9IoZR+M0UuJ2zOqbxIRum7Sn9RMPOBGMC
719IQD1/BgzCSDTvYvco6kpB6ifKSbg5gcb5KTnYxQYwRW14TANBgkqhkiG9w0BAQ4F
720AAOCAQEA2bQQu30e3OFu0bmvQHmcqYvXBu6tF6e5b5b+hj4O+Rn7BXTTmaYX3M6p
721MsfRH4YVJJMB/dc3PROR2VtnKFC6gAZX+RKM6nXnZhIlOdmQnonS1ecOL19PliUd
722VXbwKjXqAO0Ljd9y9oXaXnyPyHmUJNI5YXAcxE+XXiOZhcZuMYyWmoEKJQ/XlSga
723zWfTn1IcKhA3IC7A1n/5bkkWD1Xi1mdWFQ6DQDMp//667zz7pKOgFMlB93aPDjvI
724c78zEqNswn6xGKXpWF5xVwdFcsx9HKhJ6UAi2bQ/KQ1yb7LPUOR6wXXWrG1cLnNP
725i8eNLnKL9PXQ+5SwJFCzfEhcIZuhzg==
726-----END CERTIFICATE-----`
727
728const dnssecExpLeaf = `-----BEGIN CERTIFICATE-----
729MIIGzTCCBbWgAwIBAgIDAdD6MA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ
730TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0
731YWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3Mg
732MSBQcmltYXJ5IEludGVybWVkaWF0ZSBTZXJ2ZXIgQ0EwHhcNMTAwNzA0MTQ1MjQ1
733WhcNMTEwNzA1MTA1NzA0WjCBwTEgMB4GA1UEDRMXMjIxMTM3LWxpOWE5dHhJRzZM
734NnNyVFMxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVQZXJzb25hIE5vdCBWYWxpZGF0
735ZWQxKTAnBgNVBAsTIFN0YXJ0Q29tIEZyZWUgQ2VydGlmaWNhdGUgTWVtYmVyMRsw
736GQYDVQQDExJ3d3cuZG5zc2VjLWV4cC5vcmcxKDAmBgkqhkiG9w0BCQEWGWhvc3Rt
737YXN0ZXJAZG5zc2VjLWV4cC5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
738AoIBAQDEdF/22vaxrPbqpgVYMWi+alfpzBctpbfLBdPGuqOazJdCT0NbWcK8/+B4
739X6OlSOURNIlwLzhkmwVsWdVv6dVSaN7d4yI/fJkvgfDB9+au+iBJb6Pcz8ULBfe6
740D8HVvqKdORp6INzHz71z0sghxrQ0EAEkoWAZLh+kcn2ZHdcmZaBNUfjmGbyU6PRt
741RjdqoP+owIaC1aktBN7zl4uO7cRjlYFdusINrh2kPP02KAx2W84xjxX1uyj6oS6e
7427eBfvcwe8czW/N1rbE0CoR7h9+HnIrjnVG9RhBiZEiw3mUmF++Up26+4KTdRKbu3
743+BL4yMpfd66z0+zzqu+HkvyLpFn5AgMBAAGjggL/MIIC+zAJBgNVHRMEAjAAMAsG
744A1UdDwQEAwIDqDATBgNVHSUEDDAKBggrBgEFBQcDATAdBgNVHQ4EFgQUy04I5guM
745drzfh2JQaXhgV86+4jUwHwYDVR0jBBgwFoAU60I00Jiwq5/0G2sI98xkLu8OLEUw
746LQYDVR0RBCYwJIISd3d3LmRuc3NlYy1leHAub3Jngg5kbnNzZWMtZXhwLm9yZzCC
747AUIGA1UdIASCATkwggE1MIIBMQYLKwYBBAGBtTcBAgIwggEgMC4GCCsGAQUFBwIB
748FiJodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS9wb2xpY3kucGRmMDQGCCsGAQUFBwIB
749FihodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS9pbnRlcm1lZGlhdGUucGRmMIG3Bggr
750BgEFBQcCAjCBqjAUFg1TdGFydENvbSBMdGQuMAMCAQEagZFMaW1pdGVkIExpYWJp
751bGl0eSwgc2VlIHNlY3Rpb24gKkxlZ2FsIExpbWl0YXRpb25zKiBvZiB0aGUgU3Rh
752cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUG9saWN5IGF2YWlsYWJsZSBh
753dCBodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS9wb2xpY3kucGRmMGEGA1UdHwRaMFgw
754KqAooCaGJGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2NydDEtY3JsLmNybDAqoCig
755JoYkaHR0cDovL2NybC5zdGFydHNzbC5jb20vY3J0MS1jcmwuY3JsMIGOBggrBgEF
756BQcBAQSBgTB/MDkGCCsGAQUFBzABhi1odHRwOi8vb2NzcC5zdGFydHNzbC5jb20v
757c3ViL2NsYXNzMS9zZXJ2ZXIvY2EwQgYIKwYBBQUHMAKGNmh0dHA6Ly93d3cuc3Rh
758cnRzc2wuY29tL2NlcnRzL3N1Yi5jbGFzczEuc2VydmVyLmNhLmNydDAjBgNVHRIE
759HDAahhhodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS8wDQYJKoZIhvcNAQEFBQADggEB
760ACXj6SB59KRJPenn6gUdGEqcta97U769SATyiQ87i9er64qLwvIGLMa3o2Rcgl2Y
761kghUeyLdN/EXyFBYA8L8uvZREPoc7EZukpT/ZDLXy9i2S0jkOxvF2fD/XLbcjGjM
762iEYG1/6ASw0ri9C0k4oDDoJLCoeH9++yqF7SFCCMcDkJqiAGXNb4euDpa8vCCtEQ
763CSS+ObZbfkreRt3cNCf5LfCXe9OsTnCfc8Cuq81c0oLaG+SmaLUQNBuToq8e9/Zm
764+b+/a3RVjxmkV5OCcGVBxsXNDn54Q6wsdw0TBMcjwoEndzpLS7yWgFbbkq5ZiGpw
765Qibb2+CfKuQ+WFV1GkVQmVA=
766-----END CERTIFICATE-----`
767
768const startComIntermediate = `-----BEGIN CERTIFICATE-----
769MIIGNDCCBBygAwIBAgIBGDANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
770MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
771Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
772dGlvbiBBdXRob3JpdHkwHhcNMDcxMDI0MjA1NDE3WhcNMTcxMDI0MjA1NDE3WjCB
773jDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsT
774IlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNVBAMTL1N0
775YXJ0Q29tIENsYXNzIDEgUHJpbWFyeSBJbnRlcm1lZGlhdGUgU2VydmVyIENBMIIB
776IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtonGrO8JUngHrJJj0PREGBiE
777gFYfka7hh/oyULTTRwbw5gdfcA4Q9x3AzhA2NIVaD5Ksg8asWFI/ujjo/OenJOJA
778pgh2wJJuniptTT9uYSAK21ne0n1jsz5G/vohURjXzTCm7QduO3CHtPn66+6CPAVv
779kvek3AowHpNz/gfK11+AnSJYUq4G2ouHI2mw5CrY6oPSvfNx23BaKA+vWjhwRRI/
780ME3NO68X5Q/LoKldSKqxYVDLNM08XMML6BDAjJvwAwNi/rJsPnIO7hxDKslIDlc5
781xDEhyBDBLIf+VJVSH1I8MRKbf+fAoKVZ1eKPPvDVqOHXcDGpxLPPr21TLwb0pwID
782AQABo4IBrTCCAakwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
783VR0OBBYEFOtCNNCYsKuf9BtrCPfMZC7vDixFMB8GA1UdIwQYMBaAFE4L7xqkQFul
784F2mHMMo0aEPQQa7yMGYGCCsGAQUFBwEBBFowWDAnBggrBgEFBQcwAYYbaHR0cDov
785L29jc3Auc3RhcnRzc2wuY29tL2NhMC0GCCsGAQUFBzAChiFodHRwOi8vd3d3LnN0
786YXJ0c3NsLmNvbS9zZnNjYS5jcnQwWwYDVR0fBFQwUjAnoCWgI4YhaHR0cDovL3d3
787dy5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0
788c3NsLmNvbS9zZnNjYS5jcmwwgYAGA1UdIAR5MHcwdQYLKwYBBAGBtTcBAgEwZjAu
789BggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjA0
790BggrBgEFBQcCARYoaHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJtZWRpYXRl
791LnBkZjANBgkqhkiG9w0BAQUFAAOCAgEAIQlJPqWIbuALi0jaMU2P91ZXouHTYlfp
792tVbzhUV1O+VQHwSL5qBaPucAroXQ+/8gA2TLrQLhxpFy+KNN1t7ozD+hiqLjfDen
793xk+PNdb01m4Ge90h2c9W/8swIkn+iQTzheWq8ecf6HWQTd35RvdCNPdFWAwRDYSw
794xtpdPvkBnufh2lWVvnQce/xNFE+sflVHfXv0pQ1JHpXo9xLBzP92piVH0PN1Nb6X
795t1gW66pceG/sUzCv6gRNzKkC4/C2BBL2MLERPZBOVmTX3DxDX3M570uvh+v2/miI
796RHLq0gfGabDBoYvvF0nXYbFFSF87ICHpW7LM9NfpMfULFWE7epTj69m8f5SuauNi
797YpaoZHy4h/OZMn6SolK+u/hlz8nyMPyLwcKmltdfieFcNID1j0cHL7SRv7Gifl9L
798WtBbnySGBVFaaQNlQ0lxxeBvlDRr9hvYqbBMflPrj0jfyjO1SPo2ShpTpjMM0InN
799SRXNiTE8kMBy12VLUjWKRhFEuT2OKGWmPnmeXAhEKa2wNREuIU640ucQPl2Eg7PD
800wuTSxv0JS3QJ3fGz0xk+gA2iCxnwOOfFwq/iI9th4p1cbiCJSS4jarJiwUW0n6+L
801p/EiO/h94pDQehn7Skzj0n1fSoMD7SfWI55rjbRZotnvbIIp3XUZPD9MEI3vu3Un
8020q6Dp6jOW6c=
803-----END CERTIFICATE-----`
804
805const startComRoot = `-----BEGIN CERTIFICATE-----
806MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
807MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
808Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
809dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9
810MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
811U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
812cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
813A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
814pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
815OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
816Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
817Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
818HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
819Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
820+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
821Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
822Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
82326Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
824AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
825FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j
826ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js
827LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM
828BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0
829Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy
830dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh
831cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh
832YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg
833dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp
834bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ
835YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT
836TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ
8379GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8
838jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW
839FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz
840ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1
841ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L
842EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu
843L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
844yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC
845O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
846um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
847NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
848-----END CERTIFICATE-----`
849
850const smimeLeaf = `-----BEGIN CERTIFICATE-----
851MIIIPDCCBiSgAwIBAgIQaMDxFS0pOMxZZeOBxoTJtjANBgkqhkiG9w0BAQsFADCB
852nTELMAkGA1UEBhMCRVMxFDASBgNVBAoMC0laRU5QRSBTLkEuMTowOAYDVQQLDDFB
853WlogWml1cnRhZ2lyaSBwdWJsaWtvYSAtIENlcnRpZmljYWRvIHB1YmxpY28gU0NB
854MTwwOgYDVQQDDDNFQUVrbyBIZXJyaSBBZG1pbmlzdHJhemlvZW4gQ0EgLSBDQSBB
855QVBQIFZhc2NhcyAoMikwHhcNMTcwNzEyMDg1MzIxWhcNMjEwNzEyMDg1MzIxWjCC
856AQwxDzANBgNVBAoMBklaRU5QRTE4MDYGA1UECwwvWml1cnRhZ2lyaSBrb3Jwb3Jh
857dGlib2EtQ2VydGlmaWNhZG8gY29ycG9yYXRpdm8xQzBBBgNVBAsMOkNvbmRpY2lv
858bmVzIGRlIHVzbyBlbiB3d3cuaXplbnBlLmNvbSBub2xhIGVyYWJpbGkgamFraXRl
859a28xFzAVBgNVBC4TDi1kbmkgOTk5OTk5ODlaMSQwIgYDVQQDDBtDT1JQT1JBVElW
860TyBGSUNUSUNJTyBBQ1RJVk8xFDASBgNVBCoMC0NPUlBPUkFUSVZPMREwDwYDVQQE
861DAhGSUNUSUNJTzESMBAGA1UEBRMJOTk5OTk5ODlaMIIBIjANBgkqhkiG9w0BAQEF
862AAOCAQ8AMIIBCgKCAQEAwVOMwUDfBtsH0XuxYnb+v/L774jMH8valX7RPH8cl2Lb
863SiqSo0RchW2RGA2d1yuYHlpChC9jGmt0X/g66/E/+q2hUJlfJtqVDJFwtFYV4u2S
864yzA3J36V4PRkPQrKxAsbzZriFXAF10XgiHQz9aVeMMJ9GBhmh9+DK8Tm4cMF6i8l
865+AuC35KdngPF1x0ealTYrYZplpEJFO7CiW42aLi6vQkDR2R7nmZA4AT69teqBWsK
8660DZ93/f0G/3+vnWwNTBF0lB6dIXoaz8OMSyHLqGnmmAtMrzbjAr/O/WWgbB/BqhR
867qjJQ7Ui16cuDldXaWQ/rkMzsxmsAox0UF+zdQNvXUQIDAQABo4IDBDCCAwAwgccG
868A1UdEgSBvzCBvIYVaHR0cDovL3d3dy5pemVucGUuY29tgQ9pbmZvQGl6ZW5wZS5j
869b22kgZEwgY4xRzBFBgNVBAoMPklaRU5QRSBTLkEuIC0gQ0lGIEEwMTMzNzI2MC1S
870TWVyYy5WaXRvcmlhLUdhc3RlaXogVDEwNTUgRjYyIFM4MUMwQQYDVQQJDDpBdmRh
871IGRlbCBNZWRpdGVycmFuZW8gRXRvcmJpZGVhIDE0IC0gMDEwMTAgVml0b3JpYS1H
872YXN0ZWl6MB4GA1UdEQQXMBWBE2ZpY3RpY2lvQGl6ZW5wZS5ldXMwDgYDVR0PAQH/
873BAQDAgXgMCkGA1UdJQQiMCAGCCsGAQUFBwMCBggrBgEFBQcDBAYKKwYBBAGCNxQC
874AjAdBgNVHQ4EFgQUyeoOD4cgcljKY0JvrNuX2waFQLAwHwYDVR0jBBgwFoAUwKlK
87590clh/+8taaJzoLSRqiJ66MwggEnBgNVHSAEggEeMIIBGjCCARYGCisGAQQB8zkB
876AQEwggEGMDMGCCsGAQUFBwIBFidodHRwOi8vd3d3Lml6ZW5wZS5jb20vcnBhc2Nh
877Y29ycG9yYXRpdm8wgc4GCCsGAQUFBwICMIHBGoG+Wml1cnRhZ2lyaWEgRXVza2Fs
878IEF1dG9ub21pYSBFcmtpZGVnb2tvIHNla3RvcmUgcHVibGlrb2tvIGVyYWt1bmRl
879ZW4gYmFybmUtc2FyZWV0YW4gYmFrYXJyaWsgZXJhYmlsIGRhaXRla2UuIFVzbyBy
880ZXN0cmluZ2lkbyBhbCBhbWJpdG8gZGUgcmVkZXMgaW50ZXJuYXMgZGUgRW50aWRh
881ZGVzIGRlbCBTZWN0b3IgUHVibGljbyBWYXNjbzAyBggrBgEFBQcBAQQmMCQwIgYI
882KwYBBQUHMAGGFmh0dHA6Ly9vY3NwLml6ZW5wZS5jb20wOgYDVR0fBDMwMTAvoC2g
883K4YpaHR0cDovL2NybC5pemVucGUuY29tL2NnaS1iaW4vY3JsaW50ZXJuYTIwDQYJ
884KoZIhvcNAQELBQADggIBAIy5PQ+UZlCRq6ig43vpHwlwuD9daAYeejV0Q+ZbgWAE
885GtO0kT/ytw95ZEJMNiMw3fYfPRlh27ThqiT0VDXZJDlzmn7JZd6QFcdXkCsiuv4+
886ZoXAg/QwnA3SGUUO9aVaXyuOIIuvOfb9MzoGp9xk23SMV3eiLAaLMLqwB5DTfBdt
887BGI7L1MnGJBv8RfP/TL67aJ5bgq2ri4S8vGHtXSjcZ0+rCEOLJtmDNMnTZxancg3
888/H5edeNd+n6Z48LO+JHRxQufbC4mVNxVLMIP9EkGUejlq4E4w6zb5NwCQczJbSWL
889i31rk2orsNsDlyaLGsWZp3JSNX6RmodU4KAUPor4jUJuUhrrm3Spb73gKlV/gcIw
890bCE7mML1Kss3x1ySaXsis6SZtLpGWKkW2iguPWPs0ydV6RPhmsCxieMwPPIJ87vS
8915IejfgyBae7RSuAIHyNFy4uI5xwvwUFf6OZ7az8qtW7ImFOgng3Ds+W9k1S2CNTx
892d0cnKTfA6IpjGo8EeHcxnIXT8NPImWaRj0qqonvYady7ci6U4m3lkNSdXNn1afgw
893mYust+gxVtOZs1gk2MUCgJ1V1X+g7r/Cg7viIn6TLkLrpS1kS1hvMqkl9M+7XqPo
894Qd95nJKOkusQpy99X4dF/lfbYAQnnjnqh3DLD2gvYObXFaAYFaiBKTiMTV2X72F+
895-----END CERTIFICATE-----`
896
897const smimeIntermediate = `-----BEGIN CERTIFICATE-----
898MIIHNzCCBSGgAwIBAgIQJMXIqlZvjuhMvqcFXOFkpDALBgkqhkiG9w0BAQswODEL
899MAkGA1UEBhMCRVMxFDASBgNVBAoMC0laRU5QRSBTLkEuMRMwEQYDVQQDDApJemVu
900cGUuY29tMB4XDTEwMTAyMDA4MjMzM1oXDTM3MTIxMjIzMDAwMFowgZ0xCzAJBgNV
901BAYTAkVTMRQwEgYDVQQKDAtJWkVOUEUgUy5BLjE6MDgGA1UECwwxQVpaIFppdXJ0
902YWdpcmkgcHVibGlrb2EgLSBDZXJ0aWZpY2FkbyBwdWJsaWNvIFNDQTE8MDoGA1UE
903AwwzRUFFa28gSGVycmkgQWRtaW5pc3RyYXppb2VuIENBIC0gQ0EgQUFQUCBWYXNj
904YXMgKDIpMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoIM7nEdI0N1h
905rR5T4xuV/usKDoMIasaiKvfLhbwxaNtTt+a7W/6wV5bv3svQFIy3sUXjjdzV1nG2
906To2wo/YSPQiOt8exWvOapvL21ogiof+kelWnXFjWaKJI/vThHYLgIYEMj/y4HdtU
907ojI646rZwqsb4YGAopwgmkDfUh5jOhV2IcYE3TgJAYWVkj6jku9PLaIsHiarAHjD
908PY8dig8a4SRv0gm5Yk7FXLmW1d14oxQBDeHZ7zOEXfpafxdEDO2SNaRJjpkh8XRr
909PGqkg2y1Q3gT6b4537jz+StyDIJ3omylmlJsGCwqT7p8mEqjGJ5kC5I2VnjXKuNn
910soShc72khWZVUJiJo5SGuAkNE2ZXqltBVm5Jv6QweQKsX6bkcMc4IZok4a+hx8FM
9118IBpGf/I94pU6HzGXqCyc1d46drJgDY9mXa+6YDAJFl3xeXOOW2iGCfwXqhiCrKL
912MYvyMZzqF3QH5q4nb3ZnehYvraeMFXJXDn+Utqp8vd2r7ShfQJz01KtM4hgKdgSg
913jtW+shkVVN5ng/fPN85ovfAH2BHXFfHmQn4zKsYnLitpwYM/7S1HxlT61cdQ7Nnk
9143LZTYEgAoOmEmdheklT40WAYakksXGM5VrzG7x9S7s1Tm+Vb5LSThdHC8bxxwyTb
915KsDRDNJ84N9fPDO6qHnzaL2upQ43PycCAwEAAaOCAdkwggHVMIHHBgNVHREEgb8w
916gbyGFWh0dHA6Ly93d3cuaXplbnBlLmNvbYEPaW5mb0BpemVucGUuY29tpIGRMIGO
917MUcwRQYDVQQKDD5JWkVOUEUgUy5BLiAtIENJRiBBMDEzMzcyNjAtUk1lcmMuVml0
918b3JpYS1HYXN0ZWl6IFQxMDU1IEY2MiBTODFDMEEGA1UECQw6QXZkYSBkZWwgTWVk
919aXRlcnJhbmVvIEV0b3JiaWRlYSAxNCAtIDAxMDEwIFZpdG9yaWEtR2FzdGVpejAP
920BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUwKlK90cl
921h/+8taaJzoLSRqiJ66MwHwYDVR0jBBgwFoAUHRxlDqjyJXu0kc/ksbHmvVV0bAUw
922OgYDVR0gBDMwMTAvBgRVHSAAMCcwJQYIKwYBBQUHAgEWGWh0dHA6Ly93d3cuaXpl
923bnBlLmNvbS9jcHMwNwYIKwYBBQUHAQEEKzApMCcGCCsGAQUFBzABhhtodHRwOi8v
924b2NzcC5pemVucGUuY29tOjgwOTQwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2Ny
925bC5pemVucGUuY29tL2NnaS1iaW4vYXJsMjALBgkqhkiG9w0BAQsDggIBAMbjc3HM
9263DG9ubWPkzsF0QsktukpujbTTcGk4h20G7SPRy1DiiTxrRzdAMWGjZioOP3/fKCS
927M539qH0M+gsySNie+iKlbSZJUyE635T1tKw+G7bDUapjlH1xyv55NC5I6wCXGC6E
9283TEP5B/E7dZD0s9E4lS511ubVZivFgOzMYo1DO96diny/N/V1enaTCpRl1qH1OyL
929xUYTijV4ph2gL6exwuG7pxfRcVNHYlrRaXWfTz3F6NBKyULxrI3P/y6JAtN1GqT4
930VF/+vMygx22n0DufGepBwTQz6/rr1ulSZ+eMnuJiTXgh/BzQnkUsXTb8mHII25iR
9310oYF2qAsk6ecWbLiDpkHKIDHmML21MZE13MS8NSvTHoqJO4LyAmDe6SaeNHtrPlK
932b6mzE1BN2ug+ZaX8wLA5IMPFaf0jKhb/Cxu8INsxjt00brsErCc9ip1VNaH0M4bi
9331tGxfiew2436FaeyUxW7Pl6G5GgkNbuUc7QIoRy06DdU/U38BxW3uyJMY60zwHvS
934FlKAn0OvYp4niKhAJwaBVN3kowmJuOU5Rid+TUnfyxbJ9cttSgzaF3hP/N4zgMEM
9355tikXUskeckt8LUK96EH0QyssavAMECUEb/xrupyRdYWwjQGvNLq6T5+fViDGyOw
936k+lzD44wofy8paAy9uC9Owae0zMEzhcsyRm7
937-----END CERTIFICATE-----`
938
939const smimeRoot = `-----BEGIN CERTIFICATE-----
940MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4
941MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6
942ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD
943VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j
944b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq
945scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO
946xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H
947LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX
948uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD
949yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+
950JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q
951rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN
952BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L
953hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB
954QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+
955HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu
956Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg
957QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB
958BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx
959MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
960AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA
961A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb
962laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56
963awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo
964JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw
965LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT
966VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk
967LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb
968UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/
969QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+
970naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls
971QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==
972-----END CERTIFICATE-----`
973
974var nameConstraintsLeaf = `-----BEGIN CERTIFICATE-----
975MIIHMTCCBRmgAwIBAgIIIZaV/3ezOJkwDQYJKoZIhvcNAQEFBQAwgcsxCzAJBgNV
976BAYTAlVTMREwDwYDVQQIEwhWaXJnaW5pYTETMBEGA1UEBxMKQmxhY2tzYnVyZzEj
977MCEGA1UECxMaR2xvYmFsIFF1YWxpZmllZCBTZXJ2ZXIgQ0ExPDA6BgNVBAoTM1Zp
978cmdpbmlhIFBvbHl0ZWNobmljIEluc3RpdHV0ZSBhbmQgU3RhdGUgVW5pdmVyc2l0
979eTExMC8GA1UEAxMoVmlyZ2luaWEgVGVjaCBHbG9iYWwgUXVhbGlmaWVkIFNlcnZl
980ciBDQTAeFw0xMzA5MTkxNDM2NTVaFw0xNTA5MTkxNDM2NTVaMIHNMQswCQYDVQQG
981EwJVUzERMA8GA1UECAwIVmlyZ2luaWExEzARBgNVBAcMCkJsYWNrc2J1cmcxPDA6
982BgNVBAoMM1ZpcmdpbmlhIFBvbHl0ZWNobmljIEluc3RpdHV0ZSBhbmQgU3RhdGUg
983VW5pdmVyc2l0eTE7MDkGA1UECwwyVGVjaG5vbG9neS1lbmhhbmNlZCBMZWFybmlu
984ZyBhbmQgT25saW5lIFN0cmF0ZWdpZXMxGzAZBgNVBAMMEnNlY3VyZS5pZGRsLnZ0
985LmVkdTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkOyPpsOK/6IuPG
986WnIBlVwlHzeYf+cUlggqkLq0b0+vZbiTXgio9/VCuNQ8opSoss7J7o3ygV9to+9Y
987YwJKVC5WDT/y5JWpQey0CWILymViJnpNSwnxBc8A+Q8w5NUGDd/UhtPx/U8/hqbd
988WPDYj2hbOqyq8UlRhfS5pwtnv6BbCTaY11I6FhCLK7zttISyTuWCf9p9o/ggiipP
989ii/5oh4dkl+r5SfuSp5GPNHlYO8lWqys5NAPoDD4fc/kuflcK7Exx7XJ+Oqu0W0/
990psjEY/tES1ZgDWU/ParcxxFpFmKHbD5DXsfPOObzkVWXIY6tGMutSlE1Froy/Nn0
991OZsAOrcCAwEAAaOCAhMwggIPMIG4BggrBgEFBQcBAQSBqzCBqDBYBggrBgEFBQcw
992AoZMaHR0cDovL3d3dy5wa2kudnQuZWR1L2dsb2JhbHF1YWxpZmllZHNlcnZlci9j
993YWNlcnQvZ2xvYmFscXVhbGlmaWVkc2VydmVyLmNydDBMBggrBgEFBQcwAYZAaHR0
994cDovL3Z0Y2EtcC5lcHJvdi5zZXRpLnZ0LmVkdTo4MDgwL2VqYmNhL3B1YmxpY3dl
995Yi9zdGF0dXMvb2NzcDAdBgNVHQ4EFgQUp7xbO6iHkvtZbPE4jmndmnAbSEcwDAYD
996VR0TAQH/BAIwADAfBgNVHSMEGDAWgBS8YmAn1eM1SBfpS6tFatDIqHdxjDBqBgNV
997HSAEYzBhMA4GDCsGAQQBtGgFAgICATAOBgwrBgEEAbRoBQICAQEwPwYMKwYBBAG0
998aAUCAgMBMC8wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucGtpLnZ0LmVkdS9nbG9i
999YWwvY3BzLzBKBgNVHR8EQzBBMD+gPaA7hjlodHRwOi8vd3d3LnBraS52dC5lZHUv
1000Z2xvYmFscXVhbGlmaWVkc2VydmVyL2NybC9jYWNybC5jcmwwDgYDVR0PAQH/BAQD
1001AgTwMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHREEFjAUghJz
1002ZWN1cmUuaWRkbC52dC5lZHUwDQYJKoZIhvcNAQEFBQADggIBAEgoYo4aUtatY3gI
1003OyyKp7QlIOaLbTJZywESHqy+L5EGDdJW2DJV+mcE0LDGvqa2/1Lo+AR1ntsZwfOi
1004Y718JwgVVaX/RCd5+QKP25c5/x72xI8hb/L1bgS0ED9b0YAhd7Qm1K1ot82+6mqX
1005DW6WiGeDr8Z07MQ3143qQe2rBlq+QI69DYzm2GOqAIAnUIWv7tCyLUm31b4DwmrJ
1006TeudVreTKUbBNB1TWRFHEPkWhjjXKZnNGRO11wHXcyBu6YekIvVZ+vmx8ePee4jJ
10073GFOi7lMuWOeq57jTVL7KOKaKLVXBb6gqo5aq+Wwt8RUD5MakrCAEeQZj7DKaFmZ
1008oQCO0Pxrsl3InCGvxnGzT+bFVO9nJ/BAMj7hknFdm9Jr6Bg5q33Z+gnf909AD9QF
1009ESqUSykaHu2LVdJx2MaCH1CyKnRgMw5tEwE15EXpUjCm24m8FMOYC+rNtf18pgrz
10105D8Jhh+oxK9PjcBYqXNtnioIxiMCYcV0q5d4w4BYFEh71tk7/bYB0R55CsBUVPmp
1011timWNOdRd57Tfpk3USaVsumWZAf9MP3wPiC7gb4d5tYEEAG5BuDT8ruFw838wU8G
10121VvAVutSiYBg7k3NYO7AUqZ+Ax4klQX3aM9lgonmJ78Qt94UPtbptrfZ4/lSqEf8
1013GBUwDrQNTb+gsXsDkjd5lcYxNx6l
1014-----END CERTIFICATE-----`
1015
1016var nameConstraintsIntermediate1 = `-----BEGIN CERTIFICATE-----
1017MIINLjCCDBagAwIBAgIRIqpyf/YoGgvHc8HiDAxAI8owDQYJKoZIhvcNAQEFBQAw
1018XDELMAkGA1UEBhMCQkUxFTATBgNVBAsTDFRydXN0ZWQgUm9vdDEZMBcGA1UEChMQ
1019R2xvYmFsU2lnbiBudi1zYTEbMBkGA1UEAxMSVHJ1c3RlZCBSb290IENBIEcyMB4X
1020DTEyMTIxMzAwMDAwMFoXDTE3MTIxMzAwMDAwMFowgcsxCzAJBgNVBAYTAlVTMREw
1021DwYDVQQIEwhWaXJnaW5pYTETMBEGA1UEBxMKQmxhY2tzYnVyZzEjMCEGA1UECxMa
1022R2xvYmFsIFF1YWxpZmllZCBTZXJ2ZXIgQ0ExPDA6BgNVBAoTM1ZpcmdpbmlhIFBv
1023bHl0ZWNobmljIEluc3RpdHV0ZSBhbmQgU3RhdGUgVW5pdmVyc2l0eTExMC8GA1UE
1024AxMoVmlyZ2luaWEgVGVjaCBHbG9iYWwgUXVhbGlmaWVkIFNlcnZlciBDQTCCAiIw
1025DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALgIZhEaptBWADBqdJ45ueFGzMXa
1026GHnzNxoxR1fQIaaRQNdCg4cw3A4dWKMeEgYLtsp65ai3Xfw62Qaus0+KJ3RhgV+r
1027ihqK81NUzkls78fJlADVDI4fCTlothsrE1CTOMiy97jKHai5mVTiWxmcxpmjv7fm
10285Nhc+uHgh2hIz6npryq495mD51ZrUTIaqAQN6Pw/VHfAmR524vgriTOjtp1t4lA9
1029pXGWjF/vkhAKFFheOQSQ00rngo2wHgCqMla64UTN0oz70AsCYNZ3jDLx0kOP0YmM
1030R3Ih91VA63kLqPXA0R6yxmmhhxLZ5bcyAy1SLjr1N302MIxLM/pSy6aquEnbELhz
1031qyp9yGgRyGJay96QH7c4RJY6gtcoPDbldDcHI9nXngdAL4DrZkJ9OkDkJLyqG66W
1032ZTF5q4EIs6yMdrywz0x7QP+OXPJrjYpbeFs6tGZCFnWPFfmHCRJF8/unofYrheq+
10339J7Jx3U55S/k57NXbAM1RAJOuMTlfn9Etf9Dpoac9poI4Liav6rBoUQk3N3JWqnV
1034HNx/NdCyJ1/6UbKMJUZsStAVglsi6lVPo289HHOE4f7iwl3SyekizVOp01wUin3y
1035cnbZB/rXmZbwapSxTTSBf0EIOr9i4EGfnnhCAVA9U5uLrI5OEB69IY8PNX0071s3
1036Z2a2fio5c8m3JkdrAgMBAAGjggh5MIIIdTAOBgNVHQ8BAf8EBAMCAQYwTAYDVR0g
1037BEUwQzBBBgkrBgEEAaAyATwwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xv
1038YmFsc2lnbi5jb20vcmVwb3NpdG9yeS8wEgYDVR0TAQH/BAgwBgEB/wIBADCCBtAG
1039A1UdHgSCBscwggbDoIIGvzASghAzZGJsYWNrc2J1cmcub3JnMBiCFmFjY2VsZXJh
1040dGV2aXJnaW5pYS5jb20wGIIWYWNjZWxlcmF0ZXZpcmdpbmlhLm9yZzALgglhY3Zj
1041cC5vcmcwCYIHYmV2Lm5ldDAJggdiZXYub3JnMAuCCWNsaWdzLm9yZzAMggpjbWl3
1042ZWIub3JnMBeCFWVhc3Rlcm5icm9va3Ryb3V0Lm5ldDAXghVlYXN0ZXJuYnJvb2t0
1043cm91dC5vcmcwEYIPZWNvcnJpZG9ycy5pbmZvMBOCEWVkZ2FycmVzZWFyY2gub3Jn
1044MBKCEGdldC1lZHVjYXRlZC5jb20wE4IRZ2V0LWVkdWNhdGVkLmluZm8wEYIPZ2V0
1045ZWR1Y2F0ZWQubmV0MBKCEGdldC1lZHVjYXRlZC5uZXQwEYIPZ2V0ZWR1Y2F0ZWQu
1046b3JnMBKCEGdldC1lZHVjYXRlZC5vcmcwD4INaG9raWVjbHViLmNvbTAQgg5ob2tp
1047ZXBob3RvLmNvbTAPgg1ob2tpZXNob3AuY29tMBGCD2hva2llc3BvcnRzLmNvbTAS
1048ghBob2tpZXRpY2tldHMuY29tMBKCEGhvdGVscm9hbm9rZS5jb20wE4IRaHVtYW53
1049aWxkbGlmZS5vcmcwF4IVaW5uYXR2aXJnaW5pYXRlY2guY29tMA+CDWlzY2hwMjAx
1050MS5vcmcwD4INbGFuZHJlaGFiLm9yZzAggh5uYXRpb25hbHRpcmVyZXNlYXJjaGNl
1051bnRlci5jb20wFYITbmV0d29ya3ZpcmdpbmlhLm5ldDAMggpwZHJjdnQuY29tMBiC
1052FnBldGVkeWVyaXZlcmNvdXJzZS5jb20wDYILcmFkaW9pcS5vcmcwFYITcml2ZXJj
1053b3Vyc2Vnb2xmLmNvbTALgglzZGltaS5vcmcwEIIOc292YW1vdGlvbi5jb20wHoIc
1054c3VzdGFpbmFibGUtYmlvbWF0ZXJpYWxzLmNvbTAeghxzdXN0YWluYWJsZS1iaW9t
1055YXRlcmlhbHMub3JnMBWCE3RoaXNpc3RoZWZ1dHVyZS5jb20wGIIWdGhpcy1pcy10
1056aGUtZnV0dXJlLmNvbTAVghN0aGlzaXN0aGVmdXR1cmUubmV0MBiCFnRoaXMtaXMt
1057dGhlLWZ1dHVyZS5uZXQwCoIIdmFkcy5vcmcwDIIKdmFsZWFmLm9yZzANggt2YXRl
1058Y2guaW5mbzANggt2YXRlY2gubW9iaTAcghp2YXRlY2hsaWZlbG9uZ2xlYXJuaW5n
1059LmNvbTAcghp2YXRlY2hsaWZlbG9uZ2xlYXJuaW5nLm5ldDAcghp2YXRlY2hsaWZl
1060bG9uZ2xlYXJuaW5nLm9yZzAKggh2Y29tLmVkdTASghB2aXJnaW5pYXZpZXcubmV0
1061MDSCMnZpcmdpbmlhcG9seXRlY2huaWNpbnN0aXR1dGVhbmRzdGF0ZXVuaXZlcnNp
1062dHkuY29tMDWCM3ZpcmdpbmlhcG9seXRlY2huaWNpbnN0aXR1dGVhbmRzdGF0ZXVu
1063aXZlcnNpdHkuaW5mbzA0gjJ2aXJnaW5pYXBvbHl0ZWNobmljaW5zdGl0dXRlYW5k
1064c3RhdGV1bml2ZXJzaXR5Lm5ldDA0gjJ2aXJnaW5pYXBvbHl0ZWNobmljaW5zdGl0
1065dXRlYW5kc3RhdGV1bml2ZXJzaXR5Lm9yZzAZghd2aXJnaW5pYXB1YmxpY3JhZGlv
1066Lm9yZzASghB2aXJnaW5pYXRlY2guZWR1MBOCEXZpcmdpbmlhdGVjaC5tb2JpMByC
1067GnZpcmdpbmlhdGVjaGZvdW5kYXRpb24ub3JnMAiCBnZ0LmVkdTALggl2dGFyYy5v
1068cmcwDIIKdnQtYXJjLm9yZzALggl2dGNyYy5jb20wCoIIdnRpcC5vcmcwDIIKdnRs
1069ZWFuLm9yZzAWghR2dGtub3dsZWRnZXdvcmtzLmNvbTAYghZ2dGxpZmVsb25nbGVh
1070cm5pbmcuY29tMBiCFnZ0bGlmZWxvbmdsZWFybmluZy5uZXQwGIIWdnRsaWZlbG9u
1071Z2xlYXJuaW5nLm9yZzATghF2dHNwb3J0c21lZGlhLmNvbTALggl2dHdlaS5jb20w
1072D4INd2l3YXR3ZXJjLmNvbTAKggh3dnRmLm9yZzAIgQZ2dC5lZHUwd6R1MHMxCzAJ
1073BgNVBAYTAlVTMREwDwYDVQQIEwhWaXJnaW5pYTETMBEGA1UEBxMKQmxhY2tzYnVy
1074ZzE8MDoGA1UEChMzVmlyZ2luaWEgUG9seXRlY2huaWMgSW5zdGl0dXRlIGFuZCBT
1075dGF0ZSBVbml2ZXJzaXR5MCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDAQYI
1076KwYBBQUHAwkwPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDovL2NybC5nbG9iYWxzaWdu
1077LmNvbS9ncy90cnVzdHJvb3RnMi5jcmwwgYQGCCsGAQUFBwEBBHgwdjAzBggrBgEF
1078BQcwAYYnaHR0cDovL29jc3AyLmdsb2JhbHNpZ24uY29tL3RydXN0cm9vdGcyMD8G
1079CCsGAQUFBzAChjNodHRwOi8vc2VjdXJlLmdsb2JhbHNpZ24uY29tL2NhY2VydC90
1080cnVzdHJvb3RnMi5jcnQwHQYDVR0OBBYEFLxiYCfV4zVIF+lLq0Vq0Miod3GMMB8G
1081A1UdIwQYMBaAFBT25YsxtkWASkxt/MKHico2w5BiMA0GCSqGSIb3DQEBBQUAA4IB
1082AQAyJm/lOB2Er4tHXhc/+fSufSzgjohJgYfMkvG4LknkvnZ1BjliefR8tTXX49d2
1083SCDFWfGjqyJZwavavkl/4p3oXPG/nAMDMvxh4YAT+CfEK9HH+6ICV087kD4BLegi
1084+aFJMj8MMdReWCzn5sLnSR1rdse2mo2arX3Uod14SW+PGrbUmTuWNyvRbz3fVmxp
1085UdbGmj3laknO9YPsBGgHfv73pVVsTJkW4ZfY/7KdD/yaVv6ophpOB3coXfjl2+kd
1086Z4ypn2zK+cx9IL/LSewqd/7W9cD55PCUy4X9OTbEmAccwiz3LB66mQoUGfdHdkoB
1087jUY+v9vLQXmaVwI0AYL7g9LN
1088-----END CERTIFICATE-----`
1089
1090var nameConstraintsIntermediate2 = `-----BEGIN CERTIFICATE-----
1091MIIEXTCCA0WgAwIBAgILBAAAAAABNuk6OrMwDQYJKoZIhvcNAQEFBQAwVzELMAkG
1092A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
1093b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMjA0MjUxMTAw
1094MDBaFw0yNzA0MjUxMTAwMDBaMFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVz
1095dGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRy
1096dXN0ZWQgUm9vdCBDQSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
1097AKyuvqrtcMr7g7EuNbu4sKwxM127UsCmx1RxbxxgcArGS7rjiefpBH/w4LYrymjf
1098vcw1ueyMNoqLo9nJMz/ORXupb35NNfE667prQYHa+tTjl1IiKpB7QUwt3wXPuTMF
1099Ja1tXtjKzkqJyuJlNuPKT76HcjgNqgV1s9qG44MD5I2JvI12du8zI1bgdQ+l/KsX
1100kTfbGjUvhOLOlVNWVQDpL+YMIrGqgBYxy5TUNgrAcRtwpNdS2KkF5otSmMweVb5k
1101hoUVv3u8UxQH/WWbNhHq1RrIlg/0rBUfi/ziShYFSB7U+aLx5DxPphTFBiDquQGp
1102tB+FC4JvnukDStFihZCZ1R8CAwEAAaOCASMwggEfMA4GA1UdDwEB/wQEAwIBBjAP
1103BgNVHRMBAf8EBTADAQH/MEcGA1UdIARAMD4wPAYEVR0gADA0MDIGCCsGAQUFBwIB
1104FiZodHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBvc2l0b3J5LzAdBgNVHQ4E
1105FgQUFPblizG2RYBKTG38woeJyjbDkGIwMwYDVR0fBCwwKjAooCagJIYiaHR0cDov
1106L2NybC5nbG9iYWxzaWduLm5ldC9yb290LmNybDA+BggrBgEFBQcBAQQyMDAwLgYI
1107KwYBBQUHMAGGImh0dHA6Ly9vY3NwMi5nbG9iYWxzaWduLmNvbS9yb290cjEwHwYD
1108VR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZIhvcNAQEFBQADggEB
1109AL7IG0l+k4LkcpI+a/kvZsSRwSM4uA6zGX34e78A2oytr8RG8bJwVb8+AHMUD+Xe
11102kYdh/Uj/waQXfqR0OgxQXL9Ct4ZM+JlR1avsNKXWL5AwYXAXCOB3J5PW2XOck7H
1111Zw0vRbGQhjWjQx+B4KOUFg1b3ov/z6Xkr3yaCfRQhXh7KC0Bc0RXPPG5Nv5lCW+z
1112tbbg0zMm3kyfQITRusMSg6IBsDJqOnjaiaKQRcXiD0Sk43ZXb2bUKMxC7+Td3QL4
1113RyHcWJbQ7YylLTS/x+jxWIcOQ0oO5/54t5PTQ14neYhOz9x4gUk2AYAW6d1vePwb
1114hcC8roQwkHT7HvfYBoc74FM=
1115-----END CERTIFICATE-----`
1116
1117var globalSignRoot = `-----BEGIN CERTIFICATE-----
1118MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
1119A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
1120b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
1121MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
1122YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
1123aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
1124jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
1125xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
11261Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
1127snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
1128U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
11299iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
1130BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
1131AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
1132yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
113338NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
1134AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
1135DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
1136HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
1137-----END CERTIFICATE-----`
1138
1139const digicertRoot = `-----BEGIN CERTIFICATE-----
1140MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
1141MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
1142d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
1143QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
1144MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
1145b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
11469w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
1147CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
1148nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
114943C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
1150T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
1151gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
1152BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
1153TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
1154DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
1155hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
115606O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
1157PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
1158YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
1159CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
1160-----END CERTIFICATE-----`
1161
1162const trustAsiaSHA384Intermediate = `-----BEGIN CERTIFICATE-----
1163MIID9zCCAt+gAwIBAgIQC965p4OR4AKrGlsyW0XrDzANBgkqhkiG9w0BAQwFADBh
1164MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
1165d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
1166QTAeFw0xODA0MjcxMjQyNTlaFw0yODA0MjcxMjQyNTlaMFoxCzAJBgNVBAYTAkNO
1167MSUwIwYDVQQKExxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQwIgYDVQQD
1168ExtUcnVzdEFzaWEgRUNDIE9WIFRMUyBQcm8gQ0EwdjAQBgcqhkjOPQIBBgUrgQQA
1169IgNiAAQPIUn75M5BCQLKoPsSU2KTr3mDMh13usnAQ38XfKOzjXiyQ+W0inA7meYR
1170xS+XMQgvnbCigEsKj3ErPIzO68uC9V/KdqMaXWBJp85Ws9A4KL92NB4Okbn5dp6v
1171Qzy08PajggFeMIIBWjAdBgNVHQ4EFgQULdRyBx6HyIH/+LOvuexyH5p/3PwwHwYD
1172VR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDgYDVR0PAQH/BAQDAgGGMB0G
1173A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEA
1174MDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AuZGlnaWNl
1175cnQtY24uY29tMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwuZGlnaWNlcnQt
1176Y24uY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDBWBgNVHSAETzBNMDcGCWCG
1177SAGG/WwBATAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20v
1178Q1BTMAgGBmeBDAECAjAIBgZngQwBAgMwDQYJKoZIhvcNAQEMBQADggEBACVRufYd
1179j81xUqngFCO+Pk8EYXie0pxHKsBZnOPygAyXKx+awUasKBAnHjmhoFPXaDGAP2oV
1180OeZTWgwnURVr6wUCuTkz2/8Tgl1egC7OrVcHSa0fIIhaVo9/zRA/hr31xMG7LFBk
1181GNd7jd06Up4f/UOGbcJsqJexc5QRcUeSwe1MiUDcTNiyCjZk74QCPdcfdFYM4xsa
1182SlUpboB5vyT7jFePZ2v95CKjcr0EhiQ0gwxpdgoipZdfYTiMFGxCLsk6v8pUv7Tq
1183PT/qadOGyC+PfLuZh1PtLp20mF06K+MzheCiv+w1NT5ofhmcObvukc68wvbvRFL6
1184rRzZxAYN36q1SX8=
1185-----END CERTIFICATE-----`
1186
1187const trustAsiaLeaf = `-----BEGIN CERTIFICATE-----
1188MIIEwTCCBEegAwIBAgIQBOjomZfHfhgz2bVYZVuf2DAKBggqhkjOPQQDAzBaMQsw
1189CQYDVQQGEwJDTjElMCMGA1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywgSW5j
1190LjEkMCIGA1UEAxMbVHJ1c3RBc2lhIEVDQyBPViBUTFMgUHJvIENBMB4XDTE5MDUx
1191NzAwMDAwMFoXDTIwMDcyODEyMDAwMFowgY0xCzAJBgNVBAYTAkNOMRIwEAYDVQQI
1192DAnnpo/lu7rnnIExEjAQBgNVBAcMCeWOpumXqOW4gjEqMCgGA1UECgwh5Y6m6Zeo
11935Y+B546W5Y+B56eR5oqA5pyJ6ZmQ5YWs5Y+4MRgwFgYDVQQLDA/nn6Xor4bkuqfm
1194nYPpg6gxEDAOBgNVBAMMByoudG0uY24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
1195AARx/MDQ0oGnCLagQIzjIz57iqFYFmz4/W6gaU6N+GHBkzyvQU8aX02QkdlTTNYL
1196TCoGFJxHB0XlZVSxrqoIPlNKo4ICuTCCArUwHwYDVR0jBBgwFoAULdRyBx6HyIH/
1197+LOvuexyH5p/3PwwHQYDVR0OBBYEFGTyf5adc5smW8NvDZyummJwZRLEMBkGA1Ud
1198EQQSMBCCByoudG0uY26CBXRtLmNuMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAU
1199BggrBgEFBQcDAQYIKwYBBQUHAwIwRgYDVR0fBD8wPTA7oDmgN4Y1aHR0cDovL2Ny
1200bC5kaWdpY2VydC1jbi5jb20vVHJ1c3RBc2lhRUNDT1ZUTFNQcm9DQS5jcmwwTAYD
1201VR0gBEUwQzA3BglghkgBhv1sAQEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cu
1202ZGlnaWNlcnQuY29tL0NQUzAIBgZngQwBAgIwfgYIKwYBBQUHAQEEcjBwMCcGCCsG
1203AQUFBzABhhtodHRwOi8vb2NzcC5kaWdpY2VydC1jbi5jb20wRQYIKwYBBQUHMAKG
1204OWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LWNuLmNvbS9UcnVzdEFzaWFFQ0NPVlRM
1205U1Byb0NBLmNydDAMBgNVHRMBAf8EAjAAMIIBAwYKKwYBBAHWeQIEAgSB9ASB8QDv
1206AHUA7ku9t3XOYLrhQmkfq+GeZqMPfl+wctiDAMR7iXqo/csAAAFqxGMTnwAABAMA
1207RjBEAiAz13zKEoyqd4e/96SK/fxfjl7uR+xhfoDZeyA1BvtfOwIgTY+8nJMGekv8
1208leIVdW6AGh7oqH31CIGTAbNJJWzaSFYAdgCHdb/nWXz4jEOZX73zbv9WjUdWNv9K
1209tWDBtOr/XqCDDwAAAWrEYxTCAAAEAwBHMEUCIQDlWm7+limbRiurcqUwXav3NSmx
1210x/aMnolLbh6+f+b1XAIgQfinHwLw6pDr4R9UkndUsX8QFF4GXS3/IwRR8HCp+pIw
1211CgYIKoZIzj0EAwMDaAAwZQIwHg8JmjRtcq+OgV0vVmdVBPqehi1sQJ9PZ+51CG+Z
12120GOu+2HwS/fyLRViwSc/MZoVAjEA7NgbgpPN4OIsZn2XjMGxemtVxGFS6ZR+1364
1213EEeHB9vhZAEjQSePAfjR9aAGhXRa
1214-----END CERTIFICATE-----`
1215
1216const selfSigned = `-----BEGIN CERTIFICATE-----
1217MIIC/DCCAeSgAwIBAgIRAK0SWRVmi67xU3z0gkgY+PkwDQYJKoZIhvcNAQELBQAw
1218EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNjA4MTkxNjMzNDdaFw0xNzA4MTkxNjMz
1219NDdaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
1220ggEKAoIBAQDWkm1kdCwxyKEt6OTmZitkmLGH8cQu9z7rUdrhW8lWNm4kh2SuaUWP
1221pscBjda5iqg51aoKuWJR2rw6ElDne+X5eit2FT8zJgAU8v39lMFjbaVZfS9TFOYF
1222w0Tk0Luo/PyKJpZnwhsP++iiGQiteJbndy8aLKmJ2MpLfpDGIgxEIyNb5dgoDi0D
1223WReDCpE6K9WDYqvKVGnQ2Jvqqra6Gfx0tFkuqJxQuqA8aUOlPHcCH4KBZdNEoXdY
1224YL3E4dCAh0YiDs80wNZx4cHqEM3L8gTEFqW2Tn1TSuPZO6gjJ9QPsuUZVjaMZuuO
1225NVxqLGujZkDzARhC3fBpptMuaAfi20+BAgMBAAGjTTBLMA4GA1UdDwEB/wQEAwIF
1226oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBYGA1UdEQQPMA2C
1227C2Zvby5leGFtcGxlMA0GCSqGSIb3DQEBCwUAA4IBAQBPvvfnDhsHWt+/cfwdAVim
12284EDn+hYOMkTQwU0pouYIvY8QXYkZ8MBxpBtBMK4JhFU+ewSWoBAEH2dCCvx/BDxN
1229UGTSJHMbsvJHcFvdmsvvRxOqQ/cJz7behx0cfoeHMwcs0/vWv8ms5wHesb5Ek7L0
1230pl01FCBGTcncVqr6RK1r4fTpeCCfRIERD+YRJz8TtPH6ydesfLL8jIV40H8NiDfG
1231vRAvOtNiKtPzFeQVdbRPOskC4rcHyPeiDAMAMixeLi63+CFty4da3r5lRezeedCE
1232cw3ESZzThBwWqvPOtJdpXdm+r57pDW8qD+/0lY8wfImMNkQAyCUCLg/1Lxt/hrBj
1233-----END CERTIFICATE-----`
1234
1235const issuerSubjectMatchRoot = `-----BEGIN CERTIFICATE-----
1236MIICIDCCAYmgAwIBAgIIAj5CwoHlWuYwDQYJKoZIhvcNAQELBQAwIzEPMA0GA1UE
1237ChMGR29sYW5nMRAwDgYDVQQDEwdSb290IGNhMB4XDTE1MDEwMTAwMDAwMFoXDTI1
1238MDEwMTAwMDAwMFowIzEPMA0GA1UEChMGR29sYW5nMRAwDgYDVQQDEwdSb290IGNh
1239MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpDn8RDOZa5oaDcPZRBy4CeBH1
1240siSSOO4mYgLHlPE+oXdqwI/VImi2XeJM2uCFETXCknJJjYG0iJdrt/yyRFvZTQZw
1241+QzGj+mz36NqhGxDWb6dstB2m8PX+plZw7jl81MDvUnWs8yiQ/6twgu5AbhWKZQD
1242JKcNKCEpqa6UW0r5nwIDAQABo10wWzAOBgNVHQ8BAf8EBAMCAgQwHQYDVR0lBBYw
1243FAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMBAf8wGQYDVR0OBBIE
1244EEA31wH7QC+4HH5UBCeMWQEwDQYJKoZIhvcNAQELBQADgYEAb4TfSeCZ1HFmHTKG
1245VsvqWmsOAGrRWm4fBiMH/8vRGnTkJEMLqiqgc3Ulgry/P6n4SIis7TqUOw3TiMhn
1246RGEz33Fsxa/tFoy/gvlJu+MqB1M2NyV33pGkdwl/b7KRWMQFieqO+uE7Ge/49pS3
1247eyfm5ITdK/WT9TzYhsU4AVZcn20=
1248-----END CERTIFICATE-----`
1249
1250const issuerSubjectMatchLeaf = `-----BEGIN CERTIFICATE-----
1251MIICODCCAaGgAwIBAgIJAOjwnT/iW+qmMA0GCSqGSIb3DQEBCwUAMCMxDzANBgNV
1252BAoTBkdvbGFuZzEQMA4GA1UEAxMHUm9vdCBDQTAeFw0xNTAxMDEwMDAwMDBaFw0y
1253NTAxMDEwMDAwMDBaMCAxDzANBgNVBAoTBkdvbGFuZzENMAsGA1UEAxMETGVhZjCB
1254nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA20Z9ky4SJwZIvAYoIat+xLaiXf4e
1255UkWIejZHpQgNkkJbwoHAvpd5mED7T20U/SsTi8KlLmfY1Ame1iI4t0oLdHMrwjTx
12560ZPlltl0e/NYn2xhPMCwQdTZKyskI3dbHDu9dV3OIFTPoWOHHR4kxPMdGlCLqrYU
1257Q+2Xp3Vi9BTIUtcCAwEAAaN3MHUwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQG
1258CCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMBkGA1UdDgQSBBCfkRYf
1259Q0M+SabebbaA159gMBsGA1UdIwQUMBKAEEA31wH7QC+4HH5UBCeMWQEwDQYJKoZI
1260hvcNAQELBQADgYEAjYYF2on1HcUWFEG5NIcrXDiZ49laW3pb3gtcCEUJbxydMV8I
1261ynqjmdqDCyK+TwI1kU5dXDe/iSJYfTB20i/QoO53nnfA1hnr7KBjNWqAm4AagN5k
1262vEA4PCJprUYmoj3q9MKSSRYDlq5kIbl87mSRR4GqtAwJKxIasvOvULOxziQ=
1263-----END CERTIFICATE-----`
1264
1265const x509v1TestRoot = `-----BEGIN CERTIFICATE-----
1266MIICIDCCAYmgAwIBAgIIAj5CwoHlWuYwDQYJKoZIhvcNAQELBQAwIzEPMA0GA1UE
1267ChMGR29sYW5nMRAwDgYDVQQDEwdSb290IENBMB4XDTE1MDEwMTAwMDAwMFoXDTI1
1268MDEwMTAwMDAwMFowIzEPMA0GA1UEChMGR29sYW5nMRAwDgYDVQQDEwdSb290IENB
1269MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpDn8RDOZa5oaDcPZRBy4CeBH1
1270siSSOO4mYgLHlPE+oXdqwI/VImi2XeJM2uCFETXCknJJjYG0iJdrt/yyRFvZTQZw
1271+QzGj+mz36NqhGxDWb6dstB2m8PX+plZw7jl81MDvUnWs8yiQ/6twgu5AbhWKZQD
1272JKcNKCEpqa6UW0r5nwIDAQABo10wWzAOBgNVHQ8BAf8EBAMCAgQwHQYDVR0lBBYw
1273FAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMBAf8wGQYDVR0OBBIE
1274EEA31wH7QC+4HH5UBCeMWQEwDQYJKoZIhvcNAQELBQADgYEAcIwqeNUpQr9cOcYm
1275YjpGpYkQ6b248xijCK7zI+lOeWN89zfSXn1AvfsC9pSdTMeDklWktbF/Ad0IN8Md
1276h2NtN34ard0hEfHc8qW8mkXdsysVmq6cPvFYaHz+dBtkHuHDoy8YQnC0zdN/WyYB
1277/1JmacUUofl+HusHuLkDxmadogI=
1278-----END CERTIFICATE-----`
1279
1280const x509v1TestIntermediate = `-----BEGIN CERTIFICATE-----
1281MIIByjCCATMCCQCCdEMsT8ykqTANBgkqhkiG9w0BAQsFADAjMQ8wDQYDVQQKEwZH
1282b2xhbmcxEDAOBgNVBAMTB1Jvb3QgQ0EwHhcNMTUwMTAxMDAwMDAwWhcNMjUwMTAx
1283MDAwMDAwWjAwMQ8wDQYDVQQKEwZHb2xhbmcxHTAbBgNVBAMTFFguNTA5djEgaW50
1284ZXJtZWRpYXRlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJ2QyniAOT+5YL
1285jeinEBJr3NsC/Q2QJ/VKmgvp+xRxuKTHJiVmxVijmp0vWg8AWfkmuE4p3hXQbbqM
1286k5yxrk1n60ONhim2L4VXriEvCE7X2OXhTmBls5Ufr7aqIgPMikwjScCXwz8E8qI8
1287UxyAhnjeJwMYBU8TuwBImSd4LBHoQQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAIab
1288DRG6FbF9kL9jb/TDHkbVBk+sl/Pxi4/XjuFyIALlARgAkeZcPmL5tNW1ImHkwsHR
1289zWE77kJDibzd141u21ZbLsKvEdUJXjla43bdyMmEqf5VGpC3D4sFt3QVH7lGeRur
1290x5Wlq1u3YDL/j6s1nU2dQ3ySB/oP7J+vQ9V4QeM+
1291-----END CERTIFICATE-----`
1292
1293const x509v1TestLeaf = `-----BEGIN CERTIFICATE-----
1294MIICMzCCAZygAwIBAgIJAPo99mqJJrpJMA0GCSqGSIb3DQEBCwUAMDAxDzANBgNV
1295BAoTBkdvbGFuZzEdMBsGA1UEAxMUWC41MDl2MSBpbnRlcm1lZGlhdGUwHhcNMTUw
1296MTAxMDAwMDAwWhcNMjUwMTAxMDAwMDAwWjArMQ8wDQYDVQQKEwZHb2xhbmcxGDAW
1297BgNVBAMTD2Zvby5leGFtcGxlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
1298gYEApUh60Z+a5/oKJxG//Dn8CihSo2CJHNIIO3zEJZ1EeNSMZCynaIR6D3IPZEIR
1299+RG2oGt+f5EEukAPYxwasp6VeZEezoQWJ+97nPCT6DpwLlWp3i2MF8piK2R9vxkG
1300Z5n0+HzYk1VM8epIrZFUXSMGTX8w1y041PX/yYLxbdEifdcCAwEAAaNaMFgwDgYD
1301VR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNV
1302HRMBAf8EAjAAMBkGA1UdDgQSBBBFozXe0SnzAmjy+1U6M/cvMA0GCSqGSIb3DQEB
1303CwUAA4GBADYzYUvaToO/ucBskPdqXV16AaakIhhSENswYVSl97/sODaxsjishKq9
13045R7siu+JnIFotA7IbBe633p75xEnLN88X626N/XRFG9iScLzpj0o0PWXBUiB+fxL
1305/jt8qszOXCv2vYdUTPNuPqufXLWMoirpuXrr1liJDmedCcAHepY/
1306-----END CERTIFICATE-----`
1307
1308const ignoreCNWithSANRoot = `-----BEGIN CERTIFICATE-----
1309MIIDPzCCAiegAwIBAgIIJkzCwkNrPHMwDQYJKoZIhvcNAQELBQAwMDEQMA4GA1UE
1310ChMHVEVTVElORzEcMBoGA1UEAxMTKipUZXN0aW5nKiogUm9vdCBDQTAeFw0xNTAx
1311MDEwMDAwMDBaFw0yNTAxMDEwMDAwMDBaMDAxEDAOBgNVBAoTB1RFU1RJTkcxHDAa
1312BgNVBAMTEyoqVGVzdGluZyoqIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
1313DwAwggEKAoIBAQC4YAf5YqlXGcikvbMWtVrNICt+V/NNWljwfvSKdg4Inm7k6BwW
1314P6y4Y+n4qSYIWNU4iRkdpajufzctxQCO6ty13iw3qVktzcC5XBIiS6ymiRhhDgnY
1315VQqyakVGw9MxrPwdRZVlssUv3Hmy6tU+v5Ok31SLY5z3wKgYWvSyYs0b8bKNU8kf
13162FmSHnBN16lxGdjhe3ji58F/zFMr0ds+HakrLIvVdFcQFAnQopM8FTHpoWNNzGU3
1317KaiO0jBbMFkd6uVjVnuRJ+xjuiqi/NWwiwQA+CEr9HKzGkxOF8nAsHamdmO1wW+w
1318OsCrC0qWQ/f5NTOVATTJe0vj88OMTvo3071VAgMBAAGjXTBbMA4GA1UdDwEB/wQE
1319AwICpDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUw
1320AwEB/zAZBgNVHQ4EEgQQQDfXAftAL7gcflQEJ4xZATANBgkqhkiG9w0BAQsFAAOC
1321AQEAGOn3XjxHyHbXLKrRmpwV447B7iNBXR5VlhwOgt1kWaHDL2+8f/9/h0HMkB6j
1322fC+/yyuYVqYuOeavqMGVrh33D2ODuTQcFlOx5lXukP46j3j+Lm0jjZ1qNX7vlP8I
1323VlUXERhbelkw8O4oikakwIY9GE8syuSgYf+VeBW/lvuAZQrdnPfabxe05Tre6RXy
1324nJHMB1q07YHpbwIkcV/lfCE9pig2nPXTLwYZz9cl46Ul5RCpPUi+IKURo3x8y0FU
1325aSLjI/Ya0zwUARMmyZ3RRGCyhIarPb20mKSaMf1/Nb23pS3k1QgmZhk5pAnXYsWu
1326BJ6bvwEAasFiLGP6Zbdmxb2hIA==
1327-----END CERTIFICATE-----`
1328
1329const ignoreCNWithSANLeaf = `-----BEGIN CERTIFICATE-----
1330MIIDaTCCAlGgAwIBAgIJAONakvRTxgJhMA0GCSqGSIb3DQEBCwUAMDAxEDAOBgNV
1331BAoTB1RFU1RJTkcxHDAaBgNVBAMTEyoqVGVzdGluZyoqIFJvb3QgQ0EwHhcNMTUw
1332MTAxMDAwMDAwWhcNMjUwMTAxMDAwMDAwWjAsMRAwDgYDVQQKEwdURVNUSU5HMRgw
1333FgYDVQQDEw9mb28uZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
1334ggEKAoIBAQDBqskp89V/JMIBBqcauKSOVLcMyIE/t0jgSWVrsI4sksBTabLsfMdS
1335ui2n+dHQ1dRBuw3o4g4fPrWwS3nMnV3pZUHEn2TPi5N1xkjTaxObXgKIY2GKmFP3
1336rJ9vYqHT6mT4K93kCHoRcmJWWySc7S3JAOhTcdB4G+tIdQJN63E+XRYQQfNrn5HZ
1337hxQoOzaguHFx+ZGSD4Ntk6BSZz5NfjqCYqYxe+iCpTpEEYhIpi8joSPSmkTMTxBW
1338S1W2gXbYNQ9KjNkGM6FnQsUJrSPMrWs4v3UB/U88N5LkZeF41SqD9ySFGwbGajFV
1339nyzj12+4K4D8BLhlOc0Eo/F/8GwOwvmxAgMBAAGjgYkwgYYwDgYDVR0PAQH/BAQD
1340AgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAA
1341MBkGA1UdDgQSBBCjeab27q+5pV43jBGANOJ1MBsGA1UdIwQUMBKAEEA31wH7QC+4
1342HH5UBCeMWQEwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAGZfZ
1343ErTVxxpIg64s22mQpXSk/72THVQsfsKHzlXmztM0CJzH8ccoN67ZqKxJCfdiE/FI
1344Emb6BVV4cGPeIKpcxaM2dwX/Y+Y0JaxpQJvqLxs+EByRL0gPP3shgg86WWCjYLxv
1345AgOn862d/JXGDrC9vIlQ/DDQcyL5g0JV5UjG2G9TUigbnrXxBw7BoWK6wmoSaHnR
1346sZKEHSs3RUJvm7qqpA9Yfzm9jg+i9j32zh1xFacghAOmFRFXa9eCVeigZ/KK2mEY
1347j2kBQyvnyKsXHLAKUoUOpd6t/1PHrfXnGj+HmzZNloJ/BZ1kiWb4eLvMljoLGkZn
1348xZbqP3Krgjj4XNaXjg==
1349-----END CERTIFICATE-----`
1350
1351const excludedNamesLeaf = `-----BEGIN CERTIFICATE-----
1352MIID4DCCAsigAwIBAgIHDUSFtJknhzANBgkqhkiG9w0BAQsFADCBnjELMAkGA1UE
1353BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU
1354MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5
1355ICgzNzM0NTE1NTYyODA2Mzk3KTEhMB8GA1UEAwwYSW50ZXJtZWRpYXRlIENBIGZv
1356ciAzMzkyMB4XDTE3MDIwODIxMTUwNFoXDTE4MDIwODIwMjQ1OFowgZAxCzAJBgNV
1357BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlMb3MgR2F0b3Mx
1358FDASBgNVBAoMC05ldGZsaXggSW5jMS0wKwYDVQQLDCRQbGF0Zm9ybSBTZWN1cml0
1359eSAoMzczNDUxNTc0ODUwMjY5NikxEzARBgNVBAMMCjE3Mi4xNi4wLjEwggEiMA0G
1360CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCZ0oP1bMv6bOeqcKbzinnGpNOpenhA
1361zdFFsgea62znWsH3Wg4+1Md8uPCqlaQIsaJQKZHc50eKD3bg0Io7c6kxHkBQr1b8
1362Q7cGeK3CjdqG3NwS/aizzrLKOwL693hFwwy7JY7GGCvogbhyQRKn6iV0U9zMm7bu
1363/9pQVV/wx8u01u2uAlLttjyQ5LJkxo5t8cATFVqxdN5J9eY//VSDiTwXnlpQITBP
1364/Ow+zYuZ3kFlzH3CtCOhOEvNG3Ar1NvP3Icq35PlHV+Eki4otnKfixwByoiGpqCB
1365UEIY04VrZJjwBxk08y/3jY2B3VLYGgi+rryyCxIqkB7UpSNPMMWSG4UpAgMBAAGj
1366LzAtMAwGA1UdEwEB/wQCMAAwHQYDVR0RBBYwFIIMYmVuZGVyLmxvY2FshwSsEAAB
1367MA0GCSqGSIb3DQEBCwUAA4IBAQCLW3JO8L7LKByjzj2RciPjCGH5XF87Wd20gYLq
1368sNKcFwCIeyZhnQy5aZ164a5G9AIk2HLvH6HevBFPhA9Ivmyv/wYEfnPd1VcFkpgP
1369hDt8MCFJ8eSjCyKdtZh1MPMLrLVymmJV+Rc9JUUYM9TIeERkpl0rskcO1YGewkYt
1370qKlWE+0S16+pzsWvKn831uylqwIb8ANBPsCX4aM4muFBHavSWAHgRO+P+yXVw8Q+
1371VQDnMHUe5PbZd1/+1KKVs1K/CkBCtoHNHp1d/JT+2zUQJphwja9CcgfFdVhSnHL4
1372oEEOFtqVMIuQfR2isi08qW/JGOHc4sFoLYB8hvdaxKWSE19A
1373-----END CERTIFICATE-----`
1374
1375const excludedNamesIntermediate = `-----BEGIN CERTIFICATE-----
1376MIIDzTCCArWgAwIBAgIHDUSFqYeczDANBgkqhkiG9w0BAQsFADCBmTELMAkGA1UE
1377BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU
1378MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5
1379ICgzNzM0NTE1NDc5MDY0NjAyKTEcMBoGA1UEAwwTTG9jYWwgUm9vdCBmb3IgMzM5
1380MjAeFw0xNzAyMDgyMTE1MDRaFw0xODAyMDgyMDI0NThaMIGeMQswCQYDVQQGEwJV
1381UzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJTG9zIEdhdG9zMRQwEgYD
1382VQQKDAtOZXRmbGl4IEluYzEtMCsGA1UECwwkUGxhdGZvcm0gU2VjdXJpdHkgKDM3
1383MzQ1MTU1NjI4MDYzOTcpMSEwHwYDVQQDDBhJbnRlcm1lZGlhdGUgQ0EgZm9yIDMz
1384OTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCOyEs6tJ/t9emQTvlx
13853FS7uJSou5rKkuqVxZdIuYQ+B2ZviBYUnMRT9bXDB0nsVdKZdp0hdchdiwNXDG/I
1386CiWu48jkcv/BdynVyayOT+0pOJSYLaPYpzBx1Pb9M5651ct9GSbj6Tz0ChVonoIE
13871AIZ0kkebucZRRFHd0xbAKVRKyUzPN6HJ7WfgyauUp7RmlC35wTmrmARrFohQLlL
13887oICy+hIQePMy9x1LSFTbPxZ5AUUXVC3eUACU3vLClF/Xs8XGHebZpUXCdMQjOGS
1389nq1eFguFHR1poSB8uSmmLqm4vqUH9CDhEgiBAC8yekJ8//kZQ7lUEqZj3YxVbk+Y
1390E4H5AgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
1391ADxrnmNX5gWChgX9K5fYwhFDj5ofxZXAKVQk+WjmkwMcmCx3dtWSm++Wdksj/ZlA
1392V1cLW3ohWv1/OAZuOlw7sLf98aJpX+UUmIYYQxDubq+4/q7VA7HzEf2k/i/oN1NI
1393JgtrhpPcZ/LMO6k7DYx0qlfYq8pTSfd6MI4LnWKgLc+JSPJJjmvspgio2ZFcnYr7
1394A264BwLo6v1Mos1o1JUvFFcp4GANlw0XFiWh7JXYRl8WmS5DoouUC+aNJ3lmyF6z
1395LbIjZCSfgZnk/LK1KU1j91FI2bc2ULYZvAC1PAg8/zvIgxn6YM2Q7ZsdEgWw0FpS
1396zMBX1/lk4wkFckeUIlkD55Y=
1397-----END CERTIFICATE-----`
1398
1399const excludedNamesRoot = `-----BEGIN CERTIFICATE-----
1400MIIEGTCCAwGgAwIBAgIHDUSFpInn/zANBgkqhkiG9w0BAQsFADCBozELMAkGA1UE
1401BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU
1402MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5
1403ICgzNzMxNTA5NDM3NDYyNDg1KTEmMCQGA1UEAwwdTmFtZSBDb25zdHJhaW50cyBU
1404ZXN0IFJvb3QgQ0EwHhcNMTcwMjA4MjExNTA0WhcNMTgwMjA4MjAyNDU4WjCBmTEL
1405MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBH
1406YXRvczEUMBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNl
1407Y3VyaXR5ICgzNzM0NTE1NDc5MDY0NjAyKTEcMBoGA1UEAwwTTG9jYWwgUm9vdCBm
1408b3IgMzM5MjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJymcnX29ekc
14097+MLyr8QuAzoHWznmGdDd2sITwWRjM89/21cdlHCGKSpULUNdFp9HDLWvYECtxt+
14108TuzKiQz7qAerzGUT1zI5McIjHy0e/i4xIkfiBiNeTCuB/N9QRbZlcfM80ErkaA4
1411gCAFK8qZAcWkHIl6e+KaQFMPLKk9kckgAnVDHEJe8oLNCogCJ15558b65g05p9eb
14125Lg+E98hoPRTQaDwlz3CZPfTTA2EiEZInSi8qzodFCbTpJUVTbiVUH/JtVjlibbb
1413smdcx5PORK+8ZJkhLEh54AjaWOX4tB/7Tkk8stg2VBmrIARt/j4UVj7cTrIWU3bV
1414m8TwHJG+YgsCAwEAAaNaMFgwDwYDVR0TAQH/BAUwAwEB/zBFBgNVHR4EPjA8oBww
1415CocICgEAAP//AAAwDoIMYmVuZGVyLmxvY2FsoRwwCocICgEAAP//AAAwDoIMYmVu
1416ZGVyLmxvY2FsMA0GCSqGSIb3DQEBCwUAA4IBAQAMjbheffPxtSKSv9NySW+8qmHs
1417n7Mb5GGyCFu+cMZSoSaabstbml+zHEFJvWz6/1E95K4F8jKhAcu/CwDf4IZrSD2+
1418Hee0DolVSQhZpnHgPyj7ZATz48e3aJaQPUlhCEOh0wwF4Y0N4FV0t7R6woLylYRZ
1419yU1yRHUqUYpN0DWFpsPbBqgM6uUAVO2ayBFhPgWUaqkmSbZ/Nq7isGvknaTmcIwT
14206mOAFN0qFb4RGzfGJW7x6z7KCULS7qVDp6fU3tRoScHFEgRubks6jzQ1W5ooSm4o
1421+NQCZDd5eFeU8PpNX7rgaYE4GPq+EEmLVCBYmdctr8QVdqJ//8Xu3+1phjDy
1422-----END CERTIFICATE-----`
1423
1424const invalidCNRoot = `-----BEGIN CERTIFICATE-----
1425MIIBFjCBvgIJAIsu4r+jb70UMAoGCCqGSM49BAMCMBQxEjAQBgNVBAsMCVRlc3Qg
1426cm9vdDAeFw0xODA3MTExODMyMzVaFw0yODA3MDgxODMyMzVaMBQxEjAQBgNVBAsM
1427CVRlc3Qgcm9vdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABF6oDgMg0LV6YhPj
1428QXaPXYCc2cIyCdqp0ROUksRz0pOLTc5iY2nraUheRUD1vRRneq7GeXOVNn7uXONg
1429oCGMjNwwCgYIKoZIzj0EAwIDRwAwRAIgDSiwgIn8g1lpruYH0QD1GYeoWVunfmrI
1430XzZZl0eW/ugCICgOfXeZ2GGy3wIC0352BaC3a8r5AAb2XSGNe+e9wNN6
1431-----END CERTIFICATE-----`
1432
1433const validCNWithoutSAN = `-----BEGIN CERTIFICATE-----
1434MIIBJzCBzwIUB7q8t9mrDAL+UB1OFaMN5BEWFKQwCgYIKoZIzj0EAwIwFDESMBAG
1435A1UECwwJVGVzdCByb290MB4XDTE4MDcxMTE4NDcyNFoXDTI4MDcwODE4NDcyNFow
1436GjEYMBYGA1UEAwwPZm9vLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D
1437AQcDQgAEp6Z8IjOnR38Iky1fYTUu2kVndvKXcxiwARJKGtW3b0E8uwVp9AZd/+sr
1438p4ULTPdFToFAeqnGHbu62bkms8pQkDAKBggqhkjOPQQDAgNHADBEAiBTbNe3WWFR
1439cqUYo0sNUuoV+tCTMDJUS+0PWIW4qBqCOwIgFHdLDn5PCk9kJpfc0O2qZx03hdq0
1440h7olHCpY9yMRiz0=
1441-----END CERTIFICATE-----`
1442
1443const rootWithoutSKID = `-----BEGIN CERTIFICATE-----
1444MIIBbzCCARSgAwIBAgIQeCkq3C8SOX/JM5PqYTl9cDAKBggqhkjOPQQDAjASMRAw
1445DgYDVQQKEwdBY21lIENvMB4XDTE5MDIwNDIyNTYzNFoXDTI5MDIwMTIyNTYzNFow
1446EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABISm
1447jGlTr4dLOWT+BCTm2PzWRjk1DpLcSAh+Al8eB1Nc2eBWxYIH9qPirfatvqBOA4c5
1448ZwycRpFoaw6O+EmXnVujTDBKMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr
1449BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MBIGA1UdEQQLMAmCB2V4YW1wbGUwCgYI
1450KoZIzj0EAwIDSQAwRgIhAMaBYWFCjTfn0MNyQ0QXvYT/iIFompkIqzw6wB7qjLrA
1451AiEA3sn65V7G4tsjZEOpN0Jykn9uiTjqniqn/S/qmv8gIec=
1452-----END CERTIFICATE-----`
1453
1454const leafWithAKID = `-----BEGIN CERTIFICATE-----
1455MIIBjTCCATSgAwIBAgIRAPCKYvADhKLPaWOtcTu2XYwwCgYIKoZIzj0EAwIwEjEQ
1456MA4GA1UEChMHQWNtZSBDbzAeFw0xOTAyMDQyMzA2NTJaFw0yOTAyMDEyMzA2NTJa
1457MBMxETAPBgNVBAoTCEFjbWUgTExDMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
1458Wk5N+/8X97YT6ClFNIE5/4yc2YwKn921l0wrIJEcT2u+Uydm7EqtCJNtZjYMAnBd
1459Acp/wynpTwC6tBTsxcM0s6NqMGgwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoG
1460CCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUwitfkXg0JglCjW9R
1461ssWvTAveakIwEgYDVR0RBAswCYIHZXhhbXBsZTAKBggqhkjOPQQDAgNHADBEAiBk
14624LpWiWPOIl5PIhX9PDVkmjpre5oyoH/3aYwG8ABYuAIgCeSfbYueOOG2AdXuMqSU
1463ZZMqeJS7JldLx91sPUArY5A=
1464-----END CERTIFICATE-----`
1465
1466const rootMatchingSKIDMismatchingSubject = `-----BEGIN CERTIFICATE-----
1467MIIBQjCB6aADAgECAgEAMAoGCCqGSM49BAMCMBExDzANBgNVBAMTBlJvb3QgQTAe
1468Fw0wOTExMTAyMzAwMDBaFw0xOTExMDgyMzAwMDBaMBExDzANBgNVBAMTBlJvb3Qg
1469QTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPK4p1uXq2aAeDtKDHIokg2rTcPM
14702gq3N9Y96wiW6/7puBK1+INEW//cO9x6FpzkcsHw/TriAqy4sck/iDAvf9WjMjAw
1471MA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAMBgNVHQ4EBQQDAQID
1472MAoGCCqGSM49BAMCA0gAMEUCIQDgtAp7iVHxMnKxZPaLQPC+Tv2r7+DJc88k2SKH
1473MPs/wQIgFjjNvBoQEl7vSHTcRGCCcFMdlN4l0Dqc9YwGa9fyrQs=
1474-----END CERTIFICATE-----`
1475
1476const rootMismatchingSKIDMatchingSubject = `-----BEGIN CERTIFICATE-----
1477MIIBNDCB26ADAgECAgEAMAoGCCqGSM49BAMCMBExDzANBgNVBAMTBlJvb3QgQjAe
1478Fw0wOTExMTAyMzAwMDBaFw0xOTExMDgyMzAwMDBaMBExDzANBgNVBAMTBlJvb3Qg
1479QjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABI1YRFcIlkWzm9BdEVrIsEQJ2dT6
1480qiW8/WV9GoIhmDtX9SEDHospc0Cgm+TeD2QYW2iMrS5mvNe4GSw0Jezg/bOjJDAi
1481MA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNI
1482ADBFAiEAukWOiuellx8bugRiwCS5XQ6IOJ1SZcjuZxj76WojwxkCIHqa71qNw8FM
1483DtA5yoL9M2pDFF6ovFWnaCe+KlzSwAW/
1484-----END CERTIFICATE-----`
1485
1486const leafMatchingAKIDMatchingIssuer = `-----BEGIN CERTIFICATE-----
1487MIIBNTCB26ADAgECAgEAMAoGCCqGSM49BAMCMBExDzANBgNVBAMTBlJvb3QgQjAe
1488Fw0wOTExMTAyMzAwMDBaFw0xOTExMDgyMzAwMDBaMA8xDTALBgNVBAMTBExlYWYw
1489WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASNWERXCJZFs5vQXRFayLBECdnU+qol
1490vP1lfRqCIZg7V/UhAx6LKXNAoJvk3g9kGFtojK0uZrzXuBksNCXs4P2zoyYwJDAO
1491BgNVHSMEBzAFgAMBAgMwEgYDVR0RBAswCYIHZXhhbXBsZTAKBggqhkjOPQQDAgNJ
1492ADBGAiEAnV9XV7a4h0nfJB8pWv+pBUXRlRFA2uZz3mXEpee8NYACIQCWa+wL70GL
1493ePBQCV1F9sE2q4ZrnsT9TZoNrSe/bMDjzA==
1494-----END CERTIFICATE-----`
1495
1496var unknownAuthorityErrorTests = []struct {
1497	name     string
1498	cert     string
1499	expected string
1500}{
1501	{"self-signed, cn", selfSignedWithCommonName, "x509: certificate signed by unknown authority (possibly because of \"empty\" while trying to verify candidate authority certificate \"test\")"},
1502	{"self-signed, no cn, org", selfSignedNoCommonNameWithOrgName, "x509: certificate signed by unknown authority (possibly because of \"empty\" while trying to verify candidate authority certificate \"ca\")"},
1503	{"self-signed, no cn, no org", selfSignedNoCommonNameNoOrgName, "x509: certificate signed by unknown authority (possibly because of \"empty\" while trying to verify candidate authority certificate \"serial:0\")"},
1504}
1505
1506func TestUnknownAuthorityError(t *testing.T) {
1507	for i, tt := range unknownAuthorityErrorTests {
1508		t.Run(tt.name, func(t *testing.T) {
1509			der, _ := pem.Decode([]byte(tt.cert))
1510			if der == nil {
1511				t.Fatalf("#%d: Unable to decode PEM block", i)
1512			}
1513			c, err := ParseCertificate(der.Bytes)
1514			if err != nil {
1515				t.Fatalf("#%d: Unable to parse certificate -> %v", i, err)
1516			}
1517			uae := &UnknownAuthorityError{
1518				Cert:     c,
1519				hintErr:  fmt.Errorf("empty"),
1520				hintCert: c,
1521			}
1522			actual := uae.Error()
1523			if actual != tt.expected {
1524				t.Errorf("#%d: UnknownAuthorityError.Error() response invalid actual: %s expected: %s", i, actual, tt.expected)
1525			}
1526		})
1527	}
1528}
1529
1530var nameConstraintTests = []struct {
1531	constraint, domain string
1532	expectError        bool
1533	shouldMatch        bool
1534}{
1535	{"", "anything.com", false, true},
1536	{"example.com", "example.com", false, true},
1537	{"example.com.", "example.com", true, false},
1538	{"example.com", "example.com.", true, false},
1539	{"example.com", "ExAmPle.coM", false, true},
1540	{"example.com", "exampl1.com", false, false},
1541	{"example.com", "www.ExAmPle.coM", false, true},
1542	{"example.com", "sub.www.ExAmPle.coM", false, true},
1543	{"example.com", "notexample.com", false, false},
1544	{".example.com", "example.com", false, false},
1545	{".example.com", "www.example.com", false, true},
1546	{".example.com", "www..example.com", true, false},
1547}
1548
1549func TestNameConstraints(t *testing.T) {
1550	for i, test := range nameConstraintTests {
1551		result, err := matchDomainConstraint(test.domain, test.constraint)
1552
1553		if err != nil && !test.expectError {
1554			t.Errorf("unexpected error for test #%d: domain=%s, constraint=%s, err=%s", i, test.domain, test.constraint, err)
1555			continue
1556		}
1557
1558		if err == nil && test.expectError {
1559			t.Errorf("unexpected success for test #%d: domain=%s, constraint=%s", i, test.domain, test.constraint)
1560			continue
1561		}
1562
1563		if result != test.shouldMatch {
1564			t.Errorf("unexpected result for test #%d: domain=%s, constraint=%s, result=%t", i, test.domain, test.constraint, result)
1565		}
1566	}
1567}
1568
1569const selfSignedWithCommonName = `-----BEGIN CERTIFICATE-----
1570MIIDCjCCAfKgAwIBAgIBADANBgkqhkiG9w0BAQsFADAaMQswCQYDVQQKEwJjYTEL
1571MAkGA1UEAxMCY2EwHhcNMTYwODI4MTcwOTE4WhcNMjEwODI3MTcwOTE4WjAcMQsw
1572CQYDVQQKEwJjYTENMAsGA1UEAxMEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEP
1573ADCCAQoCggEBAOH55PfRsbvmcabfLLko1w/yuapY/hk13Cgmc3WE/Z1ZStxGiVxY
1574gQVH9n4W/TbUsrep/TmcC4MV7xEm5252ArcgaH6BeQ4QOTFj/6Jx0RT7U/ix+79x
15758RRysf7OlzNpGIctwZEM7i/G+0ZfqX9ULxL/EW9tppSxMX1jlXZQarnU7BERL5cH
1576+G2jcbU9H28FXYishqpVYE9L7xrXMm61BAwvGKB0jcVW6JdhoAOSfQbbgp7JjIlq
1577czXqUsv1UdORO/horIoJptynTvuARjZzyWatya6as7wyOgEBllE6BjPK9zpn+lp3
1578tQ8dwKVqm/qBPhIrVqYG/Ec7pIv8mJfYabMCAwEAAaNZMFcwDgYDVR0PAQH/BAQD
1579AgOoMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAMBgNVHRMBAf8EAjAA
1580MAoGA1UdDgQDBAEAMAwGA1UdIwQFMAOAAQAwDQYJKoZIhvcNAQELBQADggEBAAAM
1581XMFphzq4S5FBcRdB2fRrmcoz+jEROBWvIH/1QUJeBEBz3ZqBaJYfBtQTvqCA5Rjw
1582dxyIwVd1W3q3aSulM0tO62UCU6L6YeeY/eq8FmpD7nMJo7kCrXUUAMjxbYvS3zkT
1583v/NErK6SgWnkQiPJBZNX1Q9+aSbLT/sbaCTdbWqcGNRuLGJkmqfIyoxRt0Hhpqsx
1584jP5cBaVl50t4qoCuVIE9cOucnxYXnI7X5HpXWvu8Pfxo4SwVjb1az8Fk5s8ZnxGe
1585fPB6Q3L/pKBe0SEe5GywpwtokPLB3lAygcuHbxp/1FlQ1NQZqq+vgXRIla26bNJf
1586IuYkJwt6w+LH/9HZgf8=
1587-----END CERTIFICATE-----`
1588const selfSignedNoCommonNameWithOrgName = `-----BEGIN CERTIFICATE-----
1589MIIC+zCCAeOgAwIBAgIBADANBgkqhkiG9w0BAQsFADAaMQswCQYDVQQKEwJjYTEL
1590MAkGA1UEAxMCY2EwHhcNMTYwODI4MTgxMzQ4WhcNMjEwODI3MTgxMzQ4WjANMQsw
1591CQYDVQQKEwJjYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL5EjrUa
15927EtOMxWiIgTzp2FlQvncPsG329O3l3uNGnbigb8TmNMw2M8UhoDjd84pnU5RAfqd
15938t5TJyw/ybnIKBN131Q2xX+gPQ0dFyMvcO+i1CUgCxmYZomKVA2MXO1RD1hLTYGS
1594gOVjc3no3MBwd8uVQp0NStqJ1QvLtNG4Uy+B28qe+ZFGGbjGqx8/CU4A8Szlpf7/
1595xAZR8w5qFUUlpA2LQYeHHJ5fQVXw7kyL1diNrKNi0G3qcY0IrBh++hT+hnEEXyXu
1596g8a0Ux18hoE8D6rAr34rCZl6AWfqW5wjwm+N5Ns2ugr9U4N8uCKJYMPHb2CtdubU
159746IzVucpTfGLdaMCAwEAAaNZMFcwDgYDVR0PAQH/BAQDAgOoMB0GA1UdJQQWMBQG
1598CCsGAQUFBwMCBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMAoGA1UdDgQDBAEAMAwG
1599A1UdIwQFMAOAAQAwDQYJKoZIhvcNAQELBQADggEBAEn5SgVpJ3zjsdzPqK7Qd/sB
1600bYd1qtPHlrszjhbHBg35C6mDgKhcv4o6N+fuC+FojZb8lIxWzJtvT9pQbfy/V6u3
1601wOb816Hm71uiP89sioIOKCvSAstj/p9doKDOUaKOcZBTw0PS2m9eja8bnleZzBvK
1602rD8cNkHf74v98KvBhcwBlDifVzmkWzMG6TL1EkRXUyLKiWgoTUFSkCDV927oXXMR
1603DKnszq+AVw+K8hbeV2A7GqT7YfeqOAvSbatTDnDtKOPmlCnQui8A149VgZzXv7eU
160429ssJSqjUPyp58dlV6ZuynxPho1QVZUOQgnJToXIQ3/5vIvJRXy52GJCs4/Gh/w=
1605-----END CERTIFICATE-----`
1606const selfSignedNoCommonNameNoOrgName = `-----BEGIN CERTIFICATE-----
1607MIIC7jCCAdagAwIBAgIBADANBgkqhkiG9w0BAQsFADAaMQswCQYDVQQKEwJjYTEL
1608MAkGA1UEAxMCY2EwHhcNMTYwODI4MTgxOTQ1WhcNMjEwODI3MTgxOTQ1WjAAMIIB
1609IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp3E+Jl6DpgzogHUW/i/AAcCM
1610fnNJLOamNVKFGmmxhb4XTHxRaWoTzrlsyzIMS0WzivvJeZVe6mWbvuP2kZanKgIz
161135YXRTR9HbqkNTMuvnpUESzWxbGWE2jmt2+a/Jnz89FS4WIYRhF7nI2z8PvZOfrI
16122gETTT2tEpoF2S4soaYfm0DBeT8K0/rogAaf+oeUS6V+v3miRcAooJgpNJGu9kqm
1613S0xKPn1RCFVjpiRd6YNS0xZirjYQIBMFBvoSoHjaOdgJptNRBprYPOxVJ/ItzGf0
1614kPmzPFCx2tKfxV9HLYBPgxi+fP3IIx8aIYuJn8yReWtYEMYU11hDPeAFN5Gm+wID
1615AQABo1kwVzAOBgNVHQ8BAf8EBAMCA6gwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsG
1616AQUFBwMBMAwGA1UdEwEB/wQCMAAwCgYDVR0OBAMEAQAwDAYDVR0jBAUwA4ABADAN
1617BgkqhkiG9w0BAQsFAAOCAQEATZVOFeiCpPM5QysToLv+8k7Rjoqt6L5IxMUJGEpq
16184ENmldmwkhEKr9VnYEJY3njydnnTm97d9vOfnLj9nA9wMBODeOO3KL2uJR2oDnmM
16199z1NSe2aQKnyBb++DM3ZdikpHn/xEpGV19pYKFQVn35x3lpPh2XijqRDO/erKemb
1620w67CoNRb81dy+4Q1lGpA8ORoLWh5fIq2t2eNGc4qB8vlTIKiESzAwu7u3sRfuWQi
16214R+gnfLd37FWflMHwztFbVTuNtPOljCX0LN7KcuoXYlr05RhQrmoN7fQHsrZMNLs
16228FVjHdKKu+uPstwd04Uy4BR/H2y1yerN9j/L6ZkMl98iiA==
1623-----END CERTIFICATE-----`
1624
1625const criticalExtRoot = `-----BEGIN CERTIFICATE-----
1626MIIBqzCCAVGgAwIBAgIJAJ+mI/85cXApMAoGCCqGSM49BAMCMB0xDDAKBgNVBAoT
1627A09yZzENMAsGA1UEAxMEUm9vdDAeFw0xNTAxMDEwMDAwMDBaFw0yNTAxMDEwMDAw
1628MDBaMB0xDDAKBgNVBAoTA09yZzENMAsGA1UEAxMEUm9vdDBZMBMGByqGSM49AgEG
1629CCqGSM49AwEHA0IABJGp9joiG2QSQA+1FczEDAsWo84rFiP3GTL+n+ugcS6TyNib
1630gzMsdbJgVi+a33y0SzLZxB+YvU3/4KTk8yKLC+2jejB4MA4GA1UdDwEB/wQEAwIC
1631BDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB
1632/zAZBgNVHQ4EEgQQQDfXAftAL7gcflQEJ4xZATAbBgNVHSMEFDASgBBAN9cB+0Av
1633uBx+VAQnjFkBMAoGCCqGSM49BAMCA0gAMEUCIFeSV00fABFceWR52K+CfIgOHotY
1634FizzGiLB47hGwjMuAiEA8e0um2Kr8FPQ4wmFKaTRKHMaZizCGl3m+RG5QsE1KWo=
1635-----END CERTIFICATE-----`
1636
1637const criticalExtIntermediate = `-----BEGIN CERTIFICATE-----
1638MIIBszCCAVmgAwIBAgIJAL2kcGZKpzVqMAoGCCqGSM49BAMCMB0xDDAKBgNVBAoT
1639A09yZzENMAsGA1UEAxMEUm9vdDAeFw0xNTAxMDEwMDAwMDBaFw0yNTAxMDEwMDAw
1640MDBaMCUxDDAKBgNVBAoTA09yZzEVMBMGA1UEAxMMSW50ZXJtZWRpYXRlMFkwEwYH
1641KoZIzj0CAQYIKoZIzj0DAQcDQgAESqVq92iPEq01cL4o99WiXDc5GZjpjNlzMS1n
1642rk8oHcVDp4tQRRQG3F4A6dF1rn/L923ha3b0fhDLlAvXZB+7EKN6MHgwDgYDVR0P
1643AQH/BAQDAgIEMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAPBgNVHRMB
1644Af8EBTADAQH/MBkGA1UdDgQSBBCMGmiotXbbXVd7H40UsgajMBsGA1UdIwQUMBKA
1645EEA31wH7QC+4HH5UBCeMWQEwCgYIKoZIzj0EAwIDSAAwRQIhAOhhNRb6KV7h3wbE
1646cdap8bojzvUcPD78fbsQPCNw1jPxAiBOeAJhlTwpKn9KHpeJphYSzydj9NqcS26Y
1647xXbdbm27KQ==
1648-----END CERTIFICATE-----`
1649
1650const criticalExtLeafWithExt = `-----BEGIN CERTIFICATE-----
1651MIIBxTCCAWugAwIBAgIJAJZAUtw5ccb1MAoGCCqGSM49BAMCMCUxDDAKBgNVBAoT
1652A09yZzEVMBMGA1UEAxMMSW50ZXJtZWRpYXRlMB4XDTE1MDEwMTAwMDAwMFoXDTI1
1653MDEwMTAwMDAwMFowJDEMMAoGA1UEChMDT3JnMRQwEgYDVQQDEwtleGFtcGxlLmNv
1654bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABF3ABa2+B6gUyg6ayCaRQWYY/+No
16556PceLqEavZNUeVNuz7bS74Toy8I7R3bGMkMgbKpLSPlPTroAATvebTXoBaijgYQw
1656gYEwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
1657AjAMBgNVHRMBAf8EAjAAMBkGA1UdDgQSBBBRNtBL2vq8nCV3qVp7ycxMMBsGA1Ud
1658IwQUMBKAEIwaaKi1dttdV3sfjRSyBqMwCgYDUQMEAQH/BAAwCgYIKoZIzj0EAwID
1659SAAwRQIgVjy8GBgZFiagexEuDLqtGjIRJQtBcf7lYgf6XFPH1h4CIQCT6nHhGo6E
1660I+crEm4P5q72AnA/Iy0m24l7OvLuXObAmg==
1661-----END CERTIFICATE-----`
1662
1663const criticalExtIntermediateWithExt = `-----BEGIN CERTIFICATE-----
1664MIIB2TCCAX6gAwIBAgIIQD3NrSZtcUUwCgYIKoZIzj0EAwIwHTEMMAoGA1UEChMD
1665T3JnMQ0wCwYDVQQDEwRSb290MB4XDTE1MDEwMTAwMDAwMFoXDTI1MDEwMTAwMDAw
1666MFowPTEMMAoGA1UEChMDT3JnMS0wKwYDVQQDEyRJbnRlcm1lZGlhdGUgd2l0aCBD
1667cml0aWNhbCBFeHRlbnNpb24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQtnmzH
1668mcRm10bdDBnJE7xQEJ25cLCL5okuEphRR0Zneo6+nQZikoh+UBbtt5GV3Dms7LeP
1669oF5HOplYDCd8wi/wo4GHMIGEMA4GA1UdDwEB/wQEAwICBDAdBgNVHSUEFjAUBggr
1670BgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zAZBgNVHQ4EEgQQKxdv
1671UuQZ6sO3XvBsxgNZ3zAbBgNVHSMEFDASgBBAN9cB+0AvuBx+VAQnjFkBMAoGA1ED
1672BAEB/wQAMAoGCCqGSM49BAMCA0kAMEYCIQCQzTPd6XKex+OAPsKT/1DsoMsg8vcG
1673c2qZ4Q0apT/kvgIhAKu2TnNQMIUdcO0BYQIl+Uhxc78dc9h4lO+YJB47pHGx
1674-----END CERTIFICATE-----`
1675
1676const criticalExtLeaf = `-----BEGIN CERTIFICATE-----
1677MIIBzzCCAXWgAwIBAgIJANoWFIlhCI9MMAoGCCqGSM49BAMCMD0xDDAKBgNVBAoT
1678A09yZzEtMCsGA1UEAxMkSW50ZXJtZWRpYXRlIHdpdGggQ3JpdGljYWwgRXh0ZW5z
1679aW9uMB4XDTE1MDEwMTAwMDAwMFoXDTI1MDEwMTAwMDAwMFowJDEMMAoGA1UEChMD
1680T3JnMRQwEgYDVQQDEwtleGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEH
1681A0IABG1Lfh8A0Ho2UvZN5H0+ONil9c8jwtC0y0xIZftyQE+Fwr9XwqG3rV2g4M1h
1682GnJa9lV9MPHg8+b85Hixm0ZSw7SjdzB1MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUE
1683FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAZBgNVHQ4EEgQQ
1684UNhY4JhezH9gQYqvDMWrWDAbBgNVHSMEFDASgBArF29S5Bnqw7de8GzGA1nfMAoG
1685CCqGSM49BAMCA0gAMEUCIQClA3d4tdrDu9Eb5ZBpgyC+fU1xTZB0dKQHz6M5fPZA
16862AIgN96lM+CPGicwhN24uQI6flOsO3H0TJ5lNzBYLtnQtlc=
1687-----END CERTIFICATE-----`
1688
1689func TestValidHostname(t *testing.T) {
1690	tests := []struct {
1691		host                     string
1692		validInput, validPattern bool
1693	}{
1694		{host: "example.com", validInput: true, validPattern: true},
1695		{host: "eXample123-.com", validInput: true, validPattern: true},
1696		{host: "-eXample123-.com"},
1697		{host: ""},
1698		{host: "."},
1699		{host: "example..com"},
1700		{host: ".example.com"},
1701		{host: "example.com.", validInput: true},
1702		{host: "*.example.com."},
1703		{host: "*.example.com", validPattern: true},
1704		{host: "*foo.example.com"},
1705		{host: "foo.*.example.com"},
1706		{host: "exa_mple.com", validInput: true, validPattern: true},
1707		{host: "foo,bar"},
1708		{host: "project-dev:us-central1:main"},
1709	}
1710	for _, tt := range tests {
1711		if got := validHostnamePattern(tt.host); got != tt.validPattern {
1712			t.Errorf("validHostnamePattern(%q) = %v, want %v", tt.host, got, tt.validPattern)
1713		}
1714		if got := validHostnameInput(tt.host); got != tt.validInput {
1715			t.Errorf("validHostnameInput(%q) = %v, want %v", tt.host, got, tt.validInput)
1716		}
1717	}
1718}
1719
1720func generateCert(cn string, isCA bool, issuer *Certificate, issuerKey crypto.PrivateKey) (*Certificate, crypto.PrivateKey, error) {
1721	priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
1722	if err != nil {
1723		return nil, nil, err
1724	}
1725
1726	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
1727	serialNumber, _ := rand.Int(rand.Reader, serialNumberLimit)
1728
1729	template := &Certificate{
1730		SerialNumber: serialNumber,
1731		Subject:      pkix.Name{CommonName: cn},
1732		NotBefore:    time.Now().Add(-1 * time.Hour),
1733		NotAfter:     time.Now().Add(24 * time.Hour),
1734
1735		KeyUsage:              KeyUsageKeyEncipherment | KeyUsageDigitalSignature | KeyUsageCertSign,
1736		ExtKeyUsage:           []ExtKeyUsage{ExtKeyUsageServerAuth},
1737		BasicConstraintsValid: true,
1738		IsCA:                  isCA,
1739	}
1740	if issuer == nil {
1741		issuer = template
1742		issuerKey = priv
1743	}
1744
1745	derBytes, err := CreateCertificate(rand.Reader, template, issuer, priv.Public(), issuerKey)
1746	if err != nil {
1747		return nil, nil, err
1748	}
1749	cert, err := ParseCertificate(derBytes)
1750	if err != nil {
1751		return nil, nil, err
1752	}
1753
1754	return cert, priv, nil
1755}
1756
1757func TestPathologicalChain(t *testing.T) {
1758	if testing.Short() {
1759		t.Skip("skipping generation of a long chain of certificates in short mode")
1760	}
1761
1762	// Build a chain where all intermediates share the same subject, to hit the
1763	// path building worst behavior.
1764	roots, intermediates := NewCertPool(), NewCertPool()
1765
1766	parent, parentKey, err := generateCert("Root CA", true, nil, nil)
1767	if err != nil {
1768		t.Fatal(err)
1769	}
1770	roots.AddCert(parent)
1771
1772	for i := 1; i < 100; i++ {
1773		parent, parentKey, err = generateCert("Intermediate CA", true, parent, parentKey)
1774		if err != nil {
1775			t.Fatal(err)
1776		}
1777		intermediates.AddCert(parent)
1778	}
1779
1780	leaf, _, err := generateCert("Leaf", false, parent, parentKey)
1781	if err != nil {
1782		t.Fatal(err)
1783	}
1784
1785	start := time.Now()
1786	_, err = leaf.Verify(VerifyOptions{
1787		Roots:         roots,
1788		Intermediates: intermediates,
1789	})
1790	t.Logf("verification took %v", time.Since(start))
1791
1792	if err == nil || !strings.Contains(err.Error(), "signature check attempts limit") {
1793		t.Errorf("expected verification to fail with a signature checks limit error; got %v", err)
1794	}
1795}
1796
1797func TestLongChain(t *testing.T) {
1798	if testing.Short() {
1799		t.Skip("skipping generation of a long chain of certificates in short mode")
1800	}
1801
1802	roots, intermediates := NewCertPool(), NewCertPool()
1803
1804	parent, parentKey, err := generateCert("Root CA", true, nil, nil)
1805	if err != nil {
1806		t.Fatal(err)
1807	}
1808	roots.AddCert(parent)
1809
1810	for i := 1; i < 15; i++ {
1811		name := fmt.Sprintf("Intermediate CA #%d", i)
1812		parent, parentKey, err = generateCert(name, true, parent, parentKey)
1813		if err != nil {
1814			t.Fatal(err)
1815		}
1816		intermediates.AddCert(parent)
1817	}
1818
1819	leaf, _, err := generateCert("Leaf", false, parent, parentKey)
1820	if err != nil {
1821		t.Fatal(err)
1822	}
1823
1824	start := time.Now()
1825	if _, err := leaf.Verify(VerifyOptions{
1826		Roots:         roots,
1827		Intermediates: intermediates,
1828	}); err != nil {
1829		t.Error(err)
1830	}
1831	t.Logf("verification took %v", time.Since(start))
1832}
1833
1834func TestSystemRootsError(t *testing.T) {
1835	if runtime.GOOS == "windows" || runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
1836		t.Skip("Windows and darwin do not use (or support) systemRoots")
1837	}
1838
1839	defer func(oldSystemRoots *CertPool) { systemRoots = oldSystemRoots }(systemRootsPool())
1840
1841	opts := VerifyOptions{
1842		Intermediates: NewCertPool(),
1843		DNSName:       "www.google.com",
1844		CurrentTime:   time.Unix(1677615892, 0),
1845	}
1846
1847	if ok := opts.Intermediates.AppendCertsFromPEM([]byte(gtsIntermediate)); !ok {
1848		t.Fatalf("failed to parse intermediate")
1849	}
1850
1851	leaf, err := certificateFromPEM(googleLeaf)
1852	if err != nil {
1853		t.Fatalf("failed to parse leaf: %v", err)
1854	}
1855
1856	systemRoots = nil
1857
1858	_, err = leaf.Verify(opts)
1859	if _, ok := err.(SystemRootsError); !ok {
1860		t.Errorf("error was not SystemRootsError: %v", err)
1861	}
1862}
1863
1864func TestSystemRootsErrorUnwrap(t *testing.T) {
1865	var err1 = errors.New("err1")
1866	err := SystemRootsError{Err: err1}
1867	if !errors.Is(err, err1) {
1868		t.Error("errors.Is failed, wanted success")
1869	}
1870}
1871
1872func macosMajorVersion(t *testing.T) (int, error) {
1873	cmd := testenv.Command(t, "sw_vers", "-productVersion")
1874	out, err := cmd.Output()
1875	if err != nil {
1876		if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 {
1877			return 0, fmt.Errorf("%v: %v\n%s", cmd, err, ee.Stderr)
1878		}
1879		return 0, fmt.Errorf("%v: %v", cmd, err)
1880	}
1881	before, _, ok := strings.Cut(string(out), ".")
1882	major, err := strconv.Atoi(before)
1883	if !ok || err != nil {
1884		return 0, fmt.Errorf("%v: unexpected output: %q", cmd, out)
1885	}
1886
1887	return major, nil
1888}
1889
1890func TestIssue51759(t *testing.T) {
1891	if runtime.GOOS != "darwin" {
1892		t.Skip("only affects darwin")
1893	}
1894
1895	testenv.MustHaveExecPath(t, "sw_vers")
1896	if vers, err := macosMajorVersion(t); err != nil {
1897		if builder := testenv.Builder(); builder != "" {
1898			t.Fatalf("unable to determine macOS version: %s", err)
1899		} else {
1900			t.Skip("unable to determine macOS version")
1901		}
1902	} else if vers < 11 {
1903		t.Skip("behavior only enforced in macOS 11 and after")
1904	}
1905
1906	// badCertData contains a cert that we parse as valid
1907	// but that macOS SecCertificateCreateWithData rejects.
1908	const badCertData = "0\x82\x01U0\x82\x01\a\xa0\x03\x02\x01\x02\x02\x01\x020\x05\x06\x03+ep0R1P0N\x06\x03U\x04\x03\x13Gderpkey8dc58100b2493614ee1692831a461f3f4dd3f9b3b088e244f887f81b4906ac260\x1e\x17\r220112235755Z\x17\r220313235755Z0R1P0N\x06\x03U\x04\x03\x13Gderpkey8dc58100b2493614ee1692831a461f3f4dd3f9b3b088e244f887f81b4906ac260*0\x05\x06\x03+ep\x03!\x00bA\xd8e\xadW\xcb\xefZ\x89\xb5\"\x1eR\x9d\xba\x0e:\x1042Q@\u007f\xbd\xfb{ks\x04\xd1£\x020\x000\x05\x06\x03+ep\x03A\x00[\xa7\x06y\x86(\x94\x97\x9eLwA\x00\x01x\xaa\xbc\xbd Ê]\n(΅!ف0\xf5\x9a%I\x19<\xffo\xf1\xeaaf@\xb1\xa7\xaf\xfd\xe9R\xc7\x0f\x8d&\xd5\xfc\x0f;Ϙ\x82\x84a\xbc\r"
1909	badCert, err := ParseCertificate([]byte(badCertData))
1910	if err != nil {
1911		t.Fatal(err)
1912	}
1913
1914	t.Run("leaf", func(t *testing.T) {
1915		opts := VerifyOptions{}
1916		expectedErr := "invalid leaf certificate"
1917		_, err = badCert.Verify(opts)
1918		if err == nil || err.Error() != expectedErr {
1919			t.Fatalf("unexpected error: want %q, got %q", expectedErr, err)
1920		}
1921	})
1922
1923	goodCert, err := certificateFromPEM(googleLeaf)
1924	if err != nil {
1925		t.Fatal(err)
1926	}
1927
1928	t.Run("intermediate", func(t *testing.T) {
1929		opts := VerifyOptions{
1930			Intermediates: NewCertPool(),
1931		}
1932		opts.Intermediates.AddCert(badCert)
1933		expectedErr := "SecCertificateCreateWithData: invalid certificate"
1934		_, err = goodCert.Verify(opts)
1935		if err == nil || err.Error() != expectedErr {
1936			t.Fatalf("unexpected error: want %q, got %q", expectedErr, err)
1937		}
1938	})
1939}
1940
1941type trustGraphEdge struct {
1942	Issuer         string
1943	Subject        string
1944	Type           int
1945	MutateTemplate func(*Certificate)
1946	Constraint     func([]*Certificate) error
1947}
1948
1949type rootDescription struct {
1950	Subject        string
1951	MutateTemplate func(*Certificate)
1952	Constraint     func([]*Certificate) error
1953}
1954
1955type trustGraphDescription struct {
1956	Roots []rootDescription
1957	Leaf  string
1958	Graph []trustGraphEdge
1959}
1960
1961func genCertEdge(t *testing.T, subject string, key crypto.Signer, mutateTmpl func(*Certificate), certType int, issuer *Certificate, signer crypto.Signer) *Certificate {
1962	t.Helper()
1963
1964	serial, err := rand.Int(rand.Reader, big.NewInt(100))
1965	if err != nil {
1966		t.Fatalf("failed to generate test serial: %s", err)
1967	}
1968	tmpl := &Certificate{
1969		SerialNumber: serial,
1970		Subject:      pkix.Name{CommonName: subject},
1971		NotBefore:    time.Now().Add(-time.Hour),
1972		NotAfter:     time.Now().Add(time.Hour),
1973	}
1974	if certType == rootCertificate || certType == intermediateCertificate {
1975		tmpl.IsCA, tmpl.BasicConstraintsValid = true, true
1976		tmpl.KeyUsage = KeyUsageCertSign
1977	} else if certType == leafCertificate {
1978		tmpl.DNSNames = []string{"localhost"}
1979	}
1980	if mutateTmpl != nil {
1981		mutateTmpl(tmpl)
1982	}
1983
1984	if certType == rootCertificate {
1985		issuer = tmpl
1986		signer = key
1987	}
1988
1989	d, err := CreateCertificate(rand.Reader, tmpl, issuer, key.Public(), signer)
1990	if err != nil {
1991		t.Fatalf("failed to generate test cert: %s", err)
1992	}
1993	c, err := ParseCertificate(d)
1994	if err != nil {
1995		t.Fatalf("failed to parse test cert: %s", err)
1996	}
1997	return c
1998}
1999
2000func buildTrustGraph(t *testing.T, d trustGraphDescription) (*CertPool, *CertPool, *Certificate) {
2001	t.Helper()
2002
2003	certs := map[string]*Certificate{}
2004	keys := map[string]crypto.Signer{}
2005	rootPool := NewCertPool()
2006	for _, r := range d.Roots {
2007		k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
2008		if err != nil {
2009			t.Fatalf("failed to generate test key: %s", err)
2010		}
2011		root := genCertEdge(t, r.Subject, k, r.MutateTemplate, rootCertificate, nil, nil)
2012		if r.Constraint != nil {
2013			rootPool.AddCertWithConstraint(root, r.Constraint)
2014		} else {
2015			rootPool.AddCert(root)
2016		}
2017		certs[r.Subject] = root
2018		keys[r.Subject] = k
2019	}
2020
2021	intermediatePool := NewCertPool()
2022	var leaf *Certificate
2023	for _, e := range d.Graph {
2024		issuerCert, ok := certs[e.Issuer]
2025		if !ok {
2026			t.Fatalf("unknown issuer %s", e.Issuer)
2027		}
2028		issuerKey, ok := keys[e.Issuer]
2029		if !ok {
2030			t.Fatalf("unknown issuer %s", e.Issuer)
2031		}
2032
2033		k, ok := keys[e.Subject]
2034		if !ok {
2035			var err error
2036			k, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
2037			if err != nil {
2038				t.Fatalf("failed to generate test key: %s", err)
2039			}
2040			keys[e.Subject] = k
2041		}
2042		cert := genCertEdge(t, e.Subject, k, e.MutateTemplate, e.Type, issuerCert, issuerKey)
2043		certs[e.Subject] = cert
2044		if e.Subject == d.Leaf {
2045			leaf = cert
2046		} else {
2047			if e.Constraint != nil {
2048				intermediatePool.AddCertWithConstraint(cert, e.Constraint)
2049			} else {
2050				intermediatePool.AddCert(cert)
2051			}
2052		}
2053	}
2054
2055	return rootPool, intermediatePool, leaf
2056}
2057
2058func chainsToStrings(chains [][]*Certificate) []string {
2059	chainStrings := []string{}
2060	for _, chain := range chains {
2061		names := []string{}
2062		for _, c := range chain {
2063			names = append(names, c.Subject.String())
2064		}
2065		chainStrings = append(chainStrings, strings.Join(names, " -> "))
2066	}
2067	slices.Sort(chainStrings)
2068	return chainStrings
2069}
2070
2071func TestPathBuilding(t *testing.T) {
2072	tests := []struct {
2073		name           string
2074		graph          trustGraphDescription
2075		expectedChains []string
2076		expectedErr    string
2077	}{
2078		{
2079			// Build the following graph from RFC 4158, figure 7 (note that in this graph edges represent
2080			// certificates where the parent is the issuer and the child is the subject.) For the certificate
2081			// C->B, use an unsupported ExtKeyUsage (in this case ExtKeyUsageCodeSigning) which invalidates
2082			// the path Trust Anchor -> C -> B -> EE. The remaining valid paths should be:
2083			//   * Trust Anchor -> A -> B -> EE
2084			//   * Trust Anchor -> C -> A -> B -> EE
2085			//
2086			//     +---------+
2087			//     |  Trust  |
2088			//     | Anchor  |
2089			//     +---------+
2090			//      |       |
2091			//      v       v
2092			//   +---+    +---+
2093			//   | A |<-->| C |
2094			//   +---+    +---+
2095			//    |         |
2096			//    |  +---+  |
2097			//    +->| B |<-+
2098			//       +---+
2099			//         |
2100			//         v
2101			//       +----+
2102			//       | EE |
2103			//       +----+
2104			name: "bad EKU",
2105			graph: trustGraphDescription{
2106				Roots: []rootDescription{{Subject: "root"}},
2107				Leaf:  "leaf",
2108				Graph: []trustGraphEdge{
2109					{
2110						Issuer:  "root",
2111						Subject: "inter a",
2112						Type:    intermediateCertificate,
2113					},
2114					{
2115						Issuer:  "root",
2116						Subject: "inter c",
2117						Type:    intermediateCertificate,
2118					},
2119					{
2120						Issuer:  "inter c",
2121						Subject: "inter a",
2122						Type:    intermediateCertificate,
2123					},
2124					{
2125						Issuer:  "inter a",
2126						Subject: "inter c",
2127						Type:    intermediateCertificate,
2128					},
2129					{
2130						Issuer:  "inter c",
2131						Subject: "inter b",
2132						Type:    intermediateCertificate,
2133						MutateTemplate: func(t *Certificate) {
2134							t.ExtKeyUsage = []ExtKeyUsage{ExtKeyUsageCodeSigning}
2135						},
2136					},
2137					{
2138						Issuer:  "inter a",
2139						Subject: "inter b",
2140						Type:    intermediateCertificate,
2141					},
2142					{
2143						Issuer:  "inter b",
2144						Subject: "leaf",
2145						Type:    leafCertificate,
2146					},
2147				},
2148			},
2149			expectedChains: []string{
2150				"CN=leaf -> CN=inter b -> CN=inter a -> CN=inter c -> CN=root",
2151				"CN=leaf -> CN=inter b -> CN=inter a -> CN=root",
2152			},
2153		},
2154		{
2155			// Build the following graph from RFC 4158, figure 7 (note that in this graph edges represent
2156			// certificates where the parent is the issuer and the child is the subject.) For the certificate
2157			// C->B, use a unconstrained SAN which invalidates the path Trust Anchor -> C -> B -> EE. The
2158			// remaining valid paths should be:
2159			//   * Trust Anchor -> A -> B -> EE
2160			//   * Trust Anchor -> C -> A -> B -> EE
2161			//
2162			//     +---------+
2163			//     |  Trust  |
2164			//     | Anchor  |
2165			//     +---------+
2166			//      |       |
2167			//      v       v
2168			//   +---+    +---+
2169			//   | A |<-->| C |
2170			//   +---+    +---+
2171			//    |         |
2172			//    |  +---+  |
2173			//    +->| B |<-+
2174			//       +---+
2175			//         |
2176			//         v
2177			//       +----+
2178			//       | EE |
2179			//       +----+
2180			name: "bad EKU",
2181			graph: trustGraphDescription{
2182				Roots: []rootDescription{{Subject: "root"}},
2183				Leaf:  "leaf",
2184				Graph: []trustGraphEdge{
2185					{
2186						Issuer:  "root",
2187						Subject: "inter a",
2188						Type:    intermediateCertificate,
2189					},
2190					{
2191						Issuer:  "root",
2192						Subject: "inter c",
2193						Type:    intermediateCertificate,
2194					},
2195					{
2196						Issuer:  "inter c",
2197						Subject: "inter a",
2198						Type:    intermediateCertificate,
2199					},
2200					{
2201						Issuer:  "inter a",
2202						Subject: "inter c",
2203						Type:    intermediateCertificate,
2204					},
2205					{
2206						Issuer:  "inter c",
2207						Subject: "inter b",
2208						Type:    intermediateCertificate,
2209						MutateTemplate: func(t *Certificate) {
2210							t.PermittedDNSDomains = []string{"good"}
2211							t.DNSNames = []string{"bad"}
2212						},
2213					},
2214					{
2215						Issuer:  "inter a",
2216						Subject: "inter b",
2217						Type:    intermediateCertificate,
2218					},
2219					{
2220						Issuer:  "inter b",
2221						Subject: "leaf",
2222						Type:    leafCertificate,
2223					},
2224				},
2225			},
2226			expectedChains: []string{
2227				"CN=leaf -> CN=inter b -> CN=inter a -> CN=inter c -> CN=root",
2228				"CN=leaf -> CN=inter b -> CN=inter a -> CN=root",
2229			},
2230		},
2231		{
2232			// Build the following graph, we should find both paths:
2233			//   * Trust Anchor -> A -> C -> EE
2234			//   * Trust Anchor -> A -> B -> C -> EE
2235			//
2236			//	       +---------+
2237			//	       |  Trust  |
2238			//	       | Anchor  |
2239			//	       +---------+
2240			//	            |
2241			//	            v
2242			//	          +---+
2243			//	          | A |
2244			//	          +---+
2245			//	           | |
2246			//	           | +----+
2247			//	           |      v
2248			//	           |    +---+
2249			//	           |    | B |
2250			//	           |    +---+
2251			//	           |      |
2252			//	           |  +---v
2253			//	           v  v
2254			//            +---+
2255			//            | C |
2256			//            +---+
2257			//              |
2258			//              v
2259			//            +----+
2260			//            | EE |
2261			//            +----+
2262			name: "all paths",
2263			graph: trustGraphDescription{
2264				Roots: []rootDescription{{Subject: "root"}},
2265				Leaf:  "leaf",
2266				Graph: []trustGraphEdge{
2267					{
2268						Issuer:  "root",
2269						Subject: "inter a",
2270						Type:    intermediateCertificate,
2271					},
2272					{
2273						Issuer:  "inter a",
2274						Subject: "inter b",
2275						Type:    intermediateCertificate,
2276					},
2277					{
2278						Issuer:  "inter a",
2279						Subject: "inter c",
2280						Type:    intermediateCertificate,
2281					},
2282					{
2283						Issuer:  "inter b",
2284						Subject: "inter c",
2285						Type:    intermediateCertificate,
2286					},
2287					{
2288						Issuer:  "inter c",
2289						Subject: "leaf",
2290						Type:    leafCertificate,
2291					},
2292				},
2293			},
2294			expectedChains: []string{
2295				"CN=leaf -> CN=inter c -> CN=inter a -> CN=root",
2296				"CN=leaf -> CN=inter c -> CN=inter b -> CN=inter a -> CN=root",
2297			},
2298		},
2299		{
2300			// Build the following graph, which contains a cross-signature loop
2301			// (A and C cross sign each other). Paths that include the A -> C -> A
2302			// (and vice versa) loop should be ignored, resulting in the paths:
2303			//   * Trust Anchor -> A -> B -> EE
2304			//   * Trust Anchor -> C -> B -> EE
2305			//   * Trust Anchor -> A -> C -> B -> EE
2306			//   * Trust Anchor -> C -> A -> B -> EE
2307			//
2308			//     +---------+
2309			//     |  Trust  |
2310			//     | Anchor  |
2311			//     +---------+
2312			//      |       |
2313			//      v       v
2314			//   +---+    +---+
2315			//   | A |<-->| C |
2316			//   +---+    +---+
2317			//    |         |
2318			//    |  +---+  |
2319			//    +->| B |<-+
2320			//       +---+
2321			//         |
2322			//         v
2323			//       +----+
2324			//       | EE |
2325			//       +----+
2326			name: "ignore cross-sig loops",
2327			graph: trustGraphDescription{
2328				Roots: []rootDescription{{Subject: "root"}},
2329				Leaf:  "leaf",
2330				Graph: []trustGraphEdge{
2331					{
2332						Issuer:  "root",
2333						Subject: "inter a",
2334						Type:    intermediateCertificate,
2335					},
2336					{
2337						Issuer:  "root",
2338						Subject: "inter c",
2339						Type:    intermediateCertificate,
2340					},
2341					{
2342						Issuer:  "inter c",
2343						Subject: "inter a",
2344						Type:    intermediateCertificate,
2345					},
2346					{
2347						Issuer:  "inter a",
2348						Subject: "inter c",
2349						Type:    intermediateCertificate,
2350					},
2351					{
2352						Issuer:  "inter c",
2353						Subject: "inter b",
2354						Type:    intermediateCertificate,
2355					},
2356					{
2357						Issuer:  "inter a",
2358						Subject: "inter b",
2359						Type:    intermediateCertificate,
2360					},
2361					{
2362						Issuer:  "inter b",
2363						Subject: "leaf",
2364						Type:    leafCertificate,
2365					},
2366				},
2367			},
2368			expectedChains: []string{
2369				"CN=leaf -> CN=inter b -> CN=inter a -> CN=inter c -> CN=root",
2370				"CN=leaf -> CN=inter b -> CN=inter a -> CN=root",
2371				"CN=leaf -> CN=inter b -> CN=inter c -> CN=inter a -> CN=root",
2372				"CN=leaf -> CN=inter b -> CN=inter c -> CN=root",
2373			},
2374		},
2375		{
2376			// Build a simple two node graph, where the leaf is directly issued from
2377			// the root and both certificates have matching subject and public key, but
2378			// the leaf has SANs.
2379			name: "leaf with same subject, key, as parent but with SAN",
2380			graph: trustGraphDescription{
2381				Roots: []rootDescription{{Subject: "root"}},
2382				Leaf:  "root",
2383				Graph: []trustGraphEdge{
2384					{
2385						Issuer:  "root",
2386						Subject: "root",
2387						Type:    leafCertificate,
2388						MutateTemplate: func(c *Certificate) {
2389							c.DNSNames = []string{"localhost"}
2390						},
2391					},
2392				},
2393			},
2394			expectedChains: []string{
2395				"CN=root -> CN=root",
2396			},
2397		},
2398		{
2399			// Build a basic graph with two paths from leaf to root, but the path passing
2400			// through C should be ignored, because it has invalid EKU nesting.
2401			name: "ignore invalid EKU path",
2402			graph: trustGraphDescription{
2403				Roots: []rootDescription{{Subject: "root"}},
2404				Leaf:  "leaf",
2405				Graph: []trustGraphEdge{
2406					{
2407						Issuer:  "root",
2408						Subject: "inter a",
2409						Type:    intermediateCertificate,
2410					},
2411					{
2412						Issuer:  "root",
2413						Subject: "inter c",
2414						Type:    intermediateCertificate,
2415					},
2416					{
2417						Issuer:  "inter c",
2418						Subject: "inter b",
2419						Type:    intermediateCertificate,
2420						MutateTemplate: func(t *Certificate) {
2421							t.ExtKeyUsage = []ExtKeyUsage{ExtKeyUsageCodeSigning}
2422						},
2423					},
2424					{
2425						Issuer:  "inter a",
2426						Subject: "inter b",
2427						Type:    intermediateCertificate,
2428						MutateTemplate: func(t *Certificate) {
2429							t.ExtKeyUsage = []ExtKeyUsage{ExtKeyUsageServerAuth}
2430						},
2431					},
2432					{
2433						Issuer:  "inter b",
2434						Subject: "leaf",
2435						Type:    leafCertificate,
2436						MutateTemplate: func(t *Certificate) {
2437							t.ExtKeyUsage = []ExtKeyUsage{ExtKeyUsageServerAuth}
2438						},
2439					},
2440				},
2441			},
2442			expectedChains: []string{
2443				"CN=leaf -> CN=inter b -> CN=inter a -> CN=root",
2444			},
2445		},
2446		{
2447			// A name constraint on the root should apply to any names that appear
2448			// on the intermediate, meaning there is no valid chain.
2449			name: "constrained root, invalid intermediate",
2450			graph: trustGraphDescription{
2451				Roots: []rootDescription{
2452					{
2453						Subject: "root",
2454						MutateTemplate: func(t *Certificate) {
2455							t.PermittedDNSDomains = []string{"example.com"}
2456						},
2457					},
2458				},
2459				Leaf: "leaf",
2460				Graph: []trustGraphEdge{
2461					{
2462						Issuer:  "root",
2463						Subject: "inter",
2464						Type:    intermediateCertificate,
2465						MutateTemplate: func(t *Certificate) {
2466							t.DNSNames = []string{"beep.com"}
2467						},
2468					},
2469					{
2470						Issuer:  "inter",
2471						Subject: "leaf",
2472						Type:    leafCertificate,
2473						MutateTemplate: func(t *Certificate) {
2474							t.DNSNames = []string{"www.example.com"}
2475						},
2476					},
2477				},
2478			},
2479			expectedErr: "x509: a root or intermediate certificate is not authorized to sign for this name: DNS name \"beep.com\" is not permitted by any constraint",
2480		},
2481		{
2482			// A name constraint on the intermediate does not apply to the intermediate
2483			// itself, so this is a valid chain.
2484			name: "constrained intermediate, non-matching SAN",
2485			graph: trustGraphDescription{
2486				Roots: []rootDescription{{Subject: "root"}},
2487				Leaf:  "leaf",
2488				Graph: []trustGraphEdge{
2489					{
2490						Issuer:  "root",
2491						Subject: "inter",
2492						Type:    intermediateCertificate,
2493						MutateTemplate: func(t *Certificate) {
2494							t.DNSNames = []string{"beep.com"}
2495							t.PermittedDNSDomains = []string{"example.com"}
2496						},
2497					},
2498					{
2499						Issuer:  "inter",
2500						Subject: "leaf",
2501						Type:    leafCertificate,
2502						MutateTemplate: func(t *Certificate) {
2503							t.DNSNames = []string{"www.example.com"}
2504						},
2505					},
2506				},
2507			},
2508			expectedChains: []string{"CN=leaf -> CN=inter -> CN=root"},
2509		},
2510		{
2511			// A code constraint on the root, applying to one of two intermediates in the graph, should
2512			// result in only one valid chain.
2513			name: "code constrained root, two paths, one valid",
2514			graph: trustGraphDescription{
2515				Roots: []rootDescription{{Subject: "root", Constraint: func(chain []*Certificate) error {
2516					for _, c := range chain {
2517						if c.Subject.CommonName == "inter a" {
2518							return errors.New("bad")
2519						}
2520					}
2521					return nil
2522				}}},
2523				Leaf: "leaf",
2524				Graph: []trustGraphEdge{
2525					{
2526						Issuer:  "root",
2527						Subject: "inter a",
2528						Type:    intermediateCertificate,
2529					},
2530					{
2531						Issuer:  "root",
2532						Subject: "inter b",
2533						Type:    intermediateCertificate,
2534					},
2535					{
2536						Issuer:  "inter a",
2537						Subject: "inter c",
2538						Type:    intermediateCertificate,
2539					},
2540					{
2541						Issuer:  "inter b",
2542						Subject: "inter c",
2543						Type:    intermediateCertificate,
2544					},
2545					{
2546						Issuer:  "inter c",
2547						Subject: "leaf",
2548						Type:    leafCertificate,
2549					},
2550				},
2551			},
2552			expectedChains: []string{"CN=leaf -> CN=inter c -> CN=inter b -> CN=root"},
2553		},
2554		{
2555			// A code constraint on the root, applying to the only path, should result in an error.
2556			name: "code constrained root, one invalid path",
2557			graph: trustGraphDescription{
2558				Roots: []rootDescription{{Subject: "root", Constraint: func(chain []*Certificate) error {
2559					for _, c := range chain {
2560						if c.Subject.CommonName == "leaf" {
2561							return errors.New("bad")
2562						}
2563					}
2564					return nil
2565				}}},
2566				Leaf: "leaf",
2567				Graph: []trustGraphEdge{
2568					{
2569						Issuer:  "root",
2570						Subject: "inter",
2571						Type:    intermediateCertificate,
2572					},
2573					{
2574						Issuer:  "inter",
2575						Subject: "leaf",
2576						Type:    leafCertificate,
2577					},
2578				},
2579			},
2580			expectedErr: "x509: certificate signed by unknown authority (possibly because of \"bad\" while trying to verify candidate authority certificate \"root\")",
2581		},
2582	}
2583
2584	for _, tc := range tests {
2585		t.Run(tc.name, func(t *testing.T) {
2586			roots, intermediates, leaf := buildTrustGraph(t, tc.graph)
2587			chains, err := leaf.Verify(VerifyOptions{
2588				Roots:         roots,
2589				Intermediates: intermediates,
2590			})
2591			if err != nil && err.Error() != tc.expectedErr {
2592				t.Fatalf("unexpected error: got %q, want %q", err, tc.expectedErr)
2593			}
2594			if len(tc.expectedChains) == 0 {
2595				return
2596			}
2597			gotChains := chainsToStrings(chains)
2598			if !reflect.DeepEqual(gotChains, tc.expectedChains) {
2599				t.Errorf("unexpected chains returned:\ngot:\n\t%s\nwant:\n\t%s", strings.Join(gotChains, "\n\t"), strings.Join(tc.expectedChains, "\n\t"))
2600			}
2601		})
2602	}
2603}
2604
2605func TestEKUEnforcement(t *testing.T) {
2606	type ekuDescs struct {
2607		EKUs    []ExtKeyUsage
2608		Unknown []asn1.ObjectIdentifier
2609	}
2610	tests := []struct {
2611		name       string
2612		root       ekuDescs
2613		inters     []ekuDescs
2614		leaf       ekuDescs
2615		verifyEKUs []ExtKeyUsage
2616		err        string
2617	}{
2618		{
2619			name:       "valid, full chain",
2620			root:       ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth}},
2621			inters:     []ekuDescs{ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth}}},
2622			leaf:       ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth}},
2623			verifyEKUs: []ExtKeyUsage{ExtKeyUsageServerAuth},
2624		},
2625		{
2626			name:       "valid, only leaf has EKU",
2627			root:       ekuDescs{},
2628			inters:     []ekuDescs{ekuDescs{}},
2629			leaf:       ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth}},
2630			verifyEKUs: []ExtKeyUsage{ExtKeyUsageServerAuth},
2631		},
2632		{
2633			name:       "invalid, serverAuth not nested",
2634			root:       ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageClientAuth}},
2635			inters:     []ekuDescs{ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth, ExtKeyUsageClientAuth}}},
2636			leaf:       ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth, ExtKeyUsageClientAuth}},
2637			verifyEKUs: []ExtKeyUsage{ExtKeyUsageServerAuth},
2638			err:        "x509: certificate specifies an incompatible key usage",
2639		},
2640		{
2641			name:       "valid, two EKUs, one path",
2642			root:       ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth}},
2643			inters:     []ekuDescs{ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth, ExtKeyUsageClientAuth}}},
2644			leaf:       ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth, ExtKeyUsageClientAuth}},
2645			verifyEKUs: []ExtKeyUsage{ExtKeyUsageServerAuth, ExtKeyUsageClientAuth},
2646		},
2647		{
2648			name: "invalid, ladder",
2649			root: ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth}},
2650			inters: []ekuDescs{
2651				ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth, ExtKeyUsageClientAuth}},
2652				ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageClientAuth}},
2653				ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth, ExtKeyUsageClientAuth}},
2654				ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth}},
2655			},
2656			leaf:       ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth}},
2657			verifyEKUs: []ExtKeyUsage{ExtKeyUsageServerAuth, ExtKeyUsageClientAuth},
2658			err:        "x509: certificate specifies an incompatible key usage",
2659		},
2660		{
2661			name:       "valid, intermediate has no EKU",
2662			root:       ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth}},
2663			inters:     []ekuDescs{ekuDescs{}},
2664			leaf:       ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth}},
2665			verifyEKUs: []ExtKeyUsage{ExtKeyUsageServerAuth},
2666		},
2667		{
2668			name:       "invalid, intermediate has no EKU and no nested path",
2669			root:       ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageClientAuth}},
2670			inters:     []ekuDescs{ekuDescs{}},
2671			leaf:       ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth}},
2672			verifyEKUs: []ExtKeyUsage{ExtKeyUsageServerAuth, ExtKeyUsageClientAuth},
2673			err:        "x509: certificate specifies an incompatible key usage",
2674		},
2675		{
2676			name:       "invalid, intermediate has unknown EKU",
2677			root:       ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth}},
2678			inters:     []ekuDescs{ekuDescs{Unknown: []asn1.ObjectIdentifier{{1, 2, 3}}}},
2679			leaf:       ekuDescs{EKUs: []ExtKeyUsage{ExtKeyUsageServerAuth}},
2680			verifyEKUs: []ExtKeyUsage{ExtKeyUsageServerAuth},
2681			err:        "x509: certificate specifies an incompatible key usage",
2682		},
2683	}
2684
2685	k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
2686	if err != nil {
2687		t.Fatalf("failed to generate test key: %s", err)
2688	}
2689
2690	for _, tc := range tests {
2691		t.Run(tc.name, func(t *testing.T) {
2692			rootPool := NewCertPool()
2693			root := genCertEdge(t, "root", k, func(c *Certificate) {
2694				c.ExtKeyUsage = tc.root.EKUs
2695				c.UnknownExtKeyUsage = tc.root.Unknown
2696			}, rootCertificate, nil, k)
2697			rootPool.AddCert(root)
2698
2699			parent := root
2700			interPool := NewCertPool()
2701			for i, interEKUs := range tc.inters {
2702				inter := genCertEdge(t, fmt.Sprintf("inter %d", i), k, func(c *Certificate) {
2703					c.ExtKeyUsage = interEKUs.EKUs
2704					c.UnknownExtKeyUsage = interEKUs.Unknown
2705				}, intermediateCertificate, parent, k)
2706				interPool.AddCert(inter)
2707				parent = inter
2708			}
2709
2710			leaf := genCertEdge(t, "leaf", k, func(c *Certificate) {
2711				c.ExtKeyUsage = tc.leaf.EKUs
2712				c.UnknownExtKeyUsage = tc.leaf.Unknown
2713			}, intermediateCertificate, parent, k)
2714
2715			_, err := leaf.Verify(VerifyOptions{Roots: rootPool, Intermediates: interPool, KeyUsages: tc.verifyEKUs})
2716			if err == nil && tc.err != "" {
2717				t.Errorf("expected error")
2718			} else if err != nil && err.Error() != tc.err {
2719				t.Errorf("unexpected error: want %q, got %q", err.Error(), tc.err)
2720			}
2721		})
2722	}
2723}
2724
2725func TestVerifyEKURootAsLeaf(t *testing.T) {
2726	k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
2727	if err != nil {
2728		t.Fatalf("failed to generate key: %s", err)
2729	}
2730
2731	for _, tc := range []struct {
2732		rootEKUs   []ExtKeyUsage
2733		verifyEKUs []ExtKeyUsage
2734		succeed    bool
2735	}{
2736		{
2737			verifyEKUs: []ExtKeyUsage{ExtKeyUsageServerAuth},
2738			succeed:    true,
2739		},
2740		{
2741			rootEKUs: []ExtKeyUsage{ExtKeyUsageServerAuth},
2742			succeed:  true,
2743		},
2744		{
2745			rootEKUs:   []ExtKeyUsage{ExtKeyUsageServerAuth},
2746			verifyEKUs: []ExtKeyUsage{ExtKeyUsageServerAuth},
2747			succeed:    true,
2748		},
2749		{
2750			rootEKUs:   []ExtKeyUsage{ExtKeyUsageServerAuth},
2751			verifyEKUs: []ExtKeyUsage{ExtKeyUsageAny},
2752			succeed:    true,
2753		},
2754		{
2755			rootEKUs:   []ExtKeyUsage{ExtKeyUsageAny},
2756			verifyEKUs: []ExtKeyUsage{ExtKeyUsageServerAuth},
2757			succeed:    true,
2758		},
2759		{
2760			rootEKUs:   []ExtKeyUsage{ExtKeyUsageClientAuth},
2761			verifyEKUs: []ExtKeyUsage{ExtKeyUsageServerAuth},
2762			succeed:    false,
2763		},
2764	} {
2765		t.Run(fmt.Sprintf("root EKUs %#v, verify EKUs %#v", tc.rootEKUs, tc.verifyEKUs), func(t *testing.T) {
2766			tmpl := &Certificate{
2767				SerialNumber: big.NewInt(1),
2768				Subject:      pkix.Name{CommonName: "root"},
2769				NotBefore:    time.Now().Add(-time.Hour),
2770				NotAfter:     time.Now().Add(time.Hour),
2771				DNSNames:     []string{"localhost"},
2772				ExtKeyUsage:  tc.rootEKUs,
2773			}
2774			rootDER, err := CreateCertificate(rand.Reader, tmpl, tmpl, k.Public(), k)
2775			if err != nil {
2776				t.Fatalf("failed to create certificate: %s", err)
2777			}
2778			root, err := ParseCertificate(rootDER)
2779			if err != nil {
2780				t.Fatalf("failed to parse certificate: %s", err)
2781			}
2782			roots := NewCertPool()
2783			roots.AddCert(root)
2784
2785			_, err = root.Verify(VerifyOptions{Roots: roots, KeyUsages: tc.verifyEKUs})
2786			if err == nil && !tc.succeed {
2787				t.Error("verification succeed")
2788			} else if err != nil && tc.succeed {
2789				t.Errorf("verification failed: %q", err)
2790			}
2791		})
2792	}
2793
2794}
2795
2796func TestVerifyNilPubKey(t *testing.T) {
2797	c := &Certificate{
2798		RawIssuer:      []byte{1, 2, 3},
2799		AuthorityKeyId: []byte{1, 2, 3},
2800	}
2801	opts := &VerifyOptions{}
2802	opts.Roots = NewCertPool()
2803	r := &Certificate{
2804		RawSubject:   []byte{1, 2, 3},
2805		SubjectKeyId: []byte{1, 2, 3},
2806	}
2807	opts.Roots.AddCert(r)
2808
2809	_, err := c.buildChains([]*Certificate{r}, nil, opts)
2810	if _, ok := err.(UnknownAuthorityError); !ok {
2811		t.Fatalf("buildChains returned unexpected error, got: %v, want %v", err, UnknownAuthorityError{})
2812	}
2813}
2814
2815func TestVerifyBareWildcard(t *testing.T) {
2816	k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
2817	if err != nil {
2818		t.Fatalf("failed to generate key: %s", err)
2819	}
2820	tmpl := &Certificate{
2821		SerialNumber: big.NewInt(1),
2822		Subject:      pkix.Name{CommonName: "test"},
2823		NotBefore:    time.Now().Add(-time.Hour),
2824		NotAfter:     time.Now().Add(time.Hour),
2825		DNSNames:     []string{"*"},
2826	}
2827	cDER, err := CreateCertificate(rand.Reader, tmpl, tmpl, k.Public(), k)
2828	if err != nil {
2829		t.Fatalf("failed to create certificate: %s", err)
2830	}
2831	c, err := ParseCertificate(cDER)
2832	if err != nil {
2833		t.Fatalf("failed to parse certificate: %s", err)
2834	}
2835
2836	if err := c.VerifyHostname("label"); err == nil {
2837		t.Fatalf("VerifyHostname unexpected success with bare wildcard SAN")
2838	}
2839}
2840