Skip to content

Commit

Permalink
Add public key algorithm <-> Key Usage value consistency validator (#139
Browse files Browse the repository at this point in the history
)
  • Loading branch information
CBonnell authored Nov 22, 2024
1 parent cb98429 commit 92379f4
Show file tree
Hide file tree
Showing 13 changed files with 295 additions and 4 deletions.
8 changes: 7 additions & 1 deletion pkilint/cabf/serverauth/finding_metadata.csv
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,12 @@ ERROR,pkix.ip_address_name_constraint_invalid_cidr,"RFC 5280 4.1.2.10: ""For IPv
ERROR,pkix.ip_address_name_constraint_wrong_length,"RFC 5280 4.1.2.10: ""For IPv4 addresses, the iPAddress field of GeneralName MUST contain eight (8) octets, encoded in the style of RFC 4632 (CIDR) to represent an address range [RFC4632]. For IPv6 addresses, the iPAddress field MUST contain 32 octets similarly encoded."""
ERROR,pkix.ip_address_wrong_length,"RFC 5280 4.1.2.6: ""For IP version 4, as specified in [RFC791], the octet string MUST contain exactly four octets. For IP version 6, as specified in [RFC2460], the octet string MUST contain exactly sixteen octets."""
ERROR,pkix.issuer_unique_id_present,"RFC 5280 4.1.2.8: ""CAs conforming to this profile MUST NOT generate certificates with unique identifiers"""
ERROR,pkix.name_constraints_in_ee_certificate,"RFC 5280 4.2.1.10: ""The name constraints extension, which MUST be used only in a CA certificate�"""
ERROR,pkix.key_usage_value_prohibited_for_ec,"RFC 8813 3: If the keyUsage extension is present in a certificate that indicates id-ecPublicKey in SubjectPublicKeyInfo, then the following values MUST NOT be present..."
ERROR,pkix.key_usage_value_prohibited_for_edwards_curve,"RFC 9295 3: and any of the following MUST NOT be present..."
ERROR,pkix.key_usage_value_required_but_missing_for_edwards_curve,"RFC 9295 3: If the keyUsage extension is present in a certificate that indicates id-X25519 or id-X448 in SubjectPublicKeyInfo, then the following MUST be present..."
ERROR,pkix.key_usage_value_prohibited_for_rsa,"RFC 3279 2.3.1: If the keyUsage extension is present in an end entity/CRL issuer/CA certificate which conveys an RSA public key, any combination of the following values MAY be present..."
ERROR,pkix.key_usage_value_prohibited_for_signature_algorithm,"Various RFCs specify the allowed keyUsage values for signature algorithms"
ERROR,pkix.name_constraints_in_ee_certificate,"RFC 5280 4.2.1.10: ""The name constraints extension, which MUST be used only in a CA certificate"""
ERROR,pkix.name_constraints_maximum_specified,"RFC 5280 4.2.1.10: ""Within this profile, the minimum and maximum fields are not used with any name forms, thus, the minimum MUST be zero, and maximum MUST be absent"""
ERROR,pkix.name_constraints_no_subtrees,"RFC 5280 4.2.1.10: ""Conforming CAs MUST NOT issue certificates where name constraints is an empty sequence."""
ERROR,pkix.name_constraints_non_default_minimum,"RFC 5280 4.2.1.10: ""Within this profile, the minimum and maximum fields are not used with any name forms, thus, the minimum MUST be zero, and maximum MUST be absent"""
Expand Down Expand Up @@ -305,6 +310,7 @@ NOTICE,cabf.serverauth.unparsed_common_name_encountered,Validates that the conte
NOTICE,cabf.serverauth.unparsed_san_extension_encountered,Validates that the content of the commonName attribute conforms to BR 7.1.4.3.
NOTICE,pkix.aki_absent_self_issued_and_unsupported_public_key_algorithm,Authority Key Identifier extension is absent is a self-issued certificate and the certificate certifies a public key of an unsupported algorithm.
NOTICE,pkix.certificate_policies_policy_has_qualifier,"RFC 5280 4.2.1.4: ""To promote interoperability, this profile RECOMMENDS that policy information terms consist of only an OID. Where an OID alone is insufficient, this profile strongly recommends that the use of qualifiers be limited to those identified in this section"""
NOTICE,pkix.public_key_algorithm_unsupported,"The algorithm of the certified public key is not supported"
NOTICE,pkix.ldap_uri_not_validated,": Notice that the linter encountered a LDAP URI but did not validate the correctness of the URI, as support for LDAP validation has not (yet) been implemented. This NOTICE should probably be of a lower severity or supressed entirely."
NOTICE,pkix.unknown_subject_key_identifier_calculation_method,RFC 5280 4.2.1.2: The Subject key identifier was not calculated using one of the algorithms defined in RFC 5280
INFO,pkix.subject_key_identifier_method_1_identified,RFC 5280 4.2.1.2: The Subject key identifier was calculated using the first algorithm defined in RFC 5280
Expand Down
6 changes: 6 additions & 0 deletions pkilint/cabf/smime/finding_metadata.csv
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ ERROR,pkix.ip_address_name_constraint_invalid_cidr,RFC 5280 4.1.2.10,"""For IPv4
ERROR,pkix.ip_address_name_constraint_wrong_length,RFC 5280 4.1.2.10,"""For IPv4 addresses, the iPAddress field of GeneralName MUST contain eight (8) octets, encoded in the style of RFC 4632 (CIDR) to represent an address range [RFC4632]. For IPv6 addresses, the iPAddress field MUST contain 32 octets similarly encoded."""
ERROR,pkix.ip_address_wrong_length,RFC 5280 4.1.2.6,"""For IP version 4, as specified in [RFC791], the octet string MUST contain exactly four octets. For IP version 6, as specified in [RFC2460], the octet string MUST contain exactly sixteen octets."""
ERROR,pkix.issuer_unique_id_present,RFC 5280 4.1.2.8,"""CAs conforming to this profile MUST NOT generate certificates with unique identifiers"""
ERROR,pkix.key_usage_value_prohibited_for_ec,RFC 8813 3,"If the keyUsage extension is present in a certificate that indicates id-ecPublicKey in SubjectPublicKeyInfo, then the following values MUST NOT be present..."
ERROR,pkix.key_usage_value_prohibited_for_edwards_curve,RFC 9295 3,"... and any of the following MUST NOT be present..."
ERROR,pkix.key_usage_value_required_but_missing_for_edwards_curve,RFC 9295 3,"If the keyUsage extension is present in a certificate that indicates id-X25519 or id-X448 in SubjectPublicKeyInfo, then the following MUST be present..."
ERROR,pkix.key_usage_value_prohibited_for_rsa,RFC 3279 2.3.1,"If the keyUsage extension is present in an end entity/CRL issuer/CA certificate which conveys an RSA public key, any combination of the following values MAY be present..."
ERROR,pkix.key_usage_value_prohibited_for_signature_algorithm,,"Various RFCs specify the allowed keyUsage values for signature algorithms"
ERROR,pkix.name_constraints_in_ee_certificate,RFC 5280 4.2.1.10,"""The name constraints extension, which MUST be used only in a CA certificate�"""
ERROR,pkix.name_constraints_maximum_specified,RFC 5280 4.2.1.10,"""Within this profile, the minimum and maximum fields are not used with any name forms, thus, the minimum MUST be zero, and maximum MUST be absent"""
ERROR,pkix.name_constraints_no_subtrees,RFC 5280 4.2.1.10,"""Conforming CAs MUST NOT issue certificates where name constraints is an empty sequence."""
Expand Down Expand Up @@ -146,6 +151,7 @@ NOTICE,googl.gmail.authority_info_access_ca_issuers_missing,https://support.goog
NOTICE,googl.prohibited_rsa_modulus_length,https://support.google.com/a/answer/7300887?hl=en&ref_topic=9061730&sjid=12609481378327192584-NA,"""rsaEncryption with an RSA modulus of 2048, 3072, or 4096"""
NOTICE,pkix.aki_absent_self_issued_and_unsupported_public_key_algorithm,,Authority Key Identifier extension is absent is a self-issued certificate and the certificate certifies a public key of an unsupported algorithm.
NOTICE,pkix.certificate_policies_policy_has_qualifier,RFC 5280 4.2.1.4,"""To promote interoperability, this profile RECOMMENDS that policy information terms consist of only an OID. Where an OID alone is insufficient, this profile strongly recommends that the use of qualifiers be limited to those identified in this section"""
NOTICE,pkix.public_key_algorithm_unsupported,,"The algorithm of the certified public key is not supported"
NOTICE,pkix.ldap_uri_not_validated,,"Notice that the linter encountered a LDAP URI but did not validate the correctness of the URI, as support for LDAP validation has not (yet) been implemented. This NOTICE should probably be of a lower severity or supressed entirely."
NOTICE,pkix.unknown_subject_key_identifier_calculation_method,RFC 5280 4.2.1.2,The Subject key identifier was not calculated using one of the algorithms defined in RFC 5280
INFO,pkix.subject_key_identifier_method_1_identified,RFC 5280 4.2.1.2,The Subject key identifier was calculated using the first algorithm defined in RFC 5280
Expand Down
4 changes: 4 additions & 0 deletions pkilint/itu/bitstring.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
def has_named_bit(node, bit_name):
bit = node.pdu.namedValues[bit_name]
return len(node.pdu) > bit and node.pdu[bit] != 0


def get_asserted_bit_set(node):
return {str(b) for b in node.pdu.namedValues if has_named_bit(node, str(b))}
1 change: 1 addition & 0 deletions pkilint/pkix/certificate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ def create_extensions_validator_container(additional_validators=None):
certificate_extension.SubjectKeyIdentifierCriticalityValidator(),
certificate_extension.KeyUsageCriticalityValidator(),
certificate_extension.KeyUsageValidator(),
certificate_key.SpkiKeyUsageConsistencyValidator(),
general_name.UriSyntaxValidator(pdu_class=rfc5280.CPSuri),
general_name.GeneralNameValidatorContainer(),
certificate_extension.DuplicatePolicyValidator(),
Expand Down
179 changes: 178 additions & 1 deletion pkilint/pkix/certificate/certificate_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
from pyasn1.codec.der.encoder import encode
from pyasn1.error import PyAsn1Error
from pyasn1.type import univ
from pyasn1_alt_modules import rfc5280
from pyasn1_alt_modules import rfc5280, rfc8410, rfc3279, rfc5480

from pkilint import validation, util, document
from pkilint.itu import bitstring
from pkilint.pkix.certificate.certificate_extension import KeyUsageBitName
from pkilint.pkix.key import verify_signature


Expand Down Expand Up @@ -222,3 +224,178 @@ def validate(self, node):
raise validation.ValidationFindingEncountered(
self._validations[0], f"Prohibited encoding: {encoded_str}"
)


class SpkiKeyUsageConsistencyValidator(validation.Validator):
# all bits are allowed except for keyAgreement, see RFC 4055 section 1.2
_RSA_ALLOWED_KEY_USAGES = {
KeyUsageBitName.DIGITAL_SIGNATURE,
KeyUsageBitName.NON_REPUDIATION,
KeyUsageBitName.KEY_CERT_SIGN,
KeyUsageBitName.CRL_SIGN,
KeyUsageBitName.KEY_ENCIPHERMENT,
KeyUsageBitName.DATA_ENCIPHERMENT,
KeyUsageBitName.DECIPHER_ONLY,
KeyUsageBitName.ENCIPHER_ONLY,
}
VALIDATION_RSA_PROHIBITED_KEY_USAGE_VALUE = validation.ValidationFinding(
validation.ValidationFindingSeverity.ERROR,
"pkix.key_usage_value_prohibited_for_rsa",
)

# all bits are allowed except for keyEncipherment and dataEncipherment, see RFC 8813 section 3
_EC_ALLOWED_KEY_USAGES = {
KeyUsageBitName.DIGITAL_SIGNATURE,
KeyUsageBitName.NON_REPUDIATION,
KeyUsageBitName.KEY_CERT_SIGN,
KeyUsageBitName.CRL_SIGN,
KeyUsageBitName.KEY_AGREEMENT,
KeyUsageBitName.DECIPHER_ONLY,
KeyUsageBitName.ENCIPHER_ONLY,
}
VALIDATION_EC_PROHIBITED_KEY_USAGE_VALUE = validation.ValidationFinding(
validation.ValidationFindingSeverity.ERROR,
"pkix.key_usage_value_prohibited_for_ec",
)

# see RFC 9295, section 3
_X448_AND_X25519_REQUIRED_KEY_USAGES = {
KeyUsageBitName.KEY_AGREEMENT,
}
VALIDATION_EDWARDS_MISSING_REQUIRED_KEY_USAGE_VALUE = validation.ValidationFinding(
validation.ValidationFindingSeverity.ERROR,
"pkix.key_usage_value_required_but_missing_for_edwards_curve",
)

_X448_AND_X25519_ALLOWED_KEY_USAGES = {
KeyUsageBitName.KEY_AGREEMENT,
KeyUsageBitName.DECIPHER_ONLY,
KeyUsageBitName.ENCIPHER_ONLY,
}
VALIDATION_EDWARDS_PROHIBITED_KEY_USAGE_VALUE = validation.ValidationFinding(
validation.ValidationFindingSeverity.ERROR,
"pkix.key_usage_value_prohibited_for_edwards_curve",
)

_SIGNATURE_ALGORITHM_ALLOWED_KEY_USAGES = {
KeyUsageBitName.DIGITAL_SIGNATURE,
KeyUsageBitName.NON_REPUDIATION,
KeyUsageBitName.KEY_CERT_SIGN,
KeyUsageBitName.CRL_SIGN,
}
VALIDATION_SIGNATURE_ALGORITHM_PROHIBITED_KEY_USAGE_VALUE = (
validation.ValidationFinding(
validation.ValidationFindingSeverity.ERROR,
"pkix.key_usage_value_prohibited_for_signature_algorithm",
)
)

# _KEM_ALLOWED_KEY_USAGES = {KeyUsageBitName.KEY_ENCIPHERMENT}
# VALIDATION_KEM_PROHIBITED_KEY_USAGE_VALUE = validation.ValidationFinding(
# validation.ValidationFindingSeverity.ERROR,
# "pkix.prohibited_key_usage_value_kem",
# )

VALIDATION_UNSUPPORTED_PUBLIC_KEY_ALGORITHM = validation.ValidationFinding(
validation.ValidationFindingSeverity.NOTICE,
"pkix.public_key_algorithm_unsupported",
)

_KEY_USAGE_VALUE_ALLOWANCES = {
rfc3279.rsaEncryption: (
(_RSA_ALLOWED_KEY_USAGES, VALIDATION_RSA_PROHIBITED_KEY_USAGE_VALUE),
None,
),
rfc5480.id_ecPublicKey: (
(_EC_ALLOWED_KEY_USAGES, VALIDATION_EC_PROHIBITED_KEY_USAGE_VALUE),
None,
),
rfc8410.id_X448: (
(
_X448_AND_X25519_ALLOWED_KEY_USAGES,
VALIDATION_EDWARDS_PROHIBITED_KEY_USAGE_VALUE,
),
(
_X448_AND_X25519_REQUIRED_KEY_USAGES,
VALIDATION_EDWARDS_MISSING_REQUIRED_KEY_USAGE_VALUE,
),
),
rfc8410.id_X25519: (
(
_X448_AND_X25519_ALLOWED_KEY_USAGES,
VALIDATION_EDWARDS_PROHIBITED_KEY_USAGE_VALUE,
),
(
_X448_AND_X25519_REQUIRED_KEY_USAGES,
VALIDATION_EDWARDS_MISSING_REQUIRED_KEY_USAGE_VALUE,
),
),
rfc8410.id_Ed448: (
(
_SIGNATURE_ALGORITHM_ALLOWED_KEY_USAGES,
VALIDATION_SIGNATURE_ALGORITHM_PROHIBITED_KEY_USAGE_VALUE,
),
None,
),
rfc8410.id_Ed25519: (
(
_SIGNATURE_ALGORITHM_ALLOWED_KEY_USAGES,
VALIDATION_SIGNATURE_ALGORITHM_PROHIBITED_KEY_USAGE_VALUE,
),
None,
),
}

def __init__(self):
super().__init__(
validations=[
self.VALIDATION_UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
self.VALIDATION_EC_PROHIBITED_KEY_USAGE_VALUE,
self.VALIDATION_EDWARDS_PROHIBITED_KEY_USAGE_VALUE,
self.VALIDATION_EDWARDS_MISSING_REQUIRED_KEY_USAGE_VALUE,
self.VALIDATION_RSA_PROHIBITED_KEY_USAGE_VALUE,
self.VALIDATION_SIGNATURE_ALGORITHM_PROHIBITED_KEY_USAGE_VALUE,
],
pdu_class=rfc5280.KeyUsage,
)

def validate(self, node):
spki_alg_oid = node.navigate(
":certificate.tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm"
).pdu

allowances = self._KEY_USAGE_VALUE_ALLOWANCES.get(spki_alg_oid)

if allowances is None:
raise validation.ValidationFindingEncountered(
self.VALIDATION_UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
f"Unsupported public key algorithm: {str(spki_alg_oid)}",
)

allowed_values_and_finding, required_values_and_finding = allowances
allowed_values, prohibited_finding = allowed_values_and_finding

bit_set = bitstring.get_asserted_bit_set(node)

prohibited_bits = bit_set - allowed_values

if any(prohibited_bits):
prohibited_ku_names = ", ".join(sorted(prohibited_bits))

raise validation.ValidationFindingEncountered(
prohibited_finding,
f"Prohibited key usage value(s) present: {prohibited_ku_names}",
)

if required_values_and_finding is not None:
required_values, missing_finding = required_values_and_finding

missing_kus = required_values - bit_set

if any(missing_kus):
missing_ku_names = ", ".join(sorted(missing_kus))

raise validation.ValidationFindingEncountered(
missing_finding,
f"Required key usage value(s) missing: {missing_ku_names}",
)
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ node_path,validator,severity,code,message
certificate.tbsCertificate.signature,AlgorithmIdentifierDecodingValidator,FATAL,itu.invalid_asn1_syntax,"Value node is present, but type OID 1.2.840.10045.4.3.3 specifies that it must be absent"
certificate.tbsCertificate.extensions.7.extnValue.subjectKeyIdentifier,SubjectKeyIdentifierValidator,INFO,pkix.subject_key_identifier_method_1_identified,
certificate.signatureAlgorithm,SignatureAlgorithmMatchValidator,ERROR,pkix.certificate_signature_algorithm_mismatch,DER encoding of certificate.signatureAlgorithm and certificate.tbsCertificate.signature are not equal
certificate.tbsCertificate.extensions.8.extnValue.keyUsage,SpkiKeyUsageConsistencyValidator,ERROR,pkix.key_usage_value_prohibited_for_ec,Prohibited key usage value(s) present: keyEncipherment
Loading

0 comments on commit 92379f4

Please sign in to comment.