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 ipaddress 
    9import typing 
    10from email.utils import parseaddr 
    11 
    12from cryptography.x509.name import Name 
    13from cryptography.x509.oid import ObjectIdentifier 
    14 
    15_IPAddressTypes = typing.Union[ 
    16    ipaddress.IPv4Address, 
    17    ipaddress.IPv6Address, 
    18    ipaddress.IPv4Network, 
    19    ipaddress.IPv6Network, 
    20] 
    21 
    22 
    23class UnsupportedGeneralNameType(Exception): 
    24    pass 
    25 
    26 
    27class GeneralName(metaclass=abc.ABCMeta): 
    28    @property 
    29    @abc.abstractmethod 
    30    def value(self) -> typing.Any: 
    31        """ 
    32        Return the value of the object 
    33        """ 
    34 
    35 
    36class RFC822Name(GeneralName): 
    37    def __init__(self, value: str) -> None: 
    38        if isinstance(value, str): 
    39            try: 
    40                value.encode("ascii") 
    41            except UnicodeEncodeError: 
    42                raise ValueError( 
    43                    "RFC822Name values should be passed as an A-label string. " 
    44                    "This means unicode characters should be encoded via " 
    45                    "a library like idna." 
    46                ) 
    47        else: 
    48            raise TypeError("value must be string") 
    49 
    50        name, address = parseaddr(value) 
    51        if name or not address: 
    52            # parseaddr has found a name (e.g. Name <email>) or the entire 
    53            # value is an empty string. 
    54            raise ValueError("Invalid rfc822name value") 
    55 
    56        self._value = value 
    57 
    58    @property 
    59    def value(self) -> str: 
    60        return self._value 
    61 
    62    @classmethod 
    63    def _init_without_validation(cls, value: str) -> RFC822Name: 
    64        instance = cls.__new__(cls) 
    65        instance._value = value 
    66        return instance 
    67 
    68    def __repr__(self) -> str: 
    69        return f"<RFC822Name(value={self.value!r})>" 
    70 
    71    def __eq__(self, other: object) -> bool: 
    72        if not isinstance(other, RFC822Name): 
    73            return NotImplemented 
    74 
    75        return self.value == other.value 
    76 
    77    def __hash__(self) -> int: 
    78        return hash(self.value) 
    79 
    80 
    81class DNSName(GeneralName): 
    82    def __init__(self, value: str) -> None: 
    83        if isinstance(value, str): 
    84            try: 
    85                value.encode("ascii") 
    86            except UnicodeEncodeError: 
    87                raise ValueError( 
    88                    "DNSName values should be passed as an A-label string. " 
    89                    "This means unicode characters should be encoded via " 
    90                    "a library like idna." 
    91                ) 
    92        else: 
    93            raise TypeError("value must be string") 
    94 
    95        self._value = value 
    96 
    97    @property 
    98    def value(self) -> str: 
    99        return self._value 
    100 
    101    @classmethod 
    102    def _init_without_validation(cls, value: str) -> DNSName: 
    103        instance = cls.__new__(cls) 
    104        instance._value = value 
    105        return instance 
    106 
    107    def __repr__(self) -> str: 
    108        return f"<DNSName(value={self.value!r})>" 
    109 
    110    def __eq__(self, other: object) -> bool: 
    111        if not isinstance(other, DNSName): 
    112            return NotImplemented 
    113 
    114        return self.value == other.value 
    115 
    116    def __hash__(self) -> int: 
    117        return hash(self.value) 
    118 
    119 
    120class UniformResourceIdentifier(GeneralName): 
    121    def __init__(self, value: str) -> None: 
    122        if isinstance(value, str): 
    123            try: 
    124                value.encode("ascii") 
    125            except UnicodeEncodeError: 
    126                raise ValueError( 
    127                    "URI values should be passed as an A-label string. " 
    128                    "This means unicode characters should be encoded via " 
    129                    "a library like idna." 
    130                ) 
    131        else: 
    132            raise TypeError("value must be string") 
    133 
    134        self._value = value 
    135 
    136    @property 
    137    def value(self) -> str: 
    138        return self._value 
    139 
    140    @classmethod 
    141    def _init_without_validation(cls, value: str) -> UniformResourceIdentifier: 
    142        instance = cls.__new__(cls) 
    143        instance._value = value 
    144        return instance 
    145 
    146    def __repr__(self) -> str: 
    147        return f"<UniformResourceIdentifier(value={self.value!r})>" 
    148 
    149    def __eq__(self, other: object) -> bool: 
    150        if not isinstance(other, UniformResourceIdentifier): 
    151            return NotImplemented 
    152 
    153        return self.value == other.value 
    154 
    155    def __hash__(self) -> int: 
    156        return hash(self.value) 
    157 
    158 
    159class DirectoryName(GeneralName): 
    160    def __init__(self, value: Name) -> None: 
    161        if not isinstance(value, Name): 
    162            raise TypeError("value must be a Name") 
    163 
    164        self._value = value 
    165 
    166    @property 
    167    def value(self) -> Name: 
    168        return self._value 
    169 
    170    def __repr__(self) -> str: 
    171        return f"<DirectoryName(value={self.value})>" 
    172 
    173    def __eq__(self, other: object) -> bool: 
    174        if not isinstance(other, DirectoryName): 
    175            return NotImplemented 
    176 
    177        return self.value == other.value 
    178 
    179    def __hash__(self) -> int: 
    180        return hash(self.value) 
    181 
    182 
    183class RegisteredID(GeneralName): 
    184    def __init__(self, value: ObjectIdentifier) -> None: 
    185        if not isinstance(value, ObjectIdentifier): 
    186            raise TypeError("value must be an ObjectIdentifier") 
    187 
    188        self._value = value 
    189 
    190    @property 
    191    def value(self) -> ObjectIdentifier: 
    192        return self._value 
    193 
    194    def __repr__(self) -> str: 
    195        return f"<RegisteredID(value={self.value})>" 
    196 
    197    def __eq__(self, other: object) -> bool: 
    198        if not isinstance(other, RegisteredID): 
    199            return NotImplemented 
    200 
    201        return self.value == other.value 
    202 
    203    def __hash__(self) -> int: 
    204        return hash(self.value) 
    205 
    206 
    207class IPAddress(GeneralName): 
    208    def __init__(self, value: _IPAddressTypes) -> None: 
    209        if not isinstance( 
    210            value, 
    211            ( 
    212                ipaddress.IPv4Address, 
    213                ipaddress.IPv6Address, 
    214                ipaddress.IPv4Network, 
    215                ipaddress.IPv6Network, 
    216            ), 
    217        ): 
    218            raise TypeError( 
    219                "value must be an instance of ipaddress.IPv4Address, " 
    220                "ipaddress.IPv6Address, ipaddress.IPv4Network, or " 
    221                "ipaddress.IPv6Network" 
    222            ) 
    223 
    224        self._value = value 
    225 
    226    @property 
    227    def value(self) -> _IPAddressTypes: 
    228        return self._value 
    229 
    230    def _packed(self) -> bytes: 
    231        if isinstance( 
    232            self.value, (ipaddress.IPv4Address, ipaddress.IPv6Address) 
    233        ): 
    234            return self.value.packed 
    235        else: 
    236            return ( 
    237                self.value.network_address.packed + self.value.netmask.packed 
    238            ) 
    239 
    240    def __repr__(self) -> str: 
    241        return f"<IPAddress(value={self.value})>" 
    242 
    243    def __eq__(self, other: object) -> bool: 
    244        if not isinstance(other, IPAddress): 
    245            return NotImplemented 
    246 
    247        return self.value == other.value 
    248 
    249    def __hash__(self) -> int: 
    250        return hash(self.value) 
    251 
    252 
    253class OtherName(GeneralName): 
    254    def __init__(self, type_id: ObjectIdentifier, value: bytes) -> None: 
    255        if not isinstance(type_id, ObjectIdentifier): 
    256            raise TypeError("type_id must be an ObjectIdentifier") 
    257        if not isinstance(value, bytes): 
    258            raise TypeError("value must be a binary string") 
    259 
    260        self._type_id = type_id 
    261        self._value = value 
    262 
    263    @property 
    264    def type_id(self) -> ObjectIdentifier: 
    265        return self._type_id 
    266 
    267    @property 
    268    def value(self) -> bytes: 
    269        return self._value 
    270 
    271    def __repr__(self) -> str: 
    272        return f"<OtherName(type_id={self.type_id}, value={self.value!r})>" 
    273 
    274    def __eq__(self, other: object) -> bool: 
    275        if not isinstance(other, OtherName): 
    276            return NotImplemented 
    277 
    278        return self.type_id == other.type_id and self.value == other.value 
    279 
    280    def __hash__(self) -> int: 
    281        return hash((self.type_id, self.value))