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

Add lint for checking that Subject attributes (RDNs) appear in the order prescribed by CABF BR 7.1.4.2 #813

Merged
merged 11 commits into from
Mar 10, 2024
145 changes: 145 additions & 0 deletions v3/lints/cabf_br/lint_invalid_subject_rdn_order.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* 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.
*/

/*
* Contributed by Adriano Santoni <adriano.santoni@staff.aruba.it>
* of ACTALIS S.p.A. (www.actalis.com).
*/

package cabf_br

import (
"crypto/x509/pkix"
"encoding/asn1"

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

func init() {
lint.RegisterCertificateLint(&lint.CertificateLint{
LintMetadata: lint.LintMetadata{
Name: "e_invalid_subject_rdn_order",
Description: "Subject field attributes (RDNs) SHALL be encoded in a specific order",
Citation: "BRs: 7.1.4.2",
Source: lint.CABFBaselineRequirements,
EffectiveDate: util.CABFBRs_2_0_0_Date,
},
Lint: NewInvalidSubjectRDNOrder,
})
}

type invalidSubjectRDNOrder struct{}

func NewInvalidSubjectRDNOrder() lint.LintInterface {
return &invalidSubjectRDNOrder{}
}

func (l *invalidSubjectRDNOrder) CheckApplies(c *x509.Certificate) bool {
return util.IsSubscriberCert(c)
}

func getShortOIDName(oid string) string {
defacto64 marked this conversation as resolved.
Show resolved Hide resolved
switch oid {
case "0.9.2342.19200300.100.1.25":
return "DC"
case "2.5.4.6":
return "C"
case "2.5.4.8":
return "ST"
case "2.5.4.7":
return "L"
case "2.5.4.17":
return "postalCode"
case "2.5.4.9":
return "street"
case "2.5.4.10":
return "O"
case "2.5.4.4":
return "SN"
case "2.5.4.42":
return "givenName"
case "2.5.4.11":
return "OU"
case "2.5.4.3":
return "CN"
default:
return ""
}
}

func findElement(arr []string, target string) (int, bool) {
for i, value := range arr {
if value == target {
return i, true
}
}
return -1, false
}

func checkOrder(actualOrder []string, expectedOrder []string) bool {
var prevPosition int
prevPosition = 0

for _, targetElement := range actualOrder {
position, found := findElement(expectedOrder, targetElement)
if found {
if position < prevPosition {
return false
}
prevPosition = position
}
}
return true
}

func checkSubjectRDNOrder(cert *x509.Certificate) bool {

rawSubject := cert.RawSubject

var rdnSequence pkix.RDNSequence
_, err := asn1.Unmarshal(rawSubject, &rdnSequence)
if err != nil {
return false
}

var rdnOrder []string

for _, rdn := range rdnSequence {
for _, atv := range rdn {
rdnShortName := getShortOIDName(atv.Type.String())
if rdnShortName != "" {
rdnOrder = append(rdnOrder, rdnShortName)
}
}
}

// Expected order of RDNs as per CABF BR section 7.1.4.2
expectedRDNOrder := []string{"DC", "C", "ST", "L", "postalCode", "street", "O", "SN", "givenName", "OU", "CN"}

return checkOrder(rdnOrder, expectedRDNOrder)
}

func (l *invalidSubjectRDNOrder) Execute(c *x509.Certificate) *lint.LintResult {

var out lint.LintResult

if checkSubjectRDNOrder(c) {
out.Status = lint.Pass
} else {
out.Status = lint.Error
}
return &out
}
122 changes: 122 additions & 0 deletions v3/lints/cabf_br/lint_invalid_subject_rdn_order_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* 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.
*/

/*
* Contributed by Adriano Santoni <adriano.santoni@staff.aruba.it>
* of ACTALIS S.p.A. (www.actalis.com).
*/

package cabf_br

import (
"testing"

"github.com/zmap/zlint/v3/lint"
"github.com/zmap/zlint/v3/test"
)

//nolint:all
/*
=== Proper RDN order test cases
subject_rdn_order_ok_01.pem C, ST, L, O, CN
subject_rdn_order_ok_02.pem C, ST, L, postalCode, street, O, CN
subject_rdn_order_ok_03.pem <empty subject>
subject_rdn_order_ok_04.pem DC, DC, C, ST, L, O, CN
subject_rdn_order_ok_05.pem C, ST, L, street, O, CN, serialNumber, businessCategory, jurisdictionCountry
subject_rdn_order_ok_06.pem C, ST, L, SN, givenName, CN
subject_rdn_order_ok_07.pem CN

=== Wrong RDN order test cases
subject_rdn_order_ko_01.pem C, ST, L, CN, O
subject_rdn_order_ko_02.pem CN, O, L, ST, C
subject_rdn_order_ko_03.pem C, ST, L, O, CN, street
subject_rdn_order_ko_04.pem C, ST, L, O, CN, DC, DC
subject_rdn_order_ko_05.pem C, ST, L, givenName, SN, CN
subject_rdn_order_ko_06.pem C, ST, L, street, postalCode, O
subject_rdn_order_ko_07.pem CN, C
*/

func TestInvalidSubjectRDNOrder(t *testing.T) {
type Data struct {
input string
want lint.LintStatus
}
data := []Data{
{
input: "subject_rdn_order_ok_01.pem",
want: lint.Pass,
},
{
input: "subject_rdn_order_ok_02.pem",
want: lint.Pass,
},
{
input: "subject_rdn_order_ok_03.pem",
want: lint.Pass,
},
{
input: "subject_rdn_order_ok_04.pem",
want: lint.Pass,
},
{
input: "subject_rdn_order_ok_05.pem",
want: lint.Pass,
},
{
input: "subject_rdn_order_ok_06.pem",
want: lint.Pass,
},
{
input: "subject_rdn_order_ok_07.pem",
want: lint.Pass,
},
{
input: "subject_rdn_order_ko_01.pem",
want: lint.Error,
},
{
input: "subject_rdn_order_ko_02.pem",
want: lint.Error,
},
{
input: "subject_rdn_order_ko_03.pem",
want: lint.Error,
},
{
input: "subject_rdn_order_ko_04.pem",
want: lint.Error,
},
{
input: "subject_rdn_order_ko_05.pem",
want: lint.Error,
},
{
input: "subject_rdn_order_ko_06.pem",
want: lint.Error,
},
{
input: "subject_rdn_order_ko_07.pem",
want: lint.Error,
},
}
for _, testData := range data {
testData := testData
t.Run(testData.input, func(t *testing.T) {
out := test.TestLint("e_invalid_subject_rdn_order", testData.input)
if out.Status != testData.want {
t.Errorf("expected %s, got %s", testData.want, out.Status)
}
})
}
}
92 changes: 92 additions & 0 deletions v3/testdata/subject_rdn_order_ko_01.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 9092871303437831039 (0x7e305e463dc14b7f)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = IT, ST = Milano, L = Santa Redegonda, O = Certificati Gratis S.p.A., CN = Certificati Gratis CA
Validity
Not Before: Mar 8 10:10:00 2024 GMT
Not After : Mar 8 08:50:00 2025 GMT
Subject: C = IT, ST = Milano, L = Milano, CN = example.org, O = Example
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:bc:ae:30:0d:6a:39:0c:02:14:f6:98:c2:97:6e:
c3:e2:a3:27:f8:e1:48:da:66:17:d7:d4:23:f9:47:
e0:6c:67:ea:a4:7b:54:fa:b2:50:21:86:0b:69:7a:
67:a2:e8:44:05:9d:fc:50:82:cc:91:3d:ef:22:d3:
af:83:aa:90:db:69:89:d4:9c:e3:97:81:cf:c3:59:
d9:c1:64:3c:aa:f3:42:25:3c:ae:3d:2a:48:cd:25:
25:ae:59:d9:79:bb:e6:26:d3:cb:44:fa:21:5b:d5:
e3:89:9b:6f:96:f1:fc:3a:5b:c4:0c:52:89:46:48:
7b:41:4c:84:9f:cf:79:10:05:52:74:9c:e1:12:29:
d7:3b:d8:10:b9:7d:44:73:da:f5:60:ce:1e:54:e9:
b1:1d:7f:4c:ac:2c:23:f3:91:59:12:df:f9:07:a3:
da:be:8e:18:a1:b5:74:60:e2:f9:64:52:30:65:f9:
e8:75:22:21:4d:f6:4f:e2:47:c4:5b:f7:ea:b2:be:
90:3d:9a:13:f3:7e:51:c7:6e:3e:bb:3f:43:9c:c7:
aa:e1:26:11:e6:40:c5:ab:b2:4a:f3:44:36:19:8f:
3d:d6:4a:45:1d:d2:db:03:53:ee:64:16:92:95:6e:
92:ab:19:33:06:d8:ad:4d:a1:1e:39:4d:44:80:3c:
e9:23
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Extended Key Usage:
TLS Web Server Authentication
Signature Algorithm: sha256WithRSAEncryption
6f:1f:bd:b4:2c:a6:67:95:07:73:cb:79:1a:a5:99:e1:c8:f6:
73:6e:53:0e:15:a1:c3:3e:07:a8:0f:6b:31:09:89:f6:d1:2b:
42:aa:f8:62:4e:0d:dc:fc:03:f3:de:8e:e3:bf:c8:3c:b0:69:
f6:23:11:01:fa:aa:9c:c8:24:4e:f0:7a:86:d9:dc:79:b7:96:
ec:f5:70:6e:f0:73:7c:3f:56:5b:a7:48:d8:da:bb:bc:2c:ba:
dc:c0:c1:f5:1b:76:5d:1a:1d:ad:e6:f2:22:50:3f:06:fa:06:
f9:ec:6c:05:a2:5f:22:62:ef:80:de:20:48:31:7f:90:c0:9b:
f6:1b:d8:4e:36:55:03:fb:c6:d2:bf:bd:d5:2c:55:37:f0:75:
2f:e7:96:43:29:ea:01:f7:89:75:72:ef:af:f8:31:a6:9c:3a:
13:68:77:54:7d:75:05:fe:d6:b2:33:9b:d1:07:24:9d:8f:20:
34:7a:19:ed:ae:94:47:3d:65:42:3d:ba:87:0d:61:ce:aa:57:
0e:c5:bc:da:8b:9e:23:42:d2:76:fb:4f:c6:7f:62:66:b2:38:
67:2c:3f:32:4b:2f:0a:78:51:ae:8c:8f:4f:49:72:6e:c7:78:
65:d5:8b:e3:da:2a:55:35:b4:31:71:4c:9c:48:a0:74:ca:4e:
a2:c6:12:a3:96:fb:dd:08:49:82:0b:2e:30:18:91:3c:e2:d2:
e5:22:8f:b3:f6:d6:11:88:b6:df:ba:3b:88:49:3d:92:c6:d0:
d2:b2:0c:2b:4d:60:3f:47:a0:a9:82:4b:c8:13:09:f3:f2:71:
2b:d6:7d:cf:67:5c:a8:2c:0e:3f:a9:e8:a6:8b:17:41:9f:77:
a9:04:5c:65:a8:4d:40:17:6c:ef:07:ef:a1:4f:fa:2e:78:f5:
64:71:44:9d:b6:b0:26:e7:20:1e:06:e1:7c:24:a4:5b:2d:4e:
80:ee:69:27:1e:6e:4a:e1:33:be:8d:06:8c:14:61:50:98:7f:
5e:d8:d2:58:37:21:8a:46:6a:0c:70:4f:22:4a:05:75:9e:00:
72:e0:74:f4:f1:86:6f:3e:fa:88:0b:35:34:89:bb:53:80:b0:
29:d7:af:5c:8c:9d:7a:a3:8e:04:c2:4c:22:7a:3d:ff:c9:50:
24:8a:3a:19:62:9c:46:97:b6:aa:75:0a:d3:d5:88:eb:1a:ce:
df:fc:b8:89:f0:6c:a6:a7:7d:1c:72:49:6c:cf:5e:8b:32:f6:
e1:27:95:39:94:7c:6a:e2:9c:14:04:26:0f:45:6e:81:a2:fd:
39:45:3c:1f:9b:ff:1b:ff:71:1a:d4:12:10:57:71:bb:ab:f4:
5f:35:82:63:fb:59:b8:10
-----BEGIN CERTIFICATE-----
MIIEbDCCAlSgAwIBAgIIfjBeRj3BS38wDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE
BhMCSVQxDzANBgNVBAgTBk1pbGFubzEYMBYGA1UEBxMPU2FudGEgUmVkZWdvbmRh
MSIwIAYDVQQKExlDZXJ0aWZpY2F0aSBHcmF0aXMgUy5wLkEuMR4wHAYDVQQDExVD
ZXJ0aWZpY2F0aSBHcmF0aXMgQ0EwHhcNMjQwMzA4MTAxMDAwWhcNMjUwMzA4MDg1
MDAwWjBXMQswCQYDVQQGEwJJVDEPMA0GA1UECBMGTWlsYW5vMQ8wDQYDVQQHEwZN
aWxhbm8xFDASBgNVBAMTC2V4YW1wbGUub3JnMRAwDgYDVQQKEwdFeGFtcGxlMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvK4wDWo5DAIU9pjCl27D4qMn
+OFI2mYX19Qj+UfgbGfqpHtU+rJQIYYLaXpnouhEBZ38UILMkT3vItOvg6qQ22mJ
1Jzjl4HPw1nZwWQ8qvNCJTyuPSpIzSUlrlnZebvmJtPLRPohW9XjiZtvlvH8OlvE
DFKJRkh7QUyEn895EAVSdJzhEinXO9gQuX1Ec9r1YM4eVOmxHX9MrCwj85FZEt/5
B6Pavo4YobV0YOL5ZFIwZfnodSIhTfZP4kfEW/fqsr6QPZoT835Rx24+uz9DnMeq
4SYR5kDFq7JK80Q2GY891kpFHdLbA1PuZBaSlW6SqxkzBtitTaEeOU1EgDzpIwID
AQABoxcwFTATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAgEA
bx+9tCymZ5UHc8t5GqWZ4cj2c25TDhWhwz4HqA9rMQmJ9tErQqr4Yk4N3PwD896O
47/IPLBp9iMRAfqqnMgkTvB6htncebeW7PVwbvBzfD9WW6dI2Nq7vCy63MDB9Rt2
XRodrebyIlA/BvoG+exsBaJfImLvgN4gSDF/kMCb9hvYTjZVA/vG0r+91SxVN/B1
L+eWQynqAfeJdXLvr/gxppw6E2h3VH11Bf7WsjOb0QcknY8gNHoZ7a6URz1lQj26
hw1hzqpXDsW82oueI0LSdvtPxn9iZrI4Zyw/MksvCnhRroyPT0lybsd4ZdWL49oq
VTW0MXFMnEigdMpOosYSo5b73QhJggsuMBiRPOLS5SKPs/bWEYi237o7iEk9ksbQ
0rIMK01gP0egqYJLyBMJ8/JxK9Z9z2dcqCwOP6noposXQZ93qQRcZahNQBds7wfv
oU/6Lnj1ZHFEnbawJucgHgbhfCSkWy1OgO5pJx5uSuEzvo0GjBRhUJh/XtjSWDch
ikZqDHBPIkoFdZ4AcuB09PGGbz76iAs1NIm7U4CwKdevXIydeqOOBMJMIno9/8lQ
JIo6GWKcRpe2qnUK09WI6xrO3/y4ifBspqd9HHJJbM9eizL24SeVOZR8auKcFAQm
D0VugaL9OUU8H5v/G/9xGtQSEFdxu6v0XzWCY/tZuBA=
-----END CERTIFICATE-----
Loading
Loading