From ed4deb157eb044bab58e23928830a26faf438958 Mon Sep 17 00:00:00 2001 From: Roland Shoemaker Date: Thu, 20 Nov 2025 13:32:48 -0800 Subject: [PATCH] crypto/x509: cleanup name constraint tests Make TestConstraintCases a bit clearer by adding actual subtest names, mostly taken from the old comments. Also add a handful of extra test cases. Change-Id: Ie759d1ea85a353aeacab267bb6e175a90f20702c Reviewed-on: https://go-review.googlesource.com/c/go/+/722481 Reviewed-by: Daniel McCarney Auto-Submit: Roland Shoemaker Reviewed-by: Nicholas Husin LUCI-TryBot-Result: Go LUCI Reviewed-by: Nicholas Husin --- src/crypto/x509/name_constraints_test.go | 417 +++++++++++------------ 1 file changed, 202 insertions(+), 215 deletions(-) diff --git a/src/crypto/x509/name_constraints_test.go b/src/crypto/x509/name_constraints_test.go index a585184516..32b6823c4c 100644 --- a/src/crypto/x509/name_constraints_test.go +++ b/src/crypto/x509/name_constraints_test.go @@ -39,6 +39,7 @@ const ( ) type nameConstraintsTest struct { + name string roots []constraintsSpec intermediates [][]constraintsSpec leaf leafSpec @@ -61,17 +62,15 @@ type leafSpec struct { } var nameConstraintsTests = []nameConstraintsTest{ - // #0: dummy test for the certificate generation process itself. { + name: "certificate generation process", roots: make([]constraintsSpec, 1), leaf: leafSpec{ sans: []string{"dns:example.com"}, }, }, - - // #1: dummy test for the certificate generation process itself: single - // level of intermediate. { + name: "single level of intermediate", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -82,10 +81,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:example.com"}, }, }, - - // #2: dummy test for the certificate generation process itself: two - // levels of intermediates. { + name: "two levels of intermediates", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -99,9 +96,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:example.com"}, }, }, - - // #3: matching DNS constraint in root { + name: "matching DNS constraint in root", roots: []constraintsSpec{ { ok: []string{"dns:example.com"}, @@ -116,9 +112,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:example.com"}, }, }, - - // #4: matching DNS constraint in intermediate. { + name: "matching DNS constraint in intermediate", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -131,9 +126,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:example.com"}, }, }, - - // #5: .example.com only matches subdomains. { + name: "leading period only matches subdomains", roots: []constraintsSpec{ { ok: []string{"dns:.example.com"}, @@ -149,9 +143,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"example.com\" is not permitted", }, - - // #6: .example.com matches subdomains. { + name: "leading period matches subdomains", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -164,9 +157,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:foo.example.com"}, }, }, - - // #7: .example.com matches multiple levels of subdomains { + name: "leading period matches multiple levels of subdomains", roots: []constraintsSpec{ { ok: []string{"dns:.example.com"}, @@ -181,10 +173,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:foo.bar.example.com"}, }, }, - - // #8: specifying a permitted list of names does not exclude other name - // types { + name: "specifying a permitted list of names does not exclude other name types", roots: []constraintsSpec{ { ok: []string{"dns:.example.com"}, @@ -199,10 +189,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"ip:10.1.1.1"}, }, }, - - // #9: specifying a permitted list of names does not exclude other name - // types { + name: "specifying a permitted list of names does not exclude other name types", roots: []constraintsSpec{ { ok: []string{"ip:10.0.0.0/8"}, @@ -217,11 +205,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:example.com"}, }, }, - - // #10: intermediates can try to permit other names, which isn't - // forbidden if the leaf doesn't mention them. I.e. name constraints - // apply to names, not constraints themselves. { + name: "intermediates can try to permit other names, which isn't forbidden if the leaf doesn't mention them", roots: []constraintsSpec{ { ok: []string{"dns:example.com"}, @@ -238,10 +223,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:example.com"}, }, }, - - // #11: intermediates cannot add permitted names that the root doesn't - // grant them. { + name: "intermediates cannot add permitted names that the root doesn't grant them", roots: []constraintsSpec{ { ok: []string{"dns:example.com"}, @@ -250,7 +233,7 @@ var nameConstraintsTests = []nameConstraintsTest{ intermediates: [][]constraintsSpec{ { { - ok: []string{"dns:example.com", "dns:foo.com"}, + ok: []string{"dns:foo.example.com", "dns:foo.com"}, }, }, }, @@ -259,9 +242,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"foo.com\" is not permitted", }, - - // #12: intermediates can further limit their scope if they wish. { + name: "intermediates can further limit their scope if they wish", roots: []constraintsSpec{ { ok: []string{"dns:.example.com"}, @@ -278,10 +260,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:foo.bar.example.com"}, }, }, - - // #13: intermediates can further limit their scope and that limitation - // is effective { + name: "intermediates can further limit their scope and that limitation is effective", roots: []constraintsSpec{ { ok: []string{"dns:.example.com"}, @@ -299,9 +279,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"foo.notbar.example.com\" is not permitted", }, - - // #14: roots can exclude subtrees and that doesn't affect other names. { + name: "roots can exclude subtrees and that doesn't affect other names", roots: []constraintsSpec{ { bad: []string{"dns:.example.com"}, @@ -316,9 +295,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:foo.com"}, }, }, - - // #15: roots exclusions are effective. { + name: "roots exclusions are effective", roots: []constraintsSpec{ { bad: []string{"dns:.example.com"}, @@ -334,10 +312,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"foo.example.com\" is excluded", }, - - // #16: intermediates can also exclude names and that doesn't affect - // other names. { + name: "intermediates can also exclude names and that doesn't affect other names", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -350,9 +326,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:foo.com"}, }, }, - - // #17: intermediate exclusions are effective. { + name: "intermediate exclusions are effective", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -366,9 +341,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"foo.example.com\" is excluded", }, - - // #18: having an exclusion doesn't prohibit other types of names. { + name: "having an exclusion doesn't prohibit other types of names", roots: []constraintsSpec{ { bad: []string{"dns:.example.com"}, @@ -383,10 +357,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:foo.com", "ip:10.1.1.1"}, }, }, - - // #19: IP-based exclusions are permitted and don't affect unrelated IP - // addresses. { + name: "IP-based exclusions are permitted and don't affect unrelated IP addresses", roots: []constraintsSpec{ { bad: []string{"ip:10.0.0.0/8"}, @@ -401,9 +373,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"ip:192.168.1.1"}, }, }, - - // #20: IP-based exclusions are effective { + name: "IP-based exclusions are effective", roots: []constraintsSpec{ { bad: []string{"ip:10.0.0.0/8"}, @@ -419,9 +390,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"10.0.0.1\" is excluded", }, - - // #21: intermediates can further constrain IP ranges. { + name: "intermediates can further constrain IP ranges", roots: []constraintsSpec{ { bad: []string{"ip:0.0.0.0/1"}, @@ -439,10 +409,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"11.0.0.1\" is excluded", }, - - // #22: when multiple intermediates are present, chain building can - // avoid intermediates with incompatible constraints. { + name: "multiple intermediates with incompatible constraints", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -459,10 +427,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, noOpenSSL: true, // OpenSSL's chain building is not informed by constraints. }, - - // #23: (same as the previous test, but in the other order in ensure - // that we don't pass it by luck.) { + name: "multiple intermediates with incompatible constraints swapped", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -479,10 +445,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, noOpenSSL: true, // OpenSSL's chain building is not informed by constraints. }, - - // #24: when multiple roots are valid, chain building can avoid roots - // with incompatible constraints. { + name: "multiple roots with incompatible constraints", roots: []constraintsSpec{ {}, { @@ -499,10 +463,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, noOpenSSL: true, // OpenSSL's chain building is not informed by constraints. }, - - // #25: (same as the previous test, but in the other order in ensure - // that we don't pass it by luck.) { + name: "multiple roots with incompatible constraints swapped", roots: []constraintsSpec{ { ok: []string{"dns:foo.com"}, @@ -519,10 +481,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, noOpenSSL: true, // OpenSSL's chain building is not informed by constraints. }, - - // #26: chain building can find a valid path even with multiple levels - // of alternative intermediates and alternative roots. { + name: "chain building with multiple intermediates and roots", roots: []constraintsSpec{ { ok: []string{"dns:foo.com"}, @@ -551,9 +511,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, noOpenSSL: true, // OpenSSL's chain building is not informed by constraints. }, - - // #27: chain building doesn't get stuck when there is no valid path. { + name: "chain building fails with no valid path", roots: []constraintsSpec{ { ok: []string{"dns:foo.com"}, @@ -583,9 +542,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"bar.com\" is not permitted", }, - - // #28: unknown name types don't cause a problem without constraints. { + name: "unknown name types are unconstrained", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -596,9 +554,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"unknown:"}, }, }, - - // #29: unknown name types are allowed even in constrained chains. { + name: "unknown name types allowed in constrained chain", roots: []constraintsSpec{ { ok: []string{"dns:foo.com", "dns:.foo.com"}, @@ -613,10 +570,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"unknown:"}, }, }, - - // #30: without SANs, a certificate with a CN is still accepted in a - // constrained chain, since we ignore the CN in VerifyHostname. { + name: "CN is ignored in constrained chain", roots: []constraintsSpec{ { ok: []string{"dns:foo.com", "dns:.foo.com"}, @@ -632,10 +587,8 @@ var nameConstraintsTests = []nameConstraintsTest{ cn: "foo.com", }, }, - - // #31: IPv6 addresses work in constraints: roots can permit them as - // expected. { + name: "IPv6 permitted constraint", roots: []constraintsSpec{ { ok: []string{"ip:2000:abcd::/32"}, @@ -650,10 +603,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"ip:2000:abcd:1234::"}, }, }, - - // #32: IPv6 addresses work in constraints: root restrictions are - // effective. { + name: "IPv6 permitted constraint is effective", roots: []constraintsSpec{ { ok: []string{"ip:2000:abcd::/32"}, @@ -669,9 +620,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"2000:1234:abcd::\" is not permitted", }, - - // #33: An IPv6 permitted subtree doesn't affect DNS names. { + name: "IPv6 permitted constraint does not affect DNS", roots: []constraintsSpec{ { ok: []string{"ip:2000:abcd::/32"}, @@ -686,9 +636,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"ip:2000:abcd::", "dns:foo.com"}, }, }, - - // #34: IPv6 exclusions don't affect unrelated addresses. { + name: "IPv6 excluded constraint", roots: []constraintsSpec{ { bad: []string{"ip:2000:abcd::/32"}, @@ -703,9 +652,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"ip:2000:1234::"}, }, }, - - // #35: IPv6 exclusions are effective. { + name: "IPv6 excluded constraint is effective", roots: []constraintsSpec{ { bad: []string{"ip:2000:abcd::/32"}, @@ -721,9 +669,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"2000:abcd::\" is excluded", }, - - // #36: IPv6 constraints do not permit IPv4 addresses. { + name: "IPv6 constraint does not permit IPv4", roots: []constraintsSpec{ { ok: []string{"ip:2000:abcd::/32"}, @@ -739,9 +686,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"10.0.0.1\" is not permitted", }, - - // #37: IPv4 constraints do not permit IPv6 addresses. { + name: "IPv4 constraint does not permit IPv6", roots: []constraintsSpec{ { ok: []string{"ip:10.0.0.0/8"}, @@ -757,9 +703,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"2000:abcd::\" is not permitted", }, - - // #38: an exclusion of an unknown type doesn't affect other names. { + name: "unknown excluded constraint does not affect other names", roots: []constraintsSpec{ { bad: []string{"unknown:"}, @@ -774,10 +719,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:example.com"}, }, }, - - // #39: a permitted subtree of an unknown type doesn't affect other - // name types. { + name: "unknown permitted constraint does not affect other names", roots: []constraintsSpec{ { ok: []string{"unknown:"}, @@ -792,9 +735,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:example.com"}, }, }, - - // #40: exact email constraints work { + name: "exact email constraint", roots: []constraintsSpec{ { ok: []string{"email:foo@example.com"}, @@ -809,9 +751,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"email:foo@example.com"}, }, }, - - // #41: exact email constraints are effective { + name: "exact email constraint is effective", roots: []constraintsSpec{ { ok: []string{"email:foo@example.com"}, @@ -827,9 +768,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"bar@example.com\" is not permitted", }, - - // #42: email canonicalisation works. { + name: "email canonicalization", roots: []constraintsSpec{ { ok: []string{"email:foo@example.com"}, @@ -845,9 +785,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, noOpenSSL: true, // OpenSSL doesn't canonicalise email addresses before matching }, - - // #43: limiting email addresses to a host works. { + name: "email host constraint", roots: []constraintsSpec{ { ok: []string{"email:example.com"}, @@ -862,9 +801,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"email:foo@example.com"}, }, }, - - // #44: a leading dot matches hosts one level deep { + name: "email subdomain constraint", roots: []constraintsSpec{ { ok: []string{"email:.example.com"}, @@ -879,9 +817,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"email:foo@sub.example.com"}, }, }, - - // #45: a leading dot does not match the host itself { + name: "email subdomain constraint does not match parent", roots: []constraintsSpec{ { ok: []string{"email:.example.com"}, @@ -897,9 +834,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"foo@example.com\" is not permitted", }, - - // #46: a leading dot also matches two (or more) levels deep. { + name: "email subdomain constraint matches deeper subdomains", roots: []constraintsSpec{ { ok: []string{"email:.example.com"}, @@ -914,9 +850,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"email:foo@sub.sub.example.com"}, }, }, - - // #47: the local part of an email is case-sensitive { + name: "email local part is case-sensitive", roots: []constraintsSpec{ { ok: []string{"email:foo@example.com"}, @@ -932,9 +867,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"Foo@example.com\" is not permitted", }, - - // #48: the domain part of an email is not case-sensitive { + name: "email domain part is case-insensitive", roots: []constraintsSpec{ { ok: []string{"email:foo@EXAMPLE.com"}, @@ -949,9 +883,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"email:foo@example.com"}, }, }, - - // #49: the domain part of a DNS constraint is also not case-sensitive. { + name: "DNS domain is case-insensitive", roots: []constraintsSpec{ { ok: []string{"dns:EXAMPLE.com"}, @@ -966,9 +899,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:example.com"}, }, }, - - // #50: URI constraints only cover the host part of the URI { + name: "URI constraint covers host", roots: []constraintsSpec{ { ok: []string{"uri:example.com"}, @@ -987,9 +919,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, }, }, - - // #51: URIs with IPs are rejected { + name: "URI with IP is rejected", roots: []constraintsSpec{ { ok: []string{"uri:example.com"}, @@ -1005,9 +936,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "URI with IP", }, - - // #52: URIs with IPs and ports are rejected { + name: "URI with IP and port is rejected", roots: []constraintsSpec{ { ok: []string{"uri:example.com"}, @@ -1023,9 +953,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "URI with IP", }, - - // #53: URIs with IPv6 addresses are also rejected { + name: "URI with IPv6 is rejected", roots: []constraintsSpec{ { ok: []string{"uri:example.com"}, @@ -1041,9 +970,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "URI with IP", }, - - // #54: URIs with IPv6 addresses with ports are also rejected { + name: "URI with IPv6 and port is rejected", roots: []constraintsSpec{ { ok: []string{"uri:example.com"}, @@ -1059,9 +987,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "URI with IP", }, - - // #55: URI constraints are effective { + name: "URI permitted constraint is effective", roots: []constraintsSpec{ { ok: []string{"uri:example.com"}, @@ -1077,9 +1004,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"http://bar.com/\" is not permitted", }, - - // #56: URI constraints are effective { + name: "URI excluded constraint is effective", roots: []constraintsSpec{ { bad: []string{"uri:foo.com"}, @@ -1095,9 +1021,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"http://foo.com/\" is excluded", }, - - // #57: URI constraints can allow subdomains { + name: "URI subdomain constraint", roots: []constraintsSpec{ { ok: []string{"uri:.foo.com"}, @@ -1112,10 +1037,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"uri:http://www.foo.com/"}, }, }, - - // #58: excluding an IPv4-mapped-IPv6 address doesn't affect the IPv4 - // version of that address. { + name: "IPv4-mapped-IPv6 exclusion does not affect IPv4", roots: []constraintsSpec{ { bad: []string{"ip:::ffff:1.2.3.4/128"}, @@ -1130,9 +1053,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"ip:1.2.3.4"}, }, }, - - // #59: a URI constraint isn't matched by a URN. { + name: "URI constraint not matched by URN", roots: []constraintsSpec{ { ok: []string{"uri:example.com"}, @@ -1148,10 +1070,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "URI with empty host", }, - - // #60: excluding all IPv6 addresses doesn't exclude all IPv4 addresses - // too, even though IPv4 is mapped into the IPv6 range. { + name: "IPv6 exclusion does not exclude all IPv4", roots: []constraintsSpec{ { ok: []string{"ip:1.2.3.0/24"}, @@ -1167,10 +1087,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"ip:1.2.3.4"}, }, }, - - // #61: omitting extended key usage in a CA certificate implies that - // any usage is ok. { + name: "empty EKU in CA means any is ok", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -1182,9 +1100,8 @@ var nameConstraintsTests = []nameConstraintsTest{ ekus: []string{"serverAuth", "other"}, }, }, - - // #62: The “any” EKU also means that any usage is ok. { + name: "any EKU means any is ok", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -1198,11 +1115,9 @@ var nameConstraintsTests = []nameConstraintsTest{ ekus: []string{"serverAuth", "other"}, }, }, - - // #63: An intermediate with enumerated EKUs causes a failure if we - // test for an EKU not in that set. (ServerAuth is required by // default.) { + name: "intermediate with enumerated EKUs", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -1217,10 +1132,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "incompatible key usage", }, - - // #64: an unknown EKU in the leaf doesn't break anything, even if it's not - // correctly nested. { + name: "unknown EKU in leaf", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -1235,11 +1148,9 @@ var nameConstraintsTests = []nameConstraintsTest{ }, requestedEKUs: []ExtKeyUsage{ExtKeyUsageAny}, }, - - // #65: trying to add extra permitted key usages in an intermediate - // (after a limitation in the root) is acceptable so long as the leaf // certificate doesn't use them. { + name: "intermediate cannot add EKUs not in root if leaf uses them", roots: []constraintsSpec{ { ekus: []string{"serverAuth"}, @@ -1257,9 +1168,8 @@ var nameConstraintsTests = []nameConstraintsTest{ ekus: []string{"serverAuth"}, }, }, - - // #66: EKUs in roots are not ignored. { + name: "EKUs in root are effective", roots: []constraintsSpec{ { ekus: []string{"email"}, @@ -1278,10 +1188,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "incompatible key usage", }, - - // #67: SGC key usages used to permit serverAuth and clientAuth, - // but don't anymore. { + name: "netscapeSGC EKU does not permit server/client auth", roots: []constraintsSpec{ {}, }, @@ -1298,10 +1206,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "incompatible key usage", }, - - // #68: SGC key usages used to permit serverAuth and clientAuth, - // but don't anymore. { + name: "msSGC EKU does not permit server/client auth", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -1316,9 +1222,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "incompatible key usage", }, - - // #69: an empty DNS constraint should allow anything. { + name: "empty DNS permitted constraint allows anything", roots: []constraintsSpec{ { ok: []string{"dns:"}, @@ -1333,9 +1238,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:example.com"}, }, }, - - // #70: an empty DNS constraint should also reject everything. { + name: "empty DNS excluded constraint rejects everything", roots: []constraintsSpec{ { bad: []string{"dns:"}, @@ -1351,9 +1255,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"example.com\" is excluded", }, - - // #71: an empty email constraint should allow anything { + name: "empty email permitted constraint allows anything", roots: []constraintsSpec{ { ok: []string{"email:"}, @@ -1368,9 +1271,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"email:foo@example.com"}, }, }, - - // #72: an empty email constraint should also reject everything. { + name: "empty email excluded constraint rejects everything", roots: []constraintsSpec{ { bad: []string{"email:"}, @@ -1386,9 +1288,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"foo@example.com\" is excluded", }, - - // #73: an empty URI constraint should allow anything { + name: "empty URI permitted constraint allows anything", roots: []constraintsSpec{ { ok: []string{"uri:"}, @@ -1403,9 +1304,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"uri:https://example.com/test"}, }, }, - - // #74: an empty URI constraint should also reject everything. { + name: "empty URI excluded constraint rejects everything", roots: []constraintsSpec{ { bad: []string{"uri:"}, @@ -1421,10 +1321,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "\"https://example.com/test\" is excluded", }, - - // #75: serverAuth in a leaf shouldn't permit clientAuth when requested in - // VerifyOptions. { + name: "serverAuth EKU does not permit clientAuth", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -1438,10 +1336,8 @@ var nameConstraintsTests = []nameConstraintsTest{ requestedEKUs: []ExtKeyUsage{ExtKeyUsageClientAuth}, expectedError: "incompatible key usage", }, - - // #76: MSSGC in a leaf used to match a request for serverAuth, but doesn't - // anymore. { + name: "msSGC EKU does not permit serverAuth", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -1455,14 +1351,11 @@ var nameConstraintsTests = []nameConstraintsTest{ requestedEKUs: []ExtKeyUsage{ExtKeyUsageServerAuth}, expectedError: "incompatible key usage", }, - - // An invalid DNS SAN should be detected only at validation time so - // that we can process CA certificates in the wild that have invalid SANs. - // See https://github.com/golang/go/issues/23995 - - // #77: an invalid DNS or mail SAN will not be detected if name constraint - // checking is not triggered. { + // An invalid DNS SAN should be detected only at validation time so + // that we can process CA certificates in the wild that have invalid SANs. + // See https://github.com/golang/go/issues/23995 + name: "invalid SANs are ignored with no constraints", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -1473,10 +1366,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:this is invalid", "email:this @ is invalid"}, }, }, - - // #78: an invalid DNS SAN will be detected if any name constraint checking - // is triggered. { + name: "invalid DNS SAN detected with constraints", roots: []constraintsSpec{ { bad: []string{"uri:"}, @@ -1492,10 +1383,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "cannot parse dnsName", }, - - // #79: an invalid email SAN will be detected if any name constraint - // checking is triggered. { + name: "invalid email SAN detected with constraints", roots: []constraintsSpec{ { bad: []string{"uri:"}, @@ -1511,9 +1400,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "cannot parse rfc822Name", }, - - // #80: if several EKUs are requested, satisfying any of them is sufficient. { + name: "any requested EKU is sufficient", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -1526,10 +1414,8 @@ var nameConstraintsTests = []nameConstraintsTest{ }, requestedEKUs: []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageEmailProtection}, }, - - // #81: EKUs that are not asserted in VerifyOpts are not required to be - // nested. { + name: "unrequested EKUs not required to be nested", roots: make([]constraintsSpec, 1), intermediates: [][]constraintsSpec{ { @@ -1545,9 +1431,8 @@ var nameConstraintsTests = []nameConstraintsTest{ ekus: []string{"email", "serverAuth"}, }, }, - - // #82: a certificate without SANs and CN is accepted in a constrained chain. { + name: "empty leaf is accepted in constrained chain", roots: []constraintsSpec{ { ok: []string{"dns:foo.com", "dns:.foo.com"}, @@ -1562,10 +1447,8 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{}, }, }, - - // #83: a certificate without SANs and with a CN that does not parse as a - // hostname is accepted in a constrained chain. { + name: "no SANs and non-hostname CN is accepted in constrained chain", roots: []constraintsSpec{ { ok: []string{"dns:foo.com", "dns:.foo.com"}, @@ -1578,12 +1461,11 @@ var nameConstraintsTests = []nameConstraintsTest{ }, leaf: leafSpec{ sans: []string{}, - cn: "foo,bar", + cn: "foo.bar", }, }, - - // #84: a certificate with SANs and CN is accepted in a constrained chain. { + name: "constraints don't apply to CN", roots: []constraintsSpec{ { ok: []string{"dns:foo.com", "dns:.foo.com"}, @@ -1599,16 +1481,14 @@ var nameConstraintsTests = []nameConstraintsTest{ cn: "foo.bar", }, }, - - // #85: .example.com is an invalid DNS name, it should not match the - // constraint example.com. { + name: "DNS SAN cannot use leading period form", roots: []constraintsSpec{{ok: []string{"dns:example.com"}}}, leaf: leafSpec{sans: []string{"dns:.example.com"}}, expectedError: "cannot parse dnsName \".example.com\"", }, - // #86: URIs with IPv6 addresses with zones and ports are rejected { + name: "URI with IPv6 and zone is rejected", roots: []constraintsSpec{ { ok: []string{"uri:example.com"}, @@ -1624,6 +1504,113 @@ var nameConstraintsTests = []nameConstraintsTest{ }, expectedError: "URI with IP", }, + { + name: "intermediate can narrow permitted dns scope", + roots: []constraintsSpec{ + { + ok: []string{"dns:"}, + }, + }, + intermediates: [][]constraintsSpec{ + { + { + ok: []string{"dns:example.com"}, + }, + }, + }, + leaf: leafSpec{ + sans: []string{"dns:test.com"}, + }, + expectedError: "\"test.com\" is not permitted", + }, + { + name: "intermediate cannot narrow excluded dns scope", + roots: []constraintsSpec{ + { + bad: []string{"dns:"}, + }, + }, + intermediates: [][]constraintsSpec{ + { + { + bad: []string{"dns:example.com"}, + }, + }, + }, + leaf: leafSpec{ + sans: []string{"dns:test.com"}, + }, + expectedError: "\"test.com\" is excluded by constraint \"\"", + }, + { + name: "intermediate can narrow excluded dns scope", + roots: []constraintsSpec{ + { + bad: []string{"dns:example.com"}, + }, + }, + intermediates: [][]constraintsSpec{ + { + { + bad: []string{"dns:"}, + }, + }, + }, + leaf: leafSpec{ + sans: []string{"dns:test.com"}, + }, + expectedError: "\"test.com\" is excluded by constraint \"\"", + }, + { + name: "permitted dns constraint is not a prefix match", + roots: []constraintsSpec{ + { + ok: []string{"dns:example.com"}, + }, + }, + intermediates: [][]constraintsSpec{ + { + {}, + }, + }, + leaf: leafSpec{ + sans: []string{"dns:testexample.com"}, + }, + expectedError: "\"testexample.com\" is not permitted", + }, + { + name: "subdomain constraint does not allow wildcard", + roots: []constraintsSpec{ + { + ok: []string{"dns:a.com", "dns:foo.example.com", "dns:z.com"}, + }, + }, + intermediates: [][]constraintsSpec{ + { + {}, + }, + }, + leaf: leafSpec{ + sans: []string{"dns:*.example.com"}, + }, + expectedError: "\"*.example.com\" is not permitted", + }, + { + name: "excluded dns constraint is not a prefix match", + roots: []constraintsSpec{ + { + bad: []string{"dns:example.com"}, + }, + }, + intermediates: [][]constraintsSpec{ + { + {}, + }, + }, + leaf: leafSpec{ + sans: []string{"dns:testexample.com"}, + }, + }, } func makeConstraintsCACert(constraints constraintsSpec, name string, key *ecdsa.PrivateKey, parent *Certificate, parentKey *ecdsa.PrivateKey) (*Certificate, error) { @@ -1885,7 +1872,7 @@ func TestConstraintCases(t *testing.T) { } for i, test := range nameConstraintsTests { - t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + t.Run(test.name, func(t *testing.T) { rootPool := NewCertPool() rootKey := privateKeys.Get().(*ecdsa.PrivateKey) rootName := "Root " + strconv.Itoa(i) @@ -1974,7 +1961,7 @@ func TestConstraintCases(t *testing.T) { } _, err = leafCert.Verify(verifyOpts) - logInfo := true + logInfo := false if len(test.expectedError) == 0 { if err != nil { t.Errorf("unexpected failure: %s", err) -- 2.52.0