ECC.py 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184
  1. # ===================================================================
  2. #
  3. # Copyright (c) 2015, Legrandin <helderijs@gmail.com>
  4. # All rights reserved.
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions
  8. # are met:
  9. #
  10. # 1. Redistributions of source code must retain the above copyright
  11. # notice, this list of conditions and the following disclaimer.
  12. # 2. Redistributions in binary form must reproduce the above copyright
  13. # notice, this list of conditions and the following disclaimer in
  14. # the documentation and/or other materials provided with the
  15. # distribution.
  16. #
  17. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  21. # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  25. # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  26. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  27. # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. # POSSIBILITY OF SUCH DAMAGE.
  29. # ===================================================================
  30. from __future__ import print_function
  31. import re
  32. import sys
  33. import struct
  34. import binascii
  35. from collections import namedtuple
  36. from tls.Crypto.Util.py3compat import bord, tobytes, tostr, bchr, is_string
  37. from tls.Crypto.Util.number import bytes_to_long, long_to_bytes
  38. from tls.Crypto.Math.Numbers import Integer
  39. from tls.Crypto.Util.asn1 import (DerObjectId, DerOctetString, DerSequence,
  40. DerBitString)
  41. from tls.Crypto.PublicKey import (_expand_subject_public_key_info,
  42. _create_subject_public_key_info,
  43. _extract_subject_public_key_info)
  44. from tls.Crypto.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer,
  45. SmartPointer, c_size_t, c_uint8_ptr,
  46. c_ulonglong)
  47. from tls.Crypto.Random import get_random_bytes
  48. from tls.Crypto.Random.random import getrandbits
  49. _ec_lib = load_pycryptodome_raw_lib("Crypto.PublicKey._ec_ws", """
  50. typedef void EcContext;
  51. typedef void EcPoint;
  52. int ec_ws_new_context(EcContext **pec_ctx,
  53. const uint8_t *modulus,
  54. const uint8_t *b,
  55. const uint8_t *order,
  56. size_t len,
  57. uint64_t seed);
  58. void ec_free_context(EcContext *ec_ctx);
  59. int ec_ws_new_point(EcPoint **pecp,
  60. const uint8_t *x,
  61. const uint8_t *y,
  62. size_t len,
  63. const EcContext *ec_ctx);
  64. void ec_free_point(EcPoint *ecp);
  65. int ec_ws_get_xy(uint8_t *x,
  66. uint8_t *y,
  67. size_t len,
  68. const EcPoint *ecp);
  69. int ec_ws_double(EcPoint *p);
  70. int ec_ws_add(EcPoint *ecpa, EcPoint *ecpb);
  71. int ec_ws_scalar(EcPoint *ecp,
  72. const uint8_t *k,
  73. size_t len,
  74. uint64_t seed);
  75. int ec_ws_clone(EcPoint **pecp2, const EcPoint *ecp);
  76. int ec_ws_copy(EcPoint *ecp1, const EcPoint *ecp2);
  77. int ec_ws_cmp(const EcPoint *ecp1, const EcPoint *ecp2);
  78. int ec_ws_neg(EcPoint *p);
  79. int ec_ws_normalize(EcPoint *ecp);
  80. int ec_ws_is_pai(EcPoint *ecp);
  81. """)
  82. _Curve = namedtuple("_Curve", "p b order Gx Gy G modulus_bits oid context desc openssh")
  83. _curves = {}
  84. p256_names = ["p256", "NIST P-256", "P-256", "prime256v1", "secp256r1",
  85. "nistp256"]
  86. def init_p256():
  87. p = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff
  88. b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b
  89. order = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551
  90. Gx = 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296
  91. Gy = 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5
  92. p256_modulus = long_to_bytes(p, 32)
  93. p256_b = long_to_bytes(b, 32)
  94. p256_order = long_to_bytes(order, 32)
  95. ec_p256_context = VoidPointer()
  96. result = _ec_lib.ec_ws_new_context(ec_p256_context.address_of(),
  97. c_uint8_ptr(p256_modulus),
  98. c_uint8_ptr(p256_b),
  99. c_uint8_ptr(p256_order),
  100. c_size_t(len(p256_modulus)),
  101. c_ulonglong(getrandbits(64))
  102. )
  103. if result:
  104. raise ImportError("Error %d initializing P-256 context" % result)
  105. context = SmartPointer(ec_p256_context.get(), _ec_lib.ec_free_context)
  106. p256 = _Curve(Integer(p),
  107. Integer(b),
  108. Integer(order),
  109. Integer(Gx),
  110. Integer(Gy),
  111. None,
  112. 256,
  113. "1.2.840.10045.3.1.7", # ANSI X9.62
  114. context,
  115. "NIST P-256",
  116. "ecdsa-sha2-nistp256")
  117. global p256_names
  118. _curves.update(dict.fromkeys(p256_names, p256))
  119. init_p256()
  120. del init_p256
  121. p384_names = ["p384", "NIST P-384", "P-384", "prime384v1", "secp384r1",
  122. "nistp384"]
  123. def init_p384():
  124. p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff
  125. b = 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef
  126. order = 0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973
  127. Gx = 0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760aB7
  128. Gy = 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5F
  129. p384_modulus = long_to_bytes(p, 48)
  130. p384_b = long_to_bytes(b, 48)
  131. p384_order = long_to_bytes(order, 48)
  132. ec_p384_context = VoidPointer()
  133. result = _ec_lib.ec_ws_new_context(ec_p384_context.address_of(),
  134. c_uint8_ptr(p384_modulus),
  135. c_uint8_ptr(p384_b),
  136. c_uint8_ptr(p384_order),
  137. c_size_t(len(p384_modulus)),
  138. c_ulonglong(getrandbits(64))
  139. )
  140. if result:
  141. raise ImportError("Error %d initializing P-384 context" % result)
  142. context = SmartPointer(ec_p384_context.get(), _ec_lib.ec_free_context)
  143. p384 = _Curve(Integer(p),
  144. Integer(b),
  145. Integer(order),
  146. Integer(Gx),
  147. Integer(Gy),
  148. None,
  149. 384,
  150. "1.3.132.0.34", # SEC 2
  151. context,
  152. "NIST P-384",
  153. "ecdsa-sha2-nistp384")
  154. global p384_names
  155. _curves.update(dict.fromkeys(p384_names, p384))
  156. init_p384()
  157. del init_p384
  158. p521_names = ["p521", "NIST P-521", "P-521", "prime521v1", "secp521r1",
  159. "nistp521"]
  160. def init_p521():
  161. p = 0x000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
  162. b = 0x00000051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00
  163. order = 0x000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409
  164. Gx = 0x000000c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66
  165. Gy = 0x0000011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650
  166. p521_modulus = long_to_bytes(p, 66)
  167. p521_b = long_to_bytes(b, 66)
  168. p521_order = long_to_bytes(order, 66)
  169. ec_p521_context = VoidPointer()
  170. result = _ec_lib.ec_ws_new_context(ec_p521_context.address_of(),
  171. c_uint8_ptr(p521_modulus),
  172. c_uint8_ptr(p521_b),
  173. c_uint8_ptr(p521_order),
  174. c_size_t(len(p521_modulus)),
  175. c_ulonglong(getrandbits(64))
  176. )
  177. if result:
  178. raise ImportError("Error %d initializing P-521 context" % result)
  179. context = SmartPointer(ec_p521_context.get(), _ec_lib.ec_free_context)
  180. p521 = _Curve(Integer(p),
  181. Integer(b),
  182. Integer(order),
  183. Integer(Gx),
  184. Integer(Gy),
  185. None,
  186. 521,
  187. "1.3.132.0.35", # SEC 2
  188. context,
  189. "NIST P-521",
  190. "ecdsa-sha2-nistp521")
  191. global p521_names
  192. _curves.update(dict.fromkeys(p521_names, p521))
  193. init_p521()
  194. del init_p521
  195. class UnsupportedEccFeature(ValueError):
  196. pass
  197. class EccPoint(object):
  198. """A class to abstract a point over an Elliptic Curve.
  199. The class support special methods for:
  200. * Adding two points: ``R = S + T``
  201. * In-place addition: ``S += T``
  202. * Negating a point: ``R = -T``
  203. * Comparing two points: ``if S == T: ...``
  204. * Multiplying a point by a scalar: ``R = S*k``
  205. * In-place multiplication by a scalar: ``T *= k``
  206. :ivar x: The affine X-coordinate of the ECC point
  207. :vartype x: integer
  208. :ivar y: The affine Y-coordinate of the ECC point
  209. :vartype y: integer
  210. :ivar xy: The tuple with X- and Y- coordinates
  211. """
  212. def __init__(self, x, y, curve="p256"):
  213. try:
  214. self._curve = _curves[curve]
  215. except KeyError:
  216. raise ValueError("Unknown curve name %s" % str(curve))
  217. self._curve_name = curve
  218. modulus_bytes = self.size_in_bytes()
  219. context = self._curve.context
  220. xb = long_to_bytes(x, modulus_bytes)
  221. yb = long_to_bytes(y, modulus_bytes)
  222. if len(xb) != modulus_bytes or len(yb) != modulus_bytes:
  223. raise ValueError("Incorrect coordinate length")
  224. self._point = VoidPointer()
  225. result = _ec_lib.ec_ws_new_point(self._point.address_of(),
  226. c_uint8_ptr(xb),
  227. c_uint8_ptr(yb),
  228. c_size_t(modulus_bytes),
  229. context.get())
  230. if result:
  231. if result == 15:
  232. raise ValueError("The EC point does not belong to the curve")
  233. raise ValueError("Error %d while instantiating an EC point" % result)
  234. # Ensure that object disposal of this Python object will (eventually)
  235. # free the memory allocated by the raw library for the EC point
  236. self._point = SmartPointer(self._point.get(),
  237. _ec_lib.ec_free_point)
  238. def set(self, point):
  239. self._point = VoidPointer()
  240. result = _ec_lib.ec_ws_clone(self._point.address_of(),
  241. point._point.get())
  242. if result:
  243. raise ValueError("Error %d while cloning an EC point" % result)
  244. self._point = SmartPointer(self._point.get(),
  245. _ec_lib.ec_free_point)
  246. return self
  247. def __eq__(self, point):
  248. return 0 == _ec_lib.ec_ws_cmp(self._point.get(), point._point.get())
  249. def __neg__(self):
  250. np = self.copy()
  251. result = _ec_lib.ec_ws_neg(np._point.get())
  252. if result:
  253. raise ValueError("Error %d while inverting an EC point" % result)
  254. return np
  255. def copy(self):
  256. """Return a copy of this point."""
  257. x, y = self.xy
  258. np = EccPoint(x, y, self._curve_name)
  259. return np
  260. def is_point_at_infinity(self):
  261. """``True`` if this is the point-at-infinity."""
  262. return self.xy == (0, 0)
  263. def point_at_infinity(self):
  264. """Return the point-at-infinity for the curve this point is on."""
  265. return EccPoint(0, 0, self._curve_name)
  266. @property
  267. def x(self):
  268. return self.xy[0]
  269. @property
  270. def y(self):
  271. return self.xy[1]
  272. @property
  273. def xy(self):
  274. modulus_bytes = self.size_in_bytes()
  275. xb = bytearray(modulus_bytes)
  276. yb = bytearray(modulus_bytes)
  277. result = _ec_lib.ec_ws_get_xy(c_uint8_ptr(xb),
  278. c_uint8_ptr(yb),
  279. c_size_t(modulus_bytes),
  280. self._point.get())
  281. if result:
  282. raise ValueError("Error %d while encoding an EC point" % result)
  283. return (Integer(bytes_to_long(xb)), Integer(bytes_to_long(yb)))
  284. def size_in_bytes(self):
  285. """Size of each coordinate, in bytes."""
  286. return (self.size_in_bits() + 7) // 8
  287. def size_in_bits(self):
  288. """Size of each coordinate, in bits."""
  289. return self._curve.modulus_bits
  290. def double(self):
  291. """Double this point (in-place operation).
  292. :Return:
  293. :class:`EccPoint` : this same object (to enable chaining)
  294. """
  295. result = _ec_lib.ec_ws_double(self._point.get())
  296. if result:
  297. raise ValueError("Error %d while doubling an EC point" % result)
  298. return self
  299. def __iadd__(self, point):
  300. """Add a second point to this one"""
  301. result = _ec_lib.ec_ws_add(self._point.get(), point._point.get())
  302. if result:
  303. if result == 16:
  304. raise ValueError("EC points are not on the same curve")
  305. raise ValueError("Error %d while adding two EC points" % result)
  306. return self
  307. def __add__(self, point):
  308. """Return a new point, the addition of this one and another"""
  309. np = self.copy()
  310. np += point
  311. return np
  312. def __imul__(self, scalar):
  313. """Multiply this point by a scalar"""
  314. if scalar < 0:
  315. raise ValueError("Scalar multiplication is only defined for non-negative integers")
  316. sb = long_to_bytes(scalar)
  317. result = _ec_lib.ec_ws_scalar(self._point.get(),
  318. c_uint8_ptr(sb),
  319. c_size_t(len(sb)),
  320. c_ulonglong(getrandbits(64)))
  321. if result:
  322. raise ValueError("Error %d during scalar multiplication" % result)
  323. return self
  324. def __mul__(self, scalar):
  325. """Return a new point, the scalar product of this one"""
  326. np = self.copy()
  327. np *= scalar
  328. return np
  329. def __rmul__(self, left_hand):
  330. return self.__mul__(left_hand)
  331. # Last piece of initialization
  332. p256_G = EccPoint(_curves['p256'].Gx, _curves['p256'].Gy, "p256")
  333. p256 = _curves['p256']._replace(G=p256_G)
  334. _curves.update(dict.fromkeys(p256_names, p256))
  335. del p256_G, p256, p256_names
  336. p384_G = EccPoint(_curves['p384'].Gx, _curves['p384'].Gy, "p384")
  337. p384 = _curves['p384']._replace(G=p384_G)
  338. _curves.update(dict.fromkeys(p384_names, p384))
  339. del p384_G, p384, p384_names
  340. p521_G = EccPoint(_curves['p521'].Gx, _curves['p521'].Gy, "p521")
  341. p521 = _curves['p521']._replace(G=p521_G)
  342. _curves.update(dict.fromkeys(p521_names, p521))
  343. del p521_G, p521, p521_names
  344. class EccKey(object):
  345. r"""Class defining an ECC key.
  346. Do not instantiate directly.
  347. Use :func:`generate`, :func:`construct` or :func:`import_key` instead.
  348. :ivar curve: The name of the ECC as defined in :numref:`curve_names`.
  349. :vartype curve: string
  350. :ivar pointQ: an ECC point representating the public component
  351. :vartype pointQ: :class:`EccPoint`
  352. :ivar d: A scalar representating the private component
  353. :vartype d: integer
  354. """
  355. def __init__(self, **kwargs):
  356. """Create a new ECC key
  357. Keywords:
  358. curve : string
  359. It must be *"p256"*, *"P-256"*, *"prime256v1"* or *"secp256r1"*.
  360. d : integer
  361. Only for a private key. It must be in the range ``[1..order-1]``.
  362. point : EccPoint
  363. Mandatory for a public key. If provided for a private key,
  364. the implementation will NOT check whether it matches ``d``.
  365. """
  366. kwargs_ = dict(kwargs)
  367. curve_name = kwargs_.pop("curve", None)
  368. self._d = kwargs_.pop("d", None)
  369. self._point = kwargs_.pop("point", None)
  370. if kwargs_:
  371. raise TypeError("Unknown parameters: " + str(kwargs_))
  372. if curve_name not in _curves:
  373. raise ValueError("Unsupported curve (%s)", curve_name)
  374. self._curve = _curves[curve_name]
  375. if self._d is None:
  376. if self._point is None:
  377. raise ValueError("Either private or public ECC component must be specified, not both")
  378. else:
  379. self._d = Integer(self._d)
  380. if not 1 <= self._d < self._curve.order:
  381. raise ValueError("Invalid ECC private component")
  382. self.curve = self._curve.desc
  383. def __eq__(self, other):
  384. if other.has_private() != self.has_private():
  385. return False
  386. return other.pointQ == self.pointQ
  387. def __repr__(self):
  388. if self.has_private():
  389. extra = ", d=%d" % int(self._d)
  390. else:
  391. extra = ""
  392. x, y = self.pointQ.xy
  393. return "EccKey(curve='%s', point_x=%d, point_y=%d%s)" % (self._curve.desc, x, y, extra)
  394. def has_private(self):
  395. """``True`` if this key can be used for making signatures or decrypting data."""
  396. return self._d is not None
  397. def _sign(self, z, k):
  398. assert 0 < k < self._curve.order
  399. order = self._curve.order
  400. blind = Integer.random_range(min_inclusive=1,
  401. max_exclusive=order)
  402. blind_d = self._d * blind
  403. inv_blind_k = (blind * k).inverse(order)
  404. r = (self._curve.G * k).x % order
  405. s = inv_blind_k * (blind * z + blind_d * r) % order
  406. return (r, s)
  407. def _verify(self, z, rs):
  408. order = self._curve.order
  409. sinv = rs[1].inverse(order)
  410. point1 = self._curve.G * ((sinv * z) % order)
  411. point2 = self.pointQ * ((sinv * rs[0]) % order)
  412. return (point1 + point2).x == rs[0]
  413. @property
  414. def d(self):
  415. if not self.has_private():
  416. raise ValueError("This is not a private ECC key")
  417. return self._d
  418. @property
  419. def pointQ(self):
  420. if self._point is None:
  421. self._point = self._curve.G * self._d
  422. return self._point
  423. def public_key(self):
  424. """A matching ECC public key.
  425. Returns:
  426. a new :class:`EccKey` object
  427. """
  428. return EccKey(curve=self._curve.desc, point=self.pointQ)
  429. def _export_subjectPublicKeyInfo(self, compress):
  430. # See 2.2 in RFC5480 and 2.3.3 in SEC1
  431. # The first byte is:
  432. # - 0x02: compressed, only X-coordinate, Y-coordinate is even
  433. # - 0x03: compressed, only X-coordinate, Y-coordinate is odd
  434. # - 0x04: uncompressed, X-coordinate is followed by Y-coordinate
  435. #
  436. # PAI is in theory encoded as 0x00.
  437. modulus_bytes = self.pointQ.size_in_bytes()
  438. if compress:
  439. first_byte = 2 + self.pointQ.y.is_odd()
  440. public_key = (bchr(first_byte) +
  441. self.pointQ.x.to_bytes(modulus_bytes))
  442. else:
  443. public_key = (b'\x04' +
  444. self.pointQ.x.to_bytes(modulus_bytes) +
  445. self.pointQ.y.to_bytes(modulus_bytes))
  446. unrestricted_oid = "1.2.840.10045.2.1"
  447. return _create_subject_public_key_info(unrestricted_oid,
  448. public_key,
  449. DerObjectId(self._curve.oid))
  450. def _export_private_der(self, include_ec_params=True):
  451. assert self.has_private()
  452. # ECPrivateKey ::= SEQUENCE {
  453. # version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
  454. # privateKey OCTET STRING,
  455. # parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
  456. # publicKey [1] BIT STRING OPTIONAL
  457. # }
  458. # Public key - uncompressed form
  459. modulus_bytes = self.pointQ.size_in_bytes()
  460. public_key = (b'\x04' +
  461. self.pointQ.x.to_bytes(modulus_bytes) +
  462. self.pointQ.y.to_bytes(modulus_bytes))
  463. seq = [1,
  464. DerOctetString(self.d.to_bytes(modulus_bytes)),
  465. DerObjectId(self._curve.oid, explicit=0),
  466. DerBitString(public_key, explicit=1)]
  467. if not include_ec_params:
  468. del seq[2]
  469. return DerSequence(seq).encode()
  470. def _export_pkcs8(self, **kwargs):
  471. from tls.Crypto.IO import PKCS8
  472. if kwargs.get('passphrase', None) is not None and 'protection' not in kwargs:
  473. raise ValueError("At least the 'protection' parameter should be present")
  474. unrestricted_oid = "1.2.840.10045.2.1"
  475. private_key = self._export_private_der(include_ec_params=False)
  476. result = PKCS8.wrap(private_key,
  477. unrestricted_oid,
  478. key_params=DerObjectId(self._curve.oid),
  479. **kwargs)
  480. return result
  481. def _export_public_pem(self, compress):
  482. from tls.Crypto.IO import PEM
  483. encoded_der = self._export_subjectPublicKeyInfo(compress)
  484. return PEM.encode(encoded_der, "PUBLIC KEY")
  485. def _export_private_pem(self, passphrase, **kwargs):
  486. from tls.Crypto.IO import PEM
  487. encoded_der = self._export_private_der()
  488. return PEM.encode(encoded_der, "EC PRIVATE KEY", passphrase, **kwargs)
  489. def _export_private_clear_pkcs8_in_clear_pem(self):
  490. from tls.Crypto.IO import PEM
  491. encoded_der = self._export_pkcs8()
  492. return PEM.encode(encoded_der, "PRIVATE KEY")
  493. def _export_private_encrypted_pkcs8_in_clear_pem(self, passphrase, **kwargs):
  494. from tls.Crypto.IO import PEM
  495. assert passphrase
  496. if 'protection' not in kwargs:
  497. raise ValueError("At least the 'protection' parameter should be present")
  498. encoded_der = self._export_pkcs8(passphrase=passphrase, **kwargs)
  499. return PEM.encode(encoded_der, "ENCRYPTED PRIVATE KEY")
  500. def _export_openssh(self, compress):
  501. if self.has_private():
  502. raise ValueError("Cannot export OpenSSH private keys")
  503. desc = self._curve.openssh
  504. modulus_bytes = self.pointQ.size_in_bytes()
  505. if compress:
  506. first_byte = 2 + self.pointQ.y.is_odd()
  507. public_key = (bchr(first_byte) +
  508. self.pointQ.x.to_bytes(modulus_bytes))
  509. else:
  510. public_key = (b'\x04' +
  511. self.pointQ.x.to_bytes(modulus_bytes) +
  512. self.pointQ.y.to_bytes(modulus_bytes))
  513. middle = desc.split("-")[2]
  514. comps = (tobytes(desc), tobytes(middle), public_key)
  515. blob = b"".join([struct.pack(">I", len(x)) + x for x in comps])
  516. return desc + " " + tostr(binascii.b2a_base64(blob))
  517. def export_key(self, **kwargs):
  518. """Export this ECC key.
  519. Args:
  520. format (string):
  521. The format to use for encoding the key:
  522. - ``'DER'``. The key will be encoded in ASN.1 DER format (binary).
  523. For a public key, the ASN.1 ``subjectPublicKeyInfo`` structure
  524. defined in `RFC5480`_ will be used.
  525. For a private key, the ASN.1 ``ECPrivateKey`` structure defined
  526. in `RFC5915`_ is used instead (possibly within a PKCS#8 envelope,
  527. see the ``use_pkcs8`` flag below).
  528. - ``'PEM'``. The key will be encoded in a PEM_ envelope (ASCII).
  529. - ``'OpenSSH'``. The key will be encoded in the OpenSSH_ format
  530. (ASCII, public keys only).
  531. passphrase (byte string or string):
  532. The passphrase to use for protecting the private key.
  533. use_pkcs8 (boolean):
  534. Only relevant for private keys.
  535. If ``True`` (default and recommended), the `PKCS#8`_ representation
  536. will be used.
  537. If ``False``, the much weaker `PEM encryption`_ mechanism will be used.
  538. protection (string):
  539. When a private key is exported with password-protection
  540. and PKCS#8 (both ``DER`` and ``PEM`` formats), this parameter MUST be
  541. present and be a valid algorithm supported by :mod:`Crypto.IO.PKCS8`.
  542. It is recommended to use ``PBKDF2WithHMAC-SHA1AndAES128-CBC``.
  543. compress (boolean):
  544. If ``True``, a more compact representation of the public key
  545. with the X-coordinate only is used.
  546. If ``False`` (default), the full public key will be exported.
  547. .. warning::
  548. If you don't provide a passphrase, the private key will be
  549. exported in the clear!
  550. .. note::
  551. When exporting a private key with password-protection and `PKCS#8`_
  552. (both ``DER`` and ``PEM`` formats), any extra parameters
  553. to ``export_key()`` will be passed to :mod:`Crypto.IO.PKCS8`.
  554. .. _PEM: http://www.ietf.org/rfc/rfc1421.txt
  555. .. _`PEM encryption`: http://www.ietf.org/rfc/rfc1423.txt
  556. .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt
  557. .. _OpenSSH: http://www.openssh.com/txt/rfc5656.txt
  558. .. _RFC5480: https://tools.ietf.org/html/rfc5480
  559. .. _RFC5915: http://www.ietf.org/rfc/rfc5915.txt
  560. Returns:
  561. A multi-line string (for PEM and OpenSSH) or bytes (for DER) with the encoded key.
  562. """
  563. args = kwargs.copy()
  564. ext_format = args.pop("format")
  565. if ext_format not in ("PEM", "DER", "OpenSSH"):
  566. raise ValueError("Unknown format '%s'" % ext_format)
  567. compress = args.pop("compress", False)
  568. if self.has_private():
  569. passphrase = args.pop("passphrase", None)
  570. if is_string(passphrase):
  571. passphrase = tobytes(passphrase)
  572. if not passphrase:
  573. raise ValueError("Empty passphrase")
  574. use_pkcs8 = args.pop("use_pkcs8", True)
  575. if ext_format == "PEM":
  576. if use_pkcs8:
  577. if passphrase:
  578. return self._export_private_encrypted_pkcs8_in_clear_pem(passphrase, **args)
  579. else:
  580. return self._export_private_clear_pkcs8_in_clear_pem()
  581. else:
  582. return self._export_private_pem(passphrase, **args)
  583. elif ext_format == "DER":
  584. # DER
  585. if passphrase and not use_pkcs8:
  586. raise ValueError("Private keys can only be encrpyted with DER using PKCS#8")
  587. if use_pkcs8:
  588. return self._export_pkcs8(passphrase=passphrase, **args)
  589. else:
  590. return self._export_private_der()
  591. else:
  592. raise ValueError("Private keys cannot be exported in OpenSSH format")
  593. else: # Public key
  594. if args:
  595. raise ValueError("Unexpected parameters: '%s'" % args)
  596. if ext_format == "PEM":
  597. return self._export_public_pem(compress)
  598. elif ext_format == "DER":
  599. return self._export_subjectPublicKeyInfo(compress)
  600. else:
  601. return self._export_openssh(compress)
  602. def generate(**kwargs):
  603. """Generate a new private key on the given curve.
  604. Args:
  605. curve (string):
  606. Mandatory. It must be a curve name defined in :numref:`curve_names`.
  607. randfunc (callable):
  608. Optional. The RNG to read randomness from.
  609. If ``None``, :func:`Crypto.Random.get_random_bytes` is used.
  610. """
  611. curve_name = kwargs.pop("curve")
  612. curve = _curves[curve_name]
  613. randfunc = kwargs.pop("randfunc", get_random_bytes)
  614. if kwargs:
  615. raise TypeError("Unknown parameters: " + str(kwargs))
  616. d = Integer.random_range(min_inclusive=1,
  617. max_exclusive=curve.order,
  618. randfunc=randfunc)
  619. return EccKey(curve=curve_name, d=d)
  620. def construct(**kwargs):
  621. """Build a new ECC key (private or public) starting
  622. from some base components.
  623. Args:
  624. curve (string):
  625. Mandatory. It must be a curve name defined in :numref:`curve_names`.
  626. d (integer):
  627. Only for a private key. It must be in the range ``[1..order-1]``.
  628. point_x (integer):
  629. Mandatory for a public key. X coordinate (affine) of the ECC point.
  630. point_y (integer):
  631. Mandatory for a public key. Y coordinate (affine) of the ECC point.
  632. Returns:
  633. :class:`EccKey` : a new ECC key object
  634. """
  635. curve_name = kwargs["curve"]
  636. curve = _curves[curve_name]
  637. point_x = kwargs.pop("point_x", None)
  638. point_y = kwargs.pop("point_y", None)
  639. if "point" in kwargs:
  640. raise TypeError("Unknown keyword: point")
  641. if None not in (point_x, point_y):
  642. # ValueError is raised if the point is not on the curve
  643. kwargs["point"] = EccPoint(point_x, point_y, curve_name)
  644. # Validate that the private key matches the public one
  645. d = kwargs.get("d", None)
  646. if d is not None and "point" in kwargs:
  647. pub_key = curve.G * d
  648. if pub_key.xy != (point_x, point_y):
  649. raise ValueError("Private and public ECC keys do not match")
  650. return EccKey(**kwargs)
  651. def _import_public_der(curve_oid, ec_point):
  652. """Convert an encoded EC point into an EccKey object
  653. curve_name: string with the OID of the curve
  654. ec_point: byte string with the EC point (not DER encoded)
  655. """
  656. for curve_name, curve in _curves.items():
  657. if curve.oid == curve_oid:
  658. break
  659. else:
  660. raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid)
  661. # See 2.2 in RFC5480 and 2.3.3 in SEC1
  662. # The first byte is:
  663. # - 0x02: compressed, only X-coordinate, Y-coordinate is even
  664. # - 0x03: compressed, only X-coordinate, Y-coordinate is odd
  665. # - 0x04: uncompressed, X-coordinate is followed by Y-coordinate
  666. #
  667. # PAI is in theory encoded as 0x00.
  668. modulus_bytes = curve.p.size_in_bytes()
  669. point_type = bord(ec_point[0])
  670. # Uncompressed point
  671. if point_type == 0x04:
  672. if len(ec_point) != (1 + 2 * modulus_bytes):
  673. raise ValueError("Incorrect EC point length")
  674. x = Integer.from_bytes(ec_point[1:modulus_bytes+1])
  675. y = Integer.from_bytes(ec_point[modulus_bytes+1:])
  676. # Compressed point
  677. elif point_type in (0x02, 0x3):
  678. if len(ec_point) != (1 + modulus_bytes):
  679. raise ValueError("Incorrect EC point length")
  680. x = Integer.from_bytes(ec_point[1:])
  681. y = (x**3 - x*3 + curve.b).sqrt(curve.p) # Short Weierstrass
  682. if point_type == 0x02 and y.is_odd():
  683. y = curve.p - y
  684. if point_type == 0x03 and y.is_even():
  685. y = curve.p - y
  686. else:
  687. raise ValueError("Incorrect EC point encoding")
  688. return construct(curve=curve_name, point_x=x, point_y=y)
  689. def _import_subjectPublicKeyInfo(encoded, *kwargs):
  690. """Convert a subjectPublicKeyInfo into an EccKey object"""
  691. # See RFC5480
  692. # Parse the generic subjectPublicKeyInfo structure
  693. oid, ec_point, params = _expand_subject_public_key_info(encoded)
  694. # ec_point must be an encoded OCTET STRING
  695. # params is encoded ECParameters
  696. # We accept id-ecPublicKey, id-ecDH, id-ecMQV without making any
  697. # distiction for now.
  698. # Restrictions can be captured in the key usage certificate
  699. # extension
  700. unrestricted_oid = "1.2.840.10045.2.1"
  701. ecdh_oid = "1.3.132.1.12"
  702. ecmqv_oid = "1.3.132.1.13"
  703. if oid not in (unrestricted_oid, ecdh_oid, ecmqv_oid):
  704. raise UnsupportedEccFeature("Unsupported ECC purpose (OID: %s)" % oid)
  705. # Parameters are mandatory for all three types
  706. if not params:
  707. raise ValueError("Missing ECC parameters")
  708. # ECParameters ::= CHOICE {
  709. # namedCurve OBJECT IDENTIFIER
  710. # -- implicitCurve NULL
  711. # -- specifiedCurve SpecifiedECDomain
  712. # }
  713. #
  714. # implicitCurve and specifiedCurve are not supported (as per RFC)
  715. curve_oid = DerObjectId().decode(params).value
  716. return _import_public_der(curve_oid, ec_point)
  717. def _import_private_der(encoded, passphrase, curve_oid=None):
  718. # See RFC5915 https://tools.ietf.org/html/rfc5915
  719. #
  720. # ECPrivateKey ::= SEQUENCE {
  721. # version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
  722. # privateKey OCTET STRING,
  723. # parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
  724. # publicKey [1] BIT STRING OPTIONAL
  725. # }
  726. private_key = DerSequence().decode(encoded, nr_elements=(3, 4))
  727. if private_key[0] != 1:
  728. raise ValueError("Incorrect ECC private key version")
  729. try:
  730. parameters = DerObjectId(explicit=0).decode(private_key[2]).value
  731. if curve_oid is not None and parameters != curve_oid:
  732. raise ValueError("Curve mismatch")
  733. curve_oid = parameters
  734. except ValueError:
  735. pass
  736. if curve_oid is None:
  737. raise ValueError("No curve found")
  738. for curve_name, curve in _curves.items():
  739. if curve.oid == curve_oid:
  740. break
  741. else:
  742. raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid)
  743. scalar_bytes = DerOctetString().decode(private_key[1]).payload
  744. modulus_bytes = curve.p.size_in_bytes()
  745. if len(scalar_bytes) != modulus_bytes:
  746. raise ValueError("Private key is too small")
  747. d = Integer.from_bytes(scalar_bytes)
  748. # Decode public key (if any)
  749. if len(private_key) == 4:
  750. public_key_enc = DerBitString(explicit=1).decode(private_key[3]).value
  751. public_key = _import_public_der(curve_oid, public_key_enc)
  752. point_x = public_key.pointQ.x
  753. point_y = public_key.pointQ.y
  754. else:
  755. point_x = point_y = None
  756. return construct(curve=curve_name, d=d, point_x=point_x, point_y=point_y)
  757. def _import_pkcs8(encoded, passphrase):
  758. from tls.Crypto.IO import PKCS8
  759. # From RFC5915, Section 1:
  760. #
  761. # Distributing an EC private key with PKCS#8 [RFC5208] involves including:
  762. # a) id-ecPublicKey, id-ecDH, or id-ecMQV (from [RFC5480]) with the
  763. # namedCurve as the parameters in the privateKeyAlgorithm field; and
  764. # b) ECPrivateKey in the PrivateKey field, which is an OCTET STRING.
  765. algo_oid, private_key, params = PKCS8.unwrap(encoded, passphrase)
  766. # We accept id-ecPublicKey, id-ecDH, id-ecMQV without making any
  767. # distiction for now.
  768. unrestricted_oid = "1.2.840.10045.2.1"
  769. ecdh_oid = "1.3.132.1.12"
  770. ecmqv_oid = "1.3.132.1.13"
  771. if algo_oid not in (unrestricted_oid, ecdh_oid, ecmqv_oid):
  772. raise UnsupportedEccFeature("Unsupported ECC purpose (OID: %s)" % algo_oid)
  773. curve_oid = DerObjectId().decode(params).value
  774. return _import_private_der(private_key, passphrase, curve_oid)
  775. def _import_x509_cert(encoded, *kwargs):
  776. sp_info = _extract_subject_public_key_info(encoded)
  777. return _import_subjectPublicKeyInfo(sp_info)
  778. def _import_der(encoded, passphrase):
  779. try:
  780. return _import_subjectPublicKeyInfo(encoded, passphrase)
  781. except UnsupportedEccFeature as err:
  782. raise err
  783. except (ValueError, TypeError, IndexError):
  784. pass
  785. try:
  786. return _import_x509_cert(encoded, passphrase)
  787. except UnsupportedEccFeature as err:
  788. raise err
  789. except (ValueError, TypeError, IndexError):
  790. pass
  791. try:
  792. return _import_private_der(encoded, passphrase)
  793. except UnsupportedEccFeature as err:
  794. raise err
  795. except (ValueError, TypeError, IndexError):
  796. pass
  797. try:
  798. return _import_pkcs8(encoded, passphrase)
  799. except UnsupportedEccFeature as err:
  800. raise err
  801. except (ValueError, TypeError, IndexError):
  802. pass
  803. raise ValueError("Not an ECC DER key")
  804. def _import_openssh_public(encoded):
  805. keystring = binascii.a2b_base64(encoded.split(b' ')[1])
  806. keyparts = []
  807. while len(keystring) > 4:
  808. lk = struct.unpack(">I", keystring[:4])[0]
  809. keyparts.append(keystring[4:4 + lk])
  810. keystring = keystring[4 + lk:]
  811. for curve_name, curve in _curves.items():
  812. middle = tobytes(curve.openssh.split("-")[2])
  813. if keyparts[1] == middle:
  814. break
  815. else:
  816. raise ValueError("Unsupported ECC curve")
  817. return _import_public_der(curve.oid, keyparts[2])
  818. def _import_openssh_private_ecc(data, password):
  819. from ._openssh import (import_openssh_private_generic,
  820. read_bytes, read_string, check_padding)
  821. ssh_name, decrypted = import_openssh_private_generic(data, password)
  822. name, decrypted = read_string(decrypted)
  823. if name not in _curves:
  824. raise UnsupportedEccFeature("Unsupported ECC curve %s" % name)
  825. curve = _curves[name]
  826. modulus_bytes = (curve.modulus_bits + 7) // 8
  827. public_key, decrypted = read_bytes(decrypted)
  828. if bord(public_key[0]) != 4:
  829. raise ValueError("Only uncompressed OpenSSH EC keys are supported")
  830. if len(public_key) != 2 * modulus_bytes + 1:
  831. raise ValueError("Incorrect public key length")
  832. point_x = Integer.from_bytes(public_key[1:1+modulus_bytes])
  833. point_y = Integer.from_bytes(public_key[1+modulus_bytes:])
  834. point = EccPoint(point_x, point_y, curve=name)
  835. private_key, decrypted = read_bytes(decrypted)
  836. d = Integer.from_bytes(private_key)
  837. _, padded = read_string(decrypted) # Comment
  838. check_padding(padded)
  839. return EccKey(curve=name, d=d, point=point)
  840. def import_key(encoded, passphrase=None):
  841. """Import an ECC key (public or private).
  842. Args:
  843. encoded (bytes or multi-line string):
  844. The ECC key to import.
  845. An ECC **public** key can be:
  846. - An X.509 certificate, binary (DER) or ASCII (PEM)
  847. - An X.509 ``subjectPublicKeyInfo``, binary (DER) or ASCII (PEM)
  848. - An OpenSSH line (e.g. the content of ``~/.ssh/id_ecdsa``, ASCII)
  849. An ECC **private** key can be:
  850. - In binary format (DER, see section 3 of `RFC5915`_ or `PKCS#8`_)
  851. - In ASCII format (PEM or `OpenSSH 6.5+`_)
  852. Private keys can be in the clear or password-protected.
  853. For details about the PEM encoding, see `RFC1421`_/`RFC1423`_.
  854. passphrase (byte string):
  855. The passphrase to use for decrypting a private key.
  856. Encryption may be applied protected at the PEM level or at the PKCS#8 level.
  857. This parameter is ignored if the key in input is not encrypted.
  858. Returns:
  859. :class:`EccKey` : a new ECC key object
  860. Raises:
  861. ValueError: when the given key cannot be parsed (possibly because
  862. the pass phrase is wrong).
  863. .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt
  864. .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt
  865. .. _RFC5915: http://www.ietf.org/rfc/rfc5915.txt
  866. .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt
  867. .. _`OpenSSH 6.5+`: https://flak.tedunangst.com/post/new-openssh-key-format-and-bcrypt-pbkdf
  868. """
  869. from tls.Crypto.IO import PEM
  870. encoded = tobytes(encoded)
  871. if passphrase is not None:
  872. passphrase = tobytes(passphrase)
  873. # PEM
  874. if encoded.startswith(b'-----BEGIN OPENSSH PRIVATE KEY'):
  875. text_encoded = tostr(encoded)
  876. openssh_encoded, marker, enc_flag = PEM.decode(text_encoded, passphrase)
  877. result = _import_openssh_private_ecc(openssh_encoded, passphrase)
  878. return result
  879. elif encoded.startswith(b'-----'):
  880. text_encoded = tostr(encoded)
  881. # Remove any EC PARAMETERS section
  882. # Ignore its content because the curve type must be already given in the key
  883. if sys.version_info[:2] != (2, 6):
  884. ecparams_start = "-----BEGIN EC PARAMETERS-----"
  885. ecparams_end = "-----END EC PARAMETERS-----"
  886. text_encoded = re.sub(ecparams_start + ".*?" + ecparams_end, "",
  887. text_encoded,
  888. flags=re.DOTALL)
  889. der_encoded, marker, enc_flag = PEM.decode(text_encoded, passphrase)
  890. if enc_flag:
  891. passphrase = None
  892. try:
  893. result = _import_der(der_encoded, passphrase)
  894. except UnsupportedEccFeature as uef:
  895. raise uef
  896. except ValueError:
  897. raise ValueError("Invalid DER encoding inside the PEM file")
  898. return result
  899. # OpenSSH
  900. if encoded.startswith(b'ecdsa-sha2-'):
  901. return _import_openssh_public(encoded)
  902. # DER
  903. if len(encoded) > 0 and bord(encoded[0]) == 0x30:
  904. return _import_der(encoded, passphrase)
  905. raise ValueError("ECC key format is not supported")
  906. if __name__ == "__main__":
  907. import time
  908. d = 0xc51e4753afdec1e6b6c6a5b992f43f8dd0c7a8933072708b6522468b2ffb06fd
  909. point = _curves['p256'].G.copy()
  910. count = 3000
  911. start = time.time()
  912. for x in range(count):
  913. pointX = point * d
  914. print("(P-256 G)", (time.time() - start) / count * 1000, "ms")
  915. start = time.time()
  916. for x in range(count):
  917. pointX = pointX * d
  918. print("(P-256 arbitrary point)", (time.time() - start) / count * 1000, "ms")