diff --git a/v3/lints/cabf_br/lint_fdqn_must_not_contain_underscores.go b/v3/lints/cabf_br/lint_fdqn_must_not_contain_underscores.go new file mode 100644 index 000000000..a9ab27b07 --- /dev/null +++ b/v3/lints/cabf_br/lint_fdqn_must_not_contain_underscores.go @@ -0,0 +1,61 @@ +/* +* ZLint Copyright 2021 Regents of the University of Michigan +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may not +* use this file except in compliance with the License. You may obtain a copy +* of the License at http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +* implied. See the License for the specific language governing +* permissions and limitations under the License. + */ + +package cabf_br + +import ( + "fmt" + "strings" + + "github.com/zmap/zcrypto/x509" + "github.com/zmap/zlint/v3/lint" + "github.com/zmap/zlint/v3/util" +) + +func init() { + lint.RegisterLint(&lint.Lint{ + Name: "e_fqdn_must_not_contain_underscores", + Description: "Prior to April 1, 2019, certificates containing underscore characters (“_”) in domain labels in dNSName entries MAY be issued as follows: " + + "• dNSName entries MAY include underscore characters such that replacing all underscore characters with hyphen characters (“-“) would result in a valid domain label, and; " + + "• Underscore characters MUST NOT be placed in the left most domain label, and; " + + "* Such certificates MUST NOT be valid for longer than 30 days.", + Citation: "BR 7.1.4.2.1", + Source: lint.CABFBaselineRequirements, + EffectiveDate: util.CABFBRs_1_6_2_Date, + Lint: NewDNSNameMustNotIncludeUnderscore, + }) +} + +type DNSNameMustdNotIncludeUnderscore struct{} + +func NewDNSNameMustNotIncludeUnderscore() lint.LintInterface { + return &DNSNameMustdNotIncludeUnderscore{} +} + +func (l *DNSNameMustdNotIncludeUnderscore) CheckApplies(c *x509.Certificate) bool { + return util.IsSubscriberCert(c) && util.DNSNamesExist(c) +} + +func (l *DNSNameMustdNotIncludeUnderscore) Execute(c *x509.Certificate) *lint.LintResult { + for _, dns := range c.DNSNames { + fqdnPortion := util.RemovePrependedWildcard(dns) + labels := strings.Split(fqdnPortion, ".") + for _, label := range labels { + if strings.Contains(label, "_") { + return &lint.LintResult{Status: lint.Error, Details: fmt.Sprintf("dNSName ('%s') MUST NOT contain an underscore character ('_')", dns)} + } + } + } + return &lint.LintResult{Status: lint.Pass} +} diff --git a/v3/lints/cabf_br/lint_fdqn_must_not_contain_underscores_test.go b/v3/lints/cabf_br/lint_fdqn_must_not_contain_underscores_test.go new file mode 100644 index 000000000..9639b0bc3 --- /dev/null +++ b/v3/lints/cabf_br/lint_fdqn_must_not_contain_underscores_test.go @@ -0,0 +1,54 @@ +/* +* ZLint Copyright 2021 Regents of the University of Michigan +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may not +* use this file except in compliance with the License. You may obtain a copy +* of the License at http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +* implied. See the License for the specific language governing +* permissions and limitations under the License. + */ + +package cabf_br + +import ( + "testing" + + "github.com/zmap/zlint/v3/lint" + "github.com/zmap/zlint/v3/test" +) + +func TestFQDNMustNotContainUnderscores(t *testing.T) { + testCases := []struct { + Name string + InputFilename string + ExpectedResult lint.LintStatus + }{ + { + Name: "No underscores", + InputFilename: "dNSNameNoUnderscores.pem", + ExpectedResult: lint.Pass, + }, + { + Name: "One underscore in label", + InputFilename: "dNSNameWithUnderscore.pem", + ExpectedResult: lint.Error, + }, + { + Name: "Not effective", + InputFilename: "dNSNameUnderscoreAllowedBefore2019.pem", + ExpectedResult: lint.NE, + }, + } + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + result := test.TestLint("e_fqdn_must_not_contain_underscores", tc.InputFilename) + if result.Status != tc.ExpectedResult { + t.Errorf("expected result %v was %v", tc.ExpectedResult, result.Status) + } + }) + } +} diff --git a/v3/lints/cabf_br/lint_fqdn_may_contain_underscores.go b/v3/lints/cabf_br/lint_fqdn_may_contain_underscores.go new file mode 100644 index 000000000..27c3eda23 --- /dev/null +++ b/v3/lints/cabf_br/lint_fqdn_may_contain_underscores.go @@ -0,0 +1,93 @@ +/* +* ZLint Copyright 2021 Regents of the University of Michigan +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may not +* use this file except in compliance with the License. You may obtain a copy +* of the License at http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +* implied. See the License for the specific language governing +* permissions and limitations under the License. + */ + +package cabf_br + +import ( + "fmt" + "strings" + "time" + + "github.com/zmap/zcrypto/x509" + "github.com/zmap/zlint/v3/lint" + "github.com/zmap/zlint/v3/util" +) + +func init() { + lint.RegisterLint(&lint.Lint{ + Name: "w_fqdn_may_contain_underscores", + Description: "Prior to April 1, 2019, certificates containing underscore characters (“_”) in domain labels in dNSName entries MAY be issued as follows: " + + "• dNSName entries MAY include underscore characters such that replacing all underscore characters with hyphen characters (“-“) would result in a valid domain label, and; " + + "• Underscore characters MUST NOT be placed in the left most domain label, and; " + + "* Such certificates MUST NOT be valid for longer than 30 days.", + Citation: "BR 7.1.4.2.1", + Source: lint.CABFBaselineRequirements, + EffectiveDate: util.ZeroDate, + IneffectiveDate: util.CABFBRs_1_6_2_Date, + Lint: NewDNSNameMayIncludeUnderscore, + }) +} + +type DNSNameShouldNotIncludeUnderscore struct{} + +func NewDNSNameMayIncludeUnderscore() lint.LintInterface { + return &DNSNameShouldNotIncludeUnderscore{} +} + +func (l *DNSNameShouldNotIncludeUnderscore) CheckApplies(c *x509.Certificate) bool { + return util.IsSubscriberCert(c) && util.DNSNamesExist(c) +} + +func (l *DNSNameShouldNotIncludeUnderscore) Execute(c *x509.Certificate) *lint.LintResult { + validLongerThanThirtyDays := c.NotAfter.Sub(c.NotBefore) > time.Hour*24*30 + for _, dns := range c.DNSNames { + fqdnPortion := util.RemovePrependedWildcard(dns) + labels := strings.Split(fqdnPortion, ".") + if len(labels) > 0 { + leftMostLabel := labels[0] + if strings.Contains(leftMostLabel, "_") { + return &lint.LintResult{Status: lint.Error, Details: fmt.Sprintf("The left most label of '%s' MUST NOT contain an underscore character ('_')", dns)} + } + } + for _, label := range labels { + containsUnderscore := strings.Contains(label, "_") + wouldBeValidIfHyphenInstead := util.IsLDHLabel(strings.ReplaceAll(label, "_", "-")) + if containsUnderscore && wouldBeValidIfHyphenInstead && !validLongerThanThirtyDays { + // Fulfills all clauses, so simply warn that this is deprecated. + return &lint.LintResult{Status: lint.Warn, Details: fmt.Sprintf("%s contains an underscore "+ + "character ('_'). This character MUST NOT appear within the FQDN after April 1, 2019. For more "+ + "information, please see Ballot SC12: Sunset of Underscores in dNSNames "+ + "(https://cabforum.org/2018/11/12/ballot-sc-12-sunset-of-underscores-in-dnsnames/)", dns)} + } else if containsUnderscore && !wouldBeValidIfHyphenInstead { + // Fails the first clause about replacing the _ with - which must result in a valid LDH. + return &lint.LintResult{Status: lint.Error, Details: fmt.Sprintf("%s contains an underscore "+ + "character ('_') that, when replaces by a hyphen, does not result in a valid LDH label. This "+ + "character MUST NOT appear within the FQDN after April 1, 2019. For more information, please see "+ + "Ballot SC12: Sunset of Underscores in dNSNames "+ + "(https://cabforum.org/2018/11/12/ballot-sc-12-sunset-of-underscores-in-dnsnames/)", dns)} + } else if containsUnderscore && wouldBeValidIfHyphenInstead && validLongerThanThirtyDays { + // Fails the third clause regarding validity dates being longer than 30 days. + return &lint.LintResult{Status: lint.Error, Details: fmt.Sprintf("%s contains an underscore "+ + "character ('_') and the certificate is valid for more than 30 days. This character MUST NOT appear "+ + "within the FQDN after April 1, 2019. For more information, please see Ballot SC12: Sunset of "+ + "Underscores in dNSNames "+ + "(https://cabforum.org/2018/11/12/ballot-sc-12-sunset-of-underscores-in-dnsnames/)", dns)} + } else { + // Label passes lint. + continue + } + } + } + return &lint.LintResult{Status: lint.Pass} +} diff --git a/v3/lints/cabf_br/lint_fqdn_may_contain_underscores_test.go b/v3/lints/cabf_br/lint_fqdn_may_contain_underscores_test.go new file mode 100644 index 000000000..5fb3c213b --- /dev/null +++ b/v3/lints/cabf_br/lint_fqdn_may_contain_underscores_test.go @@ -0,0 +1,69 @@ +/* +* ZLint Copyright 2021 Regents of the University of Michigan +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may not +* use this file except in compliance with the License. You may obtain a copy +* of the License at http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +* implied. See the License for the specific language governing +* permissions and limitations under the License. + */ + +package cabf_br + +import ( + "testing" + + "github.com/zmap/zlint/v3/lint" + "github.com/zmap/zlint/v3/test" +) + +func TestFQDNMayContainUnderscores(t *testing.T) { + testCases := []struct { + Name string + InputFilename string + ExpectedResult lint.LintStatus + }{ + { + Name: "No underscores", + InputFilename: "dNSNameNoUnderscoresButAllowed.pem", + ExpectedResult: lint.Pass, + }, + { + Name: "Underscores but allowable", + InputFilename: "dNSNameUnderscoreAllowedBefore2019.pem", + ExpectedResult: lint.Warn, + }, + { + Name: "Underscore in left most label", + InputFilename: "dNSNameInLeftMostLabel.pem", + ExpectedResult: lint.Error, + }, + { + Name: "Underscore but validity is too long", + InputFilename: "dNSNameWithUnderscoreValidityTooLong.pem", + ExpectedResult: lint.Error, + }, + { + Name: "Not a valid label is _ replaced with -", + InputFilename: "dNSNameUnderscoreInvalidEvenIfReplaced.pem", + ExpectedResult: lint.Error, + }, + { + Name: "Not effective", + InputFilename: "dNSNameWithUnderscore.pem", + ExpectedResult: lint.NE, + }, + } + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + result := test.TestLint("w_fqdn_may_contain_underscores", tc.InputFilename) + if result.Status != tc.ExpectedResult { + t.Errorf("expected result %v was %v", tc.ExpectedResult, result.Status) + } + }) + } +} diff --git a/v3/testdata/dNSNameInLeftMostLabel.pem b/v3/testdata/dNSNameInLeftMostLabel.pem new file mode 100644 index 000000000..80192cde6 --- /dev/null +++ b/v3/testdata/dNSNameInLeftMostLabel.pem @@ -0,0 +1,37 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Jan 1 00:00:00 2018 GMT + Not After : Jan 2 00:00:00 2018 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:08:c3:ca:32:66:23:2f:4b:a7:16:41:29:32:11: + f3:ac:19:fa:ed:31:b1:2c:a2:6a:0e:11:de:db:05: + 34:cb:45:c7:33:9d:7e:e6:9b:35:9a:3b:32:50:79: + 59:e3:8c:b0:51:f9:d9:e6:dc:38:8f:76:3c:3d:e1: + 17:a7:4d:d1:3e + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:totally_.not_fine.with.me + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:21:00:aa:d0:d9:7b:7a:13:4b:a4:0d:64:9c:7a:b8: + c9:ef:9e:22:d8:a0:ae:c2:4a:f4:0a:6b:88:b1:00:b1:1e:e8: + 7c:02:20:5d:83:23:e9:1e:39:1a:5b:7b:3f:fa:9d:3b:3d:bf: + ec:0d:20:11:ae:e9:27:74:66:e8:74:71:3d:ed:7e:98:10 +-----BEGIN CERTIFICATE----- +MIIBFjCBvaADAgECAgEDMAoGCCqGSM49BAMCMAAwHhcNMTgwMTAxMDAwMDAwWhcN +MTgwMTAyMDAwMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECMPKMmYj +L0unFkEpMhHzrBn67TGxLKJqDhHe2wU0y0XHM51+5ps1mjsyUHlZ44ywUfnZ5tw4 +j3Y8PeEXp03RPqMoMCYwJAYDVR0RBB0wG4IZdG90YWxseV8ubm90X2ZpbmUud2l0 +aC5tZTAKBggqhkjOPQQDAgNIADBFAiEAqtDZe3oTS6QNZJx6uMnvniLYoK7CSvQK +a4ixALEe6HwCIF2DI+keORpbez/6nTs9v+wNIBGu6Sd0Zuh0cT3tfpgQ +-----END CERTIFICATE----- diff --git a/v3/testdata/dNSNameNoUnderscores.pem b/v3/testdata/dNSNameNoUnderscores.pem new file mode 100644 index 000000000..2f3ff0ee5 --- /dev/null +++ b/v3/testdata/dNSNameNoUnderscores.pem @@ -0,0 +1,37 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Nov 30 00:00:00 9997 GMT + Not After : Nov 30 00:00:00 9998 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:55:8f:1d:1a:73:7e:54:de:33:03:3a:77:b5:e9: + ad:17:e1:03:17:ea:d5:ed:86:2b:35:ba:ed:e3:ed: + 64:77:47:a8:4a:5d:d4:68:43:42:9e:18:00:6e:e0: + a5:dd:ec:c4:d6:d3:e4:29:d0:d5:54:b4:11:e7:db: + d4:77:a1:23:a0 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:totally.fine.with.me + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:21:00:e8:06:1f:e1:27:63:30:5c:30:46:35:d4:f4: + 1c:72:29:d7:3e:0d:9c:f2:12:ea:45:60:f5:2b:87:d6:77:93: + 31:02:20:3f:6f:08:8c:8f:b2:e4:e8:9a:54:2f:dc:e1:4a:63: + 9f:a6:6e:db:06:c8:f8:fc:9e:92:d4:ae:5f:71:d2:07:b6 +-----BEGIN CERTIFICATE----- +MIIBFTCBvKADAgECAgEDMAoGCCqGSM49BAMCMAAwIhgPOTk5NzExMzAwMDAwMDBa +GA85OTk4MTEzMDAwMDAwMFowADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFWP +HRpzflTeMwM6d7XprRfhAxfq1e2GKzW67ePtZHdHqEpd1GhDQp4YAG7gpd3sxNbT +5CnQ1VS0Eefb1HehI6CjIzAhMB8GA1UdEQQYMBaCFHRvdGFsbHkuZmluZS53aXRo +Lm1lMAoGCCqGSM49BAMCA0gAMEUCIQDoBh/hJ2MwXDBGNdT0HHIp1z4NnPIS6kVg +9SuH1neTMQIgP28IjI+y5OiaVC/c4Upjn6Zu2wbI+PyektSuX3HSB7Y= +-----END CERTIFICATE----- diff --git a/v3/testdata/dNSNameNoUnderscoresButAllowed.pem b/v3/testdata/dNSNameNoUnderscoresButAllowed.pem new file mode 100644 index 000000000..5cf4b388a --- /dev/null +++ b/v3/testdata/dNSNameNoUnderscoresButAllowed.pem @@ -0,0 +1,37 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Jan 1 00:00:00 2018 GMT + Not After : Jan 2 00:00:00 2018 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:bc:0a:29:a0:6d:3d:e7:dc:e9:06:a0:f4:99:b1: + f8:05:f5:62:c6:ac:ca:8b:0a:28:82:5a:25:d6:aa: + 04:a4:07:5a:ac:c4:e6:07:d0:4c:93:ef:30:57:34: + c8:4d:19:c9:0b:6b:c0:b4:3c:d7:7e:cd:f4:d6:7e: + 71:a3:d6:8a:94 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:totally.fine.with.me + Signature Algorithm: ecdsa-with-SHA256 + 30:44:02:20:4c:18:8a:0b:97:84:00:d8:f6:0a:ce:ae:60:c3: + a0:f9:9e:82:a8:31:eb:63:a6:49:81:41:49:cf:e8:5e:97:76: + 02:20:40:0e:89:66:66:2d:d7:6c:fe:77:38:21:1a:45:83:d2: + df:a8:c1:2c:fa:7c:51:0e:0d:dc:f8:d3:3e:c2:04:84 +-----BEGIN CERTIFICATE----- +MIIBEDCBuKADAgECAgEDMAoGCCqGSM49BAMCMAAwHhcNMTgwMTAxMDAwMDAwWhcN +MTgwMTAyMDAwMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvAopoG09 +59zpBqD0mbH4BfVixqzKiwooglol1qoEpAdarMTmB9BMk+8wVzTITRnJC2vAtDzX +fs301n5xo9aKlKMjMCEwHwYDVR0RBBgwFoIUdG90YWxseS5maW5lLndpdGgubWUw +CgYIKoZIzj0EAwIDRwAwRAIgTBiKC5eEANj2Cs6uYMOg+Z6CqDHrY6ZJgUFJz+he +l3YCIEAOiWZmLdds/nc4IRpFg9LfqMEs+nxRDg3c+NM+wgSE +-----END CERTIFICATE----- diff --git a/v3/testdata/dNSNameUnderscoreAllowedBefore2019.pem b/v3/testdata/dNSNameUnderscoreAllowedBefore2019.pem new file mode 100644 index 000000000..b8f28d0ec --- /dev/null +++ b/v3/testdata/dNSNameUnderscoreAllowedBefore2019.pem @@ -0,0 +1,38 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Jan 1 00:00:00 2018 GMT + Not After : Jan 2 00:00:00 2018 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:41:26:d0:cf:40:b6:fe:7b:7a:1d:10:eb:00:f2: + 41:92:f3:d6:86:2c:a1:ce:a5:43:11:8b:3c:e6:b5: + 31:ca:68:5d:f9:79:fb:08:62:bc:9f:48:2b:2c:82: + 18:6d:e2:b6:d2:ed:e9:9a:c5:64:2e:09:84:b3:33: + d3:2f:30:19:2f + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:totally.not_fine.with.me + Signature Algorithm: ecdsa-with-SHA256 + 30:46:02:21:00:a8:a8:31:09:d9:fa:6c:c5:7f:bb:31:4e:a9: + 99:89:2b:a5:94:9e:9d:ab:56:35:1a:17:a6:a4:8a:d8:b7:7b: + 30:02:21:00:b5:2a:fd:0a:ab:1e:26:0a:ce:33:a7:c8:b4:59: + ec:3d:f4:ae:fb:28:ad:21:e5:b1:a3:c1:f9:a8:e9:1f:7d:2e +-----BEGIN CERTIFICATE----- +MIIBFjCBvKADAgECAgEDMAoGCCqGSM49BAMCMAAwHhcNMTgwMTAxMDAwMDAwWhcN +MTgwMTAyMDAwMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQSbQz0C2 +/nt6HRDrAPJBkvPWhiyhzqVDEYs85rUxymhd+Xn7CGK8n0grLIIYbeK20u3pmsVk +LgmEszPTLzAZL6MnMCUwIwYDVR0RBBwwGoIYdG90YWxseS5ub3RfZmluZS53aXRo +Lm1lMAoGCCqGSM49BAMCA0kAMEYCIQCoqDEJ2fpsxX+7MU6pmYkrpZSenatWNRoX +pqSK2Ld7MAIhALUq/QqrHiYKzjOnyLRZ7D30rvsorSHlsaPB+ajpH30u +-----END CERTIFICATE----- + diff --git a/v3/testdata/dNSNameUnderscoreInvalidEvenIfReplaced.pem b/v3/testdata/dNSNameUnderscoreInvalidEvenIfReplaced.pem new file mode 100644 index 000000000..54e726a51 --- /dev/null +++ b/v3/testdata/dNSNameUnderscoreInvalidEvenIfReplaced.pem @@ -0,0 +1,37 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Jan 1 00:00:00 2018 GMT + Not After : Jan 2 00:00:00 2018 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:7d:f5:db:ee:17:8a:6b:d7:b6:2b:c8:f2:70:a9: + af:7c:15:43:d2:ca:56:9d:bc:3c:41:93:50:12:74: + ac:08:5a:cd:f8:4b:7d:26:ac:61:d5:ce:08:20:91: + 10:e6:db:ff:05:92:c6:ea:31:88:ec:89:d6:9e:ba: + 88:05:57:e1:3d + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:totally._fine.with.me + Signature Algorithm: ecdsa-with-SHA256 + 30:46:02:21:00:c5:03:bd:eb:17:ef:4f:9a:dc:57:60:7f:b4: + a3:64:70:76:6b:cf:6e:b2:da:d1:3e:01:f2:c6:86:3c:ec:8e: + 97:02:21:00:bc:8c:af:56:0e:79:3e:20:af:80:33:a7:eb:45: + 36:f5:b3:da:74:3c:a2:90:63:25:23:01:3e:3f:ed:77:56:21 +-----BEGIN CERTIFICATE----- +MIIBEzCBuaADAgECAgEDMAoGCCqGSM49BAMCMAAwHhcNMTgwMTAxMDAwMDAwWhcN +MTgwMTAyMDAwMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEffXb7heK +a9e2K8jycKmvfBVD0spWnbw8QZNQEnSsCFrN+Et9Jqxh1c4IIJEQ5tv/BZLG6jGI +7InWnrqIBVfhPaMkMCIwIAYDVR0RBBkwF4IVdG90YWxseS5fZmluZS53aXRoLm1l +MAoGCCqGSM49BAMCA0kAMEYCIQDFA73rF+9PmtxXYH+0o2RwdmvPbrLa0T4B8saG +POyOlwIhALyMr1YOeT4gr4Azp+tFNvWz2nQ8opBjJSMBPj/td1Yh +-----END CERTIFICATE----- diff --git a/v3/testdata/dNSNameWithUnderscore.pem b/v3/testdata/dNSNameWithUnderscore.pem new file mode 100644 index 000000000..ae992568b --- /dev/null +++ b/v3/testdata/dNSNameWithUnderscore.pem @@ -0,0 +1,37 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Nov 30 00:00:00 9997 GMT + Not After : Nov 30 00:00:00 9998 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:37:15:ef:e4:c6:87:1e:7f:5f:09:9e:34:db:76: + 95:17:6c:37:bb:80:85:c0:9a:0f:a7:62:f8:e8:02: + 7b:6c:7e:43:f4:35:6a:b8:c9:41:cf:99:33:b6:8d: + 74:d2:24:80:8f:27:8e:0f:c2:36:12:0b:b4:91:20: + d9:10:f9:fe:c3 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:totally.not_fine.with.me + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:21:00:d5:39:bd:96:fd:3f:05:3e:05:48:af:45:80: + 47:2e:5c:b5:13:11:7c:8c:67:d3:9b:8b:cc:84:65:f9:80:0d: + 20:02:20:67:b0:a9:68:a8:b8:9a:61:38:b0:35:d6:2f:00:f9: + 31:bf:60:7f:64:01:4d:92:2c:32:aa:a5:ae:0a:e6:5a:fc +-----BEGIN CERTIFICATE----- +MIIBGTCBwKADAgECAgEDMAoGCCqGSM49BAMCMAAwIhgPOTk5NzExMzAwMDAwMDBa +GA85OTk4MTEzMDAwMDAwMFowADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDcV +7+TGhx5/XwmeNNt2lRdsN7uAhcCaD6di+OgCe2x+Q/Q1arjJQc+ZM7aNdNIkgI8n +jg/CNhILtJEg2RD5/sOjJzAlMCMGA1UdEQQcMBqCGHRvdGFsbHkubm90X2ZpbmUu +d2l0aC5tZTAKBggqhkjOPQQDAgNIADBFAiEA1Tm9lv0/BT4FSK9FgEcuXLUTEXyM +Z9Obi8yEZfmADSACIGewqWiouJphOLA11i8A+TG/YH9kAU2SLDKqpa4K5lr8 +-----END CERTIFICATE----- diff --git a/v3/testdata/dNSNameWithUnderscoreValidityTooLong.pem b/v3/testdata/dNSNameWithUnderscoreValidityTooLong.pem new file mode 100644 index 000000000..7f71b984b --- /dev/null +++ b/v3/testdata/dNSNameWithUnderscoreValidityTooLong.pem @@ -0,0 +1,37 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Jan 1 00:00:00 2018 GMT + Not After : Feb 2 00:00:00 2018 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:4c:e5:06:27:fa:16:7b:56:8d:98:9f:4a:df:d5: + 2c:95:20:96:b4:70:8b:bd:b7:6e:70:04:80:88:4d: + 03:40:0c:df:87:49:3c:a4:c2:aa:97:1d:7b:73:23: + 67:df:66:67:9e:9e:c0:e9:1c:44:f2:73:9d:2c:32: + 01:e0:2c:2e:60 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:totally.not_fine.with.me + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:21:00:bd:8d:e4:be:2c:c7:24:08:96:e3:2b:0d:fb: + 9e:98:dd:58:bf:5f:f4:1f:7d:f7:7e:e5:3f:19:1f:68:c4:69: + f9:02:20:1f:ab:6a:d3:c9:d8:df:00:a9:2a:92:d9:a6:82:82: + 4b:63:cb:36:13:9f:00:1a:24:83:f2:4d:c1:b4:c9:97:f0 +-----BEGIN CERTIFICATE----- +MIIBFTCBvKADAgECAgEDMAoGCCqGSM49BAMCMAAwHhcNMTgwMTAxMDAwMDAwWhcN +MTgwMjAyMDAwMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETOUGJ/oW +e1aNmJ9K39UslSCWtHCLvbducASAiE0DQAzfh0k8pMKqlx17cyNn32Znnp7A6RxE +8nOdLDIB4CwuYKMnMCUwIwYDVR0RBBwwGoIYdG90YWxseS5ub3RfZmluZS53aXRo +Lm1lMAoGCCqGSM49BAMCA0gAMEUCIQC9jeS+LMckCJbjKw37npjdWL9f9B99937l +PxkfaMRp+QIgH6tq08nY3wCpKpLZpoKCS2PLNhOfABokg/JNwbTJl/A= +-----END CERTIFICATE----- diff --git a/v3/testdata/dnsNameNoUnderscore.pem b/v3/testdata/dnsNameNoUnderscore.pem new file mode 100644 index 000000000..e69de29bb diff --git a/v3/util/fqdn.go b/v3/util/fqdn.go index 705104912..02c3dbbea 100644 --- a/v3/util/fqdn.go +++ b/v3/util/fqdn.go @@ -17,6 +17,7 @@ package util import ( "net" "net/url" + "regexp" "strings" zcutil "github.com/zmap/zcrypto/util" @@ -117,3 +118,13 @@ func CommonNameIsIP(cert *x509.Certificate) bool { return true } } + +var nonLDHCharacterRegex = regexp.MustCompile(`[^a-zA-Z0-9\-]`) + +func IsLDHLabel(label string) bool { + return len(label) > 0 && + len(label) <= 63 && + !nonLDHCharacterRegex.MatchString(label) && + !strings.HasPrefix(label, "-") && + !strings.HasSuffix(label, "-") +} diff --git a/v3/util/fqdn_test.go b/v3/util/fqdn_test.go index a3779c9d6..e22c7b0a8 100644 --- a/v3/util/fqdn_test.go +++ b/v3/util/fqdn_test.go @@ -1012,3 +1012,27 @@ func TestGetHostWithUserinfoWithPortWithAbsolutePathWithQueryWithFragment(t *tes ) } } + +func TestIsLDHLabel(t *testing.T) { + data := map[string]bool{ + "": false, + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": false, + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": true, + "9": true, + "9a": true, + "a9": true, + "a": true, + ".": false, + "a-b": true, + "-a": false, + "a-": false, + "-": false, + "%": false, + } + for input, want := range data { + got := IsLDHLabel(input) + if got != want { + t.Errorf("expected %v got %v for '%s'", want, got, input) + } + } +} diff --git a/v3/util/time.go b/v3/util/time.go index 1438f6409..234bc6d8a 100644 --- a/v3/util/time.go +++ b/v3/util/time.go @@ -61,6 +61,7 @@ var ( MozillaPolicy241Date = time.Date(2017, time.March, 31, 0, 0, 0, 0, time.UTC) MozillaPolicy27Date = time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC) CABFBRs_1_2_1_Date = time.Date(2015, time.January, 16, 0, 0, 0, 0, time.UTC) + CABFBRs_1_6_2_Date = time.Date(2019, time.April, 1, 0, 0, 0, 0, time.UTC) CABFBRs_1_6_9_Date = time.Date(2020, time.March, 27, 0, 0, 0, 0, time.UTC) CABFBRs_1_7_1_Date = time.Date(2020, time.August, 20, 0, 0, 0, 0, time.UTC) AppleReducedLifetimeDate = time.Date(2020, time.September, 1, 0, 0, 0, 0, time.UTC)