Skip to content

Commit

Permalink
Reintroduce lint for inconsistent KU and EKU (#708)
Browse files Browse the repository at this point in the history
* Add function to get human friendly KeyUsage names

* Add lint to check for KU and EKU inconsistency

* Add func to get EKU strings

* Sort KeyUsage strings for consistency in messages

* Consider multiple purposes

* Update result for integration test

* Fix formatting

* Add KU/EKU inconsistent test cases

* No error on undefined extended key usage

* Move sort from util to lint and include comment

* Add some comments around the cyclomatic complexity

* Update count for test corpus incl email certs
  • Loading branch information
vanbroup committed Aug 27, 2023
1 parent 59d4dd3 commit 71d5e4b
Show file tree
Hide file tree
Showing 10 changed files with 492 additions and 3 deletions.
3 changes: 3 additions & 0 deletions v3/integration/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,9 @@
"e_invalid_certificate_version": {},
"e_issuer_dn_country_not_printable_string": {},
"e_issuer_field_empty": {},
"e_key_usage_and_extended_key_usage_inconsistent": {
"ErrCount": 31843
},
"e_key_usage_incorrect_length": {},
"e_mp_authority_key_identifier_correct": {
"ErrCount": 3704
Expand Down
2 changes: 1 addition & 1 deletion v3/integration/small.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@
"e_issuer_dn_country_not_printable_string": {},
"e_issuer_field_empty": {},
"e_key_usage_and_extended_key_usage_inconsistent": {
"ErrCount": 18858
"ErrCount": 1020
},
"e_mp_authority_key_identifier_correct": {
"ErrCount": 125
Expand Down
213 changes: 213 additions & 0 deletions v3/lints/rfc/lint_key_usage_and_extended_key_usage_inconsistent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package rfc

/*
* 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"
"sort"

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

type KUAndEKUInconsistent struct{}

func init() {
lint.RegisterLint(&lint.Lint{
Name: "e_key_usage_and_extended_key_usage_inconsistent",
Description: "The certificate MUST only be used for a purpose consistent with both key usage extension and extended key usage extension.",
Citation: "RFC 5280, Section 4.2.1.12.",
Source: lint.RFC5280,
EffectiveDate: util.RFC5280Date,
Lint: NewKUAndEKUInconsistent,
})
}

func NewKUAndEKUInconsistent() lint.LintInterface {
return &KUAndEKUInconsistent{}
}

func (l *KUAndEKUInconsistent) Initialize() error {
return nil
}

// CheckApplies returns true when the certificate contains both a key usage
// extension and an extended key usage extension.
func (l *KUAndEKUInconsistent) CheckApplies(c *x509.Certificate) bool {
return util.IsSubscriberCert(c) && util.IsExtInCert(c, util.EkuSynOid) && util.IsExtInCert(c, util.KeyUsageOID)
}

// Execute returns an Error level lint.LintResult if the purposes of the certificate
// being linted is not consistent with both extensions.
func (l *KUAndEKUInconsistent) Execute(c *x509.Certificate) *lint.LintResult {
if len(c.ExtKeyUsage) > 1 {
return l.multiPurpose(c)
}
return l.strictPurpose(c)
}

// RFC 5280 4.2.1.12 on multiple purposes:
//
// If multiple purposes are indicated the application need not recognize all purposes
// indicated, as long as the intended purpose is present.
func (l *KUAndEKUInconsistent) multiPurpose(c *x509.Certificate) *lint.LintResult {
// Create a map with each KeyUsage combination that is authorized for the
// included extKeyUsage(es).
var mp = map[x509.KeyUsage]bool{}
for _, extKeyUsage := range c.ExtKeyUsage {
var i int
if _, ok := eku[extKeyUsage]; !ok {
return &lint.LintResult{Status: lint.Pass}
}
for ku := range eku[extKeyUsage] {
// There is nothing to merge for the first EKU.
if i > 0 {
// We could see this EKU combined with any other EKU so
// create that possibility.
for mpku := range mp {
mp[mpku|ku] = true
}
}

mp[ku] = true
i++
}
}
if !mp[c.KeyUsage] {
// Sort the included KeyUsage strings for consistent error messages
// The order does not matter for this lint, but the consistency makes
// it easier to identify common errors.
keyUsage := util.GetKeyUsageStrings(c.KeyUsage)
sort.Strings(keyUsage)

return &lint.LintResult{
Status: lint.Error,
Details: fmt.Sprintf("KeyUsage %v (%08b) inconsistent with multiple purpose ExtKeyUsage %v", keyUsage, c.KeyUsage, util.GetEKUStrings(c.ExtKeyUsage)),
}
}
return &lint.LintResult{Status: lint.Pass}
}

// strictPurpose checks if the Key Usages (KU) included are permitted for each
// indicated Extended Key Usage (EKU)
func (l *KUAndEKUInconsistent) strictPurpose(c *x509.Certificate) *lint.LintResult {
for _, extKeyUsage := range c.ExtKeyUsage {
if _, ok := eku[extKeyUsage]; !ok {
continue
}
if !eku[extKeyUsage][c.KeyUsage] {
return &lint.LintResult{
Status: lint.Error,
Details: fmt.Sprintf("KeyUsage %v (%08b) inconsistent with ExtKeyUsage %s", util.GetKeyUsageStrings(c.KeyUsage), c.KeyUsage, util.GetEKUString(extKeyUsage)),
}
}
}
return &lint.LintResult{Status: lint.Pass}
}

var eku = map[x509.ExtKeyUsage]map[x509.KeyUsage]bool{

// KU combinations with Server Authentication EKU:
// RFC 5280 4.2.1.12 on KU consistency with Server Authentication EKU:
// -- TLS WWW server authentication
// -- Key usage bits that may be consistent: digitalSignature,
// -- keyEncipherment or keyAgreement

// (digitalSignature OR (keyEncipherment XOR keyAgreement))
x509.ExtKeyUsageServerAuth: {
x509.KeyUsageDigitalSignature: true,
x509.KeyUsageKeyEncipherment: true,
x509.KeyUsageKeyAgreement: true,
x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment: true,
x509.KeyUsageDigitalSignature | x509.KeyUsageKeyAgreement: true,
},

// KU combinations with Client Authentication EKU:
// RFC 5280 4.2.1.12 on KU consistency with Client Authentication EKU:
// -- TLS WWW client authentication
// -- Key usage bits that may be consistent: digitalSignature
// -- and/or keyAgreement

// (digitalSignature OR keyAgreement)
x509.ExtKeyUsageClientAuth: {
x509.KeyUsageDigitalSignature: true,
x509.KeyUsageKeyAgreement: true,
x509.KeyUsageDigitalSignature | x509.KeyUsageKeyAgreement: true,
},

// KU combinations with Code Signing EKU:
// RFC 5280 4.2.1.12 on KU consistency with Code Signing EKU:
// -- Signing of downloadable executable code
// -- Key usage bits that may be consistent: digitalSignature

// (digitalSignature)
x509.ExtKeyUsageCodeSigning: {
x509.KeyUsageDigitalSignature: true,
},

// KU combinations with Email Protection EKU:
// RFC 5280 4.2.1.12 on KU consistency with Email Protection EKU:
// -- Email protection
// -- Key usage bits that may be consistent: digitalSignature,
// -- nonRepudiation, and/or (keyEncipherment or keyAgreement)
// Note: Recent editions of X.509 have renamed nonRepudiation bit to contentCommitment

// (digitalSignature OR nonRepudiation OR (keyEncipherment XOR keyAgreement))
x509.ExtKeyUsageEmailProtection: {
x509.KeyUsageDigitalSignature: true,
x509.KeyUsageContentCommitment: true,
x509.KeyUsageKeyEncipherment: true,
x509.KeyUsageKeyAgreement: true,

x509.KeyUsageDigitalSignature | x509.KeyUsageContentCommitment: true,
x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment: true,
x509.KeyUsageDigitalSignature | x509.KeyUsageKeyAgreement: true,

x509.KeyUsageDigitalSignature | x509.KeyUsageContentCommitment | x509.KeyUsageKeyEncipherment: true,
x509.KeyUsageDigitalSignature | x509.KeyUsageContentCommitment | x509.KeyUsageKeyAgreement: true,

x509.KeyUsageContentCommitment | x509.KeyUsageKeyEncipherment: true,
x509.KeyUsageContentCommitment | x509.KeyUsageKeyAgreement: true,
},

// KU combinations with Time Stamping EKU:
// RFC 5280 4.2.1.12 on KU consistency with Time Stamping EKU:
// -- Binding the hash of an object to a time
// -- Key usage bits that may be consistent: digitalSignature
// -- and/or nonRepudiation
// Note: Recent editions of X.509 have renamed nonRepudiation bit to contentCommitment

// (digitalSignature OR nonRepudiation)
x509.ExtKeyUsageTimeStamping: {
x509.KeyUsageDigitalSignature: true,
x509.KeyUsageContentCommitment: true,
x509.KeyUsageDigitalSignature | x509.KeyUsageContentCommitment: true,
},

// KU combinations with Ocsp Signing EKU:
// RFC 5280 4.2.1.12 on KU consistency with Ocsp Signing EKU:
// -- Signing OCSP responses
// -- Key usage bits that may be consistent: digitalSignature
// -- and/or nonRepudiation
// Note: Recent editions of X.509 have renamed nonRepudiation bit to contentCommitment

// (digitalSignature OR nonRepudiation)
x509.ExtKeyUsageOcspSigning: {
x509.KeyUsageDigitalSignature: true,
x509.KeyUsageContentCommitment: true,
x509.KeyUsageDigitalSignature | x509.KeyUsageContentCommitment: true,
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package rfc

/*
* 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 TestStrictFail(t *testing.T) {
inputPath := "kuEkuInconsistent.pem"
expected := lint.Error
out := test.TestLint("e_key_usage_and_extended_key_usage_inconsistent", inputPath)
if out.Status != expected {
t.Errorf("%s: expected %s, got %s", inputPath, expected, out.Status)
}
}

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

func TestMultiPurposeFail(t *testing.T) {
inputPath := "kuEkuInconsistentMp.pem"
expected := lint.Error
out := test.TestLint("e_key_usage_and_extended_key_usage_inconsistent", inputPath)
if out.Status != expected {
t.Errorf("%s: expected %s, got %s", inputPath, expected, out.Status)
}
}

func TestMultiPurposePass(t *testing.T) {
inputPath := "kuEkuConsistentMp.pem"
expected := lint.Pass
out := test.TestLint("e_key_usage_and_extended_key_usage_inconsistent", inputPath)
if out.Status != expected {
t.Errorf("%s: expected %s, got %s", inputPath, expected, out.Status)
}
}
39 changes: 39 additions & 0 deletions v3/testdata/kuEkuConsistent.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 3 (0x3)
Signature Algorithm: ecdsa-with-SHA256
Issuer:
Validity
Not Before: May 1 00:00:00 2008 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:08:95:ad:12:a9:39:ec:b1:df:31:12:e6:79:2f:
5e:59:f1:ff:b1:25:b1:92:76:f4:e5:0b:ea:20:ba:
02:b2:7b:bd:32:ae:f5:f3:de:77:b2:2d:08:16:8b:
8c:df:08:27:25:cd:b9:1c:3c:dd:19:d4:5f:92:19:
ab:f7:62:3f:fb
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature
X509v3 Extended Key Usage:
TLS Web Server Authentication
Signature Algorithm: ecdsa-with-SHA256
30:45:02:21:00:9d:fb:3f:05:b5:28:fb:21:1e:ca:80:fe:07:
d6:92:25:12:9c:de:46:28:3e:97:f8:5c:9e:7e:17:5a:33:c5:
60:02:20:3e:8c:a5:8c:37:b5:c2:44:7d:f5:fc:33:f6:d1:e9:
f6:89:75:39:d8:73:b2:20:fe:54:7f:83:ce:30:34:e3:98
-----BEGIN CERTIFICATE-----
MIIBFzCBvqADAgECAgEDMAoGCCqGSM49BAMCMAAwIBcNMDgwNTAxMDAwMDAwWhgP
OTk5ODExMzAwMDAwMDBaMAAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQIla0S
qTnssd8xEuZ5L15Z8f+xJbGSdvTlC+ogugKye70yrvXz3neyLQgWi4zfCCclzbkc
PN0Z1F+SGav3Yj/7oycwJTAOBgNVHQ8BAf8EBAMCAIAwEwYDVR0lBAwwCgYIKwYB
BQUHAwEwCgYIKoZIzj0EAwIDSAAwRQIhAJ37PwW1KPshHsqA/gfWkiUSnN5GKD6X
+FyefhdaM8VgAiA+jKWMN7XCRH31/DP20en2iXU52HOyIP5Uf4POMDTjmA==
-----END CERTIFICATE-----
40 changes: 40 additions & 0 deletions v3/testdata/kuEkuConsistentMp.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 3 (0x3)
Signature Algorithm: ecdsa-with-SHA256
Issuer:
Validity
Not Before: May 1 00:00:00 2008 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:eb:8a:9c:e0:fd:6a:ad:be:c1:38:81:a5:44:c4:
1a:ad:90:29:90:7f:6d:38:2f:83:ce:f2:66:fc:ab:
fa:e0:b5:84:6e:ca:20:4b:69:4f:17:68:17:1c:24:
ab:51:7e:fa:cb:88:18:51:78:d8:35:bf:ff:86:96:
f8:14:d9:1c:8f
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Non Repudiation
X509v3 Extended Key Usage:
E-mail Protection, TLS Web Client Authentication
Signature Algorithm: ecdsa-with-SHA256
30:45:02:21:00:ac:7b:1b:25:7f:56:5d:32:19:ca:de:8f:44:
e6:52:fa:db:5e:5a:43:92:4e:87:f2:b8:43:7d:be:fd:df:ec:
38:02:20:6e:59:a6:36:4f:8d:2a:92:b8:9e:b6:43:0d:6a:1e:
95:ca:a1:f1:7e:3d:bb:97:58:ab:c7:fb:3f:d9:5d:85:09
-----BEGIN CERTIFICATE-----
MIIBITCByKADAgECAgEDMAoGCCqGSM49BAMCMAAwIBcNMDgwNTAxMDAwMDAwWhgP
OTk5ODExMzAwMDAwMDBaMAAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATripzg
/WqtvsE4gaVExBqtkCmQf204L4PO8mb8q/rgtYRuyiBLaU8XaBccJKtRfvrLiBhR
eNg1v/+GlvgU2RyPozEwLzAOBgNVHQ8BAf8EBAMCAMAwHQYDVR0lBBYwFAYIKwYB
BQUHAwQGCCsGAQUFBwMCMAoGCCqGSM49BAMCA0gAMEUCIQCsexslf1ZdMhnK3o9E
5lL6215aQ5JOh/K4Q32+/d/sOAIgblmmNk+NKpK4nrZDDWoelcqh8X49u5dYq8f7
P9ldhQk=
-----END CERTIFICATE-----
Loading

0 comments on commit 71d5e4b

Please sign in to comment.