From 060b3850760d832415fc7b82113f1117ef93e285 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 20 Feb 2024 12:58:54 -0800 Subject: [PATCH] Lint for S/MIME BR 7.1.2.3.g (#797) * Lint for S/MIME BR 7.1.2.3.g * Remove printf * Addresss rewview comments. Check for presence of AuthkeyOID extension. Use error details. --------- Co-authored-by: Zakir Durumeric Co-authored-by: Christopher Henderson --- .../lint_authority_key_identifier.go | 85 ++++++++++++ .../lint_authority_key_identifier_test.go | 34 +++++ .../authority_key_identifier_invalid.pem | 125 ++++++++++++++++++ .../smime/authority_key_identifier_valid.pem | 44 ++++++ 4 files changed, 288 insertions(+) create mode 100644 v3/lints/cabf_smime_br/lint_authority_key_identifier.go create mode 100644 v3/lints/cabf_smime_br/lint_authority_key_identifier_test.go create mode 100644 v3/testdata/smime/authority_key_identifier_invalid.pem create mode 100644 v3/testdata/smime/authority_key_identifier_valid.pem diff --git a/v3/lints/cabf_smime_br/lint_authority_key_identifier.go b/v3/lints/cabf_smime_br/lint_authority_key_identifier.go new file mode 100644 index 000000000..a8c3835b5 --- /dev/null +++ b/v3/lints/cabf_smime_br/lint_authority_key_identifier.go @@ -0,0 +1,85 @@ +package cabf_smime_br + +/* + * ZLint Copyright 2024 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/encoding/asn1" + "github.com/zmap/zcrypto/x509" + "github.com/zmap/zlint/v3/lint" + "github.com/zmap/zlint/v3/util" +) + +type keyIdentifier struct { + KeyIdentifier asn1.RawValue `asn1:"optional,tag:0"` + AuthorityCertIssuer asn1.RawValue `asn1:"optional,tag:1"` + AuthorityCertSerialNumber asn1.RawValue `asn1:"optional,tag:2"` +} + +type authorityKeyIdentifierCorrect struct{} + +func init() { + lint.RegisterCertificateLint(&lint.CertificateLint{ + LintMetadata: lint.LintMetadata{ + Name: "e_authority_key_identifier_correct", + Description: "authorityKeyIdentifier SHALL be present. This extension SHALL NOT be marked critical. The keyIdentifier field SHALL be present. authorityCertIssuer and authorityCertSerialNumber fields SHALL NOT be present.", + Citation: "7.1.2.3.g", + Source: lint.CABFSMIMEBaselineRequirements, + EffectiveDate: util.CABF_SMIME_BRs_1_0_0_Date, + }, + Lint: NewAuthorityKeyIdentifierCorrect, + }) +} + +func NewAuthorityKeyIdentifierCorrect() lint.LintInterface { + return &authorityKeyIdentifierCorrect{} +} + +func (l *authorityKeyIdentifierCorrect) CheckApplies(c *x509.Certificate) bool { + return util.IsSubscriberCert(c) && util.IsSMIMEBRCertificate(c) +} + +func (l *authorityKeyIdentifierCorrect) Execute(c *x509.Certificate) *lint.LintResult { + ext := util.GetExtFromCert(c, util.AuthkeyOID) + if ext == nil { + return &lint.LintResult{Status: lint.Error, Details: "missing authorityKeyIdentifier"} + } + if ext.Critical { + return &lint.LintResult{Status: lint.Error, Details: "authorityKeyIdentifier is critical"} + } + + var keyID keyIdentifier + if _, err := asn1.Unmarshal(ext.Value, &keyID); err != nil { + return &lint.LintResult{ + Status: lint.Fatal, + Details: fmt.Sprintf("error unmarshalling authority key identifier extension: %v", err), + } + } + + hasKeyID := len(keyID.KeyIdentifier.Bytes) > 0 + hasCertIssuer := len(keyID.AuthorityCertIssuer.Bytes) > 0 + hasCertSerial := len(keyID.AuthorityCertSerialNumber.Bytes) > 0 + if !hasKeyID { + return &lint.LintResult{Status: lint.Error, Details: "keyIdentifier not present"} + } + if hasCertIssuer { + return &lint.LintResult{Status: lint.Error, Details: "authorityCertIssuer is present"} + } + if hasCertSerial { + return &lint.LintResult{Status: lint.Error, Details: "authorityCertSerialNumber is present"} + } + return &lint.LintResult{Status: lint.Pass} +} diff --git a/v3/lints/cabf_smime_br/lint_authority_key_identifier_test.go b/v3/lints/cabf_smime_br/lint_authority_key_identifier_test.go new file mode 100644 index 000000000..e708f7fb0 --- /dev/null +++ b/v3/lints/cabf_smime_br/lint_authority_key_identifier_test.go @@ -0,0 +1,34 @@ +package cabf_smime_br + +import ( + "testing" + + "github.com/zmap/zlint/v3/lint" + "github.com/zmap/zlint/v3/test" +) + +func TestAuthorityKeyInfoCorrect(t *testing.T) { + testCases := []struct { + Name string + InputFilename string + ExpectedResult lint.LintStatus + }{ + { + Name: "pass - cert has keyIdentifier", + InputFilename: "smime/authority_key_identifier_valid.pem", + ExpectedResult: lint.Pass, + }, + { + Name: "Error - cert has serial and DirName", + InputFilename: "smime/authority_key_identifier_invalid.pem", + ExpectedResult: lint.Error, + }} + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + result := test.TestLint("e_authority_key_identifier_correct", tc.InputFilename) + if result.Status != tc.ExpectedResult { + t.Errorf("expected result %v was %v - details: %v", tc.ExpectedResult, result.Status, result.Details) + } + }) + } +} diff --git a/v3/testdata/smime/authority_key_identifier_invalid.pem b/v3/testdata/smime/authority_key_identifier_invalid.pem new file mode 100644 index 000000000..f9606c4fc --- /dev/null +++ b/v3/testdata/smime/authority_key_identifier_invalid.pem @@ -0,0 +1,125 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 5f:18:90:8f:43:3e:aa:41:26:31:d1:92:c0:70:a8:1e:b1:71:4f:9f + Signature Algorithm: sha256WithRSAEncryption + Issuer: C = US + Validity + Not Before: Feb 20 20:25:42 2024 GMT + Not After : Feb 19 20:25:42 2025 GMT + Subject: C = US + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (4096 bit) + Modulus: + 00:c5:2b:72:8f:9c:31:e6:93:ea:d9:69:fc:6c:91: + 66:71:d0:2e:25:c1:6d:14:78:c5:4a:18:f7:07:56: + 43:63:d1:f1:42:fa:82:c3:a6:21:a5:37:cd:e7:48: + 68:b5:42:3f:88:5c:38:91:1a:38:00:71:2f:a6:0d: + cb:ed:40:e3:78:86:af:04:0b:b4:f9:61:01:ab:be: + b9:2a:47:44:26:b1:ad:18:e0:60:bf:72:c9:d4:53: + 8c:73:09:40:8f:a3:48:66:ae:1b:b0:49:0e:8b:0c: + 3e:1c:88:1c:61:cb:df:34:b6:c2:f0:41:27:b1:52: + 33:32:ce:41:53:e4:ca:43:73:1a:eb:0c:29:3c:2f: + c0:16:90:f2:78:7a:99:01:1b:2c:96:2c:73:a8:bd: + 0d:fa:b1:65:c0:14:14:4b:5f:a8:42:17:3e:84:29: + 22:99:0b:e9:62:15:6d:c7:b5:45:36:48:a9:4d:b1: + cd:b9:0f:c7:c3:d9:ef:33:ee:a2:d9:2b:c5:aa:12: + 51:72:8a:f4:13:64:c7:c1:05:a7:69:34:30:3f:1a: + ac:ee:ed:c5:fe:31:4a:7d:ef:87:f7:56:aa:3b:1d: + ad:58:8e:bf:bc:65:5d:f3:3c:ad:bd:37:be:81:13: + 99:f8:4b:d8:d4:f2:bc:56:c1:19:4f:f9:49:78:5e: + 8d:02:da:c4:13:26:8f:19:17:bb:f1:ef:fe:bc:6f: + 90:06:ea:9d:b8:29:b6:c1:6b:91:7d:e8:00:48:75: + 83:26:35:f3:f9:78:f9:0e:f9:96:aa:16:bf:b9:58: + d7:cd:0e:e6:e0:04:a0:40:79:a5:6d:9c:65:f5:82: + 97:10:86:de:fe:74:60:ea:1b:35:f5:ca:e4:75:97: + 1c:e8:c0:d0:b3:e5:15:b4:ea:fa:c0:17:5a:cf:9d: + 71:40:d8:66:87:20:2a:de:c6:ce:8b:0d:40:3c:19: + c1:ac:18:bd:4a:2a:5e:69:28:3e:4e:44:66:0c:ee: + 0f:52:fb:06:64:3f:a7:14:14:6f:27:67:26:33:ba: + 2f:66:c9:f0:31:8d:c9:21:cf:1a:63:b3:f4:d0:f4: + 70:7f:30:68:ea:20:76:76:a2:2f:50:c5:a2:23:ed: + 1a:9d:a4:46:c9:35:05:f6:68:1e:ae:d8:39:95:82: + 31:56:72:45:4a:73:f4:a5:c4:1f:2c:32:8e:cc:0c: + 50:7d:c3:4e:7e:cc:f6:93:3b:15:53:f0:a1:a9:21: + 99:ec:25:1d:28:c8:ad:18:cd:e2:17:ab:80:7c:96: + f7:af:fe:e7:aa:8c:20:e3:bc:90:70:a2:cc:21:fd: + 8c:32:26:10:2d:2b:41:89:31:5f:49:be:3d:a9:f4: + 1d:5c:df + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 58:30:87:CF:96:C6:91:64:3D:C9:28:D0:8E:45:5C:E5:D5:67:D9:B4 + X509v3 Authority Key Identifier: + DirName:/C=US + serial:20:AB:CA:DC:2E:98:A1:29:50:AE:53:45:0D:07:8E:EB:F9:A7:DE:E6 + X509v3 Extended Key Usage: + E-mail Protection + X509v3 Certificate Policies: + Policy: 2.23.140.1.5.1.3 + X509v3 Subject Alternative Name: + email:user@example.com + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 84:da:7f:98:34:49:6b:54:3e:31:88:26:60:ea:dd:76:a3:8c: + 4d:a3:ca:de:dc:44:21:35:f5:11:c0:71:c5:b6:02:be:9e:dc: + d1:de:95:08:6b:8f:b1:5c:2d:bc:b6:39:cb:50:38:08:5e:1b: + 7a:32:49:46:c4:70:ae:87:ed:53:21:25:8a:81:ff:81:28:08: + 63:b5:5a:27:77:77:aa:da:a3:6b:50:b8:ac:4e:76:0e:e4:e4: + 7d:d8:eb:91:bc:d5:d1:a4:a1:a3:10:b4:ee:b1:44:b2:aa:59: + ff:ae:11:57:af:8c:1e:ce:a0:3f:67:e8:34:3e:82:db:be:74: + e1:e0:8c:17:8d:1b:eb:cf:f5:1b:6a:4c:da:5a:44:3f:fb:86: + 22:50:ff:65:c3:14:0b:eb:b6:73:32:b8:f8:ab:f6:d3:d0:01: + ff:98:fb:26:8b:0c:41:f4:5e:f8:b9:a9:5e:3e:bc:8b:5a:f9: + 63:f0:81:7e:3d:77:d1:ce:38:4d:b8:5c:61:89:38:b2:bc:ad: + a2:6b:f6:16:fe:e0:a3:ee:a4:13:68:17:65:3d:54:53:59:3b: + 92:86:dc:d0:1d:0f:c7:36:d7:23:73:72:94:fa:4c:bd:2f:4b: + 5c:bb:bd:8a:27:0e:50:1f:2a:87:3c:5e:d2:99:87:87:c5:01: + 43:3c:a7:a4:c0:79:7f:cf:b3:49:8d:98:2b:46:4f:21:a1:68: + c6:ae:07:19:56:1b:5c:5b:9d:71:73:bb:e9:97:da:1f:96:ca: + d3:bb:87:d6:40:c0:27:f4:58:40:81:61:4e:4c:4a:1c:6a:ec: + 3d:d4:0e:e6:42:3e:aa:41:ac:a4:8c:0f:60:25:9c:77:d4:8e: + fc:40:b0:23:39:09:c7:20:40:b0:a7:8b:1a:dd:7b:f1:79:c7: + be:10:42:76:a4:e2:6e:16:8f:46:44:16:e4:9d:c1:2e:6f:e0: + 82:2a:6a:a4:3f:ae:6b:26:0d:de:6e:01:04:ab:0c:35:5f:a1: + 17:b0:c0:ca:aa:44:85:bd:8d:68:41:77:27:03:4e:fd:4c:fc: + a9:e0:d1:49:b9:da:03:c4:a7:83:29:1e:ad:be:f4:13:9d:d1: + e8:bb:ca:ca:41:d3:9a:da:3c:3b:42:b3:71:69:01:c0:bd:8b: + 6b:24:d3:be:21:9c:b7:af:8c:fa:0b:e1:c3:d0:fa:02:9c:cf: + bd:2d:95:76:59:86:80:e5:96:36:a2:82:f8:f0:9c:8c:d6:50: + 65:42:58:c2:25:7c:94:52:4a:25:74:a7:7e:57:4c:32:a9:7f: + bb:dc:af:54:a4:07:86:83:a2:0e:7b:e8:0b:1f:ab:5f:12:19: + 67:49:27:3e:27:d5:91:ac +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIUXxiQj0M+qkEmMdGSwHCoHrFxT58wDQYJKoZIhvcNAQEL +BQAwDTELMAkGA1UEBhMCVVMwHhcNMjQwMjIwMjAyNTQyWhcNMjUwMjE5MjAyNTQy +WjANMQswCQYDVQQGEwJVUzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +AMUrco+cMeaT6tlp/GyRZnHQLiXBbRR4xUoY9wdWQ2PR8UL6gsOmIaU3zedIaLVC +P4hcOJEaOABxL6YNy+1A43iGrwQLtPlhAau+uSpHRCaxrRjgYL9yydRTjHMJQI+j +SGauG7BJDosMPhyIHGHL3zS2wvBBJ7FSMzLOQVPkykNzGusMKTwvwBaQ8nh6mQEb +LJYsc6i9DfqxZcAUFEtfqEIXPoQpIpkL6WIVbce1RTZIqU2xzbkPx8PZ7zPuotkr +xaoSUXKK9BNkx8EFp2k0MD8arO7txf4xSn3vh/dWqjsdrViOv7xlXfM8rb03voET +mfhL2NTyvFbBGU/5SXhejQLaxBMmjxkXu/Hv/rxvkAbqnbgptsFrkX3oAEh1gyY1 +8/l4+Q75lqoWv7lY180O5uAEoEB5pW2cZfWClxCG3v50YOobNfXK5HWXHOjA0LPl +FbTq+sAXWs+dcUDYZocgKt7GzosNQDwZwawYvUoqXmkoPk5EZgzuD1L7BmQ/pxQU +bydnJjO6L2bJ8DGNySHPGmOz9ND0cH8waOogdnaiL1DFoiPtGp2kRsk1BfZoHq7Y +OZWCMVZyRUpz9KXEHywyjswMUH3DTn7M9pM7FVPwoakhmewlHSjIrRjN4hergHyW +96/+56qMIOO8kHCizCH9jDImEC0rQYkxX0m+Pan0HVzfAgMBAAGjgZ4wgZswHQYD +VR0OBBYEFFgwh8+WxpFkPcko0I5FXOXVZ9m0MDIGA1UdIwQrMCmhEaQPMA0xCzAJ +BgNVBAYTAlVTghQgq8rcLpihKVCuU0UNB47r+afe5jATBgNVHSUEDDAKBggrBgEF +BQcDBDAUBgNVHSAEDTALMAkGB2eBDAEFAQMwGwYDVR0RBBQwEoEQdXNlckBleGFt +cGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAgEAhNp/mDRJa1Q+MYgmYOrddqOMTaPK +3txEITX1EcBxxbYCvp7c0d6VCGuPsVwtvLY5y1A4CF4bejJJRsRwroftUyElioH/ +gSgIY7VaJ3d3qtqja1C4rE52DuTkfdjrkbzV0aShoxC07rFEsqpZ/64RV6+MHs6g +P2foND6C27504eCMF40b68/1G2pM2lpEP/uGIlD/ZcMUC+u2czK4+Kv209AB/5j7 +JosMQfRe+LmpXj68i1r5Y/CBfj130c44TbhcYYk4srytomv2Fv7go+6kE2gXZT1U +U1k7kobc0B0PxzbXI3NylPpMvS9LXLu9iicOUB8qhzxe0pmHh8UBQzynpMB5f8+z +SY2YK0ZPIaFoxq4HGVYbXFudcXO76ZfaH5bK07uH1kDAJ/RYQIFhTkxKHGrsPdQO +5kI+qkGspIwPYCWcd9SO/ECwIzkJxyBAsKeLGt178XnHvhBCdqTibhaPRkQW5J3B +Lm/ggipqpD+uayYN3m4BBKsMNV+hF7DAyqpEhb2NaEF3JwNO/Uz8qeDRSbnaA8Sn +gykerb70E53R6LvKykHTmto8O0KzcWkBwL2LayTTviGct6+M+gvhw9D6ApzPvS2V +dlmGgOWWNqKC+PCcjNZQZUJYwiV8lFJKJXSnfldMMql/u9yvVKQHhoOiDnvoCx+r +XxIZZ0knPifVkaw= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/v3/testdata/smime/authority_key_identifier_valid.pem b/v3/testdata/smime/authority_key_identifier_valid.pem new file mode 100644 index 000000000..08d5e28b9 --- /dev/null +++ b/v3/testdata/smime/authority_key_identifier_valid.pem @@ -0,0 +1,44 @@ +ertificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Feb 13 22:41:43 2024 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:60:39:4c:31:a8:73:c7:9f:0e:eb:42:8c:ee:dc: + 4a:99:ff:f6:16:2b:d9:da:a0:70:56:7c:d8:19:55: + 73:8a:c9:1a:1c:5f:63:94:fd:45:6a:bf:7c:8d:63: + 05:25:cf:66:28:9e:e0:61:42:6a:15:87:b4:5d:be: + f9:90:14:90:38 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Extended Key Usage: + E-mail Protection + X509v3 Authority Key Identifier: + 30:3D:13:3B:33:44:3A:44:33:3A:35:30:3A:41:35:3A:44:36:3A:41:30:3A:41:44:3A:45:45:3A:46:33:3A:34:41:3A:36:30:3A:30:41:3A:36:35:3A:44:33:3A:32:31:3A:44:34:3A:46:38:3A:46:38:3A:44:36:3A:30:46 + X509v3 Certificate Policies: + Policy: 2.23.140.1.5.1.3 + Signature Algorithm: ecdsa-with-SHA256 + Signature Value: + 30:46:02:21:00:f1:95:21:f6:11:f5:ec:e1:75:22:72:dc:3a: + af:5a:79:97:64:50:5e:bf:c3:67:93:61:74:cb:a8:29:42:5f: + e7:02:21:00:ae:6b:4e:55:27:64:6e:5f:a9:a5:13:1d:ec:ca: + df:7d:76:ab:6e:ed:ab:6e:a5:87:2d:19:69:7f:59:0e:d1:b4 +-----BEGIN CERTIFICATE----- +MIIBazCCARCgAwIBAgIBAzAKBggqhkjOPQQDAjAAMCAXDTI0MDIxMzIyNDE0M1oY +Dzk5OTgxMTMwMDAwMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYDlM +Mahzx58O60KM7txKmf/2FivZ2qBwVnzYGVVziskaHF9jlP1Far98jWMFJc9mKJ7g +YUJqFYe0Xb75kBSQOKN5MHcwEwYDVR0lBAwwCgYIKwYBBQUHAwQwSgYDVR0jBEMw +QYA/MD0TOzNEOkQzOjUwOkE1OkQ2OkEwOkFEOkVFOkYzOjRBOjYwOjBBOjY1OkQz +OjIxOkQ0OkY4OkY4OkQ2OjBGMBQGA1UdIAQNMAswCQYHZ4EMAQUBAzAKBggqhkjO +PQQDAgNJADBGAiEA8ZUh9hH17OF1InLcOq9aeZdkUF6/w2eTYXTLqClCX+cCIQCu +a05VJ2RuX6mlEx3syt99dqtu7atupYctGWl/WQ7RtA== +-----END CERTIFICATE-----