Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

E ext cert policy disallowed any policy qualifier refactor #732

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
531 changes: 531 additions & 0 deletions h

Large diffs are not rendered by default.

113 changes: 109 additions & 4 deletions v3/lints/rfc/lint_ext_cert_policy_disallowed_any_policy_qualifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,21 @@ package rfc
*/

import (
"errors"

"github.com/zmap/zcrypto/encoding/asn1"
"github.com/zmap/zcrypto/x509"
"github.com/zmap/zlint/v3/lint"
"github.com/zmap/zlint/v3/util"
)

type unrecommendedQualifier struct{}

type policyInformation struct {
policyIdentifier asn1.ObjectIdentifier
policyQualifiersBytes asn1.RawValue
}

/*******************************************************************
RFC 5280: 4.2.1.4
To promote interoperability, this profile RECOMMENDS that policy
Expand Down Expand Up @@ -49,16 +57,113 @@ func NewUnrecommendedQualifier() lint.LintInterface {
}

func (l *unrecommendedQualifier) CheckApplies(c *x509.Certificate) bool {
return util.IsExtInCert(c, util.CertPolicyOID)

// TODO? extract to util method: HasAnyPolicyOID(c)
if !util.IsExtInCert(c, util.CertPolicyOID) {
return false
}

for _, policyIds := range c.PolicyIdentifiers {
if policyIds.Equal(util.AnyPolicyOID) {
return true
}
}
return false
}

func (l *unrecommendedQualifier) Execute(c *x509.Certificate) *lint.LintResult {
for _, firstLvl := range c.QualifierId {
for _, qualifierId := range firstLvl {
if !qualifierId.Equal(util.CpsOID) && !qualifierId.Equal(util.UserNoticeOID) {

var err, certificatePolicies = getCertificatePolicies(c)

if err != nil {
return &lint.LintResult{Status: lint.Fatal, Details: err.Error()}
}

for _, policyInformation := range certificatePolicies {

if !policyInformation.policyIdentifier.Equal(util.AnyPolicyOID) { // if the policyIdentifier is not anyPolicy do not examine further
continue
}

if len(policyInformation.policyQualifiersBytes.Bytes) == 0 { // this policy information does not have any policyQualifiers
continue
}

var policyQualifiersSeq, policyQualifierInfoSeq asn1.RawValue

empty, err := asn1.Unmarshal(policyInformation.policyQualifiersBytes.Bytes, &policyQualifiersSeq)

if err != nil || len(empty) != 0 || policyQualifiersSeq.Class != 0 || policyQualifiersSeq.Tag != 16 || !policyQualifiersSeq.IsCompound {
return &lint.LintResult{Status: lint.Fatal, Details: "policyExtensions: Could not unmarshal policyQualifiers sequence."}
}

//iterate over policyQualifiers ... SEQUENCE SIZE (1..MAX) OF PolicyQualifierInfo OPTIONAL
for policyQualifierInfoSeqProcessed := false; !policyQualifierInfoSeqProcessed; {
// these bytes belong to the next PolicyQualifierInfo
policyQualifiersSeq.Bytes, err = asn1.Unmarshal(policyQualifiersSeq.Bytes, &policyQualifierInfoSeq)
if err != nil || policyQualifierInfoSeq.Class != 0 || policyQualifierInfoSeq.Tag != 16 || !policyQualifierInfoSeq.IsCompound {
return &lint.LintResult{Status: lint.Fatal, Details: "policyExtensions: Could not unmarshal policy qualifiers"}
}
if len(policyQualifiersSeq.Bytes) == 0 { // no further PolicyQualifierInfo exists
policyQualifierInfoSeqProcessed = true
}

var policyQualifierId asn1.ObjectIdentifier
_, err = asn1.Unmarshal(policyQualifierInfoSeq.Bytes, &policyQualifierId)
if err != nil {
return &lint.LintResult{Status: lint.Fatal, Details: "policyExtensions: Could not unmarshal policyQualifierId."}
}

if !policyQualifierId.Equal(util.CpsOID) && !policyQualifierId.Equal(util.UserNoticeOID) {
return &lint.LintResult{Status: lint.Error}
}
}
}

return &lint.LintResult{Status: lint.Pass}
}

func getCertificatePolicies(c *x509.Certificate) (error, []policyInformation) {

extVal := util.GetExtFromCert(c, util.CertPolicyOID).Value

// adjusted code taken from v3/util/oid.go GetMappedPolicies, see comments there
var certificatePoliciesSeq, policyInformationSeq asn1.RawValue

empty, err := asn1.Unmarshal(extVal, &certificatePoliciesSeq)

if err != nil || len(empty) != 0 || certificatePoliciesSeq.Class != 0 || certificatePoliciesSeq.Tag != 16 || !certificatePoliciesSeq.IsCompound {
return errors.New("policyExtensions: Could not unmarshal certificatePolicies sequence."), nil
}

var certificatePolicies []policyInformation

// iterate over certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
for policyInformationSeqProcessed := false; !policyInformationSeqProcessed; {

// these bytes belong to the next PolicyInformation
certificatePoliciesSeq.Bytes, err = asn1.Unmarshal(certificatePoliciesSeq.Bytes, &policyInformationSeq)
if err != nil || policyInformationSeq.Class != 0 || policyInformationSeq.Tag != 16 || !policyInformationSeq.IsCompound {
return errors.New("policyExtensions: Could not unmarshal policyInformation sequence."), nil
}

if len(certificatePoliciesSeq.Bytes) == 0 { // no further PolicyInformation exists
policyInformationSeqProcessed = true
}

//PolicyInformation ::= SEQUENCE {
// policyIdentifier CertPolicyId,
// policyQualifiers SEQUENCE SIZE (1..MAX) OF PolicyQualifierInfo OPTIONAL }

var certPolicyId asn1.ObjectIdentifier
var policyQualifiers asn1.RawValue
policyQualifiers.Bytes, err = asn1.Unmarshal(policyInformationSeq.Bytes, &certPolicyId)
if err != nil {
return errors.New("policyExtensions: Could not unmarshal certPolicyId."), nil
}

information := policyInformation{certPolicyId, policyQualifiers}
certificatePolicies = append(certificatePolicies, information)
}
return nil, certificatePolicies
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,55 @@ import (
"github.com/zmap/zlint/v3/test"
)

func TestNoticeRef(t *testing.T) {
inputPath := "userNoticePres.pem"
expected := lint.Pass
out := test.TestLint("e_ext_cert_policy_disallowed_any_policy_qualifier", inputPath)
if out.Status != expected {
t.Errorf("%s: expected %s, got %s", inputPath, expected, out.Status)
func TestUnrecommendedQualifier(t *testing.T) {
testCases := []struct {
Name string
InputFilename string
ExpectedResult lint.LintStatus
}{
{
Name: "Certificate with certificate policies extension and without the anyPolicy policyIdentifier present",
InputFilename: "withoutAnyPolicy.pem",
ExpectedResult: lint.NA,
},
{
Name: "Certificate without certificate policies extension",
InputFilename: "CNWithoutSANSeptember2021.pem",
ExpectedResult: lint.NA,
},
{
Name: "Certificate with certificate policies extension, with anyPolicy policyIdentifier present, without policyQualifiers",
InputFilename: "withAnyPolicyAndNoPolicyQualifiers.pem",
ExpectedResult: lint.Pass,
},
{
Name: "Certificate with certificate policies extension, with anyPolicy policyIdentifier present and a CPS qualifier present",
InputFilename: "withAnyPolicyAndCPSQualifier.pem",
ExpectedResult: lint.Pass,
},
{
Name: "Certificate with certificate policies extension, with anyPolicy policyIdentifier present and a UserNotice qualifier present",
InputFilename: "withAnyPolicyAndUserNoticeQualifier.pem",
ExpectedResult: lint.Pass,
},
{
Name: "Certificate with certificate policies extension, with anyPolicy policyIdentifier present and neither CPS nor UserNotice qualifier present",
InputFilename: "withAnyPolicyWithoutCPSOrUserNoticeQualifier.pem",
ExpectedResult: lint.Error,
},
{
Name: "Certificate with certificate policies extension and many combinations of policies and qualifiers",
InputFilename: "withValidPoliciesRegardingAnyPolicy.pem",
ExpectedResult: lint.Pass,
},
}
}

func TestCps(t *testing.T) {
inputPath := "userNoticeMissing.pem"
expected := lint.Pass
out := test.TestLint("e_ext_cert_policy_disallowed_any_policy_qualifier", inputPath)
if out.Status != expected {
t.Errorf("%s: expected %s, got %s", inputPath, expected, out.Status)
}
}

func TestNoticeRefUnknown(t *testing.T) {
inputPath := "userNoticeUnrecommended.pem"
expected := lint.Error
out := test.TestLint("e_ext_cert_policy_disallowed_any_policy_qualifier", inputPath)
if out.Status != expected {
t.Errorf("%s: expected %s, got %s", inputPath, expected, out.Status)
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
result := test.TestLint("e_ext_cert_policy_disallowed_any_policy_qualifier", tc.InputFilename)
if result.Status != tc.ExpectedResult {
t.Errorf("expected result %v was %v", tc.ExpectedResult, result.Status)
}
})
}
}
82 changes: 82 additions & 0 deletions v3/testdata/withAnyPolicyAndCPSQualifier.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
67:32:5c:93:e9:a2:32:b8:61:f6:d6:e2
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = JLint Sub CA, O = Lint, C = DE
Validity
Not Before: Jul 1 14:48:19 2023 GMT
Not After : Jul 1 15:48:19 2024 GMT
Subject: CN = e_ext_cert_policy_disallowed_any_policy_qualifier, O = Lint, C = DE
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:c6:ee:a4:ff:af:f9:d3:57:78:a1:35:b9:b9:6e:
f1:67:fd:3e:d3:b1:e5:13:25:5a:34:eb:68:7c:ea:
ae:32:01:e1:98:15:15:32:c3:03:75:e5:d6:2e:56:
2d:03:34:28:25:e0:77:b8:db:1a:47:d9:ff:b1:d4:
31:6a:d2:8e:ab:64:3a:0e:a3:e8:53:40:4f:ff:55:
32:1d:59:a6:db:09:20:aa:c3:ee:57:ca:90:8d:de:
26:2c:f5:b3:b3:45:d6:32:81:18:46:44:ad:1e:f8:
92:a3:ed:b3:af:e5:72:80:3d:0b:c8:fc:fa:a1:e6:
20:16:d7:18:70:4b:4a:c1:5f:a7:3b:aa:26:75:36:
7a:13:62:98:2e:8f:18:5c:c0:e7:88:40:36:03:44:
91:a9:80:3c:6a:dd:36:b1:53:ff:1b:d8:8a:97:ef:
06:04:e0:ce:8b:53:4e:24:5d:89:9e:75:b1:31:75:
bf:b3:26:ba:6b:08:70:49:b8:b8:76:2c:27:07:e7:
a6:e5:ee:ac:de:f6:28:6b:b8:78:0e:b0:53:12:c2:
0e:d7:b2:b7:e6:c2:e8:1d:2c:b1:6e:ac:19:a3:88:
14:67:3e:7b:67:04:34:e5:8d:90:23:06:63:e0:c3:
6b:b0:e1:c6:75:54:7d:47:47:c9:26:14:07:1a:e1:
6f:c5
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Authority Key Identifier:
keyid:C4:8F:CF:FE:87:49:92:71:70:4E:93:BC:C1:34:21:EE:A0:93:65:84

X509v3 Subject Key Identifier:
22:BF:08:67:F1:B4:F2:53:77:63:B5:3A:39:74:A3:80:C1:2F:C3:D1
X509v3 Certificate Policies: critical
Policy: X509v3 Any Policy
CPS: https://example.com/cps

Signature Algorithm: sha256WithRSAEncryption
3e:38:b8:e6:68:5f:81:95:8f:de:5f:dc:9a:82:8b:93:78:6d:
16:ba:7e:dd:57:72:9e:91:72:21:07:b0:22:3a:83:68:b9:b2:
26:ed:5b:9b:b9:b5:ac:49:8a:4c:8d:6f:32:cc:24:e7:b8:99:
2b:b9:47:68:4a:55:9a:7a:74:de:06:a6:2d:58:57:00:89:76:
ac:ec:99:4f:44:69:28:21:25:31:9b:35:9d:82:46:bf:9d:0e:
05:ff:58:a5:df:df:19:d2:df:4f:e2:ed:0d:85:d7:7d:98:e8:
dd:80:d8:e1:c5:3c:82:1b:69:3e:82:03:fc:2b:d5:87:37:c3:
b1:dc:06:f3:8e:83:42:90:b8:1c:2d:91:44:8c:8b:5a:eb:5c:
dc:77:86:e7:39:7b:c2:3c:40:1f:1c:5e:ad:f0:b4:2c:ad:45:
81:82:a2:37:17:c5:05:80:d5:9c:ee:f8:24:ea:2f:91:e2:95:
32:38:a0:fd:77:3c:ad:97:58:ff:3b:ba:0e:fd:a7:1a:06:61:
a0:6c:02:08:20:df:4e:9e:ab:f0:92:62:65:09:83:54:3e:17:
b4:a3:3a:8c:2c:c4:03:4d:5c:a7:bf:84:0f:0a:39:61:c5:39:
5c:8d:8a:24:0b:31:84:d1:76:2a:74:1b:da:9b:f9:13:c9:9e:
5f:f0:c1:34
-----BEGIN CERTIFICATE-----
MIIDkDCCAnigAwIBAgIMZzJck+miMrhh9tbiMA0GCSqGSIb3DQEBCwUAMDMxFTAT
BgNVBAMMDEpMaW50IFN1YiBDQTENMAsGA1UECgwETGludDELMAkGA1UEBhMCREUw
HhcNMjMwNzAxMTQ0ODE5WhcNMjQwNzAxMTU0ODE5WjBYMTowOAYDVQQDDDFlX2V4
dF9jZXJ0X3BvbGljeV9kaXNhbGxvd2VkX2FueV9wb2xpY3lfcXVhbGlmaWVyMQ0w
CwYDVQQKDARMaW50MQswCQYDVQQGEwJERTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAMbupP+v+dNXeKE1ublu8Wf9PtOx5RMlWjTraHzqrjIB4ZgVFTLD
A3Xl1i5WLQM0KCXgd7jbGkfZ/7HUMWrSjqtkOg6j6FNAT/9VMh1ZptsJIKrD7lfK
kI3eJiz1s7NF1jKBGEZErR74kqPts6/lcoA9C8j8+qHmIBbXGHBLSsFfpzuqJnU2
ehNimC6PGFzA54hANgNEkamAPGrdNrFT/xvYipfvBgTgzotTTiRdiZ51sTF1v7Mm
umsIcEm4uHYsJwfnpuXurN72KGu4eA6wUxLCDteyt+bC6B0ssW6sGaOIFGc+e2cE
NOWNkCMGY+DDa7DhxnVUfUdHySYUBxrhb8UCAwEAAaN/MH0wHwYDVR0jBBgwFoAU
xI/P/odJknFwTpO8wTQh7qCTZYQwHQYDVR0OBBYEFCK/CGfxtPJTd2O1Ojl0o4DB
L8PRMDsGA1UdIAEB/wQxMC8wLQYEVR0gADAlMCMGCCsGAQUFBwIBFhdodHRwczov
L2V4YW1wbGUuY29tL2NwczANBgkqhkiG9w0BAQsFAAOCAQEAPji45mhfgZWP3l/c
moKLk3htFrp+3VdynpFyIQewIjqDaLmyJu1bm7m1rEmKTI1vMswk57iZK7lHaEpV
mnp03gamLVhXAIl2rOyZT0RpKCElMZs1nYJGv50OBf9Ypd/fGdLfT+LtDYXXfZjo
3YDY4cU8ghtpPoID/CvVhzfDsdwG846DQpC4HC2RRIyLWutc3HeG5zl7wjxAHxxe
rfC0LK1FgYKiNxfFBYDVnO74JOovkeKVMjig/Xc8rZdY/zu6Dv2nGgZhoGwCCCDf
Tp6r8JJiZQmDVD4XtKM6jCzEA01cp7+EDwo5YcU5XI2KJAsxhNF2KnQb2pv5E8me
X/DBNA==
-----END CERTIFICATE-----
80 changes: 80 additions & 0 deletions v3/testdata/withAnyPolicyAndNoPolicyQualifiers.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
87:51:1e:16:2e:f7:22:25:c8:a6:34:15
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = JLint Sub CA, O = Lint, C = DE
Validity
Not Before: Jul 1 14:48:19 2023 GMT
Not After : Jul 1 15:48:19 2024 GMT
Subject: CN = e_ext_cert_policy_disallowed_any_policy_qualifier, O = Lint, C = DE
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:a9:ab:6e:ba:1c:b8:e9:08:e2:30:06:3a:9a:16:
ee:07:a5:aa:24:27:f0:d2:67:aa:bd:82:98:53:8d:
c8:a2:82:47:ee:30:66:94:1e:ae:37:b9:81:0a:fe:
03:72:d8:00:2b:7b:1d:81:25:be:47:3d:2e:fc:9b:
64:19:eb:91:b6:a6:0e:a6:f1:60:ce:bd:e7:ff:78:
94:68:a4:96:25:df:4e:0e:c8:a5:c6:f8:15:6f:76:
34:16:ed:01:f5:c8:6e:9e:47:dd:24:c4:33:3f:d4:
d3:62:8c:51:83:d5:d1:aa:c0:ce:52:77:80:10:6d:
98:fc:41:8c:63:64:b9:81:56:f1:0b:a8:67:70:3d:
98:77:16:93:42:64:55:88:8b:39:89:32:60:91:4b:
eb:11:30:4d:49:91:fa:f5:0e:7a:b5:18:e8:45:cc:
37:b2:e3:4a:f5:8e:d1:4f:94:2e:89:5d:8c:1a:79:
d7:79:91:1c:c8:cd:fd:85:8e:c0:75:41:e0:25:a0:
fb:4e:5d:42:88:98:85:23:35:d0:39:56:2c:7f:37:
68:cf:ab:33:0f:63:98:11:77:64:74:16:bd:20:70:
e5:2d:17:ad:f7:84:4e:39:51:6b:ab:50:73:01:31:
04:54:b1:e7:02:3d:d0:1a:41:39:03:18:86:29:45:
ef:15
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Authority Key Identifier:
keyid:C4:8F:CF:FE:87:49:92:71:70:4E:93:BC:C1:34:21:EE:A0:93:65:84

X509v3 Subject Key Identifier:
A3:6D:DA:40:AC:DC:B7:A4:E2:3D:D1:5B:F3:C5:F3:65:BC:57:6B:85
X509v3 Certificate Policies: critical
Policy: X509v3 Any Policy

Signature Algorithm: sha256WithRSAEncryption
b0:84:c9:75:ab:d7:b7:c7:02:cb:eb:44:06:cd:ba:38:9a:9a:
1b:d5:fe:c5:77:65:69:38:54:26:ce:f1:d9:34:e4:2f:e8:11:
cb:89:15:2d:2d:4a:fd:5c:9f:11:93:10:d9:a6:4e:71:b6:61:
c8:41:f9:91:15:70:50:af:c6:6d:5b:ed:53:ba:a6:86:1a:68:
d9:24:2a:45:da:cd:8f:bb:55:61:68:6f:1b:39:07:8d:be:5b:
df:5e:41:a1:59:95:0b:ea:e4:b5:08:67:4b:4e:36:d8:67:78:
12:08:a4:a3:49:42:1f:98:c6:5f:7c:9c:49:39:ee:4d:ef:f0:
44:de:fc:b7:92:c1:9d:30:25:c9:58:fe:11:4a:2e:8e:99:88:
24:1c:bd:72:a0:55:22:bc:d2:1c:c3:5e:3b:d2:94:00:49:4e:
e6:ba:80:6d:19:2a:e4:32:d1:08:1d:49:cd:80:3c:48:76:9c:
30:ff:1b:c5:5d:53:0b:4c:b1:70:0a:1b:02:9e:71:66:9f:61:
76:73:d7:a1:13:53:3a:21:a4:ad:b1:e5:7e:9f:46:de:58:9b:
59:83:33:85:00:2d:87:08:a6:29:9c:b7:c9:01:10:d6:65:2b:
60:76:2c:d0:e0:7c:41:3c:8e:91:70:e5:93:0a:b3:eb:59:1e:
9a:f0:fa:b1
-----BEGIN CERTIFICATE-----
MIIDajCCAlKgAwIBAgINAIdRHhYu9yIlyKY0FTANBgkqhkiG9w0BAQsFADAzMRUw
EwYDVQQDDAxKTGludCBTdWIgQ0ExDTALBgNVBAoMBExpbnQxCzAJBgNVBAYTAkRF
MB4XDTIzMDcwMTE0NDgxOVoXDTI0MDcwMTE1NDgxOVowWDE6MDgGA1UEAwwxZV9l
eHRfY2VydF9wb2xpY3lfZGlzYWxsb3dlZF9hbnlfcG9saWN5X3F1YWxpZmllcjEN
MAsGA1UECgwETGludDELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQCpq266HLjpCOIwBjqaFu4HpaokJ/DSZ6q9gphTjciigkfuMGaU
Hq43uYEK/gNy2AArex2BJb5HPS78m2QZ65G2pg6m8WDOvef/eJRopJYl304OyKXG
+BVvdjQW7QH1yG6eR90kxDM/1NNijFGD1dGqwM5Sd4AQbZj8QYxjZLmBVvELqGdw
PZh3FpNCZFWIizmJMmCRS+sRME1Jkfr1Dnq1GOhFzDey40r1jtFPlC6JXYwaedd5
kRzIzf2FjsB1QeAloPtOXUKImIUjNdA5Vix/N2jPqzMPY5gRd2R0Fr0gcOUtF633
hE45UWurUHMBMQRUsecCPdAaQTkDGIYpRe8VAgMBAAGjWDBWMB8GA1UdIwQYMBaA
FMSPz/6HSZJxcE6TvME0Ie6gk2WEMB0GA1UdDgQWBBSjbdpArNy3pOI90VvzxfNl
vFdrhTAUBgNVHSABAf8ECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQELBQADggEBALCE
yXWr17fHAsvrRAbNujiamhvV/sV3ZWk4VCbO8dk05C/oEcuJFS0tSv1cnxGTENmm
TnG2YchB+ZEVcFCvxm1b7VO6poYaaNkkKkXazY+7VWFobxs5B42+W99eQaFZlQvq
5LUIZ0tONthneBIIpKNJQh+Yxl98nEk57k3v8ETe/LeSwZ0wJclY/hFKLo6ZiCQc
vXKgVSK80hzDXjvSlABJTua6gG0ZKuQy0QgdSc2APEh2nDD/G8VdUwtMsXAKGwKe
cWafYXZz16ETUzohpK2x5X6fRt5Ym1mDM4UALYcIpimct8kBENZlK2B2LNDgfEE8
jpFw5ZMKs+tZHprw+rE=
-----END CERTIFICATE-----
Loading