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 absolute_import, division, print_function 6 7import abc 8import ipaddress 9import warnings 10from email.utils import parseaddr 11 12import six 13from six.moves import urllib_parse 14 15from cryptography import utils 16from cryptography.x509.name import Name 17from cryptography.x509.oid import ObjectIdentifier 18 19 20_GENERAL_NAMES = { 21 0: "otherName", 22 1: "rfc822Name", 23 2: "dNSName", 24 3: "x400Address", 25 4: "directoryName", 26 5: "ediPartyName", 27 6: "uniformResourceIdentifier", 28 7: "iPAddress", 29 8: "registeredID", 30} 31 32 33def _lazy_import_idna(): 34 # Import idna lazily becase it allocates a decent amount of memory, and 35 # we're only using it in deprecated paths. 36 try: 37 import idna 38 return idna 39 except ImportError: 40 raise ImportError( 41 "idna is not installed, but a deprecated feature that requires it" 42 " was used. See: https://cryptography.io/en/latest/faq/#importe" 43 "rror-idna-is-not-installed" 44 ) 45 46 47class UnsupportedGeneralNameType(Exception): 48 def __init__(self, msg, type): 49 super(UnsupportedGeneralNameType, self).__init__(msg) 50 self.type = type 51 52 53@six.add_metaclass(abc.ABCMeta) 54class GeneralName(object): 55 @abc.abstractproperty 56 def value(self): 57 """ 58 Return the value of the object 59 """ 60 61 62@utils.register_interface(GeneralName) 63class RFC822Name(object): 64 def __init__(self, value): 65 if isinstance(value, six.text_type): 66 try: 67 value.encode("ascii") 68 except UnicodeEncodeError: 69 value = self._idna_encode(value) 70 warnings.warn( 71 "RFC822Name values should be passed as an A-label string. " 72 "This means unicode characters should be encoded via " 73 "idna. Support for passing unicode strings (aka U-label) " 74 "will be removed in a future version.", 75 utils.DeprecatedIn21, 76 stacklevel=2, 77 ) 78 else: 79 raise TypeError("value must be string") 80 81 name, address = parseaddr(value) 82 if name or not address: 83 # parseaddr has found a name (e.g. Name <email>) or the entire 84 # value is an empty string. 85 raise ValueError("Invalid rfc822name value") 86 87 self._value = value 88 89 value = utils.read_only_property("_value") 90 91 @classmethod 92 def _init_without_validation(cls, value): 93 instance = cls.__new__(cls) 94 instance._value = value 95 return instance 96 97 def _idna_encode(self, value): 98 idna = _lazy_import_idna() 99 _, address = parseaddr(value) 100 parts = address.split(u"@") 101 return parts[0] + "@" + idna.encode(parts[1]).decode("ascii") 102 103 def __repr__(self): 104 return "<RFC822Name(value={0!r})>".format(self.value) 105 106 def __eq__(self, other): 107 if not isinstance(other, RFC822Name): 108 return NotImplemented 109 110 return self.value == other.value 111 112 def __ne__(self, other): 113 return not self == other 114 115 def __hash__(self): 116 return hash(self.value) 117 118 119def _idna_encode(value): 120 idna = _lazy_import_idna() 121 # Retain prefixes '*.' for common/alt names and '.' for name constraints 122 for prefix in ['*.', '.']: 123 if value.startswith(prefix): 124 value = value[len(prefix):] 125 return prefix + idna.encode(value).decode("ascii") 126 return idna.encode(value).decode("ascii") 127 128 129@utils.register_interface(GeneralName) 130class DNSName(object): 131 def __init__(self, value): 132 if isinstance(value, six.text_type): 133 try: 134 value.encode("ascii") 135 except UnicodeEncodeError: 136 value = _idna_encode(value) 137 warnings.warn( 138 "DNSName values should be passed as an A-label string. " 139 "This means unicode characters should be encoded via " 140 "idna. Support for passing unicode strings (aka U-label) " 141 "will be removed in a future version.", 142 utils.DeprecatedIn21, 143 stacklevel=2, 144 ) 145 else: 146 raise TypeError("value must be string") 147 148 self._value = value 149 150 value = utils.read_only_property("_value") 151 152 @classmethod 153 def _init_without_validation(cls, value): 154 instance = cls.__new__(cls) 155 instance._value = value 156 return instance 157 158 def __repr__(self): 159 return "<DNSName(value={0!r})>".format(self.value) 160 161 def __eq__(self, other): 162 if not isinstance(other, DNSName): 163 return NotImplemented 164 165 return self.value == other.value 166 167 def __ne__(self, other): 168 return not self == other 169 170 def __hash__(self): 171 return hash(self.value) 172 173 174@utils.register_interface(GeneralName) 175class UniformResourceIdentifier(object): 176 def __init__(self, value): 177 if isinstance(value, six.text_type): 178 try: 179 value.encode("ascii") 180 except UnicodeEncodeError: 181 value = self._idna_encode(value) 182 warnings.warn( 183 "URI values should be passed as an A-label string. " 184 "This means unicode characters should be encoded via " 185 "idna. Support for passing unicode strings (aka U-label) " 186 " will be removed in a future version.", 187 utils.DeprecatedIn21, 188 stacklevel=2, 189 ) 190 else: 191 raise TypeError("value must be string") 192 193 self._value = value 194 195 value = utils.read_only_property("_value") 196 197 @classmethod 198 def _init_without_validation(cls, value): 199 instance = cls.__new__(cls) 200 instance._value = value 201 return instance 202 203 def _idna_encode(self, value): 204 idna = _lazy_import_idna() 205 parsed = urllib_parse.urlparse(value) 206 if parsed.port: 207 netloc = ( 208 idna.encode(parsed.hostname) + 209 ":{0}".format(parsed.port).encode("ascii") 210 ).decode("ascii") 211 else: 212 netloc = idna.encode(parsed.hostname).decode("ascii") 213 214 # Note that building a URL in this fashion means it should be 215 # semantically indistinguishable from the original but is not 216 # guaranteed to be exactly the same. 217 return urllib_parse.urlunparse(( 218 parsed.scheme, 219 netloc, 220 parsed.path, 221 parsed.params, 222 parsed.query, 223 parsed.fragment 224 )) 225 226 def __repr__(self): 227 return "<UniformResourceIdentifier(value={0!r})>".format(self.value) 228 229 def __eq__(self, other): 230 if not isinstance(other, UniformResourceIdentifier): 231 return NotImplemented 232 233 return self.value == other.value 234 235 def __ne__(self, other): 236 return not self == other 237 238 def __hash__(self): 239 return hash(self.value) 240 241 242@utils.register_interface(GeneralName) 243class DirectoryName(object): 244 def __init__(self, value): 245 if not isinstance(value, Name): 246 raise TypeError("value must be a Name") 247 248 self._value = value 249 250 value = utils.read_only_property("_value") 251 252 def __repr__(self): 253 return "<DirectoryName(value={0})>".format(self.value) 254 255 def __eq__(self, other): 256 if not isinstance(other, DirectoryName): 257 return NotImplemented 258 259 return self.value == other.value 260 261 def __ne__(self, other): 262 return not self == other 263 264 def __hash__(self): 265 return hash(self.value) 266 267 268@utils.register_interface(GeneralName) 269class RegisteredID(object): 270 def __init__(self, value): 271 if not isinstance(value, ObjectIdentifier): 272 raise TypeError("value must be an ObjectIdentifier") 273 274 self._value = value 275 276 value = utils.read_only_property("_value") 277 278 def __repr__(self): 279 return "<RegisteredID(value={0})>".format(self.value) 280 281 def __eq__(self, other): 282 if not isinstance(other, RegisteredID): 283 return NotImplemented 284 285 return self.value == other.value 286 287 def __ne__(self, other): 288 return not self == other 289 290 def __hash__(self): 291 return hash(self.value) 292 293 294@utils.register_interface(GeneralName) 295class IPAddress(object): 296 def __init__(self, value): 297 if not isinstance( 298 value, 299 ( 300 ipaddress.IPv4Address, 301 ipaddress.IPv6Address, 302 ipaddress.IPv4Network, 303 ipaddress.IPv6Network 304 ) 305 ): 306 raise TypeError( 307 "value must be an instance of ipaddress.IPv4Address, " 308 "ipaddress.IPv6Address, ipaddress.IPv4Network, or " 309 "ipaddress.IPv6Network" 310 ) 311 312 self._value = value 313 314 value = utils.read_only_property("_value") 315 316 def __repr__(self): 317 return "<IPAddress(value={0})>".format(self.value) 318 319 def __eq__(self, other): 320 if not isinstance(other, IPAddress): 321 return NotImplemented 322 323 return self.value == other.value 324 325 def __ne__(self, other): 326 return not self == other 327 328 def __hash__(self): 329 return hash(self.value) 330 331 332@utils.register_interface(GeneralName) 333class OtherName(object): 334 def __init__(self, type_id, value): 335 if not isinstance(type_id, ObjectIdentifier): 336 raise TypeError("type_id must be an ObjectIdentifier") 337 if not isinstance(value, bytes): 338 raise TypeError("value must be a binary string") 339 340 self._type_id = type_id 341 self._value = value 342 343 type_id = utils.read_only_property("_type_id") 344 value = utils.read_only_property("_value") 345 346 def __repr__(self): 347 return "<OtherName(type_id={0}, value={1!r})>".format( 348 self.type_id, self.value) 349 350 def __eq__(self, other): 351 if not isinstance(other, OtherName): 352 return NotImplemented 353 354 return self.type_id == other.type_id and self.value == other.value 355 356 def __ne__(self, other): 357 return not self == other 358 359 def __hash__(self): 360 return hash((self.type_id, self.value)) 361