diff --git a/v3/lint/base.go b/v3/lint/base.go index 6c6e5f514..1160f2fda 100644 --- a/v3/lint/base.go +++ b/v3/lint/base.go @@ -221,6 +221,9 @@ func (l *CertificateLint) Execute(cert *x509.Certificate, config Configuration) if l.Source == CABFBaselineRequirements && !util.IsServerAuthCert(cert) { return &LintResult{Status: NA} } + if l.Source == CABFSMIMEBaselineRequirements && !util.IsEmailProtectionCert(cert) { + return &LintResult{Status: NA} + } lint := l.Lint() err := config.MaybeConfigure(lint, l.Name) if err != nil { diff --git a/v3/lint/source.go b/v3/lint/source.go index c1808c063..aefeb8186 100644 --- a/v3/lint/source.go +++ b/v3/lint/source.go @@ -27,18 +27,19 @@ import ( type LintSource string const ( - UnknownLintSource LintSource = "Unknown" - RFC3279 LintSource = "RFC3279" - RFC5280 LintSource = "RFC5280" - RFC5480 LintSource = "RFC5480" - RFC5891 LintSource = "RFC5891" - RFC8813 LintSource = "RFC8813" - CABFBaselineRequirements LintSource = "CABF_BR" - CABFEVGuidelines LintSource = "CABF_EV" - MozillaRootStorePolicy LintSource = "Mozilla" - AppleRootStorePolicy LintSource = "Apple" - Community LintSource = "Community" - EtsiEsi LintSource = "ETSI_ESI" + UnknownLintSource LintSource = "Unknown" + RFC3279 LintSource = "RFC3279" + RFC5280 LintSource = "RFC5280" + RFC5480 LintSource = "RFC5480" + RFC5891 LintSource = "RFC5891" + RFC8813 LintSource = "RFC8813" + CABFBaselineRequirements LintSource = "CABF_BR" + CABFSMIMEBaselineRequirements LintSource = "CABF_SMIME_BR" + CABFEVGuidelines LintSource = "CABF_EV" + MozillaRootStorePolicy LintSource = "Mozilla" + AppleRootStorePolicy LintSource = "Apple" + Community LintSource = "Community" + EtsiEsi LintSource = "ETSI_ESI" ) // UnmarshalJSON implements the json.Unmarshaler interface. It ensures that the diff --git a/v3/lints/cabf_smime_br/mailbox_validated_enforce_subject_field_restrictions.go b/v3/lints/cabf_smime_br/mailbox_validated_enforce_subject_field_restrictions.go new file mode 100644 index 000000000..fdb366fb9 --- /dev/null +++ b/v3/lints/cabf_smime_br/mailbox_validated_enforce_subject_field_restrictions.go @@ -0,0 +1,99 @@ +package cabf_smime_br + +/* + * 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. + */ + +import ( + "fmt" + + "github.com/zmap/zcrypto/x509" + "github.com/zmap/zlint/v3/lint" + "github.com/zmap/zlint/v3/util" +) + +// mailboxValidatedEnforceSubjectFieldRestrictions - linter to enforce MAY/SHALL NOT requirements for mailbox validated SMIME certificates +type mailboxValidatedEnforceSubjectFieldRestrictions struct { + forbiddenSubjectFields map[string]string + allowedSubjectFields map[string]string +} + +func init() { + lint.RegisterCertificateLint(&lint.CertificateLint{ + LintMetadata: lint.LintMetadata{ + Name: "e_mailbox_validated_enforce_subject_field_restrictions", + Description: "SMIME certificates complying to mailbox validated profiles MAY only contain commonName, serialNumber or emailAddress attributes in the Subject DN", + Citation: "SMIME BRs: 7.1.4.2.3", + Source: lint.CABFSMIMEBaselineRequirements, + EffectiveDate: util.CABF_SMIME_BRs_1_0_0_Date, + }, + Lint: func() lint.CertificateLintInterface { + return NewMailboxValidatedEnforceSubjectFieldRestrictions() + }, + }) +} + +// NewMailboxValidatedEnforceSubjectFieldRestrictions creates a new linter to enforce MAY/SHALL NOT field requirements for mailbox validated SMIME certs +func NewMailboxValidatedEnforceSubjectFieldRestrictions() lint.LintInterface { + return &mailboxValidatedEnforceSubjectFieldRestrictions{ + forbiddenSubjectFields: map[string]string{ + "0.9.2342.19200300.100.1.25": "subject:domainComponent", + "1.3.6.1.4.1.311.60.2.1.1": "subject:jurisdictionLocality", + "1.3.6.1.4.1.311.60.2.1.2": "subject:jurisdictionProvince", + "1.3.6.1.4.1.311.60.2.1.3": "subject:jurisdictionCountry", + "2.5.4.4": "subject:surname", + "2.5.4.6": "subject:countryName", + "2.5.4.7": "subject:localityName", + "2.5.4.8": "subject:stateOrProvinceName", + "2.5.4.9": "subject:streetAddress", + "2.5.4.10": "subject:organizationName", + "2.5.4.11": "subject:organizationalUnitName", + "2.5.4.12": "subject:title", + "2.5.4.17": "subject:postalCode", + "2.5.4.42": "subject:givenName", + "2.5.4.65": "subject:pseudonym", + "2.5.4.97": "subject:organizationIdentifier", + }, + allowedSubjectFields: map[string]string{ + "1.2.840.113549.1.9.1": "subject:emailAddress", + "2.5.4.3": "subject:commonName", + "2.5.4.5": "subject:serialNumber", + }, + } +} + +// CheckApplies returns true if the provided certificate contains one-or-more of the following SMIME BR policy identifiers: +// - Mailbox Validated Legacy +// - Mailbox Validated Multipurpose +// - Mailbox Validated Strict +func (l *mailboxValidatedEnforceSubjectFieldRestrictions) CheckApplies(c *x509.Certificate) bool { + return util.IsMailboxValidatedCertificate(c) +} + +// Execute applies the requirements on what fields are allowed for mailbox validated SMIME certificates +func (l *mailboxValidatedEnforceSubjectFieldRestrictions) Execute(c *x509.Certificate) *lint.LintResult { + for _, rdnSeq := range c.Subject.OriginalRDNS { + for _, field := range rdnSeq { + oidStr := field.Type.String() + + if _, ok := l.allowedSubjectFields[oidStr]; !ok { + if fieldName, knownField := l.forbiddenSubjectFields[oidStr]; knownField { + return &lint.LintResult{Status: lint.Error, Details: fmt.Sprintf("subject DN contains forbidden field: %s (%s)", fieldName, oidStr)} + } + return &lint.LintResult{Status: lint.Error, Details: fmt.Sprintf("subject DN contains forbidden field: %s", oidStr)} + } + } + } + + return &lint.LintResult{Status: lint.Pass} +} diff --git a/v3/lints/cabf_smime_br/mailbox_validated_enforce_subject_field_restrictions_test.go b/v3/lints/cabf_smime_br/mailbox_validated_enforce_subject_field_restrictions_test.go new file mode 100644 index 000000000..fd1560dc8 --- /dev/null +++ b/v3/lints/cabf_smime_br/mailbox_validated_enforce_subject_field_restrictions_test.go @@ -0,0 +1,83 @@ +package cabf_smime_br + +/* + * 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. + */ + +import ( + "testing" + + "github.com/zmap/zlint/v3/lint" + "github.com/zmap/zlint/v3/test" +) + +func TestMailboxValidatedEnforceSubjectFieldRestrictions(t *testing.T) { + testCases := []struct { + Name string + InputFilename string + + ExpectedResult lint.LintStatus + ExpectedDetails string + }{ + { + Name: "pass - mailbox validated, legacy with commonName", + InputFilename: "smime/mailboxValidatedLegacyWithCommonName.pem", + ExpectedResult: lint.Pass, + }, + { + Name: "pass - mailbox validated, multipurpose with commonName", + InputFilename: "smime/mailboxValidatedMultipurposeWithCommonName.pem", + ExpectedResult: lint.Pass, + }, + { + Name: "pass - mailbox validated, strict with commonName", + InputFilename: "smime/mailboxValidatedStrictWithCommonName.pem", + ExpectedResult: lint.Pass, + }, + { + Name: "na - certificate without mailbox validated policy", + InputFilename: "smime/domainValidatedWithEmailCommonName.pem", + ExpectedResult: lint.NA, + }, + { + Name: "ne - certificate with NotBefore before effective date of lint", + InputFilename: "smime/mailboxValidatedLegacyWithCommonNameMay2023.pem", + ExpectedResult: lint.NE, + }, + { + Name: "error - certificate with countryName", + InputFilename: "smime/mailboxValidatedLegacyWithCountryName.pem", + ExpectedResult: lint.Error, + ExpectedDetails: "subject DN contains forbidden field: subject:countryName (2.5.4.6)", + }, + { + Name: "error - certificate containing nonsense subject field (1.2.3.4.5.6.7.8.9.0)", + InputFilename: "smime/mailboxValidatedMultipurposeWithNonsenseSubjectField.pem", + ExpectedResult: lint.Error, + ExpectedDetails: "subject DN contains forbidden field: 1.2.3.4.5.6.7.8.9.0", + }, + } + + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + result := test.TestLint("e_mailbox_validated_enforce_subject_field_restrictions", tc.InputFilename) + if result.Status != tc.ExpectedResult { + t.Errorf("expected result %v was %v - details: %v", tc.ExpectedResult, result.Status, result.Details) + } + + if tc.ExpectedDetails != "" && tc.ExpectedDetails != result.Details { + t.Errorf("expected details: %s, was %s", tc.ExpectedDetails, result.Details) + } + }) + } +} diff --git a/v3/profiles/profiles_test.go b/v3/profiles/profiles_test.go index dc28d2bfb..35cab07db 100644 --- a/v3/profiles/profiles_test.go +++ b/v3/profiles/profiles_test.go @@ -22,6 +22,7 @@ import ( _ "github.com/zmap/zlint/v3/lints/apple" _ "github.com/zmap/zlint/v3/lints/cabf_br" _ "github.com/zmap/zlint/v3/lints/cabf_ev" + _ "github.com/zmap/zlint/v3/lints/cabf_smime_br" _ "github.com/zmap/zlint/v3/lints/community" _ "github.com/zmap/zlint/v3/lints/etsi" _ "github.com/zmap/zlint/v3/lints/mozilla" @@ -45,13 +46,14 @@ func TestLintsInAllProfilesExist(t *testing.T) { // lint source in the future that we don't miss importing it into this test file. func TestNotMissingAnyLintSources(t *testing.T) { expected := map[string]bool{ - "apple": true, - "cabf_br": true, - "cabf_ev": true, - "community": true, - "etsi": true, - "mozilla": true, - "rfc": true, + "apple": true, + "cabf_br": true, + "cabf_ev": true, + "cabf_smime_br": true, + "community": true, + "etsi": true, + "mozilla": true, + "rfc": true, } dir, err := ioutil.ReadDir("../lints") if err != nil { diff --git a/v3/testdata/smime/domainValidatedWithEmailCommonName.pem b/v3/testdata/smime/domainValidatedWithEmailCommonName.pem new file mode 100644 index 000000000..79b007920 --- /dev/null +++ b/v3/testdata/smime/domainValidatedWithEmailCommonName.pem @@ -0,0 +1,39 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Sep 2 00:00:00 2023 GMT + Not After : Nov 30 00:00:00 9998 GMT + Subject: CN = johnsmith@example.com + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:30:eb:57:97:dc:90:9a:27:8f:7f:39:80:fa:21: + aa:3d:48:b1:35:6d:39:97:cf:9e:a4:ca:42:22:0c: + b2:71:67:42:bb:f4:a3:56:4a:51:fc:5e:0f:ec:ed: + 98:9e:11:cf:f0:8a:68:62:c4:bf:8f:7b:65:ec:30: + 69:d5:64:41:76 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Certificate Policies: + Policy: 2.23.140.1.2.1 + + Signature Algorithm: ecdsa-with-SHA256 + 30:46:02:21:00:ab:fa:9a:25:c9:b9:5f:c3:7c:bf:c1:dd:d2: + dc:4f:00:ad:1d:b7:18:94:0f:a2:37:9d:34:13:b7:cf:7d:a1: + da:02:21:00:f3:20:3b:d8:74:0e:b9:8d:6e:7a:74:d1:00:c8: + 72:fb:2c:34:6d:c0:c4:7e:5b:25:ef:04:27:5c:88:22:47:6f +-----BEGIN CERTIFICATE----- +MIIBKDCBzqADAgECAgEDMAoGCCqGSM49BAMCMAAwIBcNMjMwOTAyMDAwMDAwWhgP +OTk5ODExMzAwMDAwMDBaMCAxHjAcBgNVBAMMFWpvaG5zbWl0aEBleGFtcGxlLmNv +bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDDrV5fckJonj385gPohqj1IsTVt +OZfPnqTKQiIMsnFnQrv0o1ZKUfxeD+ztmJ4Rz/CKaGLEv497ZewwadVkQXajFzAV +MBMGA1UdIAQMMAowCAYGZ4EMAQIBMAoGCCqGSM49BAMCA0kAMEYCIQCr+polyblf +w3y/wd3S3E8ArR23GJQPojedNBO3z32h2gIhAPMgO9h0DrmNbnp00QDIcvssNG3A +xH5bJe8EJ1yIIkdv +-----END CERTIFICATE----- \ No newline at end of file diff --git a/v3/testdata/smime/mailboxValidatedLegacyWithCommonName.pem b/v3/testdata/smime/mailboxValidatedLegacyWithCommonName.pem new file mode 100644 index 000000000..7cc031221 --- /dev/null +++ b/v3/testdata/smime/mailboxValidatedLegacyWithCommonName.pem @@ -0,0 +1,39 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Sep 2 00:00:00 2023 GMT + Not After : Nov 30 00:00:00 9998 GMT + Subject: CN = johnsmith@example.com + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:a1:ed:8b:dd:62:fc:cc:2d:f4:28:cd:8c:8d:5a: + 1d:1f:6c:36:c3:03:81:b4:9f:6e:6d:2d:90:b1:7d: + fa:2f:eb:d6:3c:83:7c:9f:2c:5a:b4:37:3e:ae:56: + 57:6b:db:df:6a:1c:db:73:e6:d4:25:b1:15:d6:47: + f2:71:de:51:d0 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Certificate Policies: + Policy: 2.23.140.1.5.1.1 + + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:20:41:fa:93:51:d2:80:69:a5:5e:4a:cb:85:6a:1e: + 47:eb:cb:9b:b3:7b:2b:94:a7:be:a4:b2:55:cc:4a:15:16:f7: + 02:21:00:81:0c:18:bd:55:7a:16:6a:0c:84:a9:3b:bf:29:e2: + 21:d0:fd:b6:9b:99:14:5b:0b:55:a8:43:b9:64:b6:8e:dc +-----BEGIN CERTIFICATE----- +MIIBKDCBz6ADAgECAgEDMAoGCCqGSM49BAMCMAAwIBcNMjMwOTAyMDAwMDAwWhgP +OTk5ODExMzAwMDAwMDBaMCAxHjAcBgNVBAMMFWpvaG5zbWl0aEBleGFtcGxlLmNv +bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKHti91i/Mwt9CjNjI1aHR9sNsMD +gbSfbm0tkLF9+i/r1jyDfJ8sWrQ3Pq5WV2vb32oc23Pm1CWxFdZH8nHeUdCjGDAW +MBQGA1UdIAQNMAswCQYHZ4EMAQUBATAKBggqhkjOPQQDAgNIADBFAiBB+pNR0oBp +pV5Ky4VqHkfry5uzeyuUp76kslXMShUW9wIhAIEMGL1VehZqDISpO78p4iHQ/bab +mRRbC1WoQ7lkto7c +-----END CERTIFICATE----- \ No newline at end of file diff --git a/v3/testdata/smime/mailboxValidatedLegacyWithCommonNameMay2023.pem b/v3/testdata/smime/mailboxValidatedLegacyWithCommonNameMay2023.pem new file mode 100644 index 000000000..d6b8b2053 --- /dev/null +++ b/v3/testdata/smime/mailboxValidatedLegacyWithCommonNameMay2023.pem @@ -0,0 +1,39 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: May 2 00:00:00 2023 GMT + Not After : Nov 30 00:00:00 9998 GMT + Subject: CN = johnsmith@example.com + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:60:a6:a1:36:40:de:33:5a:09:73:86:a9:30:2c: + cb:43:aa:d7:77:f4:77:37:d7:bf:4c:f5:48:24:39: + 1b:8f:fc:51:0a:77:81:3a:6e:34:c2:1c:ef:a8:03: + 39:42:21:16:2e:1a:f7:ed:8d:0e:38:e0:9f:23:52: + 04:3c:9e:9d:c4 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Certificate Policies: + Policy: 2.23.140.1.5.1.1 + + Signature Algorithm: ecdsa-with-SHA256 + 30:46:02:21:00:c8:88:94:49:ba:b0:73:0f:f0:c9:26:0c:5a: + 99:a0:36:b4:6b:e0:cf:c1:2f:49:9b:cb:bc:d7:ac:52:97:f0: + ca:02:21:00:a5:14:41:7c:46:dc:dd:af:02:89:0e:3b:79:17: + 16:c0:b1:3c:4a:c2:e3:e8:e5:51:9e:e9:9b:a1:69:01:c5:a0 +-----BEGIN CERTIFICATE----- +MIIBKTCBz6ADAgECAgEDMAoGCCqGSM49BAMCMAAwIBcNMjMwNTAyMDAwMDAwWhgP +OTk5ODExMzAwMDAwMDBaMCAxHjAcBgNVBAMMFWpvaG5zbWl0aEBleGFtcGxlLmNv +bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGCmoTZA3jNaCXOGqTAsy0Oq13f0 +dzfXv0z1SCQ5G4/8UQp3gTpuNMIc76gDOUIhFi4a9+2NDjjgnyNSBDyencSjGDAW +MBQGA1UdIAQNMAswCQYHZ4EMAQUBATAKBggqhkjOPQQDAgNJADBGAiEAyIiUSbqw +cw/wySYMWpmgNrRr4M/BL0mby7zXrFKX8MoCIQClFEF8RtzdrwKJDjt5FxbAsTxK +wuPo5VGe6ZuhaQHFoA== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/v3/testdata/smime/mailboxValidatedLegacyWithCountryName.pem b/v3/testdata/smime/mailboxValidatedLegacyWithCountryName.pem new file mode 100644 index 000000000..909b19fc6 --- /dev/null +++ b/v3/testdata/smime/mailboxValidatedLegacyWithCountryName.pem @@ -0,0 +1,39 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Sep 2 00:00:00 2023 GMT + Not After : Nov 30 00:00:00 9998 GMT + Subject: CN = johnsmith@example.com, C = US + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:1d:18:18:38:d0:29:57:63:f6:1e:e6:be:c1:5e: + c6:45:65:5a:94:c4:68:6c:95:2e:47:7b:fd:d3:1b: + d8:6b:18:d1:82:88:71:46:3a:8f:c2:6f:55:a8:a1: + 4c:1e:85:fd:76:f1:a7:69:49:2e:dd:51:19:fd:b7: + e4:6c:87:b4:0e + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Certificate Policies: + Policy: 2.23.140.1.5.1.1 + + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:20:7f:b7:d1:00:a3:3e:98:dc:fb:65:b9:af:e4:2b: + 11:9a:33:bf:a6:0c:15:6d:6b:44:f1:eb:49:1b:7b:56:a2:e4: + 02:21:00:d7:07:19:62:05:db:65:41:f4:58:36:e8:81:81:6d: + fe:00:b4:83:37:ef:e7:ae:3c:85:cb:76:2e:fe:b7:47:6a +-----BEGIN CERTIFICATE----- +MIIBNTCB3KADAgECAgEDMAoGCCqGSM49BAMCMAAwIBcNMjMwOTAyMDAwMDAwWhgP +OTk5ODExMzAwMDAwMDBaMC0xHjAcBgNVBAMMFWpvaG5zbWl0aEBleGFtcGxlLmNv +bTELMAkGA1UEBhMCVVMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQdGBg40ClX +Y/Ye5r7BXsZFZVqUxGhslS5He/3TG9hrGNGCiHFGOo/Cb1WooUwehf128adpSS7d +URn9t+Rsh7QOoxgwFjAUBgNVHSAEDTALMAkGB2eBDAEFAQEwCgYIKoZIzj0EAwID +SAAwRQIgf7fRAKM+mNz7Zbmv5CsRmjO/pgwVbWtE8etJG3tWouQCIQDXBxliBdtl +QfRYNuiBgW3+ALSDN+/nrjyFy3Yu/rdHag== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/v3/testdata/smime/mailboxValidatedMultipurposeWithCommonName.pem b/v3/testdata/smime/mailboxValidatedMultipurposeWithCommonName.pem new file mode 100644 index 000000000..a3033aab9 --- /dev/null +++ b/v3/testdata/smime/mailboxValidatedMultipurposeWithCommonName.pem @@ -0,0 +1,39 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Sep 2 00:00:00 2023 GMT + Not After : Nov 30 00:00:00 9998 GMT + Subject: CN = johnsmith@example.com + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:23:7b:69:18:e3:f9:a1:37:a7:15:52:3a:93:26: + 4a:16:57:35:f9:2a:d5:63:ba:51:a5:84:27:71:db: + e8:87:cb:aa:bc:e1:37:39:0b:dc:6a:9f:c9:02:61: + a9:60:ae:e6:01:a4:c7:84:ee:65:f1:08:ba:fa:51: + 35:bf:5c:2e:27 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Certificate Policies: + Policy: 2.23.140.1.5.1.2 + + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:21:00:df:00:d5:9c:b2:9c:af:09:35:3e:fb:09:18: + 98:05:1d:83:3b:7f:56:24:68:d3:0c:aa:11:ca:b2:1d:82:6a: + 93:02:20:6b:da:55:22:5d:84:59:c2:a0:c8:22:f6:3b:ef:34: + ac:3a:67:6e:c6:b0:c2:29:db:4f:fe:68:36:c7:39:b1:02 +-----BEGIN CERTIFICATE----- +MIIBKDCBz6ADAgECAgEDMAoGCCqGSM49BAMCMAAwIBcNMjMwOTAyMDAwMDAwWhgP +OTk5ODExMzAwMDAwMDBaMCAxHjAcBgNVBAMMFWpvaG5zbWl0aEBleGFtcGxlLmNv +bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCN7aRjj+aE3pxVSOpMmShZXNfkq +1WO6UaWEJ3Hb6IfLqrzhNzkL3GqfyQJhqWCu5gGkx4TuZfEIuvpRNb9cLiejGDAW +MBQGA1UdIAQNMAswCQYHZ4EMAQUBAjAKBggqhkjOPQQDAgNIADBFAiEA3wDVnLKc +rwk1PvsJGJgFHYM7f1YkaNMMqhHKsh2CapMCIGvaVSJdhFnCoMgi9jvvNKw6Z27G +sMIp20/+aDbHObEC +-----END CERTIFICATE----- \ No newline at end of file diff --git a/v3/testdata/smime/mailboxValidatedMultipurposeWithNonsenseSubjectField.pem b/v3/testdata/smime/mailboxValidatedMultipurposeWithNonsenseSubjectField.pem new file mode 100644 index 000000000..dcfbda03b --- /dev/null +++ b/v3/testdata/smime/mailboxValidatedMultipurposeWithNonsenseSubjectField.pem @@ -0,0 +1,39 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Sep 2 00:00:00 2023 GMT + Not After : Nov 30 00:00:00 9998 GMT + Subject: 1.2.3.4.5.6.7.8.9.0 = any old rubbish + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:e7:21:34:a8:97:83:75:62:ff:6d:8f:dc:95:69: + 24:08:76:19:c4:7a:83:f4:93:ba:10:05:dd:a8:e4: + c4:20:69:22:19:f2:96:ed:d9:9b:1a:cc:78:6b:bf: + ce:1b:21:7c:c2:6e:d1:40:dc:d9:66:a8:cc:24:f2: + 6b:18:d9:59:2f + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Certificate Policies: + Policy: 2.23.140.1.5.1.2 + + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:20:38:f1:af:88:ec:b2:fc:81:9b:bb:73:a1:9c:4f: + c9:79:e8:7f:ff:a7:c4:71:45:8f:9a:1d:67:54:54:57:8a:cb: + 02:21:00:9e:4e:c7:2f:0b:54:d8:6e:5f:43:1c:e8:79:c2:c1: + 7a:46:1b:ec:da:91:d9:42:03:b5:5a:64:e0:86:95:ed:c4 +-----BEGIN CERTIFICATE----- +MIIBKDCBz6ADAgECAgEDMAoGCCqGSM49BAMCMAAwIBcNMjMwOTAyMDAwMDAwWhgP +OTk5ODExMzAwMDAwMDBaMCAxHjAcBgkqAwQFBgcICQATD2FueSBvbGQgcnViYmlz +aDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOchNKiXg3Vi/22P3JVpJAh2GcR6 +g/STuhAF3ajkxCBpIhnylu3ZmxrMeGu/zhshfMJu0UDc2WaozCTyaxjZWS+jGDAW +MBQGA1UdIAQNMAswCQYHZ4EMAQUBAjAKBggqhkjOPQQDAgNIADBFAiA48a+I7LL8 +gZu7c6GcT8l56H//p8RxRY+aHWdUVFeKywIhAJ5Oxy8LVNhuX0Mc6HnCwXpGG+za +kdlCA7VaZOCGle3E +-----END CERTIFICATE----- \ No newline at end of file diff --git a/v3/testdata/smime/mailboxValidatedStrictWithCommonName.pem b/v3/testdata/smime/mailboxValidatedStrictWithCommonName.pem new file mode 100644 index 000000000..9c546e380 --- /dev/null +++ b/v3/testdata/smime/mailboxValidatedStrictWithCommonName.pem @@ -0,0 +1,39 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Sep 2 00:00:00 2023 GMT + Not After : Nov 30 00:00:00 9998 GMT + Subject: CN = johnsmith@example.com + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:40:42:9c:5b:49:e2:31:38:01:3d:07:42:a1:4c: + c8:43:2b:0a:cd:62:3d:5b:40:4a:e1:f6:ed:df:06: + a8:d3:cc:fd:bf:21:c1:4a:48:41:bb:3f:c1:66:a8: + 12:b3:84:40:97:18:a3:b9:ce:3e:31:cb:d4:48:84: + 81:12:52:93:df + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Certificate Policies: + Policy: 2.23.140.1.5.1.3 + + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:20:09:34:af:8d:f7:20:90:a3:2e:de:44:12:8c:92: + c7:cf:02:73:b7:c3:e1:fb:fd:32:2a:19:65:7f:37:b8:f0:25: + 02:21:00:d0:50:43:27:a6:91:f0:52:7d:73:9d:ca:7c:6f:9d: + 7e:00:84:c9:3f:3f:2f:02:91:da:11:a1:6f:09:6f:a0:7a +-----BEGIN CERTIFICATE----- +MIIBKDCBz6ADAgECAgEDMAoGCCqGSM49BAMCMAAwIBcNMjMwOTAyMDAwMDAwWhgP +OTk5ODExMzAwMDAwMDBaMCAxHjAcBgNVBAMMFWpvaG5zbWl0aEBleGFtcGxlLmNv +bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEBCnFtJ4jE4AT0HQqFMyEMrCs1i +PVtASuH27d8GqNPM/b8hwUpIQbs/wWaoErOEQJcYo7nOPjHL1EiEgRJSk9+jGDAW +MBQGA1UdIAQNMAswCQYHZ4EMAQUBAzAKBggqhkjOPQQDAgNIADBFAiAJNK+N9yCQ +oy7eRBKMksfPAnO3w+H7/TIqGWV/N7jwJQIhANBQQyemkfBSfXOdynxvnX4AhMk/ +Py8CkdoRoW8Jb6B6 +-----END CERTIFICATE----- \ No newline at end of file diff --git a/v3/util/ca.go b/v3/util/ca.go index c24634811..8a1bb5504 100644 --- a/v3/util/ca.go +++ b/v3/util/ca.go @@ -62,3 +62,19 @@ func IsServerAuthCert(cert *x509.Certificate) bool { } return false } + +// IsEmailProtectionCert returns true if the certificate presented is for use protecting emails. +// A certificate is for use protecting emails if it contains the Any Purpose or emailProtection +// EKUs or if the certificate contains no EKUs. This last point is a way of being overly cautious +// and choosing to prefer false positives over false negatives. +func IsEmailProtectionCert(cert *x509.Certificate) bool { + if len(cert.ExtKeyUsage) == 0 { + return true + } + for _, eku := range cert.ExtKeyUsage { + if eku == x509.ExtKeyUsageAny || eku == x509.ExtKeyUsageEmailProtection { + return true + } + } + return false +} diff --git a/v3/util/oid.go b/v3/util/oid.go index 0f3143454..5abe25b57 100644 --- a/v3/util/oid.go +++ b/v3/util/oid.go @@ -50,11 +50,14 @@ var ( SubjectKeyIdentityOID = asn1.ObjectIdentifier{2, 5, 29, 14} // Subject Key Identifier ReasonCodeOID = asn1.ObjectIdentifier{2, 5, 29, 21} // CRL Reason Code // CA/B reserved policies - BRDomainValidatedOID = asn1.ObjectIdentifier{2, 23, 140, 1, 2, 1} // CA/B BR Domain-Validated - BROrganizationValidatedOID = asn1.ObjectIdentifier{2, 23, 140, 1, 2, 2} // CA/B BR Organization-Validated - BRIndividualValidatedOID = asn1.ObjectIdentifier{2, 23, 140, 1, 2, 3} // CA/B BR Individual-Validated - BRTorServiceDescriptor = asn1.ObjectIdentifier{2, 23, 140, 1, 31} // CA/B BR Tor Service Descriptor - CabfExtensionOrganizationIdentifier = asn1.ObjectIdentifier{2, 23, 140, 3, 1} // CA/B EV 9.8.2 cabfOrganizationIdentifier + BRDomainValidatedOID = asn1.ObjectIdentifier{2, 23, 140, 1, 2, 1} // CA/B BR Domain-Validated + BROrganizationValidatedOID = asn1.ObjectIdentifier{2, 23, 140, 1, 2, 2} // CA/B BR Organization-Validated + BRIndividualValidatedOID = asn1.ObjectIdentifier{2, 23, 140, 1, 2, 3} // CA/B BR Individual-Validated + BRTorServiceDescriptor = asn1.ObjectIdentifier{2, 23, 140, 1, 31} // CA/B BR Tor Service Descriptor + CabfExtensionOrganizationIdentifier = asn1.ObjectIdentifier{2, 23, 140, 3, 1} // CA/B EV 9.8.2 cabfOrganizationIdentifier + SMIMEBRMailboxValidatedLegacyOID = asn1.ObjectIdentifier{2, 23, 140, 1, 5, 1, 1} // CA/B SMIME BR Mailbox Validated, Legacy + SMIMEBRMailboxValidatedMultipurposeOID = asn1.ObjectIdentifier{2, 23, 140, 1, 5, 1, 2} // CA/B SMIME BR Mailbox Validated, Multipurpose + SMIMEBRMailboxValidatedStrictOID = asn1.ObjectIdentifier{2, 23, 140, 1, 5, 1, 3} // CA/B SMIME BR Mailbox Validated, Strict //X.500 attribute types CommonNameOID = asn1.ObjectIdentifier{2, 5, 4, 3} SurnameOID = asn1.ObjectIdentifier{2, 5, 4, 4} diff --git a/v3/util/smime_policies.go b/v3/util/smime_policies.go new file mode 100644 index 000000000..051e483ba --- /dev/null +++ b/v3/util/smime_policies.go @@ -0,0 +1,27 @@ +package util + +/* + * 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. + */ + +import "github.com/zmap/zcrypto/x509" + +func IsMailboxValidatedCertificate(c *x509.Certificate) bool { + for _, oid := range c.PolicyIdentifiers { + if oid.Equal(SMIMEBRMailboxValidatedLegacyOID) || oid.Equal(SMIMEBRMailboxValidatedMultipurposeOID) || oid.Equal(SMIMEBRMailboxValidatedStrictOID) { + return true + } + } + + return false +} diff --git a/v3/util/time.go b/v3/util/time.go index 79e3a39cf..c075b7eec 100644 --- a/v3/util/time.go +++ b/v3/util/time.go @@ -72,6 +72,7 @@ var ( CABFBRs_1_8_0_Date = time.Date(2021, time.August, 25, 0, 0, 0, 0, time.UTC) NoReservedDomainLabelsDate = time.Date(2021, time.October, 1, 0, 0, 0, 0, time.UTC) CABFBRs_OU_Prohibited_Date = time.Date(2022, time.September, 1, 0, 0, 0, 0, time.UTC) + CABF_SMIME_BRs_1_0_0_Date = time.Date(2023, time.September, 1, 0, 0, 0, 0, time.UTC) // Enforcement date of CRL reason codes from Ballot SC 061 CABFBRs_1_8_7_Date = time.Date(2023, time.July, 15, 0, 0, 0, 0, time.UTC) )