-
Notifications
You must be signed in to change notification settings - Fork 106
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CABF SMIME BR 7.1.2.3.e - KeyUsages (#757)
* add lints for smime ku presence and criticality, rsa KUs and ECC KUs * Finish lint for ECDSA key usages. Add lint for edwards curve key usages * strict rsa ku lint unit tests * rename rsa strict ku lint test data to reflect strictness of SMIME policy oid * add unit tests to smime rsa legacy/multipurpose ku lint * add unit tests to key usage presence lint. Fix present/presence typos * rename key usage critical lint to key usage criticality. unit tests for same * add unit tests to smime ecdsa key usage lint. Fix issue in check applies * add unit tests for smime ed25519 ku lint * use iota constants for signing, key management and dual use to make rsa and ec ku lints clearer to read * replace bit mask checks with util.HasKeyUsage calls in smime KU lints * Refactor RSA and EC SMIME KU lints to cover other KUs without digitalSignature and/or keyAgreement/Encipherment with separate lints. --------- Co-authored-by: Christopher Henderson <chris@chenderson.org>
- Loading branch information
1 parent
f9f30bc
commit 8923170
Showing
55 changed files
with
2,871 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/* | ||
* ZLint Copyright 2023 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. | ||
*/ | ||
|
||
package cabf_smime_br | ||
|
||
import ( | ||
"github.com/zmap/zcrypto/x509" | ||
"github.com/zmap/zlint/v3/lint" | ||
"github.com/zmap/zlint/v3/util" | ||
) | ||
|
||
func init() { | ||
lint.RegisterLint(&lint.Lint{ | ||
Name: "e_ecpublickey_key_usages", | ||
Description: "For signing only, bit positions SHALL be set for digitalSignature and MAY be set for nonRepudiation. For key management only, bit positions SHALL be set for keyEncipherment.For dual use, bit positions SHALL be set for digitalSignature and keyEncipherment and MAY be set for nonRepudiation.", | ||
Citation: "7.1.2.3.e", | ||
Source: lint.CABFSMIMEBaselineRequirements, | ||
EffectiveDate: util.CABF_SMIME_BRs_1_0_0_Date, | ||
Lint: NewECPublicKeyKeyUsages, | ||
}) | ||
} | ||
|
||
type ecPublicKeyKeyUsages struct{} | ||
|
||
func NewECPublicKeyKeyUsages() lint.LintInterface { | ||
return &ecPublicKeyKeyUsages{} | ||
} | ||
|
||
func (l *ecPublicKeyKeyUsages) CheckApplies(c *x509.Certificate) bool { | ||
return util.IsSubscriberCert(c) && util.IsSMIMEBRCertificate(c) && util.IsExtInCert(c, util.KeyUsageOID) && c.PublicKeyAlgorithm == x509.ECDSA | ||
} | ||
|
||
func (l *ecPublicKeyKeyUsages) Execute(c *x509.Certificate) *lint.LintResult { | ||
const ( | ||
signing = iota + 1 | ||
keyManagement | ||
dualUsage | ||
) | ||
|
||
certType := 0 | ||
if util.HasKeyUsage(c, x509.KeyUsageDigitalSignature) { | ||
certType |= signing | ||
} | ||
if util.HasKeyUsage(c, x509.KeyUsageKeyAgreement) { | ||
certType |= keyManagement | ||
} | ||
|
||
switch certType { | ||
case signing: | ||
mask := 0x1FF ^ (x509.KeyUsageDigitalSignature | x509.KeyUsageContentCommitment) | ||
if c.KeyUsage&mask != 0 { | ||
return &lint.LintResult{Status: lint.Error} | ||
} | ||
|
||
case keyManagement: | ||
mask := 0x1FF ^ (x509.KeyUsageKeyAgreement | x509.KeyUsageEncipherOnly | x509.KeyUsageDecipherOnly) | ||
if c.KeyUsage&mask != 0 { | ||
return &lint.LintResult{Status: lint.Error} | ||
} | ||
|
||
case dualUsage: | ||
mask := 0x1FF ^ (x509.KeyUsageDigitalSignature | x509.KeyUsageContentCommitment | x509.KeyUsageKeyAgreement | x509.KeyUsageEncipherOnly | x509.KeyUsageDecipherOnly) | ||
if c.KeyUsage&mask != 0 { | ||
return &lint.LintResult{Status: lint.Error} | ||
} | ||
|
||
default: | ||
return &lint.LintResult{Status: lint.NA} | ||
} | ||
|
||
return &lint.LintResult{Status: lint.Pass} | ||
} |
89 changes: 89 additions & 0 deletions
89
v3/lints/cabf_smime_br/lint_ecpublickey_key_usages_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package cabf_smime_br | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/zmap/zlint/v3/lint" | ||
"github.com/zmap/zlint/v3/test" | ||
) | ||
|
||
func TestECPublicKeyKeyUsage(t *testing.T) { | ||
testCases := []struct { | ||
Name string | ||
InputFilename string | ||
ExpectedResult lint.LintStatus | ||
}{ | ||
{ | ||
Name: "pass - cert with digitalSignature KU", | ||
InputFilename: "smime/ec_legacy_digital_signature_ku.pem", | ||
ExpectedResult: lint.Pass, | ||
}, | ||
{ | ||
Name: "pass - cert with digitalSignature and contentCommitment KUs", | ||
InputFilename: "smime/ec_multipurpose_digital_signature_content_commitment_ku.pem", | ||
ExpectedResult: lint.Pass, | ||
}, | ||
{ | ||
Name: "pass - cert with keyAgreement KU", | ||
InputFilename: "smime/ec_strict_key_agreement_ku.pem", | ||
ExpectedResult: lint.Pass, | ||
}, | ||
{ | ||
Name: "pass - cert with keyAgreement and encipherOnly KUs", | ||
InputFilename: "smime/ec_legacy_key_agreement_encipher_only_ku.pem", | ||
ExpectedResult: lint.Pass, | ||
}, | ||
{ | ||
Name: "pass - cert with keyAgreement and decipherOnly KUs", | ||
InputFilename: "smime/ec_multipurpose_key_agreement_decipher_only.pem", | ||
ExpectedResult: lint.Pass, | ||
}, | ||
{ | ||
Name: "pass - cert with digitalSignature, keyAgreement, contentCommitment, and encipherOnly KUs", | ||
InputFilename: "smime/ec_strict_digital_signature_key_agreement_content_commitment_encipher_only_ku.pem", | ||
ExpectedResult: lint.Pass, | ||
}, | ||
{ | ||
Name: "pass - cert with digitalSignature, keyAgreement, contentCommitment, and decipherOnly KUs", | ||
InputFilename: "smime/ec_legacy_digital_signature_key_agreement_content_commitment_decipher_only_ku.pem", | ||
ExpectedResult: lint.Pass, | ||
}, { | ||
Name: "NA - cert without KUs", | ||
InputFilename: "smime/without_subject_alternative_name.pem", | ||
ExpectedResult: lint.NA, | ||
}, | ||
{ | ||
Name: "NA - Certificate without digitalSignature or keyAgreement KUs", | ||
InputFilename: "smime/ec_strict_cert_sign_ku.pem", | ||
ExpectedResult: lint.NA, | ||
}, | ||
{ | ||
Name: "NE - certificate with valid KUs dated before 2020-09-01", | ||
InputFilename: "smime/ec_multipurpose_valid_ku_august_2023.pem", | ||
ExpectedResult: lint.NE, | ||
}, | ||
{ | ||
Name: "Error - Signing Certificate with unexpected KU", | ||
InputFilename: "smime/ec_strict_digital_signature_cert_sign_ku.pem", | ||
ExpectedResult: lint.Error, | ||
}, | ||
{ | ||
Name: "Error - Key Management Certificate with unexpected KU", | ||
InputFilename: "smime/ec_legacy_key_agreement_cert_sign_ku.pem", | ||
ExpectedResult: lint.Error, | ||
}, | ||
{ | ||
Name: "Error - Dual Use Certificate with unexpected KU", | ||
InputFilename: "smime/ec_multipurpose_digital_signature_key_agreement_cert_sign_ku.pem", | ||
ExpectedResult: lint.Error, | ||
}, | ||
} | ||
for _, tc := range testCases { | ||
t.Run(tc.Name, func(t *testing.T) { | ||
result := test.TestLint("e_ecpublickey_key_usages", tc.InputFilename) | ||
if result.Status != tc.ExpectedResult { | ||
t.Errorf("expected result %v was %v - details: %v", tc.ExpectedResult, result.Status, result.Details) | ||
} | ||
}) | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
v3/lints/cabf_smime_br/lint_ecpublickey_other_key_usages.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/* | ||
* ZLint Copyright 2023 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. | ||
*/ | ||
|
||
package cabf_smime_br | ||
|
||
import ( | ||
"github.com/zmap/zcrypto/x509" | ||
"github.com/zmap/zlint/v3/lint" | ||
"github.com/zmap/zlint/v3/util" | ||
) | ||
|
||
func init() { | ||
lint.RegisterLint(&lint.Lint{ | ||
Name: "e_ec_other_key_usages", | ||
Description: "Other bit positions SHALL NOT be set.", | ||
Citation: "7.1.2.3.e", | ||
Source: lint.CABFSMIMEBaselineRequirements, | ||
EffectiveDate: util.CABF_SMIME_BRs_1_0_0_Date, | ||
Lint: NewECOtherKeyUsages, | ||
}) | ||
} | ||
|
||
type ecOtherKeyUsages struct{} | ||
|
||
func NewECOtherKeyUsages() lint.LintInterface { | ||
return &ecOtherKeyUsages{} | ||
} | ||
|
||
func (l *ecOtherKeyUsages) CheckApplies(c *x509.Certificate) bool { | ||
return util.IsSubscriberCert(c) && util.IsSMIMEBRCertificate(c) && util.IsExtInCert(c, util.KeyUsageOID) && c.PublicKeyAlgorithm == x509.ECDSA | ||
} | ||
|
||
func (l *ecOtherKeyUsages) Execute(c *x509.Certificate) *lint.LintResult { | ||
if !(util.HasKeyUsage(c, x509.KeyUsageDigitalSignature) || util.HasKeyUsage(c, x509.KeyUsageKeyAgreement)) { | ||
if c.KeyUsage != 0 { | ||
return &lint.LintResult{Status: lint.Error} | ||
} | ||
|
||
return &lint.LintResult{Status: lint.NA} | ||
} | ||
|
||
return &lint.LintResult{Status: lint.Pass} | ||
} |
50 changes: 50 additions & 0 deletions
50
v3/lints/cabf_smime_br/lint_ecpublickey_other_key_usages_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package cabf_smime_br | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/zmap/zlint/v3/lint" | ||
"github.com/zmap/zlint/v3/test" | ||
) | ||
|
||
func TestECOtherKeyUsages(t *testing.T) { | ||
testCases := []struct { | ||
Name string | ||
InputFilename string | ||
ExpectedResult lint.LintStatus | ||
}{ | ||
{ | ||
Name: "pass - cert with digitalSignature KU", | ||
InputFilename: "smime/ec_legacy_digital_signature_ku.pem", | ||
ExpectedResult: lint.Pass, | ||
}, | ||
{ | ||
Name: "NE - certificate with valid KUs dated before 2020-09-01", | ||
InputFilename: "smime/ec_multipurpose_valid_ku_august_2023.pem", | ||
ExpectedResult: lint.NE, | ||
}, | ||
{ | ||
Name: "NA - cert without KUs", | ||
InputFilename: "smime/without_subject_alternative_name.pem", | ||
ExpectedResult: lint.NA, | ||
}, | ||
{ | ||
Name: "NA - cert with KU extension but no KU bits set", | ||
InputFilename: "smime/ec_no_key_usages.pem", | ||
ExpectedResult: lint.NA, | ||
}, | ||
{ | ||
Name: "Error - Certificate with non-zero KUs without digitalSignature or keyEncipherment KUs", | ||
InputFilename: "smime/ec_strict_cert_sign_ku.pem", | ||
ExpectedResult: lint.Error, | ||
}, | ||
} | ||
for _, tc := range testCases { | ||
t.Run(tc.Name, func(t *testing.T) { | ||
result := test.TestLint("e_ec_other_key_usages", tc.InputFilename) | ||
if result.Status != tc.ExpectedResult { | ||
t.Errorf("expected result %v was %v - details: %v", tc.ExpectedResult, result.Status, result.Details) | ||
} | ||
}) | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
v3/lints/cabf_smime_br/lint_edwardspublickey_key_usages.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* ZLint Copyright 2023 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. | ||
*/ | ||
|
||
package cabf_smime_br | ||
|
||
import ( | ||
"github.com/zmap/zcrypto/x509" | ||
"github.com/zmap/zlint/v3/lint" | ||
"github.com/zmap/zlint/v3/util" | ||
) | ||
|
||
func init() { | ||
lint.RegisterLint(&lint.Lint{ | ||
Name: "e_edwardspublickey_key_usages", | ||
Description: "Bit positions SHALL be set for digitalSignature and MAY be set for nonRepudiation.", | ||
Citation: "7.1.2.3.e", | ||
Source: lint.CABFSMIMEBaselineRequirements, | ||
EffectiveDate: util.CABF_SMIME_BRs_1_0_0_Date, | ||
Lint: NewEdwardsPublicKeyKeyUsages, | ||
}) | ||
} | ||
|
||
type edwardsPublicKeyKeyUsages struct{} | ||
|
||
func NewEdwardsPublicKeyKeyUsages() lint.LintInterface { | ||
return &edwardsPublicKeyKeyUsages{} | ||
} | ||
|
||
func (l *edwardsPublicKeyKeyUsages) CheckApplies(c *x509.Certificate) bool { | ||
// TODO add support for curve448 certificate linting | ||
return util.IsSubscriberCert(c) && util.IsSMIMEBRCertificate(c) && util.IsExtInCert(c, util.KeyUsageOID) && c.PublicKeyAlgorithm == x509.Ed25519 | ||
} | ||
|
||
func (l *edwardsPublicKeyKeyUsages) Execute(c *x509.Certificate) *lint.LintResult { | ||
if !util.HasKeyUsage(c, x509.KeyUsageDigitalSignature) { | ||
return &lint.LintResult{Status: lint.Error} | ||
} | ||
|
||
mask := 0x1FF ^ (x509.KeyUsageDigitalSignature | x509.KeyUsageContentCommitment) | ||
if c.KeyUsage&mask != 0 { | ||
return &lint.LintResult{Status: lint.Error} | ||
} | ||
|
||
return &lint.LintResult{Status: lint.Pass} | ||
} |
55 changes: 55 additions & 0 deletions
55
v3/lints/cabf_smime_br/lint_edwardspublickey_key_usages_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package cabf_smime_br | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/zmap/zlint/v3/lint" | ||
"github.com/zmap/zlint/v3/test" | ||
) | ||
|
||
func TestEdwardsPublicKeyKeyUsages(t *testing.T) { | ||
testCases := []struct { | ||
Name string | ||
InputFilename string | ||
ExpectedResult lint.LintStatus | ||
}{ | ||
{ | ||
Name: "pass - cert with digitalSignature KU", | ||
InputFilename: "smime/ed25519_legacy_digital_signature_ku.pem", | ||
ExpectedResult: lint.Pass, | ||
}, | ||
{ | ||
Name: "pass - cert with digitalSignature and contentCommitment KUs", | ||
InputFilename: "smime/ed25519_multipurpose_digital_signature_content_commitment_ku.pem", | ||
ExpectedResult: lint.Pass, | ||
}, | ||
{ | ||
Name: "NA - non-SMIME BR cert", | ||
InputFilename: "smime/domainValidatedWithEmailCommonName.pem", | ||
ExpectedResult: lint.NA, | ||
}, | ||
{ | ||
Name: "NA - RSA cert", | ||
InputFilename: "smime/rsa_strict_digital_signature_ku.pem", | ||
ExpectedResult: lint.NA, | ||
}, | ||
{ | ||
Name: "NE - certificate with KU extension dated before 2020-09-01", | ||
InputFilename: "smime/ed25519_strict_valid_ku_august_2023.pem", | ||
ExpectedResult: lint.NE, | ||
}, | ||
{ | ||
Name: "Error - Certificate without digitalSignature KU", | ||
InputFilename: "smime/ed25519_strict_cert_sign_ku.pem", | ||
ExpectedResult: lint.Error, | ||
}, | ||
} | ||
for _, tc := range testCases { | ||
t.Run(tc.Name, func(t *testing.T) { | ||
result := test.TestLint("e_edwardspublickey_key_usages", tc.InputFilename) | ||
if result.Status != tc.ExpectedResult { | ||
t.Errorf("expected result %v was %v - details: %v", tc.ExpectedResult, result.Status, result.Details) | ||
} | ||
}) | ||
} | ||
} |
Oops, something went wrong.