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.

1245 lines
34 KiB

7 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
  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/rfc3279#page-18
  749. '1.2.840.10040.4.1': 'dsa',
  750. # https://tools.ietf.org/html/rfc3279#page-13
  751. '1.2.840.10045.2.1': 'ec',
  752. # https://tools.ietf.org/html/rfc3279#page-10
  753. '1.2.840.10046.2.1': 'dh',
  754. }
  755. class PublicKeyAlgorithm(_ForceNullParameters, Sequence):
  756. """
  757. Original Name: AlgorithmIdentifier
  758. Source: https://tools.ietf.org/html/rfc5280#page-18
  759. """
  760. _fields = [
  761. ('algorithm', PublicKeyAlgorithmId),
  762. ('parameters', Any, {'optional': True}),
  763. ]
  764. _oid_pair = ('algorithm', 'parameters')
  765. _oid_specs = {
  766. 'dsa': DSAParams,
  767. 'ec': ECDomainParameters,
  768. 'dh': DomainParameters,
  769. }
  770. class PublicKeyInfo(Sequence):
  771. """
  772. Original Name: SubjectPublicKeyInfo
  773. Source: https://tools.ietf.org/html/rfc5280#page-17
  774. """
  775. _fields = [
  776. ('algorithm', PublicKeyAlgorithm),
  777. ('public_key', ParsableOctetBitString),
  778. ]
  779. def _public_key_spec(self):
  780. algorithm = self['algorithm']['algorithm'].native
  781. return {
  782. 'rsa': RSAPublicKey,
  783. 'dsa': Integer,
  784. # We override the field spec with ECPoint so that users can easily
  785. # decompose the byte string into the constituent X and Y coords
  786. 'ec': (ECPointBitString, None),
  787. 'dh': Integer,
  788. }[algorithm]
  789. _spec_callbacks = {
  790. 'public_key': _public_key_spec
  791. }
  792. _algorithm = None
  793. _bit_size = None
  794. _fingerprint = None
  795. _sha1 = None
  796. _sha256 = None
  797. @classmethod
  798. def wrap(cls, public_key, algorithm):
  799. """
  800. Wraps a public key in a PublicKeyInfo structure
  801. :param public_key:
  802. A byte string or Asn1Value object of the public key
  803. :param algorithm:
  804. A unicode string of "rsa"
  805. :return:
  806. A PublicKeyInfo object
  807. """
  808. if not isinstance(public_key, byte_cls) and not isinstance(public_key, Asn1Value):
  809. raise TypeError(unwrap(
  810. '''
  811. public_key must be a byte string or Asn1Value, not %s
  812. ''',
  813. type_name(public_key)
  814. ))
  815. if algorithm != 'rsa':
  816. raise ValueError(unwrap(
  817. '''
  818. algorithm must "rsa", not %s
  819. ''',
  820. repr(algorithm)
  821. ))
  822. algo = PublicKeyAlgorithm()
  823. algo['algorithm'] = PublicKeyAlgorithmId(algorithm)
  824. algo['parameters'] = Null()
  825. container = cls()
  826. container['algorithm'] = algo
  827. if isinstance(public_key, Asn1Value):
  828. public_key = public_key.untag().dump()
  829. container['public_key'] = ParsableOctetBitString(public_key)
  830. return container
  831. def unwrap(self):
  832. """
  833. Unwraps an RSA public key into an RSAPublicKey object. Does not support
  834. DSA or EC public keys since they do not have an unwrapped form.
  835. :return:
  836. An RSAPublicKey object
  837. """
  838. if self.algorithm == 'rsa':
  839. return self['public_key'].parsed
  840. key_type = self.algorithm.upper()
  841. a_an = 'an' if key_type == 'EC' else 'a'
  842. raise ValueError(unwrap(
  843. '''
  844. Only RSA public keys may be unwrapped - this key is %s %s public
  845. key
  846. ''',
  847. a_an,
  848. key_type
  849. ))
  850. @property
  851. def curve(self):
  852. """
  853. Returns information about the curve used for an EC key
  854. :raises:
  855. ValueError - when the key is not an EC key
  856. :return:
  857. A two-element tuple, with the first element being a unicode string
  858. of "implicit_ca", "specified" or "named". If the first element is
  859. "implicit_ca", the second is None. If "specified", the second is
  860. an OrderedDict that is the native version of SpecifiedECDomain. If
  861. "named", the second is a unicode string of the curve name.
  862. """
  863. if self.algorithm != 'ec':
  864. raise ValueError(unwrap(
  865. '''
  866. Only EC keys have a curve, this key is %s
  867. ''',
  868. self.algorithm.upper()
  869. ))
  870. params = self['algorithm']['parameters']
  871. chosen = params.chosen
  872. if params.name == 'implicit_ca':
  873. value = None
  874. else:
  875. value = chosen.native
  876. return (params.name, value)
  877. @property
  878. def hash_algo(self):
  879. """
  880. Returns the name of the family of hash algorithms used to generate a
  881. DSA key
  882. :raises:
  883. ValueError - when the key is not a DSA key
  884. :return:
  885. A unicode string of "sha1" or "sha2" or None if no parameters are
  886. present
  887. """
  888. if self.algorithm != 'dsa':
  889. raise ValueError(unwrap(
  890. '''
  891. Only DSA keys are generated using a hash algorithm, this key is
  892. %s
  893. ''',
  894. self.algorithm.upper()
  895. ))
  896. parameters = self['algorithm']['parameters']
  897. if parameters.native is None:
  898. return None
  899. byte_len = math.log(parameters['q'].native, 2) / 8
  900. return 'sha1' if byte_len <= 20 else 'sha2'
  901. @property
  902. def algorithm(self):
  903. """
  904. :return:
  905. A unicode string of "rsa", "dsa" or "ec"
  906. """
  907. if self._algorithm is None:
  908. self._algorithm = self['algorithm']['algorithm'].native
  909. return self._algorithm
  910. @property
  911. def bit_size(self):
  912. """
  913. :return:
  914. The bit size of the public key, as an integer
  915. """
  916. if self._bit_size is None:
  917. if self.algorithm == 'ec':
  918. self._bit_size = ((len(self['public_key'].native) - 1) / 2) * 8
  919. else:
  920. if self.algorithm == 'rsa':
  921. prime = self['public_key'].parsed['modulus'].native
  922. elif self.algorithm == 'dsa':
  923. prime = self['algorithm']['parameters']['p'].native
  924. self._bit_size = int(math.ceil(math.log(prime, 2)))
  925. modulus = self._bit_size % 8
  926. if modulus != 0:
  927. self._bit_size += 8 - modulus
  928. return self._bit_size
  929. @property
  930. def byte_size(self):
  931. """
  932. :return:
  933. The byte size of the public key, as an integer
  934. """
  935. return int(math.ceil(self.bit_size / 8))
  936. @property
  937. def sha1(self):
  938. """
  939. :return:
  940. The SHA1 hash of the DER-encoded bytes of this public key info
  941. """
  942. if self._sha1 is None:
  943. self._sha1 = hashlib.sha1(byte_cls(self['public_key'])).digest()
  944. return self._sha1
  945. @property
  946. def sha256(self):
  947. """
  948. :return:
  949. The SHA-256 hash of the DER-encoded bytes of this public key info
  950. """
  951. if self._sha256 is None:
  952. self._sha256 = hashlib.sha256(byte_cls(self['public_key'])).digest()
  953. return self._sha256
  954. @property
  955. def fingerprint(self):
  956. """
  957. Creates a fingerprint that can be compared with a private key to see if
  958. the two form a pair.
  959. This fingerprint is not compatible with fingerprints generated by any
  960. other software.
  961. :return:
  962. A byte string that is a sha256 hash of selected components (based
  963. on the key type)
  964. """
  965. if self._fingerprint is None:
  966. key_type = self['algorithm']['algorithm'].native
  967. params = self['algorithm']['parameters']
  968. if key_type == 'rsa':
  969. key = self['public_key'].parsed
  970. to_hash = '%d:%d' % (
  971. key['modulus'].native,
  972. key['public_exponent'].native,
  973. )
  974. elif key_type == 'dsa':
  975. key = self['public_key'].parsed
  976. to_hash = '%d:%d:%d:%d' % (
  977. params['p'].native,
  978. params['q'].native,
  979. params['g'].native,
  980. key.native,
  981. )
  982. elif key_type == 'ec':
  983. key = self['public_key']
  984. if params.name == 'named':
  985. to_hash = '%s:' % params.chosen.native
  986. to_hash = to_hash.encode('utf-8')
  987. to_hash += key.native
  988. elif params.name == 'implicit_ca':
  989. to_hash = key.native
  990. elif params.name == 'specified':
  991. to_hash = '%s:' % params.chosen['field_id']['parameters'].native
  992. to_hash = to_hash.encode('utf-8')
  993. to_hash += b':' + params.chosen['curve']['a'].native
  994. to_hash += b':' + params.chosen['curve']['b'].native
  995. to_hash += key.native
  996. if isinstance(to_hash, str_cls):
  997. to_hash = to_hash.encode('utf-8')
  998. self._fingerprint = hashlib.sha256(to_hash).digest()
  999. return self._fingerprint

Powered by TurnKey Linux.