ECC.py 41 KB

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