Source file
src/crypto/x509/name_constraints_test.go
1
2
3
4
5 package x509
6
7 import (
8 "bytes"
9 "crypto/ecdsa"
10 "crypto/elliptic"
11 "crypto/rand"
12 "crypto/x509/pkix"
13 "encoding/asn1"
14 "encoding/hex"
15 "encoding/pem"
16 "fmt"
17 "internal/testenv"
18 "math/big"
19 "net"
20 "net/url"
21 "os"
22 "os/exec"
23 "strconv"
24 "strings"
25 "sync"
26 "testing"
27 "time"
28 )
29
30 const (
31
32
33
34 testNameConstraintsAgainstOpenSSL = false
35
36
37
38
39 debugOpenSSLFailure = false
40 )
41
42 type nameConstraintsTest struct {
43 name string
44 roots []constraintsSpec
45 intermediates [][]constraintsSpec
46 leaf leafSpec
47 requestedEKUs []ExtKeyUsage
48 expectedError string
49 noOpenSSL bool
50 ignoreCN bool
51 }
52
53 type constraintsSpec struct {
54 ok []string
55 bad []string
56 ekus []string
57 }
58
59 type leafSpec struct {
60 sans []string
61 ekus []string
62 cn string
63 }
64
65 var nameConstraintsTests = []nameConstraintsTest{
66 {
67 name: "certificate generation process",
68 roots: make([]constraintsSpec, 1),
69 leaf: leafSpec{
70 sans: []string{"dns:example.com"},
71 },
72 },
73 {
74 name: "single level of intermediate",
75 roots: make([]constraintsSpec, 1),
76 intermediates: [][]constraintsSpec{
77 {
78 {},
79 },
80 },
81 leaf: leafSpec{
82 sans: []string{"dns:example.com"},
83 },
84 },
85 {
86 name: "two levels of intermediates",
87 roots: make([]constraintsSpec, 1),
88 intermediates: [][]constraintsSpec{
89 {
90 {},
91 },
92 {
93 {},
94 },
95 },
96 leaf: leafSpec{
97 sans: []string{"dns:example.com"},
98 },
99 },
100 {
101 name: "matching DNS constraint in root",
102 roots: []constraintsSpec{
103 {
104 ok: []string{"dns:example.com"},
105 },
106 },
107 intermediates: [][]constraintsSpec{
108 {
109 {},
110 },
111 },
112 leaf: leafSpec{
113 sans: []string{"dns:example.com"},
114 },
115 },
116 {
117 name: "matching DNS constraint in intermediate",
118 roots: make([]constraintsSpec, 1),
119 intermediates: [][]constraintsSpec{
120 {
121 {
122 ok: []string{"dns:example.com"},
123 },
124 },
125 },
126 leaf: leafSpec{
127 sans: []string{"dns:example.com"},
128 },
129 },
130 {
131 name: "leading period only matches subdomains",
132 roots: []constraintsSpec{
133 {
134 ok: []string{"dns:.example.com"},
135 },
136 },
137 intermediates: [][]constraintsSpec{
138 {
139 {},
140 },
141 },
142 leaf: leafSpec{
143 sans: []string{"dns:example.com"},
144 },
145 expectedError: "\"example.com\" is not permitted",
146 },
147 {
148 name: "leading period matches subdomains",
149 roots: make([]constraintsSpec, 1),
150 intermediates: [][]constraintsSpec{
151 {
152 {
153 ok: []string{"dns:.example.com"},
154 },
155 },
156 },
157 leaf: leafSpec{
158 sans: []string{"dns:foo.example.com"},
159 },
160 },
161 {
162 name: "leading period matches multiple levels of subdomains",
163 roots: []constraintsSpec{
164 {
165 ok: []string{"dns:.example.com"},
166 },
167 },
168 intermediates: [][]constraintsSpec{
169 {
170 {},
171 },
172 },
173 leaf: leafSpec{
174 sans: []string{"dns:foo.bar.example.com"},
175 },
176 },
177 {
178 name: "specifying a permitted list of names does not exclude other name types",
179 roots: []constraintsSpec{
180 {
181 ok: []string{"dns:.example.com"},
182 },
183 },
184 intermediates: [][]constraintsSpec{
185 {
186 {},
187 },
188 },
189 leaf: leafSpec{
190 sans: []string{"ip:10.1.1.1"},
191 },
192 },
193 {
194 name: "specifying a permitted list of names does not exclude other name types",
195 roots: []constraintsSpec{
196 {
197 ok: []string{"ip:10.0.0.0/8"},
198 },
199 },
200 intermediates: [][]constraintsSpec{
201 {
202 {},
203 },
204 },
205 leaf: leafSpec{
206 sans: []string{"dns:example.com"},
207 },
208 },
209 {
210 name: "intermediates can try to permit other names, which isn't forbidden if the leaf doesn't mention them",
211 roots: []constraintsSpec{
212 {
213 ok: []string{"dns:example.com"},
214 },
215 },
216 intermediates: [][]constraintsSpec{
217 {
218 {
219 ok: []string{"dns:example.com", "dns:foo.com"},
220 },
221 },
222 },
223 leaf: leafSpec{
224 sans: []string{"dns:example.com"},
225 },
226 },
227 {
228 name: "intermediates cannot add permitted names that the root doesn't grant them",
229 roots: []constraintsSpec{
230 {
231 ok: []string{"dns:example.com"},
232 },
233 },
234 intermediates: [][]constraintsSpec{
235 {
236 {
237 ok: []string{"dns:foo.example.com", "dns:foo.com"},
238 },
239 },
240 },
241 leaf: leafSpec{
242 sans: []string{"dns:foo.com"},
243 },
244 expectedError: "\"foo.com\" is not permitted",
245 },
246 {
247 name: "intermediates can further limit their scope if they wish",
248 roots: []constraintsSpec{
249 {
250 ok: []string{"dns:.example.com"},
251 },
252 },
253 intermediates: [][]constraintsSpec{
254 {
255 {
256 ok: []string{"dns:.bar.example.com"},
257 },
258 },
259 },
260 leaf: leafSpec{
261 sans: []string{"dns:foo.bar.example.com"},
262 },
263 },
264 {
265 name: "intermediates can further limit their scope and that limitation is effective",
266 roots: []constraintsSpec{
267 {
268 ok: []string{"dns:.example.com"},
269 },
270 },
271 intermediates: [][]constraintsSpec{
272 {
273 {
274 ok: []string{"dns:.bar.example.com"},
275 },
276 },
277 },
278 leaf: leafSpec{
279 sans: []string{"dns:foo.notbar.example.com"},
280 },
281 expectedError: "\"foo.notbar.example.com\" is not permitted",
282 },
283 {
284 name: "roots can exclude subtrees and that doesn't affect other names",
285 roots: []constraintsSpec{
286 {
287 bad: []string{"dns:.example.com"},
288 },
289 },
290 intermediates: [][]constraintsSpec{
291 {
292 {},
293 },
294 },
295 leaf: leafSpec{
296 sans: []string{"dns:foo.com"},
297 },
298 },
299 {
300 name: "roots exclusions are effective",
301 roots: []constraintsSpec{
302 {
303 bad: []string{"dns:.example.com"},
304 },
305 },
306 intermediates: [][]constraintsSpec{
307 {
308 {},
309 },
310 },
311 leaf: leafSpec{
312 sans: []string{"dns:foo.example.com"},
313 },
314 expectedError: "\"foo.example.com\" is excluded",
315 },
316 {
317 name: "intermediates can also exclude names and that doesn't affect other names",
318 roots: make([]constraintsSpec, 1),
319 intermediates: [][]constraintsSpec{
320 {
321 {
322 bad: []string{"dns:.example.com"},
323 },
324 },
325 },
326 leaf: leafSpec{
327 sans: []string{"dns:foo.com"},
328 },
329 },
330 {
331 name: "intermediate exclusions are effective",
332 roots: make([]constraintsSpec, 1),
333 intermediates: [][]constraintsSpec{
334 {
335 {
336 bad: []string{"dns:.example.com"},
337 },
338 },
339 },
340 leaf: leafSpec{
341 sans: []string{"dns:foo.example.com"},
342 },
343 expectedError: "\"foo.example.com\" is excluded",
344 },
345 {
346 name: "having an exclusion doesn't prohibit other types of names",
347 roots: []constraintsSpec{
348 {
349 bad: []string{"dns:.example.com"},
350 },
351 },
352 intermediates: [][]constraintsSpec{
353 {
354 {},
355 },
356 },
357 leaf: leafSpec{
358 sans: []string{"dns:foo.com", "ip:10.1.1.1"},
359 },
360 },
361 {
362 name: "IP-based exclusions are permitted and don't affect unrelated IP addresses",
363 roots: []constraintsSpec{
364 {
365 bad: []string{"ip:10.0.0.0/8"},
366 },
367 },
368 intermediates: [][]constraintsSpec{
369 {
370 {},
371 },
372 },
373 leaf: leafSpec{
374 sans: []string{"ip:192.168.1.1"},
375 },
376 },
377 {
378 name: "IP-based exclusions are effective",
379 roots: []constraintsSpec{
380 {
381 bad: []string{"ip:10.0.0.0/8"},
382 },
383 },
384 intermediates: [][]constraintsSpec{
385 {
386 {},
387 },
388 },
389 leaf: leafSpec{
390 sans: []string{"ip:10.0.0.1"},
391 },
392 expectedError: "\"10.0.0.1\" is excluded",
393 },
394 {
395 name: "intermediates can further constrain IP ranges",
396 roots: []constraintsSpec{
397 {
398 bad: []string{"ip:0.0.0.0/1"},
399 },
400 },
401 intermediates: [][]constraintsSpec{
402 {
403 {
404 bad: []string{"ip:11.0.0.0/8"},
405 },
406 },
407 },
408 leaf: leafSpec{
409 sans: []string{"ip:11.0.0.1"},
410 },
411 expectedError: "\"11.0.0.1\" is excluded",
412 },
413 {
414 name: "multiple intermediates with incompatible constraints",
415 roots: make([]constraintsSpec, 1),
416 intermediates: [][]constraintsSpec{
417 {
418 {
419 ok: []string{"dns:.foo.com"},
420 },
421 {
422 ok: []string{"dns:.example.com"},
423 },
424 },
425 },
426 leaf: leafSpec{
427 sans: []string{"dns:foo.example.com"},
428 },
429 noOpenSSL: true,
430 },
431 {
432 name: "multiple intermediates with incompatible constraints swapped",
433 roots: make([]constraintsSpec, 1),
434 intermediates: [][]constraintsSpec{
435 {
436 {
437 ok: []string{"dns:.example.com"},
438 },
439 {
440 ok: []string{"dns:.foo.com"},
441 },
442 },
443 },
444 leaf: leafSpec{
445 sans: []string{"dns:foo.example.com"},
446 },
447 noOpenSSL: true,
448 },
449 {
450 name: "multiple roots with incompatible constraints",
451 roots: []constraintsSpec{
452 {},
453 {
454 ok: []string{"dns:foo.com"},
455 },
456 },
457 intermediates: [][]constraintsSpec{
458 {
459 {},
460 },
461 },
462 leaf: leafSpec{
463 sans: []string{"dns:example.com"},
464 },
465 noOpenSSL: true,
466 },
467 {
468 name: "multiple roots with incompatible constraints swapped",
469 roots: []constraintsSpec{
470 {
471 ok: []string{"dns:foo.com"},
472 },
473 {},
474 },
475 intermediates: [][]constraintsSpec{
476 {
477 {},
478 },
479 },
480 leaf: leafSpec{
481 sans: []string{"dns:example.com"},
482 },
483 noOpenSSL: true,
484 },
485 {
486 name: "chain building with multiple intermediates and roots",
487 roots: []constraintsSpec{
488 {
489 ok: []string{"dns:foo.com"},
490 },
491 {
492 ok: []string{"dns:example.com"},
493 },
494 {},
495 },
496 intermediates: [][]constraintsSpec{
497 {
498 {},
499 {
500 ok: []string{"dns:foo.com"},
501 },
502 },
503 {
504 {},
505 {
506 ok: []string{"dns:foo.com"},
507 },
508 },
509 },
510 leaf: leafSpec{
511 sans: []string{"dns:bar.com"},
512 },
513 noOpenSSL: true,
514 },
515 {
516 name: "chain building fails with no valid path",
517 roots: []constraintsSpec{
518 {
519 ok: []string{"dns:foo.com"},
520 },
521 {
522 ok: []string{"dns:example.com"},
523 },
524 },
525 intermediates: [][]constraintsSpec{
526 {
527 {},
528 {
529 ok: []string{"dns:foo.com"},
530 },
531 },
532 {
533 {
534 ok: []string{"dns:bar.com"},
535 },
536 {
537 ok: []string{"dns:foo.com"},
538 },
539 },
540 },
541 leaf: leafSpec{
542 sans: []string{"dns:bar.com"},
543 },
544 expectedError: "\"bar.com\" is not permitted",
545 },
546 {
547 name: "unknown name types are unconstrained",
548 roots: make([]constraintsSpec, 1),
549 intermediates: [][]constraintsSpec{
550 {
551 {},
552 },
553 },
554 leaf: leafSpec{
555 sans: []string{"unknown:"},
556 },
557 },
558 {
559 name: "unknown name types allowed in constrained chain",
560 roots: []constraintsSpec{
561 {
562 ok: []string{"dns:foo.com", "dns:.foo.com"},
563 },
564 },
565 intermediates: [][]constraintsSpec{
566 {
567 {},
568 },
569 },
570 leaf: leafSpec{
571 sans: []string{"unknown:"},
572 },
573 },
574 {
575 name: "CN is ignored in constrained chain",
576 roots: []constraintsSpec{
577 {
578 ok: []string{"dns:foo.com", "dns:.foo.com"},
579 },
580 },
581 intermediates: [][]constraintsSpec{
582 {
583 {},
584 },
585 },
586 leaf: leafSpec{
587 sans: []string{},
588 cn: "foo.com",
589 },
590 },
591 {
592 name: "IPv6 permitted constraint",
593 roots: []constraintsSpec{
594 {
595 ok: []string{"ip:2000:abcd::/32"},
596 },
597 },
598 intermediates: [][]constraintsSpec{
599 {
600 {},
601 },
602 },
603 leaf: leafSpec{
604 sans: []string{"ip:2000:abcd:1234::"},
605 },
606 },
607 {
608 name: "IPv6 permitted constraint is effective",
609 roots: []constraintsSpec{
610 {
611 ok: []string{"ip:2000:abcd::/32"},
612 },
613 },
614 intermediates: [][]constraintsSpec{
615 {
616 {},
617 },
618 },
619 leaf: leafSpec{
620 sans: []string{"ip:2000:1234:abcd::"},
621 },
622 expectedError: "\"2000:1234:abcd::\" is not permitted",
623 },
624 {
625 name: "IPv6 permitted constraint does not affect DNS",
626 roots: []constraintsSpec{
627 {
628 ok: []string{"ip:2000:abcd::/32"},
629 },
630 },
631 intermediates: [][]constraintsSpec{
632 {
633 {},
634 },
635 },
636 leaf: leafSpec{
637 sans: []string{"ip:2000:abcd::", "dns:foo.com"},
638 },
639 },
640 {
641 name: "IPv6 excluded constraint",
642 roots: []constraintsSpec{
643 {
644 bad: []string{"ip:2000:abcd::/32"},
645 },
646 },
647 intermediates: [][]constraintsSpec{
648 {
649 {},
650 },
651 },
652 leaf: leafSpec{
653 sans: []string{"ip:2000:1234::"},
654 },
655 },
656 {
657 name: "IPv6 excluded constraint is effective",
658 roots: []constraintsSpec{
659 {
660 bad: []string{"ip:2000:abcd::/32"},
661 },
662 },
663 intermediates: [][]constraintsSpec{
664 {
665 {},
666 },
667 },
668 leaf: leafSpec{
669 sans: []string{"ip:2000:abcd::"},
670 },
671 expectedError: "\"2000:abcd::\" is excluded",
672 },
673 {
674 name: "IPv6 constraint does not permit IPv4",
675 roots: []constraintsSpec{
676 {
677 ok: []string{"ip:2000:abcd::/32"},
678 },
679 },
680 intermediates: [][]constraintsSpec{
681 {
682 {},
683 },
684 },
685 leaf: leafSpec{
686 sans: []string{"ip:10.0.0.1"},
687 },
688 expectedError: "\"10.0.0.1\" is not permitted",
689 },
690 {
691 name: "IPv4 constraint does not permit IPv6",
692 roots: []constraintsSpec{
693 {
694 ok: []string{"ip:10.0.0.0/8"},
695 },
696 },
697 intermediates: [][]constraintsSpec{
698 {
699 {},
700 },
701 },
702 leaf: leafSpec{
703 sans: []string{"ip:2000:abcd::"},
704 },
705 expectedError: "\"2000:abcd::\" is not permitted",
706 },
707 {
708 name: "unknown excluded constraint does not affect other names",
709 roots: []constraintsSpec{
710 {
711 bad: []string{"unknown:"},
712 },
713 },
714 intermediates: [][]constraintsSpec{
715 {
716 {},
717 },
718 },
719 leaf: leafSpec{
720 sans: []string{"dns:example.com"},
721 },
722 },
723 {
724 name: "unknown permitted constraint does not affect other names",
725 roots: []constraintsSpec{
726 {
727 ok: []string{"unknown:"},
728 },
729 },
730 intermediates: [][]constraintsSpec{
731 {
732 {},
733 },
734 },
735 leaf: leafSpec{
736 sans: []string{"dns:example.com"},
737 },
738 },
739 {
740 name: "exact email constraint",
741 roots: []constraintsSpec{
742 {
743 ok: []string{"email:foo@example.com"},
744 },
745 },
746 intermediates: [][]constraintsSpec{
747 {
748 {},
749 },
750 },
751 leaf: leafSpec{
752 sans: []string{"email:foo@example.com"},
753 },
754 },
755 {
756 name: "exact email constraint is effective",
757 roots: []constraintsSpec{
758 {
759 ok: []string{"email:foo@example.com"},
760 },
761 },
762 intermediates: [][]constraintsSpec{
763 {
764 {},
765 },
766 },
767 leaf: leafSpec{
768 sans: []string{"email:bar@example.com"},
769 },
770 expectedError: "\"bar@example.com\" is not permitted",
771 },
772 {
773 name: "email canonicalization",
774 roots: []constraintsSpec{
775 {
776 ok: []string{"email:foo@example.com"},
777 },
778 },
779 intermediates: [][]constraintsSpec{
780 {
781 {},
782 },
783 },
784 leaf: leafSpec{
785 sans: []string{"email:\"\\f\\o\\o\"@example.com"},
786 },
787 noOpenSSL: true,
788 },
789 {
790 name: "email host constraint",
791 roots: []constraintsSpec{
792 {
793 ok: []string{"email:example.com"},
794 },
795 },
796 intermediates: [][]constraintsSpec{
797 {
798 {},
799 },
800 },
801 leaf: leafSpec{
802 sans: []string{"email:foo@example.com"},
803 },
804 },
805 {
806 name: "email subdomain constraint",
807 roots: []constraintsSpec{
808 {
809 ok: []string{"email:.example.com"},
810 },
811 },
812 intermediates: [][]constraintsSpec{
813 {
814 {},
815 },
816 },
817 leaf: leafSpec{
818 sans: []string{"email:foo@sub.example.com"},
819 },
820 },
821 {
822 name: "email subdomain constraint does not match parent",
823 roots: []constraintsSpec{
824 {
825 ok: []string{"email:.example.com"},
826 },
827 },
828 intermediates: [][]constraintsSpec{
829 {
830 {},
831 },
832 },
833 leaf: leafSpec{
834 sans: []string{"email:foo@example.com"},
835 },
836 expectedError: "\"foo@example.com\" is not permitted",
837 },
838 {
839 name: "email subdomain constraint matches deeper subdomains",
840 roots: []constraintsSpec{
841 {
842 ok: []string{"email:.example.com"},
843 },
844 },
845 intermediates: [][]constraintsSpec{
846 {
847 {},
848 },
849 },
850 leaf: leafSpec{
851 sans: []string{"email:foo@sub.sub.example.com"},
852 },
853 },
854 {
855 name: "email local part is case-sensitive",
856 roots: []constraintsSpec{
857 {
858 ok: []string{"email:foo@example.com"},
859 },
860 },
861 intermediates: [][]constraintsSpec{
862 {
863 {},
864 },
865 },
866 leaf: leafSpec{
867 sans: []string{"email:Foo@example.com"},
868 },
869 expectedError: "\"Foo@example.com\" is not permitted",
870 },
871 {
872 name: "email domain part is case-insensitive",
873 roots: []constraintsSpec{
874 {
875 ok: []string{"email:foo@EXAMPLE.com"},
876 },
877 },
878 intermediates: [][]constraintsSpec{
879 {
880 {},
881 },
882 },
883 leaf: leafSpec{
884 sans: []string{"email:foo@example.com"},
885 },
886 },
887 {
888 name: "DNS domain is case-insensitive",
889 roots: []constraintsSpec{
890 {
891 ok: []string{"dns:EXAMPLE.com"},
892 },
893 },
894 intermediates: [][]constraintsSpec{
895 {
896 {},
897 },
898 },
899 leaf: leafSpec{
900 sans: []string{"dns:example.com"},
901 },
902 },
903 {
904 name: "URI constraint covers host",
905 roots: []constraintsSpec{
906 {
907 ok: []string{"uri:example.com"},
908 },
909 },
910 intermediates: [][]constraintsSpec{
911 {
912 {},
913 },
914 },
915 leaf: leafSpec{
916 sans: []string{
917 "uri:http://example.com/bar",
918 "uri:http://example.com:8080/",
919 "uri:https://example.com/wibble#bar",
920 },
921 },
922 },
923 {
924 name: "URI with IP is rejected",
925 roots: []constraintsSpec{
926 {
927 ok: []string{"uri:example.com"},
928 },
929 },
930 intermediates: [][]constraintsSpec{
931 {
932 {},
933 },
934 },
935 leaf: leafSpec{
936 sans: []string{"uri:http://1.2.3.4/"},
937 },
938 expectedError: "URI with IP",
939 },
940 {
941 name: "URI with IP and port is rejected",
942 roots: []constraintsSpec{
943 {
944 ok: []string{"uri:example.com"},
945 },
946 },
947 intermediates: [][]constraintsSpec{
948 {
949 {},
950 },
951 },
952 leaf: leafSpec{
953 sans: []string{"uri:http://1.2.3.4:43/"},
954 },
955 expectedError: "URI with IP",
956 },
957 {
958 name: "URI with IPv6 is rejected",
959 roots: []constraintsSpec{
960 {
961 ok: []string{"uri:example.com"},
962 },
963 },
964 intermediates: [][]constraintsSpec{
965 {
966 {},
967 },
968 },
969 leaf: leafSpec{
970 sans: []string{"uri:http://[2006:abcd::1]/"},
971 },
972 expectedError: "URI with IP",
973 },
974 {
975 name: "URI with IPv6 and port is rejected",
976 roots: []constraintsSpec{
977 {
978 ok: []string{"uri:example.com"},
979 },
980 },
981 intermediates: [][]constraintsSpec{
982 {
983 {},
984 },
985 },
986 leaf: leafSpec{
987 sans: []string{"uri:http://[2006:abcd::1]:16/"},
988 },
989 expectedError: "URI with IP",
990 },
991 {
992 name: "URI permitted constraint is effective",
993 roots: []constraintsSpec{
994 {
995 ok: []string{"uri:example.com"},
996 },
997 },
998 intermediates: [][]constraintsSpec{
999 {
1000 {},
1001 },
1002 },
1003 leaf: leafSpec{
1004 sans: []string{"uri:http://bar.com/"},
1005 },
1006 expectedError: "\"http://bar.com/\" is not permitted",
1007 },
1008 {
1009 name: "URI excluded constraint is effective",
1010 roots: []constraintsSpec{
1011 {
1012 bad: []string{"uri:foo.com"},
1013 },
1014 },
1015 intermediates: [][]constraintsSpec{
1016 {
1017 {},
1018 },
1019 },
1020 leaf: leafSpec{
1021 sans: []string{"uri:http://foo.com/"},
1022 },
1023 expectedError: "\"http://foo.com/\" is excluded",
1024 },
1025 {
1026 name: "URI subdomain constraint",
1027 roots: []constraintsSpec{
1028 {
1029 ok: []string{"uri:.foo.com"},
1030 },
1031 },
1032 intermediates: [][]constraintsSpec{
1033 {
1034 {},
1035 },
1036 },
1037 leaf: leafSpec{
1038 sans: []string{"uri:http://www.foo.com/"},
1039 },
1040 },
1041 {
1042 name: "URI constraint not matched by URN",
1043 roots: []constraintsSpec{
1044 {
1045 ok: []string{"uri:example.com"},
1046 },
1047 },
1048 intermediates: [][]constraintsSpec{
1049 {
1050 {},
1051 },
1052 },
1053 leaf: leafSpec{
1054 sans: []string{"uri:urn:example"},
1055 },
1056 expectedError: "URI with empty host",
1057 },
1058 {
1059 name: "IPv6 exclusion does not exclude all IPv4",
1060 roots: []constraintsSpec{
1061 {
1062 ok: []string{"ip:1.2.3.0/24"},
1063 bad: []string{"ip:::0/0"},
1064 },
1065 },
1066 intermediates: [][]constraintsSpec{
1067 {
1068 {},
1069 },
1070 },
1071 leaf: leafSpec{
1072 sans: []string{"ip:1.2.3.4"},
1073 },
1074 },
1075 {
1076 name: "empty EKU in CA means any is ok",
1077 roots: make([]constraintsSpec, 1),
1078 intermediates: [][]constraintsSpec{
1079 {
1080 {},
1081 },
1082 },
1083 leaf: leafSpec{
1084 sans: []string{"dns:example.com"},
1085 ekus: []string{"serverAuth", "other"},
1086 },
1087 },
1088 {
1089 name: "any EKU means any is ok",
1090 roots: make([]constraintsSpec, 1),
1091 intermediates: [][]constraintsSpec{
1092 {
1093 {
1094 ekus: []string{"any"},
1095 },
1096 },
1097 },
1098 leaf: leafSpec{
1099 sans: []string{"dns:example.com"},
1100 ekus: []string{"serverAuth", "other"},
1101 },
1102 },
1103
1104 {
1105 name: "intermediate with enumerated EKUs",
1106 roots: make([]constraintsSpec, 1),
1107 intermediates: [][]constraintsSpec{
1108 {
1109 {
1110 ekus: []string{"email"},
1111 },
1112 },
1113 },
1114 leaf: leafSpec{
1115 sans: []string{"dns:example.com"},
1116 ekus: []string{"serverAuth"},
1117 },
1118 expectedError: "incompatible key usage",
1119 },
1120 {
1121 name: "unknown EKU in leaf",
1122 roots: make([]constraintsSpec, 1),
1123 intermediates: [][]constraintsSpec{
1124 {
1125 {
1126 ekus: []string{"email"},
1127 },
1128 },
1129 },
1130 leaf: leafSpec{
1131 sans: []string{"dns:example.com"},
1132 ekus: []string{"other"},
1133 },
1134 requestedEKUs: []ExtKeyUsage{ExtKeyUsageAny},
1135 },
1136
1137 {
1138 name: "intermediate cannot add EKUs not in root if leaf uses them",
1139 roots: []constraintsSpec{
1140 {
1141 ekus: []string{"serverAuth"},
1142 },
1143 },
1144 intermediates: [][]constraintsSpec{
1145 {
1146 {
1147 ekus: []string{"serverAuth", "email"},
1148 },
1149 },
1150 },
1151 leaf: leafSpec{
1152 sans: []string{"dns:example.com"},
1153 ekus: []string{"serverAuth"},
1154 },
1155 },
1156 {
1157 name: "EKUs in root are effective",
1158 roots: []constraintsSpec{
1159 {
1160 ekus: []string{"email"},
1161 },
1162 },
1163 intermediates: [][]constraintsSpec{
1164 {
1165 {
1166 ekus: []string{"serverAuth"},
1167 },
1168 },
1169 },
1170 leaf: leafSpec{
1171 sans: []string{"dns:example.com"},
1172 ekus: []string{"serverAuth"},
1173 },
1174 expectedError: "incompatible key usage",
1175 },
1176 {
1177 name: "netscapeSGC EKU does not permit server/client auth",
1178 roots: []constraintsSpec{
1179 {},
1180 },
1181 intermediates: [][]constraintsSpec{
1182 {
1183 {
1184 ekus: []string{"netscapeSGC"},
1185 },
1186 },
1187 },
1188 leaf: leafSpec{
1189 sans: []string{"dns:example.com"},
1190 ekus: []string{"serverAuth", "clientAuth"},
1191 },
1192 expectedError: "incompatible key usage",
1193 },
1194 {
1195 name: "msSGC EKU does not permit server/client auth",
1196 roots: make([]constraintsSpec, 1),
1197 intermediates: [][]constraintsSpec{
1198 {
1199 {
1200 ekus: []string{"msSGC"},
1201 },
1202 },
1203 },
1204 leaf: leafSpec{
1205 sans: []string{"dns:example.com"},
1206 ekus: []string{"serverAuth", "clientAuth"},
1207 },
1208 expectedError: "incompatible key usage",
1209 },
1210 {
1211 name: "empty DNS permitted constraint allows anything",
1212 roots: []constraintsSpec{
1213 {
1214 ok: []string{"dns:"},
1215 },
1216 },
1217 intermediates: [][]constraintsSpec{
1218 {
1219 {},
1220 },
1221 },
1222 leaf: leafSpec{
1223 sans: []string{"dns:example.com"},
1224 },
1225 },
1226 {
1227 name: "empty DNS excluded constraint rejects everything",
1228 roots: []constraintsSpec{
1229 {
1230 bad: []string{"dns:"},
1231 },
1232 },
1233 intermediates: [][]constraintsSpec{
1234 {
1235 {},
1236 },
1237 },
1238 leaf: leafSpec{
1239 sans: []string{"dns:example.com"},
1240 },
1241 expectedError: "\"example.com\" is excluded",
1242 },
1243 {
1244 name: "empty email permitted constraint allows anything",
1245 roots: []constraintsSpec{
1246 {
1247 ok: []string{"email:"},
1248 },
1249 },
1250 intermediates: [][]constraintsSpec{
1251 {
1252 {},
1253 },
1254 },
1255 leaf: leafSpec{
1256 sans: []string{"email:foo@example.com"},
1257 },
1258 },
1259 {
1260 name: "empty email excluded constraint rejects everything",
1261 roots: []constraintsSpec{
1262 {
1263 bad: []string{"email:"},
1264 },
1265 },
1266 intermediates: [][]constraintsSpec{
1267 {
1268 {},
1269 },
1270 },
1271 leaf: leafSpec{
1272 sans: []string{"email:foo@example.com"},
1273 },
1274 expectedError: "\"foo@example.com\" is excluded",
1275 },
1276 {
1277 name: "empty URI permitted constraint allows anything",
1278 roots: []constraintsSpec{
1279 {
1280 ok: []string{"uri:"},
1281 },
1282 },
1283 intermediates: [][]constraintsSpec{
1284 {
1285 {},
1286 },
1287 },
1288 leaf: leafSpec{
1289 sans: []string{"uri:https://example.com/test"},
1290 },
1291 },
1292 {
1293 name: "empty URI excluded constraint rejects everything",
1294 roots: []constraintsSpec{
1295 {
1296 bad: []string{"uri:"},
1297 },
1298 },
1299 intermediates: [][]constraintsSpec{
1300 {
1301 {},
1302 },
1303 },
1304 leaf: leafSpec{
1305 sans: []string{"uri:https://example.com/test"},
1306 },
1307 expectedError: "\"https://example.com/test\" is excluded",
1308 },
1309 {
1310 name: "serverAuth EKU does not permit clientAuth",
1311 roots: make([]constraintsSpec, 1),
1312 intermediates: [][]constraintsSpec{
1313 {
1314 {},
1315 },
1316 },
1317 leaf: leafSpec{
1318 sans: []string{"dns:example.com"},
1319 ekus: []string{"serverAuth"},
1320 },
1321 requestedEKUs: []ExtKeyUsage{ExtKeyUsageClientAuth},
1322 expectedError: "incompatible key usage",
1323 },
1324 {
1325 name: "msSGC EKU does not permit serverAuth",
1326 roots: make([]constraintsSpec, 1),
1327 intermediates: [][]constraintsSpec{
1328 {
1329 {},
1330 },
1331 },
1332 leaf: leafSpec{
1333 sans: []string{"dns:example.com"},
1334 ekus: []string{"msSGC"},
1335 },
1336 requestedEKUs: []ExtKeyUsage{ExtKeyUsageServerAuth},
1337 expectedError: "incompatible key usage",
1338 },
1339 {
1340
1341
1342
1343 name: "invalid SANs are ignored with no constraints",
1344 roots: make([]constraintsSpec, 1),
1345 intermediates: [][]constraintsSpec{
1346 {
1347 {},
1348 },
1349 },
1350 leaf: leafSpec{
1351 sans: []string{"dns:this is invalid", "email:this @ is invalid"},
1352 },
1353 },
1354 {
1355 name: "invalid DNS SAN detected with constraints",
1356 roots: []constraintsSpec{
1357 {
1358 bad: []string{"uri:"},
1359 },
1360 },
1361 intermediates: [][]constraintsSpec{
1362 {
1363 {},
1364 },
1365 },
1366 leaf: leafSpec{
1367 sans: []string{"dns:this is invalid"},
1368 },
1369 expectedError: "cannot parse dnsName",
1370 },
1371 {
1372 name: "invalid email SAN detected with constraints",
1373 roots: []constraintsSpec{
1374 {
1375 bad: []string{"uri:"},
1376 },
1377 },
1378 intermediates: [][]constraintsSpec{
1379 {
1380 {},
1381 },
1382 },
1383 leaf: leafSpec{
1384 sans: []string{"email:this @ is invalid"},
1385 },
1386 expectedError: "cannot parse rfc822Name",
1387 },
1388 {
1389 name: "any requested EKU is sufficient",
1390 roots: make([]constraintsSpec, 1),
1391 intermediates: [][]constraintsSpec{
1392 {
1393 {},
1394 },
1395 },
1396 leaf: leafSpec{
1397 sans: []string{"dns:example.com"},
1398 ekus: []string{"email"},
1399 },
1400 requestedEKUs: []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageEmailProtection},
1401 },
1402 {
1403 name: "unrequested EKUs not required to be nested",
1404 roots: make([]constraintsSpec, 1),
1405 intermediates: [][]constraintsSpec{
1406 {
1407 {
1408 ekus: []string{"serverAuth"},
1409 },
1410 },
1411 },
1412 leaf: leafSpec{
1413 sans: []string{"dns:example.com"},
1414
1415
1416 ekus: []string{"email", "serverAuth"},
1417 },
1418 },
1419 {
1420 name: "empty leaf is accepted in constrained chain",
1421 roots: []constraintsSpec{
1422 {
1423 ok: []string{"dns:foo.com", "dns:.foo.com"},
1424 },
1425 },
1426 intermediates: [][]constraintsSpec{
1427 {
1428 {},
1429 },
1430 },
1431 leaf: leafSpec{
1432 sans: []string{},
1433 },
1434 },
1435 {
1436 name: "no SANs and non-hostname CN is accepted in constrained chain",
1437 roots: []constraintsSpec{
1438 {
1439 ok: []string{"dns:foo.com", "dns:.foo.com"},
1440 },
1441 },
1442 intermediates: [][]constraintsSpec{
1443 {
1444 {},
1445 },
1446 },
1447 leaf: leafSpec{
1448 sans: []string{},
1449 cn: "foo.bar",
1450 },
1451 },
1452 {
1453 name: "constraints don't apply to CN",
1454 roots: []constraintsSpec{
1455 {
1456 ok: []string{"dns:foo.com", "dns:.foo.com"},
1457 },
1458 },
1459 intermediates: [][]constraintsSpec{
1460 {
1461 {},
1462 },
1463 },
1464 leaf: leafSpec{
1465 sans: []string{"dns:foo.com"},
1466 cn: "foo.bar",
1467 },
1468 },
1469 {
1470 name: "DNS SAN cannot use leading period form",
1471 roots: []constraintsSpec{{ok: []string{"dns:example.com"}}},
1472 leaf: leafSpec{sans: []string{"dns:.example.com"}},
1473 expectedError: "cannot parse dnsName \".example.com\"",
1474 },
1475 {
1476 name: "URI with IPv6 and zone is rejected",
1477 roots: []constraintsSpec{
1478 {
1479 ok: []string{"uri:example.com"},
1480 },
1481 },
1482 intermediates: [][]constraintsSpec{
1483 {
1484 {},
1485 },
1486 },
1487 leaf: leafSpec{
1488 sans: []string{"uri:http://[2006:abcd::1%25.example.com]:16/"},
1489 },
1490 expectedError: "URI with IP",
1491 },
1492 {
1493 name: "intermediate can narrow permitted dns scope",
1494 roots: []constraintsSpec{
1495 {
1496 ok: []string{"dns:"},
1497 },
1498 },
1499 intermediates: [][]constraintsSpec{
1500 {
1501 {
1502 ok: []string{"dns:example.com"},
1503 },
1504 },
1505 },
1506 leaf: leafSpec{
1507 sans: []string{"dns:test.com"},
1508 },
1509 expectedError: "\"test.com\" is not permitted",
1510 },
1511 {
1512 name: "intermediate cannot narrow excluded dns scope",
1513 roots: []constraintsSpec{
1514 {
1515 bad: []string{"dns:"},
1516 },
1517 },
1518 intermediates: [][]constraintsSpec{
1519 {
1520 {
1521 bad: []string{"dns:example.com"},
1522 },
1523 },
1524 },
1525 leaf: leafSpec{
1526 sans: []string{"dns:test.com"},
1527 },
1528 expectedError: "\"test.com\" is excluded by constraint \"\"",
1529 },
1530 {
1531 name: "intermediate can narrow excluded dns scope",
1532 roots: []constraintsSpec{
1533 {
1534 bad: []string{"dns:example.com"},
1535 },
1536 },
1537 intermediates: [][]constraintsSpec{
1538 {
1539 {
1540 bad: []string{"dns:"},
1541 },
1542 },
1543 },
1544 leaf: leafSpec{
1545 sans: []string{"dns:test.com"},
1546 },
1547 expectedError: "\"test.com\" is excluded by constraint \"\"",
1548 },
1549 {
1550 name: "permitted dns constraint is not a prefix match",
1551 roots: []constraintsSpec{
1552 {
1553 ok: []string{"dns:example.com"},
1554 },
1555 },
1556 intermediates: [][]constraintsSpec{
1557 {
1558 {},
1559 },
1560 },
1561 leaf: leafSpec{
1562 sans: []string{"dns:testexample.com"},
1563 },
1564 expectedError: "\"testexample.com\" is not permitted",
1565 },
1566 {
1567 name: "subdomain constraint does not allow wildcard",
1568 roots: []constraintsSpec{
1569 {
1570 ok: []string{"dns:a.com", "dns:foo.example.com", "dns:z.com"},
1571 },
1572 },
1573 intermediates: [][]constraintsSpec{
1574 {
1575 {},
1576 },
1577 },
1578 leaf: leafSpec{
1579 sans: []string{"dns:*.example.com"},
1580 },
1581 expectedError: "\"*.example.com\" is not permitted",
1582 },
1583 {
1584 name: "excluded dns constraint is not a prefix match",
1585 roots: []constraintsSpec{
1586 {
1587 bad: []string{"dns:example.com"},
1588 },
1589 },
1590 intermediates: [][]constraintsSpec{
1591 {
1592 {},
1593 },
1594 },
1595 leaf: leafSpec{
1596 sans: []string{"dns:testexample.com"},
1597 },
1598 },
1599 {
1600 name: "excluded email constraint, multiple email with matching local portion",
1601 roots: []constraintsSpec{
1602 {
1603 bad: []string{"email:a@example.com", "email:a@test.com"},
1604 },
1605 },
1606 intermediates: [][]constraintsSpec{
1607 {
1608 {},
1609 },
1610 },
1611 leaf: leafSpec{
1612 sans: []string{"email:a@example.com"},
1613 },
1614 expectedError: "\"a@example.com\" is excluded by constraint \"a@example.com\"",
1615 },
1616 {
1617 name: "email_case_check",
1618 roots: []constraintsSpec{
1619 {
1620 ok: []string{"email:a@example.com"},
1621 },
1622 },
1623 intermediates: [][]constraintsSpec{
1624 {
1625 {},
1626 },
1627 },
1628 leaf: leafSpec{
1629 sans: []string{"email:a@ExAmple.com"},
1630 },
1631 },
1632 {
1633 name: "excluded constraint, empty DNS san",
1634 roots: []constraintsSpec{
1635 {
1636 bad: []string{"dns:example.com"},
1637 },
1638 },
1639 leaf: leafSpec{
1640 sans: []string{"dns:"},
1641 },
1642 },
1643
1644 {
1645 name: "subdomain excluded constraints preclude outer wildcard names",
1646 roots: []constraintsSpec{
1647 {
1648 bad: []string{"dns:foo.example.com"},
1649 },
1650 },
1651 intermediates: [][]constraintsSpec{
1652 {
1653 {},
1654 },
1655 },
1656 leaf: leafSpec{
1657 sans: []string{"dns:*.example.com"},
1658 },
1659 expectedError: "\"*.example.com\" is excluded by constraint \"foo.example.com\"",
1660 },
1661 {
1662 name: "subdomain excluded constraints do not preclude far outer wildcard names",
1663 roots: []constraintsSpec{
1664 {
1665 bad: []string{"dns:foo.example.com"},
1666 },
1667 },
1668 intermediates: [][]constraintsSpec{
1669 {
1670 {},
1671 },
1672 },
1673 leaf: leafSpec{
1674 sans: []string{"dns:*.com"},
1675 },
1676 },
1677 {
1678 name: "subdomain excluded constraints preclude inner wildcard names",
1679 roots: []constraintsSpec{
1680 {
1681 bad: []string{"dns:foo.example.com"},
1682 },
1683 },
1684 intermediates: [][]constraintsSpec{
1685 {
1686 {},
1687 },
1688 },
1689 leaf: leafSpec{
1690 sans: []string{"dns:*.foo.example.com"},
1691 },
1692 expectedError: "\"*.foo.example.com\" is excluded by constraint \"foo.example.com\"",
1693 },
1694 {
1695 name: "subdomain excluded constraints preclude far inner wildcard names",
1696 roots: []constraintsSpec{
1697 {
1698 bad: []string{"dns:foo.example.com"},
1699 },
1700 },
1701 intermediates: [][]constraintsSpec{
1702 {
1703 {},
1704 },
1705 },
1706 leaf: leafSpec{
1707 sans: []string{"dns:*.bar.foo.example.com"},
1708 },
1709 expectedError: "\"*.bar.foo.example.com\" is excluded by constraint \"foo.example.com\"",
1710 },
1711 {
1712 name: "outer wildcard names are not matched by subdomain permitted constraints",
1713 roots: []constraintsSpec{
1714 {
1715 ok: []string{"dns:foo.example.com"},
1716 },
1717 },
1718 intermediates: [][]constraintsSpec{
1719 {
1720 {},
1721 },
1722 },
1723 leaf: leafSpec{
1724 sans: []string{"dns:*.example.com"},
1725 },
1726 expectedError: "\"*.example.com\" is not permitted",
1727 },
1728 {
1729 name: "far outer wildcard names are not matched by subdomain permitted constraints",
1730 roots: []constraintsSpec{
1731 {
1732 ok: []string{"dns:foo.example.com"},
1733 },
1734 },
1735 intermediates: [][]constraintsSpec{
1736 {
1737 {},
1738 },
1739 },
1740 leaf: leafSpec{
1741 sans: []string{"dns:*.com"},
1742 },
1743 expectedError: "\"*.com\" is not permitted",
1744 },
1745 {
1746 name: "inner wildcard names are matched by subdomain permitted constraints",
1747 roots: []constraintsSpec{
1748 {
1749 ok: []string{"dns:foo.example.com"},
1750 },
1751 },
1752 intermediates: [][]constraintsSpec{
1753 {
1754 {},
1755 },
1756 },
1757 leaf: leafSpec{
1758 sans: []string{"dns:*.foo.example.com"},
1759 },
1760 },
1761 {
1762 name: "far inner wildcard names are matched by subdomain permitted constraints",
1763 roots: []constraintsSpec{
1764 {
1765 ok: []string{"dns:foo.example.com"},
1766 },
1767 },
1768 intermediates: [][]constraintsSpec{
1769 {
1770 {},
1771 },
1772 },
1773 leaf: leafSpec{
1774 sans: []string{"dns:*.bar.foo.example.com"},
1775 },
1776 },
1777
1778 {
1779 name: "cross include should not match",
1780 roots: []constraintsSpec{
1781 {
1782 ok: []string{"dns:foo.example.com"},
1783 },
1784 },
1785 intermediates: [][]constraintsSpec{
1786 {
1787 {},
1788 },
1789 },
1790 leaf: leafSpec{
1791 sans: []string{"dns:*.bar.example.com"},
1792 },
1793 expectedError: "\"*.bar.example.com\" is not permitted by any constraint",
1794 },
1795 {
1796 name: "cross exclude should not match",
1797 roots: []constraintsSpec{
1798 {
1799 bad: []string{"dns:foo.example.com"},
1800 },
1801 },
1802 intermediates: [][]constraintsSpec{
1803 {
1804 {},
1805 },
1806 },
1807 leaf: leafSpec{
1808 sans: []string{"dns:*.bar.example.com"},
1809 },
1810 },
1811 {
1812 name: "subdomain exclusion blocks uppercase wildcard",
1813 roots: []constraintsSpec{{
1814 bad: []string{"dns:sub.example.com"},
1815 }},
1816 intermediates: [][]constraintsSpec{{{}}},
1817 leaf: leafSpec{
1818 sans: []string{"dns:*.EXAMPLE.COM"},
1819 },
1820 expectedError: "\"*.EXAMPLE.COM\" is excluded by constraint \"sub.example.com\"",
1821 },
1822 {
1823 name: "uppercase subdomain exclusion blocks lowercase wildcard",
1824 roots: []constraintsSpec{{
1825 bad: []string{"dns:SUB.EXAMPLE.COM"},
1826 }},
1827 intermediates: [][]constraintsSpec{{{}}},
1828 leaf: leafSpec{
1829 sans: []string{"dns:*.example.com"},
1830 },
1831 expectedError: "\"*.example.com\" is excluded by constraint \"sub.example.com\"",
1832 },
1833 }
1834
1835 func makeConstraintsCACert(constraints constraintsSpec, name string, key *ecdsa.PrivateKey, parent *Certificate, parentKey *ecdsa.PrivateKey) (*Certificate, error) {
1836 var serialBytes [16]byte
1837 rand.Read(serialBytes[:])
1838
1839 template := &Certificate{
1840 SerialNumber: new(big.Int).SetBytes(serialBytes[:]),
1841 Subject: pkix.Name{
1842 CommonName: name,
1843 },
1844 NotBefore: time.Unix(1000, 0),
1845 NotAfter: time.Unix(2000, 0),
1846 KeyUsage: KeyUsageCertSign,
1847 BasicConstraintsValid: true,
1848 IsCA: true,
1849 }
1850
1851 if err := addConstraintsToTemplate(constraints, template); err != nil {
1852 return nil, err
1853 }
1854
1855 if parent == nil {
1856 parent = template
1857 }
1858 derBytes, err := CreateCertificate(rand.Reader, template, parent, &key.PublicKey, parentKey)
1859 if err != nil {
1860 return nil, err
1861 }
1862
1863 caCert, err := ParseCertificate(derBytes)
1864 if err != nil {
1865 return nil, err
1866 }
1867
1868 return caCert, nil
1869 }
1870
1871 func makeConstraintsLeafCert(leaf leafSpec, key *ecdsa.PrivateKey, parent *Certificate, parentKey *ecdsa.PrivateKey) (*Certificate, error) {
1872 var serialBytes [16]byte
1873 rand.Read(serialBytes[:])
1874
1875 template := &Certificate{
1876 SerialNumber: new(big.Int).SetBytes(serialBytes[:]),
1877 Subject: pkix.Name{
1878 OrganizationalUnit: []string{"Leaf"},
1879 CommonName: leaf.cn,
1880 },
1881 NotBefore: time.Unix(1000, 0),
1882 NotAfter: time.Unix(2000, 0),
1883 KeyUsage: KeyUsageDigitalSignature,
1884 BasicConstraintsValid: true,
1885 IsCA: false,
1886 }
1887
1888 for _, name := range leaf.sans {
1889 switch {
1890 case strings.HasPrefix(name, "dns:"):
1891 template.DNSNames = append(template.DNSNames, name[4:])
1892
1893 case strings.HasPrefix(name, "ip:"):
1894 ip := net.ParseIP(name[3:])
1895 if ip == nil {
1896 return nil, fmt.Errorf("cannot parse IP %q", name[3:])
1897 }
1898 template.IPAddresses = append(template.IPAddresses, ip)
1899
1900 case strings.HasPrefix(name, "invalidip:"):
1901 ipBytes, err := hex.DecodeString(name[10:])
1902 if err != nil {
1903 return nil, fmt.Errorf("cannot parse invalid IP: %s", err)
1904 }
1905 template.IPAddresses = append(template.IPAddresses, net.IP(ipBytes))
1906
1907 case strings.HasPrefix(name, "email:"):
1908 template.EmailAddresses = append(template.EmailAddresses, name[6:])
1909
1910 case strings.HasPrefix(name, "uri:"):
1911 uri, err := url.Parse(name[4:])
1912 if err != nil {
1913 return nil, fmt.Errorf("cannot parse URI %q: %s", name[4:], err)
1914 }
1915 template.URIs = append(template.URIs, uri)
1916
1917 case strings.HasPrefix(name, "unknown:"):
1918
1919
1920
1921 if len(leaf.sans) != 1 {
1922 panic("when using unknown name types, it must be the sole name")
1923 }
1924
1925 template.ExtraExtensions = append(template.ExtraExtensions, pkix.Extension{
1926 Id: []int{2, 5, 29, 17},
1927 Value: []byte{
1928 0x30,
1929 3,
1930 9,
1931 1,
1932 1,
1933 },
1934 })
1935
1936 default:
1937 return nil, fmt.Errorf("unknown name type %q", name)
1938 }
1939 }
1940
1941 var err error
1942 if template.ExtKeyUsage, template.UnknownExtKeyUsage, err = parseEKUs(leaf.ekus); err != nil {
1943 return nil, err
1944 }
1945
1946 if parent == nil {
1947 parent = template
1948 }
1949
1950 derBytes, err := CreateCertificate(rand.Reader, template, parent, &key.PublicKey, parentKey)
1951 if err != nil {
1952 return nil, err
1953 }
1954
1955 return ParseCertificate(derBytes)
1956 }
1957
1958 func customConstraintsExtension(typeNum int, constraint []byte, isExcluded bool) pkix.Extension {
1959 appendConstraint := func(contents []byte, tag uint8) []byte {
1960 contents = append(contents, tag|32 |0x80 )
1961 contents = append(contents, byte(4+len(constraint)) )
1962 contents = append(contents, 0x30 )
1963 contents = append(contents, byte(2+len(constraint)) )
1964 contents = append(contents, byte(typeNum) )
1965 contents = append(contents, byte(len(constraint)))
1966 return append(contents, constraint...)
1967 }
1968
1969 var contents []byte
1970 if !isExcluded {
1971 contents = appendConstraint(contents, 0 )
1972 } else {
1973 contents = appendConstraint(contents, 1 )
1974 }
1975
1976 var value []byte
1977 value = append(value, 0x30 )
1978 value = append(value, byte(len(contents)))
1979 value = append(value, contents...)
1980
1981 return pkix.Extension{
1982 Id: []int{2, 5, 29, 30},
1983 Value: value,
1984 }
1985 }
1986
1987 func addConstraintsToTemplate(constraints constraintsSpec, template *Certificate) error {
1988 parse := func(constraints []string) (dnsNames []string, ips []*net.IPNet, emailAddrs []string, uriDomains []string, err error) {
1989 for _, constraint := range constraints {
1990 switch {
1991 case strings.HasPrefix(constraint, "dns:"):
1992 dnsNames = append(dnsNames, constraint[4:])
1993
1994 case strings.HasPrefix(constraint, "ip:"):
1995 _, ipNet, err := net.ParseCIDR(constraint[3:])
1996 if err != nil {
1997 return nil, nil, nil, nil, err
1998 }
1999 ips = append(ips, ipNet)
2000
2001 case strings.HasPrefix(constraint, "email:"):
2002 emailAddrs = append(emailAddrs, constraint[6:])
2003
2004 case strings.HasPrefix(constraint, "uri:"):
2005 uriDomains = append(uriDomains, constraint[4:])
2006
2007 default:
2008 return nil, nil, nil, nil, fmt.Errorf("unknown constraint %q", constraint)
2009 }
2010 }
2011
2012 return dnsNames, ips, emailAddrs, uriDomains, err
2013 }
2014
2015 handleSpecialConstraint := func(constraint string, isExcluded bool) bool {
2016 switch {
2017 case constraint == "unknown:":
2018 template.ExtraExtensions = append(template.ExtraExtensions, customConstraintsExtension(9 , []byte{1}, isExcluded))
2019
2020 default:
2021 return false
2022 }
2023
2024 return true
2025 }
2026
2027 if len(constraints.ok) == 1 && len(constraints.bad) == 0 {
2028 if handleSpecialConstraint(constraints.ok[0], false) {
2029 return nil
2030 }
2031 }
2032
2033 if len(constraints.bad) == 1 && len(constraints.ok) == 0 {
2034 if handleSpecialConstraint(constraints.bad[0], true) {
2035 return nil
2036 }
2037 }
2038
2039 var err error
2040 template.PermittedDNSDomains, template.PermittedIPRanges, template.PermittedEmailAddresses, template.PermittedURIDomains, err = parse(constraints.ok)
2041 if err != nil {
2042 return err
2043 }
2044
2045 template.ExcludedDNSDomains, template.ExcludedIPRanges, template.ExcludedEmailAddresses, template.ExcludedURIDomains, err = parse(constraints.bad)
2046 if err != nil {
2047 return err
2048 }
2049
2050 if template.ExtKeyUsage, template.UnknownExtKeyUsage, err = parseEKUs(constraints.ekus); err != nil {
2051 return err
2052 }
2053
2054 return nil
2055 }
2056
2057 func parseEKUs(ekuStrs []string) (ekus []ExtKeyUsage, unknowns []asn1.ObjectIdentifier, err error) {
2058 for _, s := range ekuStrs {
2059 switch s {
2060 case "serverAuth":
2061 ekus = append(ekus, ExtKeyUsageServerAuth)
2062 case "clientAuth":
2063 ekus = append(ekus, ExtKeyUsageClientAuth)
2064 case "email":
2065 ekus = append(ekus, ExtKeyUsageEmailProtection)
2066 case "netscapeSGC":
2067 ekus = append(ekus, ExtKeyUsageNetscapeServerGatedCrypto)
2068 case "msSGC":
2069 ekus = append(ekus, ExtKeyUsageMicrosoftServerGatedCrypto)
2070 case "any":
2071 ekus = append(ekus, ExtKeyUsageAny)
2072 case "other":
2073 unknowns = append(unknowns, asn1.ObjectIdentifier{2, 4, 1, 2, 3})
2074 default:
2075 return nil, nil, fmt.Errorf("unknown EKU %q", s)
2076 }
2077 }
2078
2079 return
2080 }
2081
2082 func TestConstraintCases(t *testing.T) {
2083 privateKeys := sync.Pool{
2084 New: func() any {
2085 priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
2086 if err != nil {
2087 panic(err)
2088 }
2089 return priv
2090 },
2091 }
2092
2093 for i, test := range nameConstraintsTests {
2094 t.Run(test.name, func(t *testing.T) {
2095 rootPool := NewCertPool()
2096 rootKey := privateKeys.Get().(*ecdsa.PrivateKey)
2097 rootName := "Root " + strconv.Itoa(i)
2098
2099
2100
2101 keys := []*ecdsa.PrivateKey{rootKey}
2102
2103
2104
2105
2106
2107
2108
2109 var parent *Certificate
2110 parentKey := rootKey
2111
2112 for _, root := range test.roots {
2113 rootCert, err := makeConstraintsCACert(root, rootName, rootKey, nil, rootKey)
2114 if err != nil {
2115 t.Fatalf("failed to create root: %s", err)
2116 }
2117
2118 parent = rootCert
2119 rootPool.AddCert(rootCert)
2120 }
2121
2122 intermediatePool := NewCertPool()
2123
2124 for level, intermediates := range test.intermediates {
2125 levelKey := privateKeys.Get().(*ecdsa.PrivateKey)
2126 keys = append(keys, levelKey)
2127 levelName := "Intermediate level " + strconv.Itoa(level)
2128 var last *Certificate
2129
2130 for _, intermediate := range intermediates {
2131 caCert, err := makeConstraintsCACert(intermediate, levelName, levelKey, parent, parentKey)
2132 if err != nil {
2133 t.Fatalf("failed to create %q: %s", levelName, err)
2134 }
2135
2136 last = caCert
2137 intermediatePool.AddCert(caCert)
2138 }
2139
2140 parent = last
2141 parentKey = levelKey
2142 }
2143
2144 leafKey := privateKeys.Get().(*ecdsa.PrivateKey)
2145 keys = append(keys, leafKey)
2146
2147 leafCert, err := makeConstraintsLeafCert(test.leaf, leafKey, parent, parentKey)
2148 if err != nil {
2149 t.Fatalf("cannot create leaf: %s", err)
2150 }
2151
2152
2153
2154 if !test.noOpenSSL && testNameConstraintsAgainstOpenSSL && test.leaf.cn == "" {
2155 output, err := testChainAgainstOpenSSL(t, leafCert, intermediatePool, rootPool)
2156 if err == nil && len(test.expectedError) > 0 {
2157 t.Error("unexpectedly succeeded against OpenSSL")
2158 if debugOpenSSLFailure {
2159 return
2160 }
2161 }
2162
2163 if err != nil {
2164 if _, ok := err.(*exec.ExitError); !ok {
2165 t.Errorf("OpenSSL failed to run: %s", err)
2166 } else if len(test.expectedError) == 0 {
2167 t.Errorf("OpenSSL unexpectedly failed: %v", output)
2168 if debugOpenSSLFailure {
2169 return
2170 }
2171 }
2172 }
2173 }
2174
2175 verifyOpts := VerifyOptions{
2176 Roots: rootPool,
2177 Intermediates: intermediatePool,
2178 CurrentTime: time.Unix(1500, 0),
2179 KeyUsages: test.requestedEKUs,
2180 }
2181 _, err = leafCert.Verify(verifyOpts)
2182
2183 logInfo := false
2184 if len(test.expectedError) == 0 {
2185 if err != nil {
2186 t.Errorf("unexpected failure: %s", err)
2187 } else {
2188 logInfo = false
2189 }
2190 } else {
2191 if err == nil {
2192 t.Error("unexpected success")
2193 } else if !strings.Contains(err.Error(), test.expectedError) {
2194 t.Errorf("expected error containing %q, but got: %s", test.expectedError, err)
2195 } else {
2196 logInfo = false
2197 }
2198 }
2199
2200 if logInfo {
2201 certAsPEM := func(cert *Certificate) string {
2202 var buf bytes.Buffer
2203 pem.Encode(&buf, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
2204 return buf.String()
2205 }
2206 t.Errorf("root:\n%s", certAsPEM(rootPool.mustCert(t, 0)))
2207 if intermediates := allCerts(t, intermediatePool); len(intermediates) > 0 {
2208 for ii, intermediate := range intermediates {
2209 t.Errorf("intermediate %d:\n%s", ii, certAsPEM(intermediate))
2210 }
2211 }
2212 t.Errorf("leaf:\n%s", certAsPEM(leafCert))
2213 }
2214
2215 for _, key := range keys {
2216 privateKeys.Put(key)
2217 }
2218 })
2219 }
2220 }
2221
2222 func TestNameConstraintIPNonZeroHostBits(t *testing.T) {
2223 rootKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
2224 if err != nil {
2225 t.Fatal(err)
2226 }
2227 leafKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
2228 if err != nil {
2229 t.Fatal(err)
2230 }
2231
2232
2233
2234
2235 subtree := func(b ...byte) []byte {
2236 gn := append([]byte{0x87, byte(len(b))}, b...)
2237 return append([]byte{0x30, byte(len(gn))}, gn...)
2238 }
2239 var subtrees []byte
2240 subtrees = append(subtrees, subtree(10, 0, 0, 0, 255, 255, 255, 252)...)
2241 subtrees = append(subtrees, subtree(10, 10, 10, 10, 255, 255, 0, 0)...)
2242 excluded := append([]byte{0xa1, byte(len(subtrees))}, subtrees...)
2243 ncValue := append([]byte{0x30, byte(len(excluded))}, excluded...)
2244
2245 var serial [16]byte
2246 rand.Read(serial[:])
2247 rootTmpl := &Certificate{
2248 SerialNumber: new(big.Int).SetBytes(serial[:]),
2249 Subject: pkix.Name{CommonName: "Root"},
2250 NotBefore: time.Unix(1000, 0),
2251 NotAfter: time.Unix(2000, 0),
2252 KeyUsage: KeyUsageCertSign,
2253 BasicConstraintsValid: true,
2254 IsCA: true,
2255 ExtraExtensions: []pkix.Extension{
2256 {Id: []int{2, 5, 29, 30}, Critical: true, Value: ncValue},
2257 },
2258 }
2259 rootDER, err := CreateCertificate(rand.Reader, rootTmpl, rootTmpl, &rootKey.PublicKey, rootKey)
2260 if err != nil {
2261 t.Fatal(err)
2262 }
2263 root, err := ParseCertificate(rootDER)
2264 if err != nil {
2265 t.Fatal(err)
2266 }
2267
2268
2269 if len(root.ExcludedIPRanges) != 2 {
2270 t.Fatalf("got %d excluded IP ranges, want 2", len(root.ExcludedIPRanges))
2271 }
2272 if got := root.ExcludedIPRanges[1].IP; !got.Equal(net.IP{10, 10, 10, 10}) {
2273 t.Errorf("excluded range IP = %v, want 10.10.10.10", got)
2274 }
2275
2276 leaf, err := makeConstraintsLeafCert(leafSpec{sans: []string{"ip:10.10.0.1"}}, leafKey, root, rootKey)
2277 if err != nil {
2278 t.Fatal(err)
2279 }
2280
2281 roots := NewCertPool()
2282 roots.AddCert(root)
2283 if _, err := leaf.Verify(VerifyOptions{Roots: roots, CurrentTime: time.Unix(1500, 0)}); err == nil {
2284 t.Error("leaf with IP SAN inside excluded range was accepted")
2285 } else if !strings.Contains(err.Error(), "excluded by constraint") {
2286 t.Errorf("got error %q, want excluded-by-constraint", err)
2287 }
2288 }
2289
2290 func writePEMsToTempFile(certs []*Certificate) *os.File {
2291 file, err := os.CreateTemp("", "name_constraints_test")
2292 if err != nil {
2293 panic("cannot create tempfile")
2294 }
2295
2296 pemBlock := &pem.Block{Type: "CERTIFICATE"}
2297 for _, cert := range certs {
2298 pemBlock.Bytes = cert.Raw
2299 pem.Encode(file, pemBlock)
2300 }
2301
2302 return file
2303 }
2304
2305 func testChainAgainstOpenSSL(t *testing.T, leaf *Certificate, intermediates, roots *CertPool) (string, error) {
2306 args := []string{"verify", "-no_check_time"}
2307
2308 rootsFile := writePEMsToTempFile(allCerts(t, roots))
2309 if debugOpenSSLFailure {
2310 println("roots file:", rootsFile.Name())
2311 } else {
2312 defer os.Remove(rootsFile.Name())
2313 }
2314 args = append(args, "-CAfile", rootsFile.Name())
2315
2316 if intermediates.len() > 0 {
2317 intermediatesFile := writePEMsToTempFile(allCerts(t, intermediates))
2318 if debugOpenSSLFailure {
2319 println("intermediates file:", intermediatesFile.Name())
2320 } else {
2321 defer os.Remove(intermediatesFile.Name())
2322 }
2323 args = append(args, "-untrusted", intermediatesFile.Name())
2324 }
2325
2326 leafFile := writePEMsToTempFile([]*Certificate{leaf})
2327 if debugOpenSSLFailure {
2328 println("leaf file:", leafFile.Name())
2329 } else {
2330 defer os.Remove(leafFile.Name())
2331 }
2332 args = append(args, leafFile.Name())
2333
2334 cmd := testenv.Command(t, "openssl", args...)
2335 out, err := cmd.CombinedOutput()
2336 return string(out), err
2337 }
2338
2339 var rfc2821Tests = []struct {
2340 in string
2341 localPart, domain string
2342 }{
2343 {"foo@example.com", "foo", "example.com"},
2344 {"@example.com", "", ""},
2345 {"\"@example.com", "", ""},
2346 {"\"\"@example.com", "", "example.com"},
2347 {"\"a\"@example.com", "a", "example.com"},
2348 {"\"\\a\"@example.com", "a", "example.com"},
2349 {"a\"@example.com", "", ""},
2350 {"foo..bar@example.com", "", ""},
2351 {".foo.bar@example.com", "", ""},
2352 {"foo.bar.@example.com", "", ""},
2353 {"|{}?'@example.com", "|{}?'", "example.com"},
2354 {"a@b@c.com", "", ""},
2355
2356
2357 {"Abc\\@def@example.com", "Abc@def", "example.com"},
2358 {"Fred\\ Bloggs@example.com", "Fred Bloggs", "example.com"},
2359 {"Joe.\\\\Blow@example.com", "Joe.\\Blow", "example.com"},
2360 {"\"Abc@def\"@example.com", "Abc@def", "example.com"},
2361 {"\"Fred Bloggs\"@example.com", "Fred Bloggs", "example.com"},
2362 {"customer/department=shipping@example.com", "customer/department=shipping", "example.com"},
2363 {"$A12345@example.com", "$A12345", "example.com"},
2364 {"!def!xyz%abc@example.com", "!def!xyz%abc", "example.com"},
2365 {"_somename@example.com", "_somename", "example.com"},
2366 }
2367
2368 func TestRFC2821Parsing(t *testing.T) {
2369 for i, test := range rfc2821Tests {
2370 mailbox, ok := parseRFC2821Mailbox(test.in)
2371 expectedFailure := len(test.localPart) == 0 && len(test.domain) == 0
2372
2373 if ok && expectedFailure {
2374 t.Errorf("#%d: %q unexpectedly parsed as (%q, %q)", i, test.in, mailbox.local, mailbox.domain)
2375 continue
2376 }
2377
2378 if !ok && !expectedFailure {
2379 t.Errorf("#%d: unexpected failure for %q", i, test.in)
2380 continue
2381 }
2382
2383 if !ok {
2384 continue
2385 }
2386
2387 if mailbox.local != test.localPart || mailbox.domain != test.domain {
2388 t.Errorf("#%d: %q parsed as (%q, %q), but wanted (%q, %q)", i, test.in, mailbox.local, mailbox.domain, test.localPart, test.domain)
2389 }
2390 }
2391 }
2392
2393 func TestBadNamesInConstraints(t *testing.T) {
2394 constraintParseError := func(err error) bool {
2395 str := err.Error()
2396 return strings.Contains(str, "failed to parse ") && strings.Contains(str, "constraint")
2397 }
2398
2399 encodingError := func(err error) bool {
2400 return strings.Contains(err.Error(), "cannot be encoded as an IA5String")
2401 }
2402
2403
2404 badNames := []struct {
2405 name string
2406 matcher func(error) bool
2407 }{
2408 {"dns:foo.com.", constraintParseError},
2409 {"email:abc@foo.com.", constraintParseError},
2410 {"email:foo.com.", constraintParseError},
2411 {"uri:example.com.", constraintParseError},
2412 {"uri:1.2.3.4", constraintParseError},
2413 {"uri:ffff::1", constraintParseError},
2414 {"dns:not–hyphen.com", encodingError},
2415 {"email:foo@not–hyphen.com", encodingError},
2416 {"uri:not–hyphen.com", encodingError},
2417 }
2418
2419 priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
2420 if err != nil {
2421 panic(err)
2422 }
2423
2424 for _, test := range badNames {
2425 _, err := makeConstraintsCACert(constraintsSpec{
2426 ok: []string{test.name},
2427 }, "TestAbsoluteNamesInConstraints", priv, nil, priv)
2428
2429 if err == nil {
2430 t.Errorf("bad name %q unexpectedly accepted in name constraint", test.name)
2431 continue
2432 } else {
2433 if !test.matcher(err) {
2434 t.Errorf("bad name %q triggered unrecognised error: %s", test.name, err)
2435 }
2436 }
2437 }
2438 }
2439
2440 func TestBadNamesInSANs(t *testing.T) {
2441
2442
2443
2444 badNames := []string{
2445 "uri:https://example.com./dsf",
2446 "invalidip:0102",
2447 "invalidip:0102030405",
2448 }
2449
2450 priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
2451 if err != nil {
2452 panic(err)
2453 }
2454
2455 for _, badName := range badNames {
2456 _, err := makeConstraintsLeafCert(leafSpec{sans: []string{badName}}, priv, nil, priv)
2457
2458 if err == nil {
2459 t.Errorf("bad name %q unexpectedly accepted in SAN", badName)
2460 continue
2461 }
2462
2463 if str := err.Error(); !strings.Contains(str, "cannot parse ") {
2464 t.Errorf("bad name %q triggered unrecognised error: %s", badName, str)
2465 }
2466 }
2467 }
2468
View as plain text