1# This file is dual licensed under the terms of the Apache License, Version 
    2# 2.0, and the BSD License. See the LICENSE file in the root of this repository 
    3# for complete details. 
    4 
    5from __future__ import annotations 
    6 
    7import abc 
    8import datetime 
    9import hashlib 
    10import ipaddress 
    11import typing 
    12from collections.abc import Iterable, Iterator 
    13 
    14from cryptography import utils 
    15from cryptography.hazmat.bindings._rust import asn1 
    16from cryptography.hazmat.bindings._rust import x509 as rust_x509 
    17from cryptography.hazmat.primitives import constant_time, serialization 
    18from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey 
    19from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey 
    20from cryptography.hazmat.primitives.asymmetric.types import ( 
    21    CertificateIssuerPublicKeyTypes, 
    22    CertificatePublicKeyTypes, 
    23) 
    24from cryptography.x509.certificate_transparency import ( 
    25    SignedCertificateTimestamp, 
    26) 
    27from cryptography.x509.general_name import ( 
    28    DirectoryName, 
    29    DNSName, 
    30    GeneralName, 
    31    IPAddress, 
    32    OtherName, 
    33    RegisteredID, 
    34    RFC822Name, 
    35    UniformResourceIdentifier, 
    36    _IPAddressTypes, 
    37) 
    38from cryptography.x509.name import Name, RelativeDistinguishedName 
    39from cryptography.x509.oid import ( 
    40    CRLEntryExtensionOID, 
    41    ExtensionOID, 
    42    ObjectIdentifier, 
    43    OCSPExtensionOID, 
    44) 
    45 
    46ExtensionTypeVar = typing.TypeVar( 
    47    "ExtensionTypeVar", bound="ExtensionType", covariant=True 
    48) 
    49 
    50 
    51def _key_identifier_from_public_key( 
    52    public_key: CertificatePublicKeyTypes, 
    53) -> bytes: 
    54    if isinstance(public_key, RSAPublicKey): 
    55        data = public_key.public_bytes( 
    56            serialization.Encoding.DER, 
    57            serialization.PublicFormat.PKCS1, 
    58        ) 
    59    elif isinstance(public_key, EllipticCurvePublicKey): 
    60        data = public_key.public_bytes( 
    61            serialization.Encoding.X962, 
    62            serialization.PublicFormat.UncompressedPoint, 
    63        ) 
    64    else: 
    65        # This is a very slow way to do this. 
    66        serialized = public_key.public_bytes( 
    67            serialization.Encoding.DER, 
    68            serialization.PublicFormat.SubjectPublicKeyInfo, 
    69        ) 
    70        data = asn1.parse_spki_for_data(serialized) 
    71 
    72    return hashlib.sha1(data).digest() 
    73 
    74 
    75def _make_sequence_methods(field_name: str): 
    76    def len_method(self) -> int: 
    77        return len(getattr(self, field_name)) 
    78 
    79    def iter_method(self): 
    80        return iter(getattr(self, field_name)) 
    81 
    82    def getitem_method(self, idx): 
    83        return getattr(self, field_name)[idx] 
    84 
    85    return len_method, iter_method, getitem_method 
    86 
    87 
    88class DuplicateExtension(Exception): 
    89    def __init__(self, msg: str, oid: ObjectIdentifier) -> None: 
    90        super().__init__(msg) 
    91        self.oid = oid 
    92 
    93 
    94class ExtensionNotFound(Exception): 
    95    def __init__(self, msg: str, oid: ObjectIdentifier) -> None: 
    96        super().__init__(msg) 
    97        self.oid = oid 
    98 
    99 
    100class ExtensionType(metaclass=abc.ABCMeta): 
    101    oid: typing.ClassVar[ObjectIdentifier] 
    102 
    103    def public_bytes(self) -> bytes: 
    104        """ 
    105        Serializes the extension type to DER. 
    106        """ 
    107        raise NotImplementedError( 
    108            f"public_bytes is not implemented for extension type {self!r}" 
    109        ) 
    110 
    111 
    112class Extensions: 
    113    def __init__(self, extensions: Iterable[Extension[ExtensionType]]) -> None: 
    114        self._extensions = list(extensions) 
    115 
    116    def get_extension_for_oid( 
    117        self, oid: ObjectIdentifier 
    118    ) -> Extension[ExtensionType]: 
    119        for ext in self: 
    120            if ext.oid == oid: 
    121                return ext 
    122 
    123        raise ExtensionNotFound(f"No {oid} extension was found", oid) 
    124 
    125    def get_extension_for_class( 
    126        self, extclass: type[ExtensionTypeVar] 
    127    ) -> Extension[ExtensionTypeVar]: 
    128        if extclass is UnrecognizedExtension: 
    129            raise TypeError( 
    130                "UnrecognizedExtension can't be used with " 
    131                "get_extension_for_class because more than one instance of the" 
    132                " class may be present." 
    133            ) 
    134 
    135        for ext in self: 
    136            if isinstance(ext.value, extclass): 
    137                return ext 
    138 
    139        raise ExtensionNotFound( 
    140            f"No {extclass} extension was found", extclass.oid 
    141        ) 
    142 
    143    __len__, __iter__, __getitem__ = _make_sequence_methods("_extensions") 
    144 
    145    def __repr__(self) -> str: 
    146        return f"<Extensions({self._extensions})>" 
    147 
    148 
    149class CRLNumber(ExtensionType): 
    150    oid = ExtensionOID.CRL_NUMBER 
    151 
    152    def __init__(self, crl_number: int) -> None: 
    153        if not isinstance(crl_number, int): 
    154            raise TypeError("crl_number must be an integer") 
    155 
    156        self._crl_number = crl_number 
    157 
    158    def __eq__(self, other: object) -> bool: 
    159        if not isinstance(other, CRLNumber): 
    160            return NotImplemented 
    161 
    162        return self.crl_number == other.crl_number 
    163 
    164    def __hash__(self) -> int: 
    165        return hash(self.crl_number) 
    166 
    167    def __repr__(self) -> str: 
    168        return f"<CRLNumber({self.crl_number})>" 
    169 
    170    @property 
    171    def crl_number(self) -> int: 
    172        return self._crl_number 
    173 
    174    def public_bytes(self) -> bytes: 
    175        return rust_x509.encode_extension_value(self) 
    176 
    177 
    178class AuthorityKeyIdentifier(ExtensionType): 
    179    oid = ExtensionOID.AUTHORITY_KEY_IDENTIFIER 
    180 
    181    def __init__( 
    182        self, 
    183        key_identifier: bytes | None, 
    184        authority_cert_issuer: Iterable[GeneralName] | None, 
    185        authority_cert_serial_number: int | None, 
    186    ) -> None: 
    187        if (authority_cert_issuer is None) != ( 
    188            authority_cert_serial_number is None 
    189        ): 
    190            raise ValueError( 
    191                "authority_cert_issuer and authority_cert_serial_number " 
    192                "must both be present or both None" 
    193            ) 
    194 
    195        if authority_cert_issuer is not None: 
    196            authority_cert_issuer = list(authority_cert_issuer) 
    197            if not all( 
    198                isinstance(x, GeneralName) for x in authority_cert_issuer 
    199            ): 
    200                raise TypeError( 
    201                    "authority_cert_issuer must be a list of GeneralName " 
    202                    "objects" 
    203                ) 
    204 
    205        if authority_cert_serial_number is not None and not isinstance( 
    206            authority_cert_serial_number, int 
    207        ): 
    208            raise TypeError("authority_cert_serial_number must be an integer") 
    209 
    210        self._key_identifier = key_identifier 
    211        self._authority_cert_issuer = authority_cert_issuer 
    212        self._authority_cert_serial_number = authority_cert_serial_number 
    213 
    214    # This takes a subset of CertificatePublicKeyTypes because an issuer 
    215    # cannot have an X25519/X448 key. This introduces some unfortunate 
    216    # asymmetry that requires typing users to explicitly 
    217    # narrow their type, but we should make this accurate and not just 
    218    # convenient. 
    219    @classmethod 
    220    def from_issuer_public_key( 
    221        cls, public_key: CertificateIssuerPublicKeyTypes 
    222    ) -> AuthorityKeyIdentifier: 
    223        digest = _key_identifier_from_public_key(public_key) 
    224        return cls( 
    225            key_identifier=digest, 
    226            authority_cert_issuer=None, 
    227            authority_cert_serial_number=None, 
    228        ) 
    229 
    230    @classmethod 
    231    def from_issuer_subject_key_identifier( 
    232        cls, ski: SubjectKeyIdentifier 
    233    ) -> AuthorityKeyIdentifier: 
    234        return cls( 
    235            key_identifier=ski.digest, 
    236            authority_cert_issuer=None, 
    237            authority_cert_serial_number=None, 
    238        ) 
    239 
    240    def __repr__(self) -> str: 
    241        return ( 
    242            f"<AuthorityKeyIdentifier(key_identifier={self.key_identifier!r}, " 
    243            f"authority_cert_issuer={self.authority_cert_issuer}, " 
    244            f"authority_cert_serial_number={self.authority_cert_serial_number}" 
    245            ")>" 
    246        ) 
    247 
    248    def __eq__(self, other: object) -> bool: 
    249        if not isinstance(other, AuthorityKeyIdentifier): 
    250            return NotImplemented 
    251 
    252        return ( 
    253            self.key_identifier == other.key_identifier 
    254            and self.authority_cert_issuer == other.authority_cert_issuer 
    255            and self.authority_cert_serial_number 
    256            == other.authority_cert_serial_number 
    257        ) 
    258 
    259    def __hash__(self) -> int: 
    260        if self.authority_cert_issuer is None: 
    261            aci = None 
    262        else: 
    263            aci = tuple(self.authority_cert_issuer) 
    264        return hash( 
    265            (self.key_identifier, aci, self.authority_cert_serial_number) 
    266        ) 
    267 
    268    @property 
    269    def key_identifier(self) -> bytes | None: 
    270        return self._key_identifier 
    271 
    272    @property 
    273    def authority_cert_issuer( 
    274        self, 
    275    ) -> list[GeneralName] | None: 
    276        return self._authority_cert_issuer 
    277 
    278    @property 
    279    def authority_cert_serial_number(self) -> int | None: 
    280        return self._authority_cert_serial_number 
    281 
    282    def public_bytes(self) -> bytes: 
    283        return rust_x509.encode_extension_value(self) 
    284 
    285 
    286class SubjectKeyIdentifier(ExtensionType): 
    287    oid = ExtensionOID.SUBJECT_KEY_IDENTIFIER 
    288 
    289    def __init__(self, digest: bytes) -> None: 
    290        self._digest = digest 
    291 
    292    @classmethod 
    293    def from_public_key( 
    294        cls, public_key: CertificatePublicKeyTypes 
    295    ) -> SubjectKeyIdentifier: 
    296        return cls(_key_identifier_from_public_key(public_key)) 
    297 
    298    @property 
    299    def digest(self) -> bytes: 
    300        return self._digest 
    301 
    302    @property 
    303    def key_identifier(self) -> bytes: 
    304        return self._digest 
    305 
    306    def __repr__(self) -> str: 
    307        return f"<SubjectKeyIdentifier(digest={self.digest!r})>" 
    308 
    309    def __eq__(self, other: object) -> bool: 
    310        if not isinstance(other, SubjectKeyIdentifier): 
    311            return NotImplemented 
    312 
    313        return constant_time.bytes_eq(self.digest, other.digest) 
    314 
    315    def __hash__(self) -> int: 
    316        return hash(self.digest) 
    317 
    318    def public_bytes(self) -> bytes: 
    319        return rust_x509.encode_extension_value(self) 
    320 
    321 
    322class AuthorityInformationAccess(ExtensionType): 
    323    oid = ExtensionOID.AUTHORITY_INFORMATION_ACCESS 
    324 
    325    def __init__(self, descriptions: Iterable[AccessDescription]) -> None: 
    326        descriptions = list(descriptions) 
    327        if not all(isinstance(x, AccessDescription) for x in descriptions): 
    328            raise TypeError( 
    329                "Every item in the descriptions list must be an " 
    330                "AccessDescription" 
    331            ) 
    332 
    333        self._descriptions = descriptions 
    334 
    335    __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions") 
    336 
    337    def __repr__(self) -> str: 
    338        return f"<AuthorityInformationAccess({self._descriptions})>" 
    339 
    340    def __eq__(self, other: object) -> bool: 
    341        if not isinstance(other, AuthorityInformationAccess): 
    342            return NotImplemented 
    343 
    344        return self._descriptions == other._descriptions 
    345 
    346    def __hash__(self) -> int: 
    347        return hash(tuple(self._descriptions)) 
    348 
    349    def public_bytes(self) -> bytes: 
    350        return rust_x509.encode_extension_value(self) 
    351 
    352 
    353class SubjectInformationAccess(ExtensionType): 
    354    oid = ExtensionOID.SUBJECT_INFORMATION_ACCESS 
    355 
    356    def __init__(self, descriptions: Iterable[AccessDescription]) -> None: 
    357        descriptions = list(descriptions) 
    358        if not all(isinstance(x, AccessDescription) for x in descriptions): 
    359            raise TypeError( 
    360                "Every item in the descriptions list must be an " 
    361                "AccessDescription" 
    362            ) 
    363 
    364        self._descriptions = descriptions 
    365 
    366    __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions") 
    367 
    368    def __repr__(self) -> str: 
    369        return f"<SubjectInformationAccess({self._descriptions})>" 
    370 
    371    def __eq__(self, other: object) -> bool: 
    372        if not isinstance(other, SubjectInformationAccess): 
    373            return NotImplemented 
    374 
    375        return self._descriptions == other._descriptions 
    376 
    377    def __hash__(self) -> int: 
    378        return hash(tuple(self._descriptions)) 
    379 
    380    def public_bytes(self) -> bytes: 
    381        return rust_x509.encode_extension_value(self) 
    382 
    383 
    384class AccessDescription: 
    385    def __init__( 
    386        self, access_method: ObjectIdentifier, access_location: GeneralName 
    387    ) -> None: 
    388        if not isinstance(access_method, ObjectIdentifier): 
    389            raise TypeError("access_method must be an ObjectIdentifier") 
    390 
    391        if not isinstance(access_location, GeneralName): 
    392            raise TypeError("access_location must be a GeneralName") 
    393 
    394        self._access_method = access_method 
    395        self._access_location = access_location 
    396 
    397    def __repr__(self) -> str: 
    398        return ( 
    399            f"<AccessDescription(access_method={self.access_method}, " 
    400            f"access_location={self.access_location})>" 
    401        ) 
    402 
    403    def __eq__(self, other: object) -> bool: 
    404        if not isinstance(other, AccessDescription): 
    405            return NotImplemented 
    406 
    407        return ( 
    408            self.access_method == other.access_method 
    409            and self.access_location == other.access_location 
    410        ) 
    411 
    412    def __hash__(self) -> int: 
    413        return hash((self.access_method, self.access_location)) 
    414 
    415    @property 
    416    def access_method(self) -> ObjectIdentifier: 
    417        return self._access_method 
    418 
    419    @property 
    420    def access_location(self) -> GeneralName: 
    421        return self._access_location 
    422 
    423 
    424class BasicConstraints(ExtensionType): 
    425    oid = ExtensionOID.BASIC_CONSTRAINTS 
    426 
    427    def __init__(self, ca: bool, path_length: int | None) -> None: 
    428        if not isinstance(ca, bool): 
    429            raise TypeError("ca must be a boolean value") 
    430 
    431        if path_length is not None and not ca: 
    432            raise ValueError("path_length must be None when ca is False") 
    433 
    434        if path_length is not None and ( 
    435            not isinstance(path_length, int) or path_length < 0 
    436        ): 
    437            raise TypeError( 
    438                "path_length must be a non-negative integer or None" 
    439            ) 
    440 
    441        self._ca = ca 
    442        self._path_length = path_length 
    443 
    444    @property 
    445    def ca(self) -> bool: 
    446        return self._ca 
    447 
    448    @property 
    449    def path_length(self) -> int | None: 
    450        return self._path_length 
    451 
    452    def __repr__(self) -> str: 
    453        return ( 
    454            f"<BasicConstraints(ca={self.ca}, path_length={self.path_length})>" 
    455        ) 
    456 
    457    def __eq__(self, other: object) -> bool: 
    458        if not isinstance(other, BasicConstraints): 
    459            return NotImplemented 
    460 
    461        return self.ca == other.ca and self.path_length == other.path_length 
    462 
    463    def __hash__(self) -> int: 
    464        return hash((self.ca, self.path_length)) 
    465 
    466    def public_bytes(self) -> bytes: 
    467        return rust_x509.encode_extension_value(self) 
    468 
    469 
    470class DeltaCRLIndicator(ExtensionType): 
    471    oid = ExtensionOID.DELTA_CRL_INDICATOR 
    472 
    473    def __init__(self, crl_number: int) -> None: 
    474        if not isinstance(crl_number, int): 
    475            raise TypeError("crl_number must be an integer") 
    476 
    477        self._crl_number = crl_number 
    478 
    479    @property 
    480    def crl_number(self) -> int: 
    481        return self._crl_number 
    482 
    483    def __eq__(self, other: object) -> bool: 
    484        if not isinstance(other, DeltaCRLIndicator): 
    485            return NotImplemented 
    486 
    487        return self.crl_number == other.crl_number 
    488 
    489    def __hash__(self) -> int: 
    490        return hash(self.crl_number) 
    491 
    492    def __repr__(self) -> str: 
    493        return f"<DeltaCRLIndicator(crl_number={self.crl_number})>" 
    494 
    495    def public_bytes(self) -> bytes: 
    496        return rust_x509.encode_extension_value(self) 
    497 
    498 
    499class CRLDistributionPoints(ExtensionType): 
    500    oid = ExtensionOID.CRL_DISTRIBUTION_POINTS 
    501 
    502    def __init__( 
    503        self, distribution_points: Iterable[DistributionPoint] 
    504    ) -> None: 
    505        distribution_points = list(distribution_points) 
    506        if not all( 
    507            isinstance(x, DistributionPoint) for x in distribution_points 
    508        ): 
    509            raise TypeError( 
    510                "distribution_points must be a list of DistributionPoint " 
    511                "objects" 
    512            ) 
    513 
    514        self._distribution_points = distribution_points 
    515 
    516    __len__, __iter__, __getitem__ = _make_sequence_methods( 
    517        "_distribution_points" 
    518    ) 
    519 
    520    def __repr__(self) -> str: 
    521        return f"<CRLDistributionPoints({self._distribution_points})>" 
    522 
    523    def __eq__(self, other: object) -> bool: 
    524        if not isinstance(other, CRLDistributionPoints): 
    525            return NotImplemented 
    526 
    527        return self._distribution_points == other._distribution_points 
    528 
    529    def __hash__(self) -> int: 
    530        return hash(tuple(self._distribution_points)) 
    531 
    532    def public_bytes(self) -> bytes: 
    533        return rust_x509.encode_extension_value(self) 
    534 
    535 
    536class FreshestCRL(ExtensionType): 
    537    oid = ExtensionOID.FRESHEST_CRL 
    538 
    539    def __init__( 
    540        self, distribution_points: Iterable[DistributionPoint] 
    541    ) -> None: 
    542        distribution_points = list(distribution_points) 
    543        if not all( 
    544            isinstance(x, DistributionPoint) for x in distribution_points 
    545        ): 
    546            raise TypeError( 
    547                "distribution_points must be a list of DistributionPoint " 
    548                "objects" 
    549            ) 
    550 
    551        self._distribution_points = distribution_points 
    552 
    553    __len__, __iter__, __getitem__ = _make_sequence_methods( 
    554        "_distribution_points" 
    555    ) 
    556 
    557    def __repr__(self) -> str: 
    558        return f"<FreshestCRL({self._distribution_points})>" 
    559 
    560    def __eq__(self, other: object) -> bool: 
    561        if not isinstance(other, FreshestCRL): 
    562            return NotImplemented 
    563 
    564        return self._distribution_points == other._distribution_points 
    565 
    566    def __hash__(self) -> int: 
    567        return hash(tuple(self._distribution_points)) 
    568 
    569    def public_bytes(self) -> bytes: 
    570        return rust_x509.encode_extension_value(self) 
    571 
    572 
    573class DistributionPoint: 
    574    def __init__( 
    575        self, 
    576        full_name: Iterable[GeneralName] | None, 
    577        relative_name: RelativeDistinguishedName | None, 
    578        reasons: frozenset[ReasonFlags] | None, 
    579        crl_issuer: Iterable[GeneralName] | None, 
    580    ) -> None: 
    581        if full_name and relative_name: 
    582            raise ValueError( 
    583                "You cannot provide both full_name and relative_name, at " 
    584                "least one must be None." 
    585            ) 
    586        if not full_name and not relative_name and not crl_issuer: 
    587            raise ValueError( 
    588                "Either full_name, relative_name or crl_issuer must be " 
    589                "provided." 
    590            ) 
    591 
    592        if full_name is not None: 
    593            full_name = list(full_name) 
    594            if not all(isinstance(x, GeneralName) for x in full_name): 
    595                raise TypeError( 
    596                    "full_name must be a list of GeneralName objects" 
    597                ) 
    598 
    599        if relative_name: 
    600            if not isinstance(relative_name, RelativeDistinguishedName): 
    601                raise TypeError( 
    602                    "relative_name must be a RelativeDistinguishedName" 
    603                ) 
    604 
    605        if crl_issuer is not None: 
    606            crl_issuer = list(crl_issuer) 
    607            if not all(isinstance(x, GeneralName) for x in crl_issuer): 
    608                raise TypeError( 
    609                    "crl_issuer must be None or a list of general names" 
    610                ) 
    611 
    612        if reasons and ( 
    613            not isinstance(reasons, frozenset) 
    614            or not all(isinstance(x, ReasonFlags) for x in reasons) 
    615        ): 
    616            raise TypeError("reasons must be None or frozenset of ReasonFlags") 
    617 
    618        if reasons and ( 
    619            ReasonFlags.unspecified in reasons 
    620            or ReasonFlags.remove_from_crl in reasons 
    621        ): 
    622            raise ValueError( 
    623                "unspecified and remove_from_crl are not valid reasons in a " 
    624                "DistributionPoint" 
    625            ) 
    626 
    627        self._full_name = full_name 
    628        self._relative_name = relative_name 
    629        self._reasons = reasons 
    630        self._crl_issuer = crl_issuer 
    631 
    632    def __repr__(self) -> str: 
    633        return ( 
    634            "<DistributionPoint(full_name={0.full_name}, relative_name={0.rela" 
    635            "tive_name}, reasons={0.reasons}, " 
    636            "crl_issuer={0.crl_issuer})>".format(self) 
    637        ) 
    638 
    639    def __eq__(self, other: object) -> bool: 
    640        if not isinstance(other, DistributionPoint): 
    641            return NotImplemented 
    642 
    643        return ( 
    644            self.full_name == other.full_name 
    645            and self.relative_name == other.relative_name 
    646            and self.reasons == other.reasons 
    647            and self.crl_issuer == other.crl_issuer 
    648        ) 
    649 
    650    def __hash__(self) -> int: 
    651        if self.full_name is not None: 
    652            fn: tuple[GeneralName, ...] | None = tuple(self.full_name) 
    653        else: 
    654            fn = None 
    655 
    656        if self.crl_issuer is not None: 
    657            crl_issuer: tuple[GeneralName, ...] | None = tuple(self.crl_issuer) 
    658        else: 
    659            crl_issuer = None 
    660 
    661        return hash((fn, self.relative_name, self.reasons, crl_issuer)) 
    662 
    663    @property 
    664    def full_name(self) -> list[GeneralName] | None: 
    665        return self._full_name 
    666 
    667    @property 
    668    def relative_name(self) -> RelativeDistinguishedName | None: 
    669        return self._relative_name 
    670 
    671    @property 
    672    def reasons(self) -> frozenset[ReasonFlags] | None: 
    673        return self._reasons 
    674 
    675    @property 
    676    def crl_issuer(self) -> list[GeneralName] | None: 
    677        return self._crl_issuer 
    678 
    679 
    680class ReasonFlags(utils.Enum): 
    681    unspecified = "unspecified" 
    682    key_compromise = "keyCompromise" 
    683    ca_compromise = "cACompromise" 
    684    affiliation_changed = "affiliationChanged" 
    685    superseded = "superseded" 
    686    cessation_of_operation = "cessationOfOperation" 
    687    certificate_hold = "certificateHold" 
    688    privilege_withdrawn = "privilegeWithdrawn" 
    689    aa_compromise = "aACompromise" 
    690    remove_from_crl = "removeFromCRL" 
    691 
    692 
    693# These are distribution point bit string mappings. Not to be confused with 
    694# CRLReason reason flags bit string mappings. 
    695# ReasonFlags ::= BIT STRING { 
    696#      unused                  (0), 
    697#      keyCompromise           (1), 
    698#      cACompromise            (2), 
    699#      affiliationChanged      (3), 
    700#      superseded              (4), 
    701#      cessationOfOperation    (5), 
    702#      certificateHold         (6), 
    703#      privilegeWithdrawn      (7), 
    704#      aACompromise            (8) } 
    705_REASON_BIT_MAPPING = { 
    706    1: ReasonFlags.key_compromise, 
    707    2: ReasonFlags.ca_compromise, 
    708    3: ReasonFlags.affiliation_changed, 
    709    4: ReasonFlags.superseded, 
    710    5: ReasonFlags.cessation_of_operation, 
    711    6: ReasonFlags.certificate_hold, 
    712    7: ReasonFlags.privilege_withdrawn, 
    713    8: ReasonFlags.aa_compromise, 
    714} 
    715 
    716_CRLREASONFLAGS = { 
    717    ReasonFlags.key_compromise: 1, 
    718    ReasonFlags.ca_compromise: 2, 
    719    ReasonFlags.affiliation_changed: 3, 
    720    ReasonFlags.superseded: 4, 
    721    ReasonFlags.cessation_of_operation: 5, 
    722    ReasonFlags.certificate_hold: 6, 
    723    ReasonFlags.privilege_withdrawn: 7, 
    724    ReasonFlags.aa_compromise: 8, 
    725} 
    726 
    727#    CRLReason ::= ENUMERATED { 
    728#        unspecified             (0), 
    729#        keyCompromise           (1), 
    730#        cACompromise            (2), 
    731#        affiliationChanged      (3), 
    732#        superseded              (4), 
    733#        cessationOfOperation    (5), 
    734#        certificateHold         (6), 
    735#             -- value 7 is not used 
    736#        removeFromCRL           (8), 
    737#        privilegeWithdrawn      (9), 
    738#        aACompromise           (10) } 
    739_CRL_ENTRY_REASON_ENUM_TO_CODE = { 
    740    ReasonFlags.unspecified: 0, 
    741    ReasonFlags.key_compromise: 1, 
    742    ReasonFlags.ca_compromise: 2, 
    743    ReasonFlags.affiliation_changed: 3, 
    744    ReasonFlags.superseded: 4, 
    745    ReasonFlags.cessation_of_operation: 5, 
    746    ReasonFlags.certificate_hold: 6, 
    747    ReasonFlags.remove_from_crl: 8, 
    748    ReasonFlags.privilege_withdrawn: 9, 
    749    ReasonFlags.aa_compromise: 10, 
    750} 
    751 
    752 
    753class PolicyConstraints(ExtensionType): 
    754    oid = ExtensionOID.POLICY_CONSTRAINTS 
    755 
    756    def __init__( 
    757        self, 
    758        require_explicit_policy: int | None, 
    759        inhibit_policy_mapping: int | None, 
    760    ) -> None: 
    761        if require_explicit_policy is not None and not isinstance( 
    762            require_explicit_policy, int 
    763        ): 
    764            raise TypeError( 
    765                "require_explicit_policy must be a non-negative integer or " 
    766                "None" 
    767            ) 
    768 
    769        if inhibit_policy_mapping is not None and not isinstance( 
    770            inhibit_policy_mapping, int 
    771        ): 
    772            raise TypeError( 
    773                "inhibit_policy_mapping must be a non-negative integer or None" 
    774            ) 
    775 
    776        if inhibit_policy_mapping is None and require_explicit_policy is None: 
    777            raise ValueError( 
    778                "At least one of require_explicit_policy and " 
    779                "inhibit_policy_mapping must not be None" 
    780            ) 
    781 
    782        self._require_explicit_policy = require_explicit_policy 
    783        self._inhibit_policy_mapping = inhibit_policy_mapping 
    784 
    785    def __repr__(self) -> str: 
    786        return ( 
    787            "<PolicyConstraints(require_explicit_policy={0.require_explicit" 
    788            "_policy}, inhibit_policy_mapping={0.inhibit_policy_" 
    789            "mapping})>".format(self) 
    790        ) 
    791 
    792    def __eq__(self, other: object) -> bool: 
    793        if not isinstance(other, PolicyConstraints): 
    794            return NotImplemented 
    795 
    796        return ( 
    797            self.require_explicit_policy == other.require_explicit_policy 
    798            and self.inhibit_policy_mapping == other.inhibit_policy_mapping 
    799        ) 
    800 
    801    def __hash__(self) -> int: 
    802        return hash( 
    803            (self.require_explicit_policy, self.inhibit_policy_mapping) 
    804        ) 
    805 
    806    @property 
    807    def require_explicit_policy(self) -> int | None: 
    808        return self._require_explicit_policy 
    809 
    810    @property 
    811    def inhibit_policy_mapping(self) -> int | None: 
    812        return self._inhibit_policy_mapping 
    813 
    814    def public_bytes(self) -> bytes: 
    815        return rust_x509.encode_extension_value(self) 
    816 
    817 
    818class CertificatePolicies(ExtensionType): 
    819    oid = ExtensionOID.CERTIFICATE_POLICIES 
    820 
    821    def __init__(self, policies: Iterable[PolicyInformation]) -> None: 
    822        policies = list(policies) 
    823        if not all(isinstance(x, PolicyInformation) for x in policies): 
    824            raise TypeError( 
    825                "Every item in the policies list must be a PolicyInformation" 
    826            ) 
    827 
    828        self._policies = policies 
    829 
    830    __len__, __iter__, __getitem__ = _make_sequence_methods("_policies") 
    831 
    832    def __repr__(self) -> str: 
    833        return f"<CertificatePolicies({self._policies})>" 
    834 
    835    def __eq__(self, other: object) -> bool: 
    836        if not isinstance(other, CertificatePolicies): 
    837            return NotImplemented 
    838 
    839        return self._policies == other._policies 
    840 
    841    def __hash__(self) -> int: 
    842        return hash(tuple(self._policies)) 
    843 
    844    def public_bytes(self) -> bytes: 
    845        return rust_x509.encode_extension_value(self) 
    846 
    847 
    848class PolicyInformation: 
    849    def __init__( 
    850        self, 
    851        policy_identifier: ObjectIdentifier, 
    852        policy_qualifiers: Iterable[str | UserNotice] | None, 
    853    ) -> None: 
    854        if not isinstance(policy_identifier, ObjectIdentifier): 
    855            raise TypeError("policy_identifier must be an ObjectIdentifier") 
    856 
    857        self._policy_identifier = policy_identifier 
    858 
    859        if policy_qualifiers is not None: 
    860            policy_qualifiers = list(policy_qualifiers) 
    861            if not all( 
    862                isinstance(x, (str, UserNotice)) for x in policy_qualifiers 
    863            ): 
    864                raise TypeError( 
    865                    "policy_qualifiers must be a list of strings and/or " 
    866                    "UserNotice objects or None" 
    867                ) 
    868 
    869        self._policy_qualifiers = policy_qualifiers 
    870 
    871    def __repr__(self) -> str: 
    872        return ( 
    873            f"<PolicyInformation(policy_identifier={self.policy_identifier}, " 
    874            f"policy_qualifiers={self.policy_qualifiers})>" 
    875        ) 
    876 
    877    def __eq__(self, other: object) -> bool: 
    878        if not isinstance(other, PolicyInformation): 
    879            return NotImplemented 
    880 
    881        return ( 
    882            self.policy_identifier == other.policy_identifier 
    883            and self.policy_qualifiers == other.policy_qualifiers 
    884        ) 
    885 
    886    def __hash__(self) -> int: 
    887        if self.policy_qualifiers is not None: 
    888            pq = tuple(self.policy_qualifiers) 
    889        else: 
    890            pq = None 
    891 
    892        return hash((self.policy_identifier, pq)) 
    893 
    894    @property 
    895    def policy_identifier(self) -> ObjectIdentifier: 
    896        return self._policy_identifier 
    897 
    898    @property 
    899    def policy_qualifiers( 
    900        self, 
    901    ) -> list[str | UserNotice] | None: 
    902        return self._policy_qualifiers 
    903 
    904 
    905class UserNotice: 
    906    def __init__( 
    907        self, 
    908        notice_reference: NoticeReference | None, 
    909        explicit_text: str | None, 
    910    ) -> None: 
    911        if notice_reference and not isinstance( 
    912            notice_reference, NoticeReference 
    913        ): 
    914            raise TypeError( 
    915                "notice_reference must be None or a NoticeReference" 
    916            ) 
    917 
    918        self._notice_reference = notice_reference 
    919        self._explicit_text = explicit_text 
    920 
    921    def __repr__(self) -> str: 
    922        return ( 
    923            f"<UserNotice(notice_reference={self.notice_reference}, " 
    924            f"explicit_text={self.explicit_text!r})>" 
    925        ) 
    926 
    927    def __eq__(self, other: object) -> bool: 
    928        if not isinstance(other, UserNotice): 
    929            return NotImplemented 
    930 
    931        return ( 
    932            self.notice_reference == other.notice_reference 
    933            and self.explicit_text == other.explicit_text 
    934        ) 
    935 
    936    def __hash__(self) -> int: 
    937        return hash((self.notice_reference, self.explicit_text)) 
    938 
    939    @property 
    940    def notice_reference(self) -> NoticeReference | None: 
    941        return self._notice_reference 
    942 
    943    @property 
    944    def explicit_text(self) -> str | None: 
    945        return self._explicit_text 
    946 
    947 
    948class NoticeReference: 
    949    def __init__( 
    950        self, 
    951        organization: str | None, 
    952        notice_numbers: Iterable[int], 
    953    ) -> None: 
    954        self._organization = organization 
    955        notice_numbers = list(notice_numbers) 
    956        if not all(isinstance(x, int) for x in notice_numbers): 
    957            raise TypeError("notice_numbers must be a list of integers") 
    958 
    959        self._notice_numbers = notice_numbers 
    960 
    961    def __repr__(self) -> str: 
    962        return ( 
    963            f"<NoticeReference(organization={self.organization!r}, " 
    964            f"notice_numbers={self.notice_numbers})>" 
    965        ) 
    966 
    967    def __eq__(self, other: object) -> bool: 
    968        if not isinstance(other, NoticeReference): 
    969            return NotImplemented 
    970 
    971        return ( 
    972            self.organization == other.organization 
    973            and self.notice_numbers == other.notice_numbers 
    974        ) 
    975 
    976    def __hash__(self) -> int: 
    977        return hash((self.organization, tuple(self.notice_numbers))) 
    978 
    979    @property 
    980    def organization(self) -> str | None: 
    981        return self._organization 
    982 
    983    @property 
    984    def notice_numbers(self) -> list[int]: 
    985        return self._notice_numbers 
    986 
    987 
    988class ExtendedKeyUsage(ExtensionType): 
    989    oid = ExtensionOID.EXTENDED_KEY_USAGE 
    990 
    991    def __init__(self, usages: Iterable[ObjectIdentifier]) -> None: 
    992        usages = list(usages) 
    993        if not all(isinstance(x, ObjectIdentifier) for x in usages): 
    994            raise TypeError( 
    995                "Every item in the usages list must be an ObjectIdentifier" 
    996            ) 
    997 
    998        self._usages = usages 
    999 
    1000    __len__, __iter__, __getitem__ = _make_sequence_methods("_usages") 
    1001 
    1002    def __repr__(self) -> str: 
    1003        return f"<ExtendedKeyUsage({self._usages})>" 
    1004 
    1005    def __eq__(self, other: object) -> bool: 
    1006        if not isinstance(other, ExtendedKeyUsage): 
    1007            return NotImplemented 
    1008 
    1009        return self._usages == other._usages 
    1010 
    1011    def __hash__(self) -> int: 
    1012        return hash(tuple(self._usages)) 
    1013 
    1014    def public_bytes(self) -> bytes: 
    1015        return rust_x509.encode_extension_value(self) 
    1016 
    1017 
    1018class OCSPNoCheck(ExtensionType): 
    1019    oid = ExtensionOID.OCSP_NO_CHECK 
    1020 
    1021    def __eq__(self, other: object) -> bool: 
    1022        if not isinstance(other, OCSPNoCheck): 
    1023            return NotImplemented 
    1024 
    1025        return True 
    1026 
    1027    def __hash__(self) -> int: 
    1028        return hash(OCSPNoCheck) 
    1029 
    1030    def __repr__(self) -> str: 
    1031        return "<OCSPNoCheck()>" 
    1032 
    1033    def public_bytes(self) -> bytes: 
    1034        return rust_x509.encode_extension_value(self) 
    1035 
    1036 
    1037class PrecertPoison(ExtensionType): 
    1038    oid = ExtensionOID.PRECERT_POISON 
    1039 
    1040    def __eq__(self, other: object) -> bool: 
    1041        if not isinstance(other, PrecertPoison): 
    1042            return NotImplemented 
    1043 
    1044        return True 
    1045 
    1046    def __hash__(self) -> int: 
    1047        return hash(PrecertPoison) 
    1048 
    1049    def __repr__(self) -> str: 
    1050        return "<PrecertPoison()>" 
    1051 
    1052    def public_bytes(self) -> bytes: 
    1053        return rust_x509.encode_extension_value(self) 
    1054 
    1055 
    1056class TLSFeature(ExtensionType): 
    1057    oid = ExtensionOID.TLS_FEATURE 
    1058 
    1059    def __init__(self, features: Iterable[TLSFeatureType]) -> None: 
    1060        features = list(features) 
    1061        if ( 
    1062            not all(isinstance(x, TLSFeatureType) for x in features) 
    1063            or len(features) == 0 
    1064        ): 
    1065            raise TypeError( 
    1066                "features must be a list of elements from the TLSFeatureType " 
    1067                "enum" 
    1068            ) 
    1069 
    1070        self._features = features 
    1071 
    1072    __len__, __iter__, __getitem__ = _make_sequence_methods("_features") 
    1073 
    1074    def __repr__(self) -> str: 
    1075        return f"<TLSFeature(features={self._features})>" 
    1076 
    1077    def __eq__(self, other: object) -> bool: 
    1078        if not isinstance(other, TLSFeature): 
    1079            return NotImplemented 
    1080 
    1081        return self._features == other._features 
    1082 
    1083    def __hash__(self) -> int: 
    1084        return hash(tuple(self._features)) 
    1085 
    1086    def public_bytes(self) -> bytes: 
    1087        return rust_x509.encode_extension_value(self) 
    1088 
    1089 
    1090class TLSFeatureType(utils.Enum): 
    1091    # status_request is defined in RFC 6066 and is used for what is commonly 
    1092    # called OCSP Must-Staple when present in the TLS Feature extension in an 
    1093    # X.509 certificate. 
    1094    status_request = 5 
    1095    # status_request_v2 is defined in RFC 6961 and allows multiple OCSP 
    1096    # responses to be provided. It is not currently in use by clients or 
    1097    # servers. 
    1098    status_request_v2 = 17 
    1099 
    1100 
    1101_TLS_FEATURE_TYPE_TO_ENUM = {x.value: x for x in TLSFeatureType} 
    1102 
    1103 
    1104class InhibitAnyPolicy(ExtensionType): 
    1105    oid = ExtensionOID.INHIBIT_ANY_POLICY 
    1106 
    1107    def __init__(self, skip_certs: int) -> None: 
    1108        if not isinstance(skip_certs, int): 
    1109            raise TypeError("skip_certs must be an integer") 
    1110 
    1111        if skip_certs < 0: 
    1112            raise ValueError("skip_certs must be a non-negative integer") 
    1113 
    1114        self._skip_certs = skip_certs 
    1115 
    1116    def __repr__(self) -> str: 
    1117        return f"<InhibitAnyPolicy(skip_certs={self.skip_certs})>" 
    1118 
    1119    def __eq__(self, other: object) -> bool: 
    1120        if not isinstance(other, InhibitAnyPolicy): 
    1121            return NotImplemented 
    1122 
    1123        return self.skip_certs == other.skip_certs 
    1124 
    1125    def __hash__(self) -> int: 
    1126        return hash(self.skip_certs) 
    1127 
    1128    @property 
    1129    def skip_certs(self) -> int: 
    1130        return self._skip_certs 
    1131 
    1132    def public_bytes(self) -> bytes: 
    1133        return rust_x509.encode_extension_value(self) 
    1134 
    1135 
    1136class KeyUsage(ExtensionType): 
    1137    oid = ExtensionOID.KEY_USAGE 
    1138 
    1139    def __init__( 
    1140        self, 
    1141        digital_signature: bool, 
    1142        content_commitment: bool, 
    1143        key_encipherment: bool, 
    1144        data_encipherment: bool, 
    1145        key_agreement: bool, 
    1146        key_cert_sign: bool, 
    1147        crl_sign: bool, 
    1148        encipher_only: bool, 
    1149        decipher_only: bool, 
    1150    ) -> None: 
    1151        if not key_agreement and (encipher_only or decipher_only): 
    1152            raise ValueError( 
    1153                "encipher_only and decipher_only can only be true when " 
    1154                "key_agreement is true" 
    1155            ) 
    1156 
    1157        self._digital_signature = digital_signature 
    1158        self._content_commitment = content_commitment 
    1159        self._key_encipherment = key_encipherment 
    1160        self._data_encipherment = data_encipherment 
    1161        self._key_agreement = key_agreement 
    1162        self._key_cert_sign = key_cert_sign 
    1163        self._crl_sign = crl_sign 
    1164        self._encipher_only = encipher_only 
    1165        self._decipher_only = decipher_only 
    1166 
    1167    @property 
    1168    def digital_signature(self) -> bool: 
    1169        return self._digital_signature 
    1170 
    1171    @property 
    1172    def content_commitment(self) -> bool: 
    1173        return self._content_commitment 
    1174 
    1175    @property 
    1176    def key_encipherment(self) -> bool: 
    1177        return self._key_encipherment 
    1178 
    1179    @property 
    1180    def data_encipherment(self) -> bool: 
    1181        return self._data_encipherment 
    1182 
    1183    @property 
    1184    def key_agreement(self) -> bool: 
    1185        return self._key_agreement 
    1186 
    1187    @property 
    1188    def key_cert_sign(self) -> bool: 
    1189        return self._key_cert_sign 
    1190 
    1191    @property 
    1192    def crl_sign(self) -> bool: 
    1193        return self._crl_sign 
    1194 
    1195    @property 
    1196    def encipher_only(self) -> bool: 
    1197        if not self.key_agreement: 
    1198            raise ValueError( 
    1199                "encipher_only is undefined unless key_agreement is true" 
    1200            ) 
    1201        else: 
    1202            return self._encipher_only 
    1203 
    1204    @property 
    1205    def decipher_only(self) -> bool: 
    1206        if not self.key_agreement: 
    1207            raise ValueError( 
    1208                "decipher_only is undefined unless key_agreement is true" 
    1209            ) 
    1210        else: 
    1211            return self._decipher_only 
    1212 
    1213    def __repr__(self) -> str: 
    1214        try: 
    1215            encipher_only = self.encipher_only 
    1216            decipher_only = self.decipher_only 
    1217        except ValueError: 
    1218            # Users found None confusing because even though encipher/decipher 
    1219            # have no meaning unless key_agreement is true, to construct an 
    1220            # instance of the class you still need to pass False. 
    1221            encipher_only = False 
    1222            decipher_only = False 
    1223 
    1224        return ( 
    1225            f"<KeyUsage(digital_signature={self.digital_signature}, " 
    1226            f"content_commitment={self.content_commitment}, " 
    1227            f"key_encipherment={self.key_encipherment}, " 
    1228            f"data_encipherment={self.data_encipherment}, " 
    1229            f"key_agreement={self.key_agreement}, " 
    1230            f"key_cert_sign={self.key_cert_sign}, crl_sign={self.crl_sign}, " 
    1231            f"encipher_only={encipher_only}, decipher_only={decipher_only})>" 
    1232        ) 
    1233 
    1234    def __eq__(self, other: object) -> bool: 
    1235        if not isinstance(other, KeyUsage): 
    1236            return NotImplemented 
    1237 
    1238        return ( 
    1239            self.digital_signature == other.digital_signature 
    1240            and self.content_commitment == other.content_commitment 
    1241            and self.key_encipherment == other.key_encipherment 
    1242            and self.data_encipherment == other.data_encipherment 
    1243            and self.key_agreement == other.key_agreement 
    1244            and self.key_cert_sign == other.key_cert_sign 
    1245            and self.crl_sign == other.crl_sign 
    1246            and self._encipher_only == other._encipher_only 
    1247            and self._decipher_only == other._decipher_only 
    1248        ) 
    1249 
    1250    def __hash__(self) -> int: 
    1251        return hash( 
    1252            ( 
    1253                self.digital_signature, 
    1254                self.content_commitment, 
    1255                self.key_encipherment, 
    1256                self.data_encipherment, 
    1257                self.key_agreement, 
    1258                self.key_cert_sign, 
    1259                self.crl_sign, 
    1260                self._encipher_only, 
    1261                self._decipher_only, 
    1262            ) 
    1263        ) 
    1264 
    1265    def public_bytes(self) -> bytes: 
    1266        return rust_x509.encode_extension_value(self) 
    1267 
    1268 
    1269class PrivateKeyUsagePeriod(ExtensionType): 
    1270    oid = ExtensionOID.PRIVATE_KEY_USAGE_PERIOD 
    1271 
    1272    def __init__( 
    1273        self, 
    1274        not_before: datetime.datetime | None, 
    1275        not_after: datetime.datetime | None, 
    1276    ) -> None: 
    1277        if ( 
    1278            not isinstance(not_before, datetime.datetime) 
    1279            and not_before is not None 
    1280        ): 
    1281            raise TypeError("not_before must be a datetime.datetime or None") 
    1282 
    1283        if ( 
    1284            not isinstance(not_after, datetime.datetime) 
    1285            and not_after is not None 
    1286        ): 
    1287            raise TypeError("not_after must be a datetime.datetime or None") 
    1288 
    1289        if not_before is None and not_after is None: 
    1290            raise ValueError( 
    1291                "At least one of not_before and not_after must not be None" 
    1292            ) 
    1293 
    1294        if ( 
    1295            not_before is not None 
    1296            and not_after is not None 
    1297            and not_before > not_after 
    1298        ): 
    1299            raise ValueError("not_before must be before not_after") 
    1300 
    1301        self._not_before = not_before 
    1302        self._not_after = not_after 
    1303 
    1304    @property 
    1305    def not_before(self) -> datetime.datetime | None: 
    1306        return self._not_before 
    1307 
    1308    @property 
    1309    def not_after(self) -> datetime.datetime | None: 
    1310        return self._not_after 
    1311 
    1312    def __repr__(self) -> str: 
    1313        return ( 
    1314            f"<PrivateKeyUsagePeriod(not_before={self.not_before}, " 
    1315            f"not_after={self.not_after})>" 
    1316        ) 
    1317 
    1318    def __eq__(self, other: object) -> bool: 
    1319        if not isinstance(other, PrivateKeyUsagePeriod): 
    1320            return NotImplemented 
    1321 
    1322        return ( 
    1323            self.not_before == other.not_before 
    1324            and self.not_after == other.not_after 
    1325        ) 
    1326 
    1327    def __hash__(self) -> int: 
    1328        return hash((self.not_before, self.not_after)) 
    1329 
    1330    def public_bytes(self) -> bytes: 
    1331        return rust_x509.encode_extension_value(self) 
    1332 
    1333 
    1334class NameConstraints(ExtensionType): 
    1335    oid = ExtensionOID.NAME_CONSTRAINTS 
    1336 
    1337    def __init__( 
    1338        self, 
    1339        permitted_subtrees: Iterable[GeneralName] | None, 
    1340        excluded_subtrees: Iterable[GeneralName] | None, 
    1341    ) -> None: 
    1342        if permitted_subtrees is not None: 
    1343            permitted_subtrees = list(permitted_subtrees) 
    1344            if not permitted_subtrees: 
    1345                raise ValueError( 
    1346                    "permitted_subtrees must be a non-empty list or None" 
    1347                ) 
    1348            if not all(isinstance(x, GeneralName) for x in permitted_subtrees): 
    1349                raise TypeError( 
    1350                    "permitted_subtrees must be a list of GeneralName objects " 
    1351                    "or None" 
    1352                ) 
    1353 
    1354            self._validate_tree(permitted_subtrees) 
    1355 
    1356        if excluded_subtrees is not None: 
    1357            excluded_subtrees = list(excluded_subtrees) 
    1358            if not excluded_subtrees: 
    1359                raise ValueError( 
    1360                    "excluded_subtrees must be a non-empty list or None" 
    1361                ) 
    1362            if not all(isinstance(x, GeneralName) for x in excluded_subtrees): 
    1363                raise TypeError( 
    1364                    "excluded_subtrees must be a list of GeneralName objects " 
    1365                    "or None" 
    1366                ) 
    1367 
    1368            self._validate_tree(excluded_subtrees) 
    1369 
    1370        if permitted_subtrees is None and excluded_subtrees is None: 
    1371            raise ValueError( 
    1372                "At least one of permitted_subtrees and excluded_subtrees " 
    1373                "must not be None" 
    1374            ) 
    1375 
    1376        self._permitted_subtrees = permitted_subtrees 
    1377        self._excluded_subtrees = excluded_subtrees 
    1378 
    1379    def __eq__(self, other: object) -> bool: 
    1380        if not isinstance(other, NameConstraints): 
    1381            return NotImplemented 
    1382 
    1383        return ( 
    1384            self.excluded_subtrees == other.excluded_subtrees 
    1385            and self.permitted_subtrees == other.permitted_subtrees 
    1386        ) 
    1387 
    1388    def _validate_tree(self, tree: Iterable[GeneralName]) -> None: 
    1389        self._validate_ip_name(tree) 
    1390        self._validate_dns_name(tree) 
    1391 
    1392    def _validate_ip_name(self, tree: Iterable[GeneralName]) -> None: 
    1393        if any( 
    1394            isinstance(name, IPAddress) 
    1395            and not isinstance( 
    1396                name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network) 
    1397            ) 
    1398            for name in tree 
    1399        ): 
    1400            raise TypeError( 
    1401                "IPAddress name constraints must be an IPv4Network or" 
    1402                " IPv6Network object" 
    1403            ) 
    1404 
    1405    def _validate_dns_name(self, tree: Iterable[GeneralName]) -> None: 
    1406        if any( 
    1407            isinstance(name, DNSName) and "*" in name.value for name in tree 
    1408        ): 
    1409            raise ValueError( 
    1410                "DNSName name constraints must not contain the '*' wildcard" 
    1411                " character" 
    1412            ) 
    1413 
    1414    def __repr__(self) -> str: 
    1415        return ( 
    1416            f"<NameConstraints(permitted_subtrees={self.permitted_subtrees}, " 
    1417            f"excluded_subtrees={self.excluded_subtrees})>" 
    1418        ) 
    1419 
    1420    def __hash__(self) -> int: 
    1421        if self.permitted_subtrees is not None: 
    1422            ps: tuple[GeneralName, ...] | None = tuple(self.permitted_subtrees) 
    1423        else: 
    1424            ps = None 
    1425 
    1426        if self.excluded_subtrees is not None: 
    1427            es: tuple[GeneralName, ...] | None = tuple(self.excluded_subtrees) 
    1428        else: 
    1429            es = None 
    1430 
    1431        return hash((ps, es)) 
    1432 
    1433    @property 
    1434    def permitted_subtrees( 
    1435        self, 
    1436    ) -> list[GeneralName] | None: 
    1437        return self._permitted_subtrees 
    1438 
    1439    @property 
    1440    def excluded_subtrees( 
    1441        self, 
    1442    ) -> list[GeneralName] | None: 
    1443        return self._excluded_subtrees 
    1444 
    1445    def public_bytes(self) -> bytes: 
    1446        return rust_x509.encode_extension_value(self) 
    1447 
    1448 
    1449class Extension(typing.Generic[ExtensionTypeVar]): 
    1450    def __init__( 
    1451        self, oid: ObjectIdentifier, critical: bool, value: ExtensionTypeVar 
    1452    ) -> None: 
    1453        if not isinstance(oid, ObjectIdentifier): 
    1454            raise TypeError( 
    1455                "oid argument must be an ObjectIdentifier instance." 
    1456            ) 
    1457 
    1458        if not isinstance(critical, bool): 
    1459            raise TypeError("critical must be a boolean value") 
    1460 
    1461        self._oid = oid 
    1462        self._critical = critical 
    1463        self._value = value 
    1464 
    1465    @property 
    1466    def oid(self) -> ObjectIdentifier: 
    1467        return self._oid 
    1468 
    1469    @property 
    1470    def critical(self) -> bool: 
    1471        return self._critical 
    1472 
    1473    @property 
    1474    def value(self) -> ExtensionTypeVar: 
    1475        return self._value 
    1476 
    1477    def __repr__(self) -> str: 
    1478        return ( 
    1479            f"<Extension(oid={self.oid}, critical={self.critical}, " 
    1480            f"value={self.value})>" 
    1481        ) 
    1482 
    1483    def __eq__(self, other: object) -> bool: 
    1484        if not isinstance(other, Extension): 
    1485            return NotImplemented 
    1486 
    1487        return ( 
    1488            self.oid == other.oid 
    1489            and self.critical == other.critical 
    1490            and self.value == other.value 
    1491        ) 
    1492 
    1493    def __hash__(self) -> int: 
    1494        return hash((self.oid, self.critical, self.value)) 
    1495 
    1496 
    1497class GeneralNames: 
    1498    def __init__(self, general_names: Iterable[GeneralName]) -> None: 
    1499        general_names = list(general_names) 
    1500        if not all(isinstance(x, GeneralName) for x in general_names): 
    1501            raise TypeError( 
    1502                "Every item in the general_names list must be an " 
    1503                "object conforming to the GeneralName interface" 
    1504            ) 
    1505 
    1506        self._general_names = general_names 
    1507 
    1508    __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") 
    1509 
    1510    @typing.overload 
    1511    def get_values_for_type( 
    1512        self, 
    1513        type: type[DNSName] 
    1514        | type[UniformResourceIdentifier] 
    1515        | type[RFC822Name], 
    1516    ) -> list[str]: ... 
    1517 
    1518    @typing.overload 
    1519    def get_values_for_type( 
    1520        self, 
    1521        type: type[DirectoryName], 
    1522    ) -> list[Name]: ... 
    1523 
    1524    @typing.overload 
    1525    def get_values_for_type( 
    1526        self, 
    1527        type: type[RegisteredID], 
    1528    ) -> list[ObjectIdentifier]: ... 
    1529 
    1530    @typing.overload 
    1531    def get_values_for_type( 
    1532        self, type: type[IPAddress] 
    1533    ) -> list[_IPAddressTypes]: ... 
    1534 
    1535    @typing.overload 
    1536    def get_values_for_type( 
    1537        self, type: type[OtherName] 
    1538    ) -> list[OtherName]: ... 
    1539 
    1540    def get_values_for_type( 
    1541        self, 
    1542        type: type[DNSName] 
    1543        | type[DirectoryName] 
    1544        | type[IPAddress] 
    1545        | type[OtherName] 
    1546        | type[RFC822Name] 
    1547        | type[RegisteredID] 
    1548        | type[UniformResourceIdentifier], 
    1549    ) -> ( 
    1550        list[_IPAddressTypes] 
    1551        | list[str] 
    1552        | list[OtherName] 
    1553        | list[Name] 
    1554        | list[ObjectIdentifier] 
    1555    ): 
    1556        # Return the value of each GeneralName, except for OtherName instances 
    1557        # which we return directly because it has two important properties not 
    1558        # just one value. 
    1559        objs = (i for i in self if isinstance(i, type)) 
    1560        if type != OtherName: 
    1561            return [i.value for i in objs] 
    1562        return list(objs) 
    1563 
    1564    def __repr__(self) -> str: 
    1565        return f"<GeneralNames({self._general_names})>" 
    1566 
    1567    def __eq__(self, other: object) -> bool: 
    1568        if not isinstance(other, GeneralNames): 
    1569            return NotImplemented 
    1570 
    1571        return self._general_names == other._general_names 
    1572 
    1573    def __hash__(self) -> int: 
    1574        return hash(tuple(self._general_names)) 
    1575 
    1576 
    1577class SubjectAlternativeName(ExtensionType): 
    1578    oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME 
    1579 
    1580    def __init__(self, general_names: Iterable[GeneralName]) -> None: 
    1581        self._general_names = GeneralNames(general_names) 
    1582 
    1583    __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") 
    1584 
    1585    @typing.overload 
    1586    def get_values_for_type( 
    1587        self, 
    1588        type: type[DNSName] 
    1589        | type[UniformResourceIdentifier] 
    1590        | type[RFC822Name], 
    1591    ) -> list[str]: ... 
    1592 
    1593    @typing.overload 
    1594    def get_values_for_type( 
    1595        self, 
    1596        type: type[DirectoryName], 
    1597    ) -> list[Name]: ... 
    1598 
    1599    @typing.overload 
    1600    def get_values_for_type( 
    1601        self, 
    1602        type: type[RegisteredID], 
    1603    ) -> list[ObjectIdentifier]: ... 
    1604 
    1605    @typing.overload 
    1606    def get_values_for_type( 
    1607        self, type: type[IPAddress] 
    1608    ) -> list[_IPAddressTypes]: ... 
    1609 
    1610    @typing.overload 
    1611    def get_values_for_type( 
    1612        self, type: type[OtherName] 
    1613    ) -> list[OtherName]: ... 
    1614 
    1615    def get_values_for_type( 
    1616        self, 
    1617        type: type[DNSName] 
    1618        | type[DirectoryName] 
    1619        | type[IPAddress] 
    1620        | type[OtherName] 
    1621        | type[RFC822Name] 
    1622        | type[RegisteredID] 
    1623        | type[UniformResourceIdentifier], 
    1624    ) -> ( 
    1625        list[_IPAddressTypes] 
    1626        | list[str] 
    1627        | list[OtherName] 
    1628        | list[Name] 
    1629        | list[ObjectIdentifier] 
    1630    ): 
    1631        return self._general_names.get_values_for_type(type) 
    1632 
    1633    def __repr__(self) -> str: 
    1634        return f"<SubjectAlternativeName({self._general_names})>" 
    1635 
    1636    def __eq__(self, other: object) -> bool: 
    1637        if not isinstance(other, SubjectAlternativeName): 
    1638            return NotImplemented 
    1639 
    1640        return self._general_names == other._general_names 
    1641 
    1642    def __hash__(self) -> int: 
    1643        return hash(self._general_names) 
    1644 
    1645    def public_bytes(self) -> bytes: 
    1646        return rust_x509.encode_extension_value(self) 
    1647 
    1648 
    1649class IssuerAlternativeName(ExtensionType): 
    1650    oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME 
    1651 
    1652    def __init__(self, general_names: Iterable[GeneralName]) -> None: 
    1653        self._general_names = GeneralNames(general_names) 
    1654 
    1655    __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") 
    1656 
    1657    @typing.overload 
    1658    def get_values_for_type( 
    1659        self, 
    1660        type: type[DNSName] 
    1661        | type[UniformResourceIdentifier] 
    1662        | type[RFC822Name], 
    1663    ) -> list[str]: ... 
    1664 
    1665    @typing.overload 
    1666    def get_values_for_type( 
    1667        self, 
    1668        type: type[DirectoryName], 
    1669    ) -> list[Name]: ... 
    1670 
    1671    @typing.overload 
    1672    def get_values_for_type( 
    1673        self, 
    1674        type: type[RegisteredID], 
    1675    ) -> list[ObjectIdentifier]: ... 
    1676 
    1677    @typing.overload 
    1678    def get_values_for_type( 
    1679        self, type: type[IPAddress] 
    1680    ) -> list[_IPAddressTypes]: ... 
    1681 
    1682    @typing.overload 
    1683    def get_values_for_type( 
    1684        self, type: type[OtherName] 
    1685    ) -> list[OtherName]: ... 
    1686 
    1687    def get_values_for_type( 
    1688        self, 
    1689        type: type[DNSName] 
    1690        | type[DirectoryName] 
    1691        | type[IPAddress] 
    1692        | type[OtherName] 
    1693        | type[RFC822Name] 
    1694        | type[RegisteredID] 
    1695        | type[UniformResourceIdentifier], 
    1696    ) -> ( 
    1697        list[_IPAddressTypes] 
    1698        | list[str] 
    1699        | list[OtherName] 
    1700        | list[Name] 
    1701        | list[ObjectIdentifier] 
    1702    ): 
    1703        return self._general_names.get_values_for_type(type) 
    1704 
    1705    def __repr__(self) -> str: 
    1706        return f"<IssuerAlternativeName({self._general_names})>" 
    1707 
    1708    def __eq__(self, other: object) -> bool: 
    1709        if not isinstance(other, IssuerAlternativeName): 
    1710            return NotImplemented 
    1711 
    1712        return self._general_names == other._general_names 
    1713 
    1714    def __hash__(self) -> int: 
    1715        return hash(self._general_names) 
    1716 
    1717    def public_bytes(self) -> bytes: 
    1718        return rust_x509.encode_extension_value(self) 
    1719 
    1720 
    1721class CertificateIssuer(ExtensionType): 
    1722    oid = CRLEntryExtensionOID.CERTIFICATE_ISSUER 
    1723 
    1724    def __init__(self, general_names: Iterable[GeneralName]) -> None: 
    1725        self._general_names = GeneralNames(general_names) 
    1726 
    1727    __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") 
    1728 
    1729    @typing.overload 
    1730    def get_values_for_type( 
    1731        self, 
    1732        type: type[DNSName] 
    1733        | type[UniformResourceIdentifier] 
    1734        | type[RFC822Name], 
    1735    ) -> list[str]: ... 
    1736 
    1737    @typing.overload 
    1738    def get_values_for_type( 
    1739        self, 
    1740        type: type[DirectoryName], 
    1741    ) -> list[Name]: ... 
    1742 
    1743    @typing.overload 
    1744    def get_values_for_type( 
    1745        self, 
    1746        type: type[RegisteredID], 
    1747    ) -> list[ObjectIdentifier]: ... 
    1748 
    1749    @typing.overload 
    1750    def get_values_for_type( 
    1751        self, type: type[IPAddress] 
    1752    ) -> list[_IPAddressTypes]: ... 
    1753 
    1754    @typing.overload 
    1755    def get_values_for_type( 
    1756        self, type: type[OtherName] 
    1757    ) -> list[OtherName]: ... 
    1758 
    1759    def get_values_for_type( 
    1760        self, 
    1761        type: type[DNSName] 
    1762        | type[DirectoryName] 
    1763        | type[IPAddress] 
    1764        | type[OtherName] 
    1765        | type[RFC822Name] 
    1766        | type[RegisteredID] 
    1767        | type[UniformResourceIdentifier], 
    1768    ) -> ( 
    1769        list[_IPAddressTypes] 
    1770        | list[str] 
    1771        | list[OtherName] 
    1772        | list[Name] 
    1773        | list[ObjectIdentifier] 
    1774    ): 
    1775        return self._general_names.get_values_for_type(type) 
    1776 
    1777    def __repr__(self) -> str: 
    1778        return f"<CertificateIssuer({self._general_names})>" 
    1779 
    1780    def __eq__(self, other: object) -> bool: 
    1781        if not isinstance(other, CertificateIssuer): 
    1782            return NotImplemented 
    1783 
    1784        return self._general_names == other._general_names 
    1785 
    1786    def __hash__(self) -> int: 
    1787        return hash(self._general_names) 
    1788 
    1789    def public_bytes(self) -> bytes: 
    1790        return rust_x509.encode_extension_value(self) 
    1791 
    1792 
    1793class CRLReason(ExtensionType): 
    1794    oid = CRLEntryExtensionOID.CRL_REASON 
    1795 
    1796    def __init__(self, reason: ReasonFlags) -> None: 
    1797        if not isinstance(reason, ReasonFlags): 
    1798            raise TypeError("reason must be an element from ReasonFlags") 
    1799 
    1800        self._reason = reason 
    1801 
    1802    def __repr__(self) -> str: 
    1803        return f"<CRLReason(reason={self._reason})>" 
    1804 
    1805    def __eq__(self, other: object) -> bool: 
    1806        if not isinstance(other, CRLReason): 
    1807            return NotImplemented 
    1808 
    1809        return self.reason == other.reason 
    1810 
    1811    def __hash__(self) -> int: 
    1812        return hash(self.reason) 
    1813 
    1814    @property 
    1815    def reason(self) -> ReasonFlags: 
    1816        return self._reason 
    1817 
    1818    def public_bytes(self) -> bytes: 
    1819        return rust_x509.encode_extension_value(self) 
    1820 
    1821 
    1822class InvalidityDate(ExtensionType): 
    1823    oid = CRLEntryExtensionOID.INVALIDITY_DATE 
    1824 
    1825    def __init__(self, invalidity_date: datetime.datetime) -> None: 
    1826        if not isinstance(invalidity_date, datetime.datetime): 
    1827            raise TypeError("invalidity_date must be a datetime.datetime") 
    1828 
    1829        self._invalidity_date = invalidity_date 
    1830 
    1831    def __repr__(self) -> str: 
    1832        return f"<InvalidityDate(invalidity_date={self._invalidity_date})>" 
    1833 
    1834    def __eq__(self, other: object) -> bool: 
    1835        if not isinstance(other, InvalidityDate): 
    1836            return NotImplemented 
    1837 
    1838        return self.invalidity_date == other.invalidity_date 
    1839 
    1840    def __hash__(self) -> int: 
    1841        return hash(self.invalidity_date) 
    1842 
    1843    @property 
    1844    def invalidity_date(self) -> datetime.datetime: 
    1845        return self._invalidity_date 
    1846 
    1847    @property 
    1848    def invalidity_date_utc(self) -> datetime.datetime: 
    1849        if self._invalidity_date.tzinfo is None: 
    1850            return self._invalidity_date.replace(tzinfo=datetime.timezone.utc) 
    1851        else: 
    1852            return self._invalidity_date.astimezone(tz=datetime.timezone.utc) 
    1853 
    1854    def public_bytes(self) -> bytes: 
    1855        return rust_x509.encode_extension_value(self) 
    1856 
    1857 
    1858class PrecertificateSignedCertificateTimestamps(ExtensionType): 
    1859    oid = ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS 
    1860 
    1861    def __init__( 
    1862        self, 
    1863        signed_certificate_timestamps: Iterable[SignedCertificateTimestamp], 
    1864    ) -> None: 
    1865        signed_certificate_timestamps = list(signed_certificate_timestamps) 
    1866        if not all( 
    1867            isinstance(sct, SignedCertificateTimestamp) 
    1868            for sct in signed_certificate_timestamps 
    1869        ): 
    1870            raise TypeError( 
    1871                "Every item in the signed_certificate_timestamps list must be " 
    1872                "a SignedCertificateTimestamp" 
    1873            ) 
    1874        self._signed_certificate_timestamps = signed_certificate_timestamps 
    1875 
    1876    __len__, __iter__, __getitem__ = _make_sequence_methods( 
    1877        "_signed_certificate_timestamps" 
    1878    ) 
    1879 
    1880    def __repr__(self) -> str: 
    1881        return f"<PrecertificateSignedCertificateTimestamps({list(self)})>" 
    1882 
    1883    def __hash__(self) -> int: 
    1884        return hash(tuple(self._signed_certificate_timestamps)) 
    1885 
    1886    def __eq__(self, other: object) -> bool: 
    1887        if not isinstance(other, PrecertificateSignedCertificateTimestamps): 
    1888            return NotImplemented 
    1889 
    1890        return ( 
    1891            self._signed_certificate_timestamps 
    1892            == other._signed_certificate_timestamps 
    1893        ) 
    1894 
    1895    def public_bytes(self) -> bytes: 
    1896        return rust_x509.encode_extension_value(self) 
    1897 
    1898 
    1899class SignedCertificateTimestamps(ExtensionType): 
    1900    oid = ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS 
    1901 
    1902    def __init__( 
    1903        self, 
    1904        signed_certificate_timestamps: Iterable[SignedCertificateTimestamp], 
    1905    ) -> None: 
    1906        signed_certificate_timestamps = list(signed_certificate_timestamps) 
    1907        if not all( 
    1908            isinstance(sct, SignedCertificateTimestamp) 
    1909            for sct in signed_certificate_timestamps 
    1910        ): 
    1911            raise TypeError( 
    1912                "Every item in the signed_certificate_timestamps list must be " 
    1913                "a SignedCertificateTimestamp" 
    1914            ) 
    1915        self._signed_certificate_timestamps = signed_certificate_timestamps 
    1916 
    1917    __len__, __iter__, __getitem__ = _make_sequence_methods( 
    1918        "_signed_certificate_timestamps" 
    1919    ) 
    1920 
    1921    def __repr__(self) -> str: 
    1922        return f"<SignedCertificateTimestamps({list(self)})>" 
    1923 
    1924    def __hash__(self) -> int: 
    1925        return hash(tuple(self._signed_certificate_timestamps)) 
    1926 
    1927    def __eq__(self, other: object) -> bool: 
    1928        if not isinstance(other, SignedCertificateTimestamps): 
    1929            return NotImplemented 
    1930 
    1931        return ( 
    1932            self._signed_certificate_timestamps 
    1933            == other._signed_certificate_timestamps 
    1934        ) 
    1935 
    1936    def public_bytes(self) -> bytes: 
    1937        return rust_x509.encode_extension_value(self) 
    1938 
    1939 
    1940class OCSPNonce(ExtensionType): 
    1941    oid = OCSPExtensionOID.NONCE 
    1942 
    1943    def __init__(self, nonce: bytes) -> None: 
    1944        if not isinstance(nonce, bytes): 
    1945            raise TypeError("nonce must be bytes") 
    1946 
    1947        self._nonce = nonce 
    1948 
    1949    def __eq__(self, other: object) -> bool: 
    1950        if not isinstance(other, OCSPNonce): 
    1951            return NotImplemented 
    1952 
    1953        return self.nonce == other.nonce 
    1954 
    1955    def __hash__(self) -> int: 
    1956        return hash(self.nonce) 
    1957 
    1958    def __repr__(self) -> str: 
    1959        return f"<OCSPNonce(nonce={self.nonce!r})>" 
    1960 
    1961    @property 
    1962    def nonce(self) -> bytes: 
    1963        return self._nonce 
    1964 
    1965    def public_bytes(self) -> bytes: 
    1966        return rust_x509.encode_extension_value(self) 
    1967 
    1968 
    1969class OCSPAcceptableResponses(ExtensionType): 
    1970    oid = OCSPExtensionOID.ACCEPTABLE_RESPONSES 
    1971 
    1972    def __init__(self, responses: Iterable[ObjectIdentifier]) -> None: 
    1973        responses = list(responses) 
    1974        if any(not isinstance(r, ObjectIdentifier) for r in responses): 
    1975            raise TypeError("All responses must be ObjectIdentifiers") 
    1976 
    1977        self._responses = responses 
    1978 
    1979    def __eq__(self, other: object) -> bool: 
    1980        if not isinstance(other, OCSPAcceptableResponses): 
    1981            return NotImplemented 
    1982 
    1983        return self._responses == other._responses 
    1984 
    1985    def __hash__(self) -> int: 
    1986        return hash(tuple(self._responses)) 
    1987 
    1988    def __repr__(self) -> str: 
    1989        return f"<OCSPAcceptableResponses(responses={self._responses})>" 
    1990 
    1991    def __iter__(self) -> Iterator[ObjectIdentifier]: 
    1992        return iter(self._responses) 
    1993 
    1994    def public_bytes(self) -> bytes: 
    1995        return rust_x509.encode_extension_value(self) 
    1996 
    1997 
    1998class IssuingDistributionPoint(ExtensionType): 
    1999    oid = ExtensionOID.ISSUING_DISTRIBUTION_POINT 
    2000 
    2001    def __init__( 
    2002        self, 
    2003        full_name: Iterable[GeneralName] | None, 
    2004        relative_name: RelativeDistinguishedName | None, 
    2005        only_contains_user_certs: bool, 
    2006        only_contains_ca_certs: bool, 
    2007        only_some_reasons: frozenset[ReasonFlags] | None, 
    2008        indirect_crl: bool, 
    2009        only_contains_attribute_certs: bool, 
    2010    ) -> None: 
    2011        if full_name is not None: 
    2012            full_name = list(full_name) 
    2013 
    2014        if only_some_reasons and ( 
    2015            not isinstance(only_some_reasons, frozenset) 
    2016            or not all(isinstance(x, ReasonFlags) for x in only_some_reasons) 
    2017        ): 
    2018            raise TypeError( 
    2019                "only_some_reasons must be None or frozenset of ReasonFlags" 
    2020            ) 
    2021 
    2022        if only_some_reasons and ( 
    2023            ReasonFlags.unspecified in only_some_reasons 
    2024            or ReasonFlags.remove_from_crl in only_some_reasons 
    2025        ): 
    2026            raise ValueError( 
    2027                "unspecified and remove_from_crl are not valid reasons in an " 
    2028                "IssuingDistributionPoint" 
    2029            ) 
    2030 
    2031        if not ( 
    2032            isinstance(only_contains_user_certs, bool) 
    2033            and isinstance(only_contains_ca_certs, bool) 
    2034            and isinstance(indirect_crl, bool) 
    2035            and isinstance(only_contains_attribute_certs, bool) 
    2036        ): 
    2037            raise TypeError( 
    2038                "only_contains_user_certs, only_contains_ca_certs, " 
    2039                "indirect_crl and only_contains_attribute_certs " 
    2040                "must all be boolean." 
    2041            ) 
    2042 
    2043        # Per RFC5280 Section 5.2.5, the Issuing Distribution Point extension 
    2044        # in a CRL can have only one of onlyContainsUserCerts, 
    2045        # onlyContainsCACerts, onlyContainsAttributeCerts set to TRUE. 
    2046        crl_constraints = [ 
    2047            only_contains_user_certs, 
    2048            only_contains_ca_certs, 
    2049            only_contains_attribute_certs, 
    2050        ] 
    2051 
    2052        if len([x for x in crl_constraints if x]) > 1: 
    2053            raise ValueError( 
    2054                "Only one of the following can be set to True: " 
    2055                "only_contains_user_certs, only_contains_ca_certs, " 
    2056                "only_contains_attribute_certs" 
    2057            ) 
    2058 
    2059        if not any( 
    2060            [ 
    2061                only_contains_user_certs, 
    2062                only_contains_ca_certs, 
    2063                indirect_crl, 
    2064                only_contains_attribute_certs, 
    2065                full_name, 
    2066                relative_name, 
    2067                only_some_reasons, 
    2068            ] 
    2069        ): 
    2070            raise ValueError( 
    2071                "Cannot create empty extension: " 
    2072                "if only_contains_user_certs, only_contains_ca_certs, " 
    2073                "indirect_crl, and only_contains_attribute_certs are all False" 
    2074                ", then either full_name, relative_name, or only_some_reasons " 
    2075                "must have a value." 
    2076            ) 
    2077 
    2078        self._only_contains_user_certs = only_contains_user_certs 
    2079        self._only_contains_ca_certs = only_contains_ca_certs 
    2080        self._indirect_crl = indirect_crl 
    2081        self._only_contains_attribute_certs = only_contains_attribute_certs 
    2082        self._only_some_reasons = only_some_reasons 
    2083        self._full_name = full_name 
    2084        self._relative_name = relative_name 
    2085 
    2086    def __repr__(self) -> str: 
    2087        return ( 
    2088            f"<IssuingDistributionPoint(full_name={self.full_name}, " 
    2089            f"relative_name={self.relative_name}, " 
    2090            f"only_contains_user_certs={self.only_contains_user_certs}, " 
    2091            f"only_contains_ca_certs={self.only_contains_ca_certs}, " 
    2092            f"only_some_reasons={self.only_some_reasons}, " 
    2093            f"indirect_crl={self.indirect_crl}, " 
    2094            "only_contains_attribute_certs=" 
    2095            f"{self.only_contains_attribute_certs})>" 
    2096        ) 
    2097 
    2098    def __eq__(self, other: object) -> bool: 
    2099        if not isinstance(other, IssuingDistributionPoint): 
    2100            return NotImplemented 
    2101 
    2102        return ( 
    2103            self.full_name == other.full_name 
    2104            and self.relative_name == other.relative_name 
    2105            and self.only_contains_user_certs == other.only_contains_user_certs 
    2106            and self.only_contains_ca_certs == other.only_contains_ca_certs 
    2107            and self.only_some_reasons == other.only_some_reasons 
    2108            and self.indirect_crl == other.indirect_crl 
    2109            and self.only_contains_attribute_certs 
    2110            == other.only_contains_attribute_certs 
    2111        ) 
    2112 
    2113    def __hash__(self) -> int: 
    2114        return hash( 
    2115            ( 
    2116                self.full_name, 
    2117                self.relative_name, 
    2118                self.only_contains_user_certs, 
    2119                self.only_contains_ca_certs, 
    2120                self.only_some_reasons, 
    2121                self.indirect_crl, 
    2122                self.only_contains_attribute_certs, 
    2123            ) 
    2124        ) 
    2125 
    2126    @property 
    2127    def full_name(self) -> list[GeneralName] | None: 
    2128        return self._full_name 
    2129 
    2130    @property 
    2131    def relative_name(self) -> RelativeDistinguishedName | None: 
    2132        return self._relative_name 
    2133 
    2134    @property 
    2135    def only_contains_user_certs(self) -> bool: 
    2136        return self._only_contains_user_certs 
    2137 
    2138    @property 
    2139    def only_contains_ca_certs(self) -> bool: 
    2140        return self._only_contains_ca_certs 
    2141 
    2142    @property 
    2143    def only_some_reasons( 
    2144        self, 
    2145    ) -> frozenset[ReasonFlags] | None: 
    2146        return self._only_some_reasons 
    2147 
    2148    @property 
    2149    def indirect_crl(self) -> bool: 
    2150        return self._indirect_crl 
    2151 
    2152    @property 
    2153    def only_contains_attribute_certs(self) -> bool: 
    2154        return self._only_contains_attribute_certs 
    2155 
    2156    def public_bytes(self) -> bytes: 
    2157        return rust_x509.encode_extension_value(self) 
    2158 
    2159 
    2160class MSCertificateTemplate(ExtensionType): 
    2161    oid = ExtensionOID.MS_CERTIFICATE_TEMPLATE 
    2162 
    2163    def __init__( 
    2164        self, 
    2165        template_id: ObjectIdentifier, 
    2166        major_version: int | None, 
    2167        minor_version: int | None, 
    2168    ) -> None: 
    2169        if not isinstance(template_id, ObjectIdentifier): 
    2170            raise TypeError("oid must be an ObjectIdentifier") 
    2171        self._template_id = template_id 
    2172        if ( 
    2173            major_version is not None and not isinstance(major_version, int) 
    2174        ) or ( 
    2175            minor_version is not None and not isinstance(minor_version, int) 
    2176        ): 
    2177            raise TypeError( 
    2178                "major_version and minor_version must be integers or None" 
    2179            ) 
    2180        self._major_version = major_version 
    2181        self._minor_version = minor_version 
    2182 
    2183    @property 
    2184    def template_id(self) -> ObjectIdentifier: 
    2185        return self._template_id 
    2186 
    2187    @property 
    2188    def major_version(self) -> int | None: 
    2189        return self._major_version 
    2190 
    2191    @property 
    2192    def minor_version(self) -> int | None: 
    2193        return self._minor_version 
    2194 
    2195    def __repr__(self) -> str: 
    2196        return ( 
    2197            f"<MSCertificateTemplate(template_id={self.template_id}, " 
    2198            f"major_version={self.major_version}, " 
    2199            f"minor_version={self.minor_version})>" 
    2200        ) 
    2201 
    2202    def __eq__(self, other: object) -> bool: 
    2203        if not isinstance(other, MSCertificateTemplate): 
    2204            return NotImplemented 
    2205 
    2206        return ( 
    2207            self.template_id == other.template_id 
    2208            and self.major_version == other.major_version 
    2209            and self.minor_version == other.minor_version 
    2210        ) 
    2211 
    2212    def __hash__(self) -> int: 
    2213        return hash((self.template_id, self.major_version, self.minor_version)) 
    2214 
    2215    def public_bytes(self) -> bytes: 
    2216        return rust_x509.encode_extension_value(self) 
    2217 
    2218 
    2219class NamingAuthority: 
    2220    def __init__( 
    2221        self, 
    2222        id: ObjectIdentifier | None, 
    2223        url: str | None, 
    2224        text: str | None, 
    2225    ) -> None: 
    2226        if id is not None and not isinstance(id, ObjectIdentifier): 
    2227            raise TypeError("id must be an ObjectIdentifier") 
    2228 
    2229        if url is not None and not isinstance(url, str): 
    2230            raise TypeError("url must be a str") 
    2231 
    2232        if text is not None and not isinstance(text, str): 
    2233            raise TypeError("text must be a str") 
    2234 
    2235        self._id = id 
    2236        self._url = url 
    2237        self._text = text 
    2238 
    2239    @property 
    2240    def id(self) -> ObjectIdentifier | None: 
    2241        return self._id 
    2242 
    2243    @property 
    2244    def url(self) -> str | None: 
    2245        return self._url 
    2246 
    2247    @property 
    2248    def text(self) -> str | None: 
    2249        return self._text 
    2250 
    2251    def __repr__(self) -> str: 
    2252        return ( 
    2253            f"<NamingAuthority(" 
    2254            f"id={self.id}, url={self.url}, text={self.text})>" 
    2255        ) 
    2256 
    2257    def __eq__(self, other: object) -> bool: 
    2258        if not isinstance(other, NamingAuthority): 
    2259            return NotImplemented 
    2260 
    2261        return ( 
    2262            self.id == other.id 
    2263            and self.url == other.url 
    2264            and self.text == other.text 
    2265        ) 
    2266 
    2267    def __hash__(self) -> int: 
    2268        return hash( 
    2269            ( 
    2270                self.id, 
    2271                self.url, 
    2272                self.text, 
    2273            ) 
    2274        ) 
    2275 
    2276 
    2277class ProfessionInfo: 
    2278    def __init__( 
    2279        self, 
    2280        naming_authority: NamingAuthority | None, 
    2281        profession_items: Iterable[str], 
    2282        profession_oids: Iterable[ObjectIdentifier] | None, 
    2283        registration_number: str | None, 
    2284        add_profession_info: bytes | None, 
    2285    ) -> None: 
    2286        if naming_authority is not None and not isinstance( 
    2287            naming_authority, NamingAuthority 
    2288        ): 
    2289            raise TypeError("naming_authority must be a NamingAuthority") 
    2290 
    2291        profession_items = list(profession_items) 
    2292        if not all(isinstance(item, str) for item in profession_items): 
    2293            raise TypeError( 
    2294                "Every item in the profession_items list must be a str" 
    2295            ) 
    2296 
    2297        if profession_oids is not None: 
    2298            profession_oids = list(profession_oids) 
    2299            if not all( 
    2300                isinstance(oid, ObjectIdentifier) for oid in profession_oids 
    2301            ): 
    2302                raise TypeError( 
    2303                    "Every item in the profession_oids list must be an " 
    2304                    "ObjectIdentifier" 
    2305                ) 
    2306 
    2307        if registration_number is not None and not isinstance( 
    2308            registration_number, str 
    2309        ): 
    2310            raise TypeError("registration_number must be a str") 
    2311 
    2312        if add_profession_info is not None and not isinstance( 
    2313            add_profession_info, bytes 
    2314        ): 
    2315            raise TypeError("add_profession_info must be bytes") 
    2316 
    2317        self._naming_authority = naming_authority 
    2318        self._profession_items = profession_items 
    2319        self._profession_oids = profession_oids 
    2320        self._registration_number = registration_number 
    2321        self._add_profession_info = add_profession_info 
    2322 
    2323    @property 
    2324    def naming_authority(self) -> NamingAuthority | None: 
    2325        return self._naming_authority 
    2326 
    2327    @property 
    2328    def profession_items(self) -> list[str]: 
    2329        return self._profession_items 
    2330 
    2331    @property 
    2332    def profession_oids(self) -> list[ObjectIdentifier] | None: 
    2333        return self._profession_oids 
    2334 
    2335    @property 
    2336    def registration_number(self) -> str | None: 
    2337        return self._registration_number 
    2338 
    2339    @property 
    2340    def add_profession_info(self) -> bytes | None: 
    2341        return self._add_profession_info 
    2342 
    2343    def __repr__(self) -> str: 
    2344        return ( 
    2345            f"<ProfessionInfo(naming_authority={self.naming_authority}, " 
    2346            f"profession_items={self.profession_items}, " 
    2347            f"profession_oids={self.profession_oids}, " 
    2348            f"registration_number={self.registration_number}, " 
    2349            f"add_profession_info={self.add_profession_info!r})>" 
    2350        ) 
    2351 
    2352    def __eq__(self, other: object) -> bool: 
    2353        if not isinstance(other, ProfessionInfo): 
    2354            return NotImplemented 
    2355 
    2356        return ( 
    2357            self.naming_authority == other.naming_authority 
    2358            and self.profession_items == other.profession_items 
    2359            and self.profession_oids == other.profession_oids 
    2360            and self.registration_number == other.registration_number 
    2361            and self.add_profession_info == other.add_profession_info 
    2362        ) 
    2363 
    2364    def __hash__(self) -> int: 
    2365        if self.profession_oids is not None: 
    2366            profession_oids = tuple(self.profession_oids) 
    2367        else: 
    2368            profession_oids = None 
    2369        return hash( 
    2370            ( 
    2371                self.naming_authority, 
    2372                tuple(self.profession_items), 
    2373                profession_oids, 
    2374                self.registration_number, 
    2375                self.add_profession_info, 
    2376            ) 
    2377        ) 
    2378 
    2379 
    2380class Admission: 
    2381    def __init__( 
    2382        self, 
    2383        admission_authority: GeneralName | None, 
    2384        naming_authority: NamingAuthority | None, 
    2385        profession_infos: Iterable[ProfessionInfo], 
    2386    ) -> None: 
    2387        if admission_authority is not None and not isinstance( 
    2388            admission_authority, GeneralName 
    2389        ): 
    2390            raise TypeError("admission_authority must be a GeneralName") 
    2391 
    2392        if naming_authority is not None and not isinstance( 
    2393            naming_authority, NamingAuthority 
    2394        ): 
    2395            raise TypeError("naming_authority must be a NamingAuthority") 
    2396 
    2397        profession_infos = list(profession_infos) 
    2398        if not all( 
    2399            isinstance(info, ProfessionInfo) for info in profession_infos 
    2400        ): 
    2401            raise TypeError( 
    2402                "Every item in the profession_infos list must be a " 
    2403                "ProfessionInfo" 
    2404            ) 
    2405 
    2406        self._admission_authority = admission_authority 
    2407        self._naming_authority = naming_authority 
    2408        self._profession_infos = profession_infos 
    2409 
    2410    @property 
    2411    def admission_authority(self) -> GeneralName | None: 
    2412        return self._admission_authority 
    2413 
    2414    @property 
    2415    def naming_authority(self) -> NamingAuthority | None: 
    2416        return self._naming_authority 
    2417 
    2418    @property 
    2419    def profession_infos(self) -> list[ProfessionInfo]: 
    2420        return self._profession_infos 
    2421 
    2422    def __repr__(self) -> str: 
    2423        return ( 
    2424            f"<Admission(admission_authority={self.admission_authority}, " 
    2425            f"naming_authority={self.naming_authority}, " 
    2426            f"profession_infos={self.profession_infos})>" 
    2427        ) 
    2428 
    2429    def __eq__(self, other: object) -> bool: 
    2430        if not isinstance(other, Admission): 
    2431            return NotImplemented 
    2432 
    2433        return ( 
    2434            self.admission_authority == other.admission_authority 
    2435            and self.naming_authority == other.naming_authority 
    2436            and self.profession_infos == other.profession_infos 
    2437        ) 
    2438 
    2439    def __hash__(self) -> int: 
    2440        return hash( 
    2441            ( 
    2442                self.admission_authority, 
    2443                self.naming_authority, 
    2444                tuple(self.profession_infos), 
    2445            ) 
    2446        ) 
    2447 
    2448 
    2449class Admissions(ExtensionType): 
    2450    oid = ExtensionOID.ADMISSIONS 
    2451 
    2452    def __init__( 
    2453        self, 
    2454        authority: GeneralName | None, 
    2455        admissions: Iterable[Admission], 
    2456    ) -> None: 
    2457        if authority is not None and not isinstance(authority, GeneralName): 
    2458            raise TypeError("authority must be a GeneralName") 
    2459 
    2460        admissions = list(admissions) 
    2461        if not all( 
    2462            isinstance(admission, Admission) for admission in admissions 
    2463        ): 
    2464            raise TypeError( 
    2465                "Every item in the contents_of_admissions list must be an " 
    2466                "Admission" 
    2467            ) 
    2468 
    2469        self._authority = authority 
    2470        self._admissions = admissions 
    2471 
    2472    __len__, __iter__, __getitem__ = _make_sequence_methods("_admissions") 
    2473 
    2474    @property 
    2475    def authority(self) -> GeneralName | None: 
    2476        return self._authority 
    2477 
    2478    def __repr__(self) -> str: 
    2479        return ( 
    2480            f"<Admissions(authority={self._authority}, " 
    2481            f"admissions={self._admissions})>" 
    2482        ) 
    2483 
    2484    def __eq__(self, other: object) -> bool: 
    2485        if not isinstance(other, Admissions): 
    2486            return NotImplemented 
    2487 
    2488        return ( 
    2489            self.authority == other.authority 
    2490            and self._admissions == other._admissions 
    2491        ) 
    2492 
    2493    def __hash__(self) -> int: 
    2494        return hash((self.authority, tuple(self._admissions))) 
    2495 
    2496    def public_bytes(self) -> bytes: 
    2497        return rust_x509.encode_extension_value(self) 
    2498 
    2499 
    2500class UnrecognizedExtension(ExtensionType): 
    2501    def __init__(self, oid: ObjectIdentifier, value: bytes) -> None: 
    2502        if not isinstance(oid, ObjectIdentifier): 
    2503            raise TypeError("oid must be an ObjectIdentifier") 
    2504        self._oid = oid 
    2505        self._value = value 
    2506 
    2507    @property 
    2508    def oid(self) -> ObjectIdentifier:  # type: ignore[override] 
    2509        return self._oid 
    2510 
    2511    @property 
    2512    def value(self) -> bytes: 
    2513        return self._value 
    2514 
    2515    def __repr__(self) -> str: 
    2516        return f"<UnrecognizedExtension(oid={self.oid}, value={self.value!r})>" 
    2517 
    2518    def __eq__(self, other: object) -> bool: 
    2519        if not isinstance(other, UnrecognizedExtension): 
    2520            return NotImplemented 
    2521 
    2522        return self.oid == other.oid and self.value == other.value 
    2523 
    2524    def __hash__(self) -> int: 
    2525        return hash((self.oid, self.value)) 
    2526 
    2527    def public_bytes(self) -> bytes: 
    2528        return self.value