def get_signer_key_usages(self, name=None) -> KeyUsageConstraints: vc_config = self._get_validation_settings_raw(name) try: policy_settings = dict(vc_config['signer-key-usage-policy']) except KeyError: policy_settings = {} # fallbacks to stay compatible with the simpler 0.5.0 signer-key-usage # and signer-extd-key-usage settings: copy old settings keys to # their corresponding values in the new one try: key_usage_strings = vc_config['signer-key-usage'] policy_settings.setdefault('key-usage', key_usage_strings) except KeyError: pass try: key_usage_strings = vc_config['signer-extd-key-usage'] policy_settings.setdefault('extd-key-usage', key_usage_strings) except KeyError: pass return KeyUsageConstraints.from_config(policy_settings)
def test_eku_accept(cfg: KeyUsageConstraints, cert_eku): cfg._validate_extd_key_usage(cert_eku)
def test_ku_reject(cfg: KeyUsageConstraints, cert_ku): with pytest.raises(InvalidCertificateError): cfg._validate_key_usage(cert_ku)
def test_eku_reject(cfg: KeyUsageConstraints, cert_eku, err): with pytest.raises(InvalidCertificateError, match=err): cfg._validate_extd_key_usage(cert_eku)
import pytest from asn1crypto.x509 import KeyUsage, ExtKeyUsageSyntax from pyhanko.sign.general import KeyUsageConstraints from pyhanko_certvalidator import InvalidCertificateError @pytest.mark.parametrize('cfg, cert_ku', [ (KeyUsageConstraints(key_usage={'non_repudiation'}), KeyUsage({'non_repudiation'})), (KeyUsageConstraints(key_usage={'non_repudiation', 'digital_signature'}), KeyUsage({'non_repudiation'})), (KeyUsageConstraints(key_usage={'non_repudiation'}), KeyUsage({'non_repudiation', 'digital_signature'})), (KeyUsageConstraints(key_usage={'non_repudiation'}, match_all_key_usages=True), KeyUsage({'non_repudiation', 'digital_signature'})), (KeyUsageConstraints(key_usage=set()), KeyUsage({'non_repudiation'})), (KeyUsageConstraints(), KeyUsage({'non_repudiation'})), (KeyUsageConstraints(), None), ]) def test_ku_accept(cfg: KeyUsageConstraints, cert_ku): cfg._validate_key_usage(cert_ku) @pytest.mark.parametrize('cfg, cert_ku', [ (KeyUsageConstraints(key_usage={'non_repudiation'}), KeyUsage({'digital_signature'})), (KeyUsageConstraints( key_usage={'non_repudiation', 'digital_signature'}, match_all_key_usages=True), KeyUsage({'digital_signature'})),
def satisfied_by(self, signer: x509.Certificate, validation_path: Optional[ValidationPath]): """ Evaluate whether a signing certificate satisfies the required constraints of this :class:`.SigCertConstraints` object. :param signer: The candidate signer's certificate. :param validation_path: Validation path of the signer's certificate. :raises UnacceptableSignerError: Raised if the conditions are not met. """ # this function assumes that key usage & trust checks have # passed already. flags = self.flags if flags & SigCertConstraintFlags.UNSUPPORTED: raise NotImplementedError( "Certificate constraint flags include mandatory constraints " "that are not supported.") if (flags & SigCertConstraintFlags.SUBJECT) \ and self.subjects is not None: # Explicit whitelist of approved signer certificates # compare using issuer_serial acceptable = (s.issuer_serial for s in self.subjects) if signer.issuer_serial not in acceptable: raise UnacceptableSignerError( "Signer certificate not on SVCert whitelist.") if (flags & SigCertConstraintFlags.ISSUER) \ and self.issuers is not None: if validation_path is None: raise UnacceptableSignerError("Validation path not provided.") # Here, we need to match any issuer in the chain of trust to # any of the issuers on the approved list. # To do so, we collect all issuer_serial identifiers in the chain # for all certificates except the last one (i.e. the current signer) path_iss_serials = { entry.issuer_serial for entry in validation_path.copy().pop() } for issuer in self.issuers: if issuer.issuer_serial in path_iss_serials: break else: # raise error if the loop runs to completion raise UnacceptableSignerError( "Signer certificate cannot be traced back to approved " "issuer.") if (flags & SigCertConstraintFlags.SUBJECT_DN) and self.subject_dn: # I'm not entirely sure whether my reading of the standard is # is correct, but I believe that this is the intention: # A DistinguishedName object is a sequence of # relative distinguished names (RDNs). The contents of the # /SubjectDN specify a list of constraints that might apply to each # of these RDNs. I believe the requirement is that each of the # SubjectDN entries must match one of these RDNs. requirement_list = list(x509_name_keyval_pairs(self.subject_dn)) subject_name = list(x509_name_keyval_pairs(signer.subject)) if not all(attr in subject_name for attr in requirement_list): raise UnacceptableSignerError( "Subject does not have some of the following required " "attributes: " + self.subject_dn.human_friendly) if (flags & SigCertConstraintFlags.KEY_USAGE) \ and self.key_usage is not None: for ku in self.key_usage: try: KeyUsageConstraints( key_usage=ku.must_have_set(), key_usage_forbidden=ku.forbidden_set()).validate( signer) break except InvalidCertificateError: continue else: raise UnacceptableSignerError( "The signer satisfies none of the key usage " "extension profiles specified in the seed value dictionary." )