You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

361 lines
10 KiB

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