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.

1249 lines
34 KiB

4 years ago
  1. # coding: utf-8
  2. """
  3. ASN.1 type classes for public and private keys. Exports the following items:
  4. - DSAPrivateKey()
  5. - ECPrivateKey()
  6. - EncryptedPrivateKeyInfo()
  7. - PrivateKeyInfo()
  8. - PublicKeyInfo()
  9. - RSAPrivateKey()
  10. - RSAPublicKey()
  11. Other type classes are defined that help compose the types listed above.
  12. """
  13. from __future__ import unicode_literals, division, absolute_import, print_function
  14. import hashlib
  15. import math
  16. from ._elliptic_curve import (
  17. SECP192R1_BASE_POINT,
  18. SECP224R1_BASE_POINT,
  19. SECP256R1_BASE_POINT,
  20. SECP384R1_BASE_POINT,
  21. SECP521R1_BASE_POINT,
  22. PrimeCurve,
  23. PrimePoint,
  24. )
  25. from ._errors import unwrap
  26. from ._types import type_name, str_cls, byte_cls
  27. from .algos import _ForceNullParameters, DigestAlgorithm, EncryptionAlgorithm, RSAESOAEPParams
  28. from .core import (
  29. Any,
  30. Asn1Value,
  31. BitString,
  32. Choice,
  33. Integer,
  34. IntegerOctetString,
  35. Null,
  36. ObjectIdentifier,
  37. OctetBitString,
  38. OctetString,
  39. ParsableOctetString,
  40. ParsableOctetBitString,
  41. Sequence,
  42. SequenceOf,
  43. SetOf,
  44. )
  45. from .util import int_from_bytes, int_to_bytes
  46. class OtherPrimeInfo(Sequence):
  47. """
  48. Source: https://tools.ietf.org/html/rfc3447#page-46
  49. """
  50. _fields = [
  51. ('prime', Integer),
  52. ('exponent', Integer),
  53. ('coefficient', Integer),
  54. ]
  55. class OtherPrimeInfos(SequenceOf):
  56. """
  57. Source: https://tools.ietf.org/html/rfc3447#page-46
  58. """
  59. _child_spec = OtherPrimeInfo
  60. class RSAPrivateKeyVersion(Integer):
  61. """
  62. Original Name: Version
  63. Source: https://tools.ietf.org/html/rfc3447#page-45
  64. """
  65. _map = {
  66. 0: 'two-prime',
  67. 1: 'multi',
  68. }
  69. class RSAPrivateKey(Sequence):
  70. """
  71. Source: https://tools.ietf.org/html/rfc3447#page-45
  72. """
  73. _fields = [
  74. ('version', RSAPrivateKeyVersion),
  75. ('modulus', Integer),
  76. ('public_exponent', Integer),
  77. ('private_exponent', Integer),
  78. ('prime1', Integer),
  79. ('prime2', Integer),
  80. ('exponent1', Integer),
  81. ('exponent2', Integer),
  82. ('coefficient', Integer),
  83. ('other_prime_infos', OtherPrimeInfos, {'optional': True})
  84. ]
  85. class RSAPublicKey(Sequence):
  86. """
  87. Source: https://tools.ietf.org/html/rfc3447#page-44
  88. """
  89. _fields = [
  90. ('modulus', Integer),
  91. ('public_exponent', Integer)
  92. ]
  93. class DSAPrivateKey(Sequence):
  94. """
  95. The ASN.1 structure that OpenSSL uses to store a DSA private key that is
  96. not part of a PKCS#8 structure. Reversed engineered from english-language
  97. description on linked OpenSSL documentation page.
  98. Original Name: None
  99. Source: https://www.openssl.org/docs/apps/dsa.html
  100. """
  101. _fields = [
  102. ('version', Integer),
  103. ('p', Integer),
  104. ('q', Integer),
  105. ('g', Integer),
  106. ('public_key', Integer),
  107. ('private_key', Integer),
  108. ]
  109. class _ECPoint():
  110. """
  111. In both PublicKeyInfo and PrivateKeyInfo, the EC public key is a byte
  112. string that is encoded as a bit string. This class adds convenience
  113. methods for converting to and from the byte string to a pair of integers
  114. that are the X and Y coordinates.
  115. """
  116. @classmethod
  117. def from_coords(cls, x, y):
  118. """
  119. Creates an ECPoint object from the X and Y integer coordinates of the
  120. point
  121. :param x:
  122. The X coordinate, as an integer
  123. :param y:
  124. The Y coordinate, as an integer
  125. :return:
  126. An ECPoint object
  127. """
  128. x_bytes = int(math.ceil(math.log(x, 2) / 8.0))
  129. y_bytes = int(math.ceil(math.log(y, 2) / 8.0))
  130. num_bytes = max(x_bytes, y_bytes)
  131. byte_string = b'\x04'
  132. byte_string += int_to_bytes(x, width=num_bytes)
  133. byte_string += int_to_bytes(y, width=num_bytes)
  134. return cls(byte_string)
  135. def to_coords(self):
  136. """
  137. Returns the X and Y coordinates for this EC point, as native Python
  138. integers
  139. :return:
  140. A 2-element tuple containing integers (X, Y)
  141. """
  142. data = self.native
  143. first_byte = data[0:1]
  144. # Uncompressed
  145. if first_byte == b'\x04':
  146. remaining = data[1:]
  147. field_len = len(remaining) // 2
  148. x = int_from_bytes(remaining[0:field_len])
  149. y = int_from_bytes(remaining[field_len:])
  150. return (x, y)
  151. if first_byte not in set([b'\x02', b'\x03']):
  152. raise ValueError(unwrap(
  153. '''
  154. Invalid EC public key - first byte is incorrect
  155. '''
  156. ))
  157. raise ValueError(unwrap(
  158. '''
  159. Compressed representations of EC public keys are not supported due
  160. to patent US6252960
  161. '''
  162. ))
  163. class ECPoint(OctetString, _ECPoint):
  164. pass
  165. class ECPointBitString(OctetBitString, _ECPoint):
  166. pass
  167. class SpecifiedECDomainVersion(Integer):
  168. """
  169. Source: http://www.secg.org/sec1-v2.pdf page 104
  170. """
  171. _map = {
  172. 1: 'ecdpVer1',
  173. 2: 'ecdpVer2',
  174. 3: 'ecdpVer3',
  175. }
  176. class FieldType(ObjectIdentifier):
  177. """
  178. Original Name: None
  179. Source: http://www.secg.org/sec1-v2.pdf page 101
  180. """
  181. _map = {
  182. '1.2.840.10045.1.1': 'prime_field',
  183. '1.2.840.10045.1.2': 'characteristic_two_field',
  184. }
  185. class CharacteristicTwoBasis(ObjectIdentifier):
  186. """
  187. Original Name: None
  188. Source: http://www.secg.org/sec1-v2.pdf page 102
  189. """
  190. _map = {
  191. '1.2.840.10045.1.2.1.1': 'gn_basis',
  192. '1.2.840.10045.1.2.1.2': 'tp_basis',
  193. '1.2.840.10045.1.2.1.3': 'pp_basis',
  194. }
  195. class Pentanomial(Sequence):
  196. """
  197. Source: http://www.secg.org/sec1-v2.pdf page 102
  198. """
  199. _fields = [
  200. ('k1', Integer),
  201. ('k2', Integer),
  202. ('k3', Integer),
  203. ]
  204. class CharacteristicTwo(Sequence):
  205. """
  206. Original Name: Characteristic-two
  207. Source: http://www.secg.org/sec1-v2.pdf page 101
  208. """
  209. _fields = [
  210. ('m', Integer),
  211. ('basis', CharacteristicTwoBasis),
  212. ('parameters', Any),
  213. ]
  214. _oid_pair = ('basis', 'parameters')
  215. _oid_specs = {
  216. 'gn_basis': Null,
  217. 'tp_basis': Integer,
  218. 'pp_basis': Pentanomial,
  219. }
  220. class FieldID(Sequence):
  221. """
  222. Source: http://www.secg.org/sec1-v2.pdf page 100
  223. """
  224. _fields = [
  225. ('field_type', FieldType),
  226. ('parameters', Any),
  227. ]
  228. _oid_pair = ('field_type', 'parameters')
  229. _oid_specs = {
  230. 'prime_field': Integer,
  231. 'characteristic_two_field': CharacteristicTwo,
  232. }
  233. class Curve(Sequence):
  234. """
  235. Source: http://www.secg.org/sec1-v2.pdf page 104
  236. """
  237. _fields = [
  238. ('a', OctetString),
  239. ('b', OctetString),
  240. ('seed', OctetBitString, {'optional': True}),
  241. ]
  242. class SpecifiedECDomain(Sequence):
  243. """
  244. Source: http://www.secg.org/sec1-v2.pdf page 103
  245. """
  246. _fields = [
  247. ('version', SpecifiedECDomainVersion),
  248. ('field_id', FieldID),
  249. ('curve', Curve),
  250. ('base', ECPoint),
  251. ('order', Integer),
  252. ('cofactor', Integer, {'optional': True}),
  253. ('hash', DigestAlgorithm, {'optional': True}),
  254. ]
  255. class NamedCurve(ObjectIdentifier):
  256. """
  257. Various named curves
  258. Original Name: None
  259. Source: https://tools.ietf.org/html/rfc3279#page-23,
  260. https://tools.ietf.org/html/rfc5480#page-5
  261. """
  262. _map = {
  263. # https://tools.ietf.org/html/rfc3279#page-23
  264. '1.2.840.10045.3.0.1': 'c2pnb163v1',
  265. '1.2.840.10045.3.0.2': 'c2pnb163v2',
  266. '1.2.840.10045.3.0.3': 'c2pnb163v3',
  267. '1.2.840.10045.3.0.4': 'c2pnb176w1',
  268. '1.2.840.10045.3.0.5': 'c2tnb191v1',
  269. '1.2.840.10045.3.0.6': 'c2tnb191v2',
  270. '1.2.840.10045.3.0.7': 'c2tnb191v3',
  271. '1.2.840.10045.3.0.8': 'c2onb191v4',
  272. '1.2.840.10045.3.0.9': 'c2onb191v5',
  273. '1.2.840.10045.3.0.10': 'c2pnb208w1',
  274. '1.2.840.10045.3.0.11': 'c2tnb239v1',
  275. '1.2.840.10045.3.0.12': 'c2tnb239v2',
  276. '1.2.840.10045.3.0.13': 'c2tnb239v3',
  277. '1.2.840.10045.3.0.14': 'c2onb239v4',
  278. '1.2.840.10045.3.0.15': 'c2onb239v5',
  279. '1.2.840.10045.3.0.16': 'c2pnb272w1',
  280. '1.2.840.10045.3.0.17': 'c2pnb304w1',
  281. '1.2.840.10045.3.0.18': 'c2tnb359v1',
  282. '1.2.840.10045.3.0.19': 'c2pnb368w1',
  283. '1.2.840.10045.3.0.20': 'c2tnb431r1',
  284. '1.2.840.10045.3.1.2': 'prime192v2',
  285. '1.2.840.10045.3.1.3': 'prime192v3',
  286. '1.2.840.10045.3.1.4': 'prime239v1',
  287. '1.2.840.10045.3.1.5': 'prime239v2',
  288. '1.2.840.10045.3.1.6': 'prime239v3',
  289. # https://tools.ietf.org/html/rfc5480#page-5
  290. '1.3.132.0.1': 'sect163k1',
  291. '1.3.132.0.15': 'sect163r2',
  292. '1.2.840.10045.3.1.1': 'secp192r1',
  293. '1.3.132.0.33': 'secp224r1',
  294. '1.3.132.0.26': 'sect233k1',
  295. '1.2.840.10045.3.1.7': 'secp256r1',
  296. '1.3.132.0.27': 'sect233r1',
  297. '1.3.132.0.16': 'sect283k1',
  298. '1.3.132.0.17': 'sect283r1',
  299. '1.3.132.0.34': 'secp384r1',
  300. '1.3.132.0.36': 'sect409k1',
  301. '1.3.132.0.37': 'sect409r1',
  302. '1.3.132.0.35': 'secp521r1',
  303. '1.3.132.0.38': 'sect571k1',
  304. '1.3.132.0.39': 'sect571r1',
  305. }
  306. class ECDomainParameters(Choice):
  307. """
  308. Source: http://www.secg.org/sec1-v2.pdf page 102
  309. """
  310. _alternatives = [
  311. ('specified', SpecifiedECDomain),
  312. ('named', NamedCurve),
  313. ('implicit_ca', Null),
  314. ]
  315. class ECPrivateKeyVersion(Integer):
  316. """
  317. Original Name: None
  318. Source: http://www.secg.org/sec1-v2.pdf page 108
  319. """
  320. _map = {
  321. 1: 'ecPrivkeyVer1',
  322. }
  323. class ECPrivateKey(Sequence):
  324. """
  325. Source: http://www.secg.org/sec1-v2.pdf page 108
  326. """
  327. _fields = [
  328. ('version', ECPrivateKeyVersion),
  329. ('private_key', IntegerOctetString),
  330. ('parameters', ECDomainParameters, {'explicit': 0, 'optional': True}),
  331. ('public_key', ECPointBitString, {'explicit': 1, 'optional': True}),
  332. ]
  333. class DSAParams(Sequence):
  334. """
  335. Parameters for a DSA public or private key
  336. Original Name: Dss-Parms
  337. Source: https://tools.ietf.org/html/rfc3279#page-9
  338. """
  339. _fields = [
  340. ('p', Integer),
  341. ('q', Integer),
  342. ('g', Integer),
  343. ]
  344. class Attribute(Sequence):
  345. """
  346. Source: https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-X.501-198811-S!!PDF-E&type=items page 8
  347. """
  348. _fields = [
  349. ('type', ObjectIdentifier),
  350. ('values', SetOf, {'spec': Any}),
  351. ]
  352. class Attributes(SetOf):
  353. """
  354. Source: https://tools.ietf.org/html/rfc5208#page-3
  355. """
  356. _child_spec = Attribute
  357. class PrivateKeyAlgorithmId(ObjectIdentifier):
  358. """
  359. These OIDs for various public keys are reused when storing private keys
  360. inside of a PKCS#8 structure
  361. Original Name: None
  362. Source: https://tools.ietf.org/html/rfc3279
  363. """
  364. _map = {
  365. # https://tools.ietf.org/html/rfc3279#page-19
  366. '1.2.840.113549.1.1.1': 'rsa',
  367. # https://tools.ietf.org/html/rfc3279#page-18
  368. '1.2.840.10040.4.1': 'dsa',
  369. # https://tools.ietf.org/html/rfc3279#page-13
  370. '1.2.840.10045.2.1': 'ec',
  371. }
  372. class PrivateKeyAlgorithm(_ForceNullParameters, Sequence):
  373. """
  374. Original Name: PrivateKeyAlgorithmIdentifier
  375. Source: https://tools.ietf.org/html/rfc5208#page-3
  376. """
  377. _fields = [
  378. ('algorithm', PrivateKeyAlgorithmId),
  379. ('parameters', Any, {'optional': True}),
  380. ]
  381. _oid_pair = ('algorithm', 'parameters')
  382. _oid_specs = {
  383. 'dsa': DSAParams,
  384. 'ec': ECDomainParameters,
  385. }
  386. class PrivateKeyInfo(Sequence):
  387. """
  388. Source: https://tools.ietf.org/html/rfc5208#page-3
  389. """
  390. _fields = [
  391. ('version', Integer),
  392. ('private_key_algorithm', PrivateKeyAlgorithm),
  393. ('private_key', ParsableOctetString),
  394. ('attributes', Attributes, {'implicit': 0, 'optional': True}),
  395. ]
  396. def _private_key_spec(self):
  397. algorithm = self['private_key_algorithm']['algorithm'].native
  398. return {
  399. 'rsa': RSAPrivateKey,
  400. 'dsa': Integer,
  401. 'ec': ECPrivateKey,
  402. }[algorithm]
  403. _spec_callbacks = {
  404. 'private_key': _private_key_spec
  405. }
  406. _algorithm = None
  407. _bit_size = None
  408. _public_key = None
  409. _fingerprint = None
  410. @classmethod
  411. def wrap(cls, private_key, algorithm):
  412. """
  413. Wraps a private key in a PrivateKeyInfo structure
  414. :param private_key:
  415. A byte string or Asn1Value object of the private key
  416. :param algorithm:
  417. A unicode string of "rsa", "dsa" or "ec"
  418. :return:
  419. A PrivateKeyInfo object
  420. """
  421. if not isinstance(private_key, byte_cls) and not isinstance(private_key, Asn1Value):
  422. raise TypeError(unwrap(
  423. '''
  424. private_key must be a byte string or Asn1Value, not %s
  425. ''',
  426. type_name(private_key)
  427. ))
  428. if algorithm == 'rsa':
  429. if not isinstance(private_key, RSAPrivateKey):
  430. private_key = RSAPrivateKey.load(private_key)
  431. params = Null()
  432. elif algorithm == 'dsa':
  433. if not isinstance(private_key, DSAPrivateKey):
  434. private_key = DSAPrivateKey.load(private_key)
  435. params = DSAParams()
  436. params['p'] = private_key['p']
  437. params['q'] = private_key['q']
  438. params['g'] = private_key['g']
  439. public_key = private_key['public_key']
  440. private_key = private_key['private_key']
  441. elif algorithm == 'ec':
  442. if not isinstance(private_key, ECPrivateKey):
  443. private_key = ECPrivateKey.load(private_key)
  444. else:
  445. private_key = private_key.copy()
  446. params = private_key['parameters']
  447. del private_key['parameters']
  448. else:
  449. raise ValueError(unwrap(
  450. '''
  451. algorithm must be one of "rsa", "dsa", "ec", not %s
  452. ''',
  453. repr(algorithm)
  454. ))
  455. private_key_algo = PrivateKeyAlgorithm()
  456. private_key_algo['algorithm'] = PrivateKeyAlgorithmId(algorithm)
  457. private_key_algo['parameters'] = params
  458. container = cls()
  459. container._algorithm = algorithm
  460. container['version'] = Integer(0)
  461. container['private_key_algorithm'] = private_key_algo
  462. container['private_key'] = private_key
  463. # Here we save the DSA public key if possible since it is not contained
  464. # within the PKCS#8 structure for a DSA key
  465. if algorithm == 'dsa':
  466. container._public_key = public_key
  467. return container
  468. def _compute_public_key(self):
  469. """
  470. Computes the public key corresponding to the current private key.
  471. :return:
  472. For RSA keys, an RSAPublicKey object. For DSA keys, an Integer
  473. object. For EC keys, an ECPointBitString.
  474. """
  475. if self.algorithm == 'dsa':
  476. params = self['private_key_algorithm']['parameters']
  477. return Integer(pow(
  478. params['g'].native,
  479. self['private_key'].parsed.native,
  480. params['p'].native
  481. ))
  482. if self.algorithm == 'rsa':
  483. key = self['private_key'].parsed
  484. return RSAPublicKey({
  485. 'modulus': key['modulus'],
  486. 'public_exponent': key['public_exponent'],
  487. })
  488. if self.algorithm == 'ec':
  489. curve_type, details = self.curve
  490. if curve_type == 'implicit_ca':
  491. raise ValueError(unwrap(
  492. '''
  493. Unable to compute public key for EC key using Implicit CA
  494. parameters
  495. '''
  496. ))
  497. if curve_type == 'specified':
  498. if details['field_id']['field_type'] == 'characteristic_two_field':
  499. raise ValueError(unwrap(
  500. '''
  501. Unable to compute public key for EC key over a
  502. characteristic two field
  503. '''
  504. ))
  505. curve = PrimeCurve(
  506. details['field_id']['parameters'],
  507. int_from_bytes(details['curve']['a']),
  508. int_from_bytes(details['curve']['b'])
  509. )
  510. base_x, base_y = self['private_key_algorithm']['parameters'].chosen['base'].to_coords()
  511. base_point = PrimePoint(curve, base_x, base_y)
  512. elif curve_type == 'named':
  513. if details not in ('secp192r1', 'secp224r1', 'secp256r1', 'secp384r1', 'secp521r1'):
  514. raise ValueError(unwrap(
  515. '''
  516. Unable to compute public key for EC named curve %s,
  517. parameters not currently included
  518. ''',
  519. details
  520. ))
  521. base_point = {
  522. 'secp192r1': SECP192R1_BASE_POINT,
  523. 'secp224r1': SECP224R1_BASE_POINT,
  524. 'secp256r1': SECP256R1_BASE_POINT,
  525. 'secp384r1': SECP384R1_BASE_POINT,
  526. 'secp521r1': SECP521R1_BASE_POINT,
  527. }[details]
  528. public_point = base_point * self['private_key'].parsed['private_key'].native
  529. return ECPointBitString.from_coords(public_point.x, public_point.y)
  530. def unwrap(self):
  531. """
  532. Unwraps the private key into an RSAPrivateKey, DSAPrivateKey or
  533. ECPrivateKey object
  534. :return:
  535. An RSAPrivateKey, DSAPrivateKey or ECPrivateKey object
  536. """
  537. if self.algorithm == 'rsa':
  538. return self['private_key'].parsed
  539. if self.algorithm == 'dsa':
  540. params = self['private_key_algorithm']['parameters']
  541. return DSAPrivateKey({
  542. 'version': 0,
  543. 'p': params['p'],
  544. 'q': params['q'],
  545. 'g': params['g'],
  546. 'public_key': self.public_key,
  547. 'private_key': self['private_key'].parsed,
  548. })
  549. if self.algorithm == 'ec':
  550. output = self['private_key'].parsed
  551. output['parameters'] = self['private_key_algorithm']['parameters']
  552. output['public_key'] = self.public_key
  553. return output
  554. @property
  555. def curve(self):
  556. """
  557. Returns information about the curve used for an EC key
  558. :raises:
  559. ValueError - when the key is not an EC key
  560. :return:
  561. A two-element tuple, with the first element being a unicode string
  562. of "implicit_ca", "specified" or "named". If the first element is
  563. "implicit_ca", the second is None. If "specified", the second is
  564. an OrderedDict that is the native version of SpecifiedECDomain. If
  565. "named", the second is a unicode string of the curve name.
  566. """
  567. if self.algorithm != 'ec':
  568. raise ValueError(unwrap(
  569. '''
  570. Only EC keys have a curve, this key is %s
  571. ''',
  572. self.algorithm.upper()
  573. ))
  574. params = self['private_key_algorithm']['parameters']
  575. chosen = params.chosen
  576. if params.name == 'implicit_ca':
  577. value = None
  578. else:
  579. value = chosen.native
  580. return (params.name, value)
  581. @property
  582. def hash_algo(self):
  583. """
  584. Returns the name of the family of hash algorithms used to generate a
  585. DSA key
  586. :raises:
  587. ValueError - when the key is not a DSA key
  588. :return:
  589. A unicode string of "sha1" or "sha2"
  590. """
  591. if self.algorithm != 'dsa':
  592. raise ValueError(unwrap(
  593. '''
  594. Only DSA keys are generated using a hash algorithm, this key is
  595. %s
  596. ''',
  597. self.algorithm.upper()
  598. ))
  599. byte_len = math.log(self['private_key_algorithm']['parameters']['q'].native, 2) / 8
  600. return 'sha1' if byte_len <= 20 else 'sha2'
  601. @property
  602. def algorithm(self):
  603. """
  604. :return:
  605. A unicode string of "rsa", "dsa" or "ec"
  606. """
  607. if self._algorithm is None:
  608. self._algorithm = self['private_key_algorithm']['algorithm'].native
  609. return self._algorithm
  610. @property
  611. def bit_size(self):
  612. """
  613. :return:
  614. The bit size of the private key, as an integer
  615. """
  616. if self._bit_size is None:
  617. if self.algorithm == 'rsa':
  618. prime = self['private_key'].parsed['modulus'].native
  619. elif self.algorithm == 'dsa':
  620. prime = self['private_key_algorithm']['parameters']['p'].native
  621. elif self.algorithm == 'ec':
  622. prime = self['private_key'].parsed['private_key'].native
  623. self._bit_size = int(math.ceil(math.log(prime, 2)))
  624. modulus = self._bit_size % 8
  625. if modulus != 0:
  626. self._bit_size += 8 - modulus
  627. return self._bit_size
  628. @property
  629. def byte_size(self):
  630. """
  631. :return:
  632. The byte size of the private key, as an integer
  633. """
  634. return int(math.ceil(self.bit_size / 8))
  635. @property
  636. def public_key(self):
  637. """
  638. :return:
  639. If an RSA key, an RSAPublicKey object. If a DSA key, an Integer
  640. object. If an EC key, an ECPointBitString object.
  641. """
  642. if self._public_key is None:
  643. if self.algorithm == 'ec':
  644. key = self['private_key'].parsed
  645. if key['public_key']:
  646. self._public_key = key['public_key'].untag()
  647. else:
  648. self._public_key = self._compute_public_key()
  649. else:
  650. self._public_key = self._compute_public_key()
  651. return self._public_key
  652. @property
  653. def public_key_info(self):
  654. """
  655. :return:
  656. A PublicKeyInfo object derived from this private key.
  657. """
  658. return PublicKeyInfo({
  659. 'algorithm': {
  660. 'algorithm': self.algorithm,
  661. 'parameters': self['private_key_algorithm']['parameters']
  662. },
  663. 'public_key': self.public_key
  664. })
  665. @property
  666. def fingerprint(self):
  667. """
  668. Creates a fingerprint that can be compared with a public key to see if
  669. the two form a pair.
  670. This fingerprint is not compatible with fingerprints generated by any
  671. other software.
  672. :return:
  673. A byte string that is a sha256 hash of selected components (based
  674. on the key type)
  675. """
  676. if self._fingerprint is None:
  677. params = self['private_key_algorithm']['parameters']
  678. key = self['private_key'].parsed
  679. if self.algorithm == 'rsa':
  680. to_hash = '%d:%d' % (
  681. key['modulus'].native,
  682. key['public_exponent'].native,
  683. )
  684. elif self.algorithm == 'dsa':
  685. public_key = self.public_key
  686. to_hash = '%d:%d:%d:%d' % (
  687. params['p'].native,
  688. params['q'].native,
  689. params['g'].native,
  690. public_key.native,
  691. )
  692. elif self.algorithm == 'ec':
  693. public_key = key['public_key'].native
  694. if public_key is None:
  695. public_key = self.public_key.native
  696. if params.name == 'named':
  697. to_hash = '%s:' % params.chosen.native
  698. to_hash = to_hash.encode('utf-8')
  699. to_hash += public_key
  700. elif params.name == 'implicit_ca':
  701. to_hash = public_key
  702. elif params.name == 'specified':
  703. to_hash = '%s:' % params.chosen['field_id']['parameters'].native
  704. to_hash = to_hash.encode('utf-8')
  705. to_hash += b':' + params.chosen['curve']['a'].native
  706. to_hash += b':' + params.chosen['curve']['b'].native
  707. to_hash += public_key
  708. if isinstance(to_hash, str_cls):
  709. to_hash = to_hash.encode('utf-8')
  710. self._fingerprint = hashlib.sha256(to_hash).digest()
  711. return self._fingerprint
  712. class EncryptedPrivateKeyInfo(Sequence):
  713. """
  714. Source: https://tools.ietf.org/html/rfc5208#page-4
  715. """
  716. _fields = [
  717. ('encryption_algorithm', EncryptionAlgorithm),
  718. ('encrypted_data', OctetString),
  719. ]
  720. # These structures are from https://tools.ietf.org/html/rfc3279
  721. class ValidationParms(Sequence):
  722. """
  723. Source: https://tools.ietf.org/html/rfc3279#page-10
  724. """
  725. _fields = [
  726. ('seed', BitString),
  727. ('pgen_counter', Integer),
  728. ]
  729. class DomainParameters(Sequence):
  730. """
  731. Source: https://tools.ietf.org/html/rfc3279#page-10
  732. """
  733. _fields = [
  734. ('p', Integer),
  735. ('g', Integer),
  736. ('q', Integer),
  737. ('j', Integer, {'optional': True}),
  738. ('validation_params', ValidationParms, {'optional': True}),
  739. ]
  740. class PublicKeyAlgorithmId(ObjectIdentifier):
  741. """
  742. Original Name: None
  743. Source: https://tools.ietf.org/html/rfc3279
  744. """
  745. _map = {
  746. # https://tools.ietf.org/html/rfc3279#page-19
  747. '1.2.840.113549.1.1.1': 'rsa',
  748. # https://tools.ietf.org/html/rfc3447#page-47
  749. '1.2.840.113549.1.1.7': 'rsaes_oaep',
  750. # https://tools.ietf.org/html/rfc3279#page-18
  751. '1.2.840.10040.4.1': 'dsa',
  752. # https://tools.ietf.org/html/rfc3279#page-13
  753. '1.2.840.10045.2.1': 'ec',
  754. # https://tools.ietf.org/html/rfc3279#page-10
  755. '1.2.840.10046.2.1': 'dh',
  756. }
  757. class PublicKeyAlgorithm(_ForceNullParameters, Sequence):
  758. """
  759. Original Name: AlgorithmIdentifier
  760. Source: https://tools.ietf.org/html/rfc5280#page-18
  761. """
  762. _fields = [
  763. ('algorithm', PublicKeyAlgorithmId),
  764. ('parameters', Any, {'optional': True}),
  765. ]
  766. _oid_pair = ('algorithm', 'parameters')
  767. _oid_specs = {
  768. 'dsa': DSAParams,
  769. 'ec': ECDomainParameters,
  770. 'dh': DomainParameters,
  771. 'rsaes_oaep': RSAESOAEPParams,
  772. }
  773. class PublicKeyInfo(Sequence):
  774. """
  775. Original Name: SubjectPublicKeyInfo
  776. Source: https://tools.ietf.org/html/rfc5280#page-17
  777. """
  778. _fields = [
  779. ('algorithm', PublicKeyAlgorithm),
  780. ('public_key', ParsableOctetBitString),
  781. ]
  782. def _public_key_spec(self):
  783. algorithm = self['algorithm']['algorithm'].native
  784. return {
  785. 'rsa': RSAPublicKey,
  786. 'rsaes_oaep': RSAPublicKey,
  787. 'dsa': Integer,
  788. # We override the field spec with ECPoint so that users can easily
  789. # decompose the byte string into the constituent X and Y coords
  790. 'ec': (ECPointBitString, None),
  791. 'dh': Integer,
  792. }[algorithm]
  793. _spec_callbacks = {
  794. 'public_key': _public_key_spec
  795. }
  796. _algorithm = None
  797. _bit_size = None
  798. _fingerprint = None
  799. _sha1 = None
  800. _sha256 = None
  801. @classmethod
  802. def wrap(cls, public_key, algorithm):
  803. """
  804. Wraps a public key in a PublicKeyInfo structure
  805. :param public_key:
  806. A byte string or Asn1Value object of the public key
  807. :param algorithm:
  808. A unicode string of "rsa"
  809. :return:
  810. A PublicKeyInfo object
  811. """
  812. if not isinstance(public_key, byte_cls) and not isinstance(public_key, Asn1Value):
  813. raise TypeError(unwrap(
  814. '''
  815. public_key must be a byte string or Asn1Value, not %s
  816. ''',
  817. type_name(public_key)
  818. ))
  819. if algorithm != 'rsa':
  820. raise ValueError(unwrap(
  821. '''
  822. algorithm must "rsa", not %s
  823. ''',
  824. repr(algorithm)
  825. ))
  826. algo = PublicKeyAlgorithm()
  827. algo['algorithm'] = PublicKeyAlgorithmId(algorithm)
  828. algo['parameters'] = Null()
  829. container = cls()
  830. container['algorithm'] = algo
  831. if isinstance(public_key, Asn1Value):
  832. public_key = public_key.untag().dump()
  833. container['public_key'] = ParsableOctetBitString(public_key)
  834. return container
  835. def unwrap(self):
  836. """
  837. Unwraps an RSA public key into an RSAPublicKey object. Does not support
  838. DSA or EC public keys since they do not have an unwrapped form.
  839. :return:
  840. An RSAPublicKey object
  841. """
  842. if self.algorithm == 'rsa':
  843. return self['public_key'].parsed
  844. key_type = self.algorithm.upper()
  845. a_an = 'an' if key_type == 'EC' else 'a'
  846. raise ValueError(unwrap(
  847. '''
  848. Only RSA public keys may be unwrapped - this key is %s %s public
  849. key
  850. ''',
  851. a_an,
  852. key_type
  853. ))
  854. @property
  855. def curve(self):
  856. """
  857. Returns information about the curve used for an EC key
  858. :raises:
  859. ValueError - when the key is not an EC key
  860. :return:
  861. A two-element tuple, with the first element being a unicode string
  862. of "implicit_ca", "specified" or "named". If the first element is
  863. "implicit_ca", the second is None. If "specified", the second is
  864. an OrderedDict that is the native version of SpecifiedECDomain. If
  865. "named", the second is a unicode string of the curve name.
  866. """
  867. if self.algorithm != 'ec':
  868. raise ValueError(unwrap(
  869. '''
  870. Only EC keys have a curve, this key is %s
  871. ''',
  872. self.algorithm.upper()
  873. ))
  874. params = self['algorithm']['parameters']
  875. chosen = params.chosen
  876. if params.name == 'implicit_ca':
  877. value = None
  878. else:
  879. value = chosen.native
  880. return (params.name, value)
  881. @property
  882. def hash_algo(self):
  883. """
  884. Returns the name of the family of hash algorithms used to generate a
  885. DSA key
  886. :raises:
  887. ValueError - when the key is not a DSA key
  888. :return:
  889. A unicode string of "sha1" or "sha2" or None if no parameters are
  890. present
  891. """
  892. if self.algorithm != 'dsa':
  893. raise ValueError(unwrap(
  894. '''
  895. Only DSA keys are generated using a hash algorithm, this key is
  896. %s
  897. ''',
  898. self.algorithm.upper()
  899. ))
  900. parameters = self['algorithm']['parameters']
  901. if parameters.native is None:
  902. return None
  903. byte_len = math.log(parameters['q'].native, 2) / 8
  904. return 'sha1' if byte_len <= 20 else 'sha2'
  905. @property
  906. def algorithm(self):
  907. """
  908. :return:
  909. A unicode string of "rsa", "dsa" or "ec"
  910. """
  911. if self._algorithm is None:
  912. self._algorithm = self['algorithm']['algorithm'].native
  913. return self._algorithm
  914. @property
  915. def bit_size(self):
  916. """
  917. :return:
  918. The bit size of the public key, as an integer
  919. """
  920. if self._bit_size is None:
  921. if self.algorithm == 'ec':
  922. self._bit_size = ((len(self['public_key'].native) - 1) / 2) * 8
  923. else:
  924. if self.algorithm == 'rsa':
  925. prime = self['public_key'].parsed['modulus'].native
  926. elif self.algorithm == 'dsa':
  927. prime = self['algorithm']['parameters']['p'].native
  928. self._bit_size = int(math.ceil(math.log(prime, 2)))
  929. modulus = self._bit_size % 8
  930. if modulus != 0:
  931. self._bit_size += 8 - modulus
  932. return self._bit_size
  933. @property
  934. def byte_size(self):
  935. """
  936. :return:
  937. The byte size of the public key, as an integer
  938. """
  939. return int(math.ceil(self.bit_size / 8))
  940. @property
  941. def sha1(self):
  942. """
  943. :return:
  944. The SHA1 hash of the DER-encoded bytes of this public key info
  945. """
  946. if self._sha1 is None:
  947. self._sha1 = hashlib.sha1(byte_cls(self['public_key'])).digest()
  948. return self._sha1
  949. @property
  950. def sha256(self):
  951. """
  952. :return:
  953. The SHA-256 hash of the DER-encoded bytes of this public key info
  954. """
  955. if self._sha256 is None:
  956. self._sha256 = hashlib.sha256(byte_cls(self['public_key'])).digest()
  957. return self._sha256
  958. @property
  959. def fingerprint(self):
  960. """
  961. Creates a fingerprint that can be compared with a private key to see if
  962. the two form a pair.
  963. This fingerprint is not compatible with fingerprints generated by any
  964. other software.
  965. :return:
  966. A byte string that is a sha256 hash of selected components (based
  967. on the key type)
  968. """
  969. if self._fingerprint is None:
  970. key_type = self['algorithm']['algorithm'].native
  971. params = self['algorithm']['parameters']
  972. if key_type == 'rsa':
  973. key = self['public_key'].parsed
  974. to_hash = '%d:%d' % (
  975. key['modulus'].native,
  976. key['public_exponent'].native,
  977. )
  978. elif key_type == 'dsa':
  979. key = self['public_key'].parsed
  980. to_hash = '%d:%d:%d:%d' % (
  981. params['p'].native,
  982. params['q'].native,
  983. params['g'].native,
  984. key.native,
  985. )
  986. elif key_type == 'ec':
  987. key = self['public_key']
  988. if params.name == 'named':
  989. to_hash = '%s:' % params.chosen.native
  990. to_hash = to_hash.encode('utf-8')
  991. to_hash += key.native
  992. elif params.name == 'implicit_ca':
  993. to_hash = key.native
  994. elif params.name == 'specified':
  995. to_hash = '%s:' % params.chosen['field_id']['parameters'].native
  996. to_hash = to_hash.encode('utf-8')
  997. to_hash += b':' + params.chosen['curve']['a'].native
  998. to_hash += b':' + params.chosen['curve']['b'].native
  999. to_hash += key.native
  1000. if isinstance(to_hash, str_cls):
  1001. to_hash = to_hash.encode('utf-8')
  1002. self._fingerprint = hashlib.sha256(to_hash).digest()
  1003. return self._fingerprint