
| Current Path : /var/www/bavspeed/venvxxx/lib64/python3.12/site-packages/pyhanko_certvalidator/ |
Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64 |
| Current File : /var/www/bavspeed/venvxxx/lib64/python3.12/site-packages/pyhanko_certvalidator/authority.py |
import abc
from dataclasses import dataclass
from typing import Optional
from asn1crypto import keys, x509
from .name_trees import process_general_subtrees
from .policy_decl import PKIXValidationParams
# TODO add support for roots that are limited in time?
@dataclass(frozen=True)
class TrustQualifiers:
"""
.. versionadded 0.20.0
Parameters that allow a trust root to be qualified.
"""
standard_parameters: Optional['PKIXValidationParams'] = None
"""
Standard validation parameters that will apply when initialising
the PKIX validation process.
"""
max_path_length: Optional[int] = None
"""
Maximal allowed path length for this trust root, excluding self-issued
intermediate CA certificates. If ``None``, any path length will be accepted.
"""
max_aa_path_length: Optional[int] = None
"""
Maximal allowed path length for this trust root for the purposes of
AAControls. If ``None``, any path length will be accepted.
"""
class Authority(abc.ABC):
"""
.. versionadded:: 0.20.0
Abstract authority, i.e. a named key.
"""
@property
def name(self) -> x509.Name:
"""
The authority's name.
"""
raise NotImplementedError
@property
def public_key(self) -> keys.PublicKeyInfo:
"""
The authority's public key.
"""
raise NotImplementedError
@property
def hashable(self):
"""
A hashable unique identifier of the authority, used in ``__eq__``
and ``__hash__``.
"""
raise NotImplementedError
def __hash__(self):
return hash(self.hashable)
def __eq__(self, other):
if not isinstance(other, Authority):
return False
return self.hashable == other.hashable
@property
def key_id(self) -> Optional[bytes]:
"""
Key ID as (potentially) referenced in an authorityKeyIdentifier
extension. Only used to eliminate non-matching trust anchors,
never to retrieve keys or to definitively identify trust anchors.
"""
raise NotImplementedError
def is_potential_issuer_of(self, cert: x509.Certificate) -> bool:
"""
Function to determine whether this trust root could potentially be an
issuer of a given certificate.
This function is used during path building.
:param cert:
The certificate to evaluate.
"""
if cert.issuer != self.name:
return False
if cert.authority_key_identifier and self.key_id:
if cert.authority_key_identifier != self.key_id:
return False
return True
class TrustAnchor:
"""
Abstract trust root. A trust root is an authority with trust qualifiers.
Equality of trust roots reduces to equality of authorities.
"""
def __init__(
self, authority: Authority, quals: Optional[TrustQualifiers] = None
):
self._authority = authority
self._quals = quals
@property
def authority(self) -> Authority:
return self._authority
@property
def trust_qualifiers(self) -> TrustQualifiers:
"""
Qualifiers for the trust root.
"""
return self._quals or TrustQualifiers()
def __eq__(self, other):
return (
isinstance(other, TrustAnchor)
and other._authority == self._authority
)
def __hash__(self):
return hash(self._authority)
def derive_quals_from_cert(cert: x509.Certificate) -> TrustQualifiers:
"""
Extract trust qualifiers from data and extensions of a certificate.
.. note::
Recall that any property of a trust root other than its name and public
key are in principle irrelevant to the PKIX validation algorithm
itself.
This function is merely a helper function that allows the certificate's
other data to be conveniently gathered to populate the default
validation parameters for paths deriving from that trust root.
:param cert:
The certificate from which to extract qualifiers (usually a
self-signed one)
:return:
A :class:`TrustQualifiers` object with the extracted qualifiers.
"""
# TODO align with RFC 5937?
ext_found = False
permitted_subtrees = excluded_subtrees = None
if cert.name_constraints_value is not None:
ext_found = True
nc_ext: x509.NameConstraints = cert.name_constraints_value
permitted_val = nc_ext['permitted_subtrees']
if isinstance(permitted_val, x509.GeneralSubtrees):
permitted_subtrees = process_general_subtrees(permitted_val)
excluded_val = nc_ext['excluded_subtrees']
if isinstance(excluded_val, x509.GeneralSubtrees):
excluded_subtrees = process_general_subtrees(excluded_val)
acceptable_policies = None
if cert.certificate_policies_value is not None:
ext_found = True
policies_val: x509.CertificatePolicies = cert.certificate_policies_value
acceptable_policies = frozenset(
[pol_info['policy_identifier'].dotted for pol_info in policies_val]
)
params = None
if ext_found:
params = PKIXValidationParams(
user_initial_policy_set=(
acceptable_policies or frozenset(['any_policy'])
),
# For trust roots where the user asked for this derivation,
# let's assume that they want the policies to be enforced.
initial_explicit_policy=acceptable_policies is not None,
initial_permitted_subtrees=permitted_subtrees,
initial_excluded_subtrees=excluded_subtrees,
)
return TrustQualifiers(
max_path_length=cert.max_path_length, standard_parameters=params
)
class AuthorityWithCert(Authority):
"""
.. versionadded:: 0.20.0
Authority provisioned as a certificate.
:param cert:
The certificate.
"""
def __init__(self, cert: x509.Certificate):
self._cert = cert
@property
def name(self) -> x509.Name:
return self._cert.subject
@property
def public_key(self):
return self._cert.public_key
@property
def hashable(self):
cert = self._cert
return cert.subject.hashable, cert.public_key.dump()
@property
def key_id(self) -> Optional[bytes]:
return self._cert.key_identifier
@property
def certificate(self) -> x509.Certificate:
return self._cert
def is_potential_issuer_of(self, cert: x509.Certificate):
if not super().is_potential_issuer_of(cert):
return False
if cert.authority_issuer_serial:
if cert.authority_issuer_serial != self._cert.issuer_serial:
return False
return True
class CertTrustAnchor(TrustAnchor):
"""
.. versionadded:: 0.20.0
Trust anchor provisioned as a certificate.
:param cert:
The certificate, usually self-signed.
:param quals:
Explicit trust qualifiers.
:param derive_default_quals_from_cert:
Flag indicating to derive default trust qualifiers from the certificate
content if explicit ones are not provided. Defaults to ``False``.
"""
def __init__(
self,
cert: x509.Certificate,
quals: Optional[TrustQualifiers] = None,
derive_default_quals_from_cert: bool = False,
):
authority = AuthorityWithCert(cert)
self._cert = cert
super().__init__(authority, quals)
self._derive = derive_default_quals_from_cert
@property
def certificate(self) -> x509.Certificate:
return self._cert
@property
def trust_qualifiers(self) -> TrustQualifiers:
if self._quals is not None:
return self._quals
elif self._derive:
self._quals = quals = derive_quals_from_cert(self._cert)
return quals
else:
return TrustQualifiers()
class NamedKeyAuthority(Authority):
"""
Authority provisioned as a named key.
:param entity_name:
The name of the entity that controls the private key of the trust root.
:param public_key:
The trust root's public key.
"""
def __init__(self, entity_name: x509.Name, public_key: keys.PublicKeyInfo):
self._name = entity_name
self._public_key = public_key
@property
def name(self) -> x509.Name:
return self._name
@property
def public_key(self):
return self._public_key
@property
def key_id(self) -> Optional[bytes]:
return None
@property
def hashable(self):
return self._name.hashable, self._public_key.dump()