diff --git a/pkilint/pkix/certificate/__init__.py b/pkilint/pkix/certificate/__init__.py index 9b7dced..0cb6427 100644 --- a/pkilint/pkix/certificate/__init__.py +++ b/pkilint/pkix/certificate/__init__.py @@ -3,7 +3,7 @@ import logging from typing import Set, Optional -from cryptography import x509 +from cryptography import x509, exceptions from pyasn1.codec.der.encoder import encode from pyasn1.type import univ from pyasn1.type.base import Asn1Type @@ -128,6 +128,15 @@ def public_key_object(self): self.root.navigate("tbsCertificate.subjectPublicKeyInfo") ) + def is_signed_with_key(self, public_key): + tbs_octets = self.cryptography_object.tbs_certificate_bytes + signature_hash_alg = self.cryptography_object.signature_hash_algorithm + signature_octets = self.cryptography_object.signature + + return key.verify_signature( + public_key, tbs_octets, signature_octets, signature_hash_alg + ) + @functools.cached_property def is_self_signed(self): if not self.is_self_issued: @@ -139,13 +148,12 @@ def is_self_signed(self): if public_key is None: return False - tbs_octets = self.cryptography_object.tbs_certificate_bytes - signature_hash_alg = self.cryptography_object.signature_hash_algorithm - signature_octets = self.cryptography_object.signature + try: + self.is_signed_with_key(public_key) - return key.verify_signature( - public_key, tbs_octets, signature_octets, signature_hash_alg - ) + # gracefully handle unsupported signature algorithms + except exceptions.UnsupportedAlgorithm: + return False def get_extension_by_oid(self, oid): tbs_cert = self.root.children["tbsCertificate"] diff --git a/pkilint/pkix/certificate/certificate_extension.py b/pkilint/pkix/certificate/certificate_extension.py index 4f0f11c..cd28bb8 100644 --- a/pkilint/pkix/certificate/certificate_extension.py +++ b/pkilint/pkix/certificate/certificate_extension.py @@ -1,6 +1,7 @@ from typing import NamedTuple, Set import unicodedata +from cryptography import exceptions from pyasn1.type.univ import ObjectIdentifier from pyasn1_alt_modules import rfc5280, rfc4262, rfc6962, rfc3739 @@ -628,16 +629,16 @@ class AuthorityKeyIdentifierPresenceValidator(validation.Validator): "pkix.authority_key_identifier_extension_absent", ) - VALIDATION_UNSUPPORTED_PUBLIC_KEY_ALGORITHM = validation.ValidationFinding( + VALIDATION_UNSUPPORTED_ALGORITHM = validation.ValidationFinding( validation.ValidationFindingSeverity.NOTICE, - "pkix.aki_absent_self_issued_and_unsupported_public_key_algorithm", + "pkix.aki_absent_self_issued_and_unsupported_algorithm", ) def __init__(self): super().__init__( validations=[ self.VALIDATION_AKI_EXTENSION_NOT_PRESENT, - self.VALIDATION_UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + self.VALIDATION_UNSUPPORTED_ALGORITHM, ], pdu_class=rfc5280.Certificate, ) @@ -650,25 +651,40 @@ def validate(self, node): if ext is not None: return - if not node.document.is_self_signed: - # check if certificate is not considered self-signed merely due to unsupported key algorithm - if node.document.is_self_issued and cert_doc.public_key_object is None: + if cert_doc.is_self_issued: + public_key = cert_doc.public_key_object + + if public_key is None: key_oid = str( node.navigate( - "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm" + ":certificate.tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm" ).pdu ) raise validation.ValidationFindingEncountered( - self.VALIDATION_UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - f"Self-issued certificate certifies a public key of unsupported algorithm: {key_oid}", + self.VALIDATION_UNSUPPORTED_ALGORITHM, + f"Self-issued certificate uses unsupported public key algorithm: {key_oid}", ) - # if the cert is not self-issued and/or certifies a supported key type, then report the PKIX error - else: + + try: + is_self_signed = cert_doc.is_signed_with_key(public_key) + + if is_self_signed: + return + except exceptions.UnsupportedAlgorithm: + sig_oid = str( + node.navigate(":certificate.signatureAlgorithm.algorithm").pdu + ) + raise validation.ValidationFindingEncountered( - self.VALIDATION_AKI_EXTENSION_NOT_PRESENT + self.VALIDATION_UNSUPPORTED_ALGORITHM, + f"Self-issued certificate uses unsupported signature algorithm: {sig_oid}", ) + raise validation.ValidationFindingEncountered( + self.VALIDATION_AKI_EXTENSION_NOT_PRESENT + ) + class AuthorityInformationAccessCriticalityValidator(ExtensionCriticalityValidator): VALIDATION_AIA_EXTENSION_CRITICAL = validation.ValidationFinding( diff --git a/pkilint/pkix/certificate/certificate_key.py b/pkilint/pkix/certificate/certificate_key.py index 5c158e1..aa17fe1 100644 --- a/pkilint/pkix/certificate/certificate_key.py +++ b/pkilint/pkix/certificate/certificate_key.py @@ -1,5 +1,6 @@ import binascii +from cryptography import exceptions from cryptography.hazmat.primitives import hashes from pyasn1.codec.der.encoder import encode from pyasn1.error import PyAsn1Error @@ -169,16 +170,16 @@ class SubjectSignatureVerificationValidator(validation.Validator): validation.ValidationFindingSeverity.ERROR, "pkix.signature_verification_failed" ) - VALIDATION_UNSUPPORTED_PUBLIC_KEY_ALGORITHM = validation.ValidationFinding( + VALIDATION_UNSUPPORTED_ALGORITHM = validation.ValidationFinding( validation.ValidationFindingSeverity.NOTICE, - "pkix.unsupported_public_key_algorithm", + "pkix.unsupported_algorithm", ) def __init__(self, *, tbs_node_retriever, **kwargs): super().__init__( validations=[ self.VALIDATION_SIGNATURE_MISMATCH, - self.VALIDATION_UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + self.VALIDATION_UNSUPPORTED_ALGORITHM, ], **kwargs, ) @@ -191,21 +192,26 @@ def validate(self, node): public_key = issuer_cert_doc.public_key_object if public_key is None: raise validation.ValidationFindingEncountered( - self.VALIDATION_UNSUPPORTED_PUBLIC_KEY_ALGORITHM + self.VALIDATION_UNSUPPORTED_ALGORITHM ) subject_crypto_doc = node.document.cryptography_object tbs_octets = encode(self._tbs_node_retriever(node).pdu) - if not verify_signature( - public_key, - tbs_octets, - node.pdu.asOctets(), - subject_crypto_doc.signature_hash_algorithm, - ): + try: + if not verify_signature( + public_key, + tbs_octets, + node.pdu.asOctets(), + subject_crypto_doc.signature_hash_algorithm, + ): + raise validation.ValidationFindingEncountered( + self.VALIDATION_SIGNATURE_MISMATCH + ) + except exceptions.UnsupportedAlgorithm: raise validation.ValidationFindingEncountered( - self.VALIDATION_SIGNATURE_MISMATCH + self.VALIDATION_UNSUPPORTED_ALGORITHM ) diff --git a/pkilint/pkix/key.py b/pkilint/pkix/key.py index 2a8ccfc..581d8ff 100644 --- a/pkilint/pkix/key.py +++ b/pkilint/pkix/key.py @@ -98,6 +98,12 @@ def verify_signature(public_key, message, signature, signature_hash_algorithm=No public_key.verify(signature, message) elif isinstance(public_key, ed448.Ed448PublicKey): public_key.verify(signature, message) + else: + type_name = type(public_key).__name__ + + raise exceptions.UnsupportedAlgorithm( + f'Unsupported public key type "{type_name}"' + ) except exceptions.InvalidSignature: return False diff --git a/tests/integration_certificate/pkix/mldsa44_root.crttest b/tests/integration_certificate/pkix/mldsa44_root.crttest index ec0ef5f..e191fd9 100644 --- a/tests/integration_certificate/pkix/mldsa44_root.crttest +++ b/tests/integration_certificate/pkix/mldsa44_root.crttest @@ -85,6 +85,6 @@ YNuixctKaeFUW6J1qIEi7SQ0n3wR0KM44FcCQ3LaZEYG7U/zVgI552D9+kJJENEB -----END CERTIFICATE----- node_path,validator,severity,code,message -certificate,AuthorityKeyIdentifierPresenceValidator,NOTICE,pkix.aki_absent_self_issued_and_unsupported_public_key_algorithm,Self-issued certificate certifies a public key of unsupported algorithm: 2.16.840.1.101.3.4.3.17 +certificate,AuthorityKeyIdentifierPresenceValidator,NOTICE,pkix.aki_absent_self_issued_and_unsupported_algorithm,Self-issued certificate uses unsupported public key algorithm: 2.16.840.1.101.3.4.3.17 certificate.tbsCertificate.extensions.0.extnValue.subjectKeyIdentifier,SubjectKeyIdentifierValidator,INFO,pkix.subject_key_identifier_method_1_identified, certificate.tbsCertificate.extensions.2.extnValue.keyUsage,SpkiKeyUsageConsistencyValidator,NOTICE,pkix.public_key_algorithm_unsupported,Unsupported public key algorithm: 2.16.840.1.101.3.4.3.17 diff --git a/tests/integration_certificate/pkix/unknown_key_type_self_issued_no_aki.crttest b/tests/integration_certificate/pkix/unknown_key_type_self_issued_no_aki.crttest index 9b53bf8..ea27923 100644 --- a/tests/integration_certificate/pkix/unknown_key_type_self_issued_no_aki.crttest +++ b/tests/integration_certificate/pkix/unknown_key_type_self_issued_no_aki.crttest @@ -25,6 +25,6 @@ AiAMA1aqNACzlZdAvhHk1o2fjCYYodSWbLbAd5cNAzPftw== -----END CERTIFICATE----- node_path,validator,severity,code,message -certificate,AuthorityKeyIdentifierPresenceValidator,NOTICE,pkix.aki_absent_self_issued_and_unsupported_public_key_algorithm,Self-issued certificate certifies a public key of unsupported algorithm: 1.2.840.113549.1.1.1.999 certificate.tbsCertificate.extensions.1.extnValue.subjectKeyIdentifier,SubjectKeyIdentifierValidator,INFO,pkix.subject_key_identifier_method_1_identified, +certificate,AuthorityKeyIdentifierPresenceValidator,NOTICE,pkix.aki_absent_self_issued_and_unsupported_algorithm,Self-issued certificate uses unsupported public key algorithm: 1.2.840.113549.1.1.1.999 certificate.tbsCertificate.extensions,KeyUsagePresenceValidator,ERROR,pkix.ca_certificate_no_ku_extension, diff --git a/tests/integration_certificate/pkix/v1_root_signed_with_md2.crttest b/tests/integration_certificate/pkix/v1_root_signed_with_md2.crttest new file mode 100644 index 0000000..4b43552 --- /dev/null +++ b/tests/integration_certificate/pkix/v1_root_signed_with_md2.crttest @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 +MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV +BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE +BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is +I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G +CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do +lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc +AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k +-----END CERTIFICATE----- + +node_path,validator,severity,code,message +certificate,SubjectKeyIdentifierPresenceValidator,WARNING,pkix.certificate_skid_end_entity_missing, +certificate.tbsCertificate.version,CorrectVersionValidator,ERROR,pkix.certificate_version_is_not_v3,"Expected=""2"", actual=""v1""" +certificate,AuthorityKeyIdentifierPresenceValidator,NOTICE,pkix.aki_absent_self_issued_and_unsupported_algorithm,Self-issued certificate uses unsupported signature algorithm: 1.2.840.113549.1.1.2