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.

293 lines
8.4 KiB

4 years ago
  1. #! /usr/bin/env python
  2. #
  3. # Implementation of elliptic curves, for cryptographic applications.
  4. #
  5. # This module doesn't provide any way to choose a random elliptic
  6. # curve, nor to verify that an elliptic curve was chosen randomly,
  7. # because one can simply use NIST's standard curves.
  8. #
  9. # Notes from X9.62-1998 (draft):
  10. # Nomenclature:
  11. # - Q is a public key.
  12. # The "Elliptic Curve Domain Parameters" include:
  13. # - q is the "field size", which in our case equals p.
  14. # - p is a big prime.
  15. # - G is a point of prime order (5.1.1.1).
  16. # - n is the order of G (5.1.1.1).
  17. # Public-key validation (5.2.2):
  18. # - Verify that Q is not the point at infinity.
  19. # - Verify that X_Q and Y_Q are in [0,p-1].
  20. # - Verify that Q is on the curve.
  21. # - Verify that nQ is the point at infinity.
  22. # Signature generation (5.3):
  23. # - Pick random k from [1,n-1].
  24. # Signature checking (5.4.2):
  25. # - Verify that r and s are in [1,n-1].
  26. #
  27. # Version of 2008.11.25.
  28. #
  29. # Revision history:
  30. # 2005.12.31 - Initial version.
  31. # 2008.11.25 - Change CurveFp.is_on to contains_point.
  32. #
  33. # Written in 2005 by Peter Pearson and placed in the public domain.
  34. from __future__ import division
  35. from .six import print_
  36. from . import numbertheory
  37. class CurveFp( object ):
  38. """Elliptic Curve over the field of integers modulo a prime."""
  39. def __init__( self, p, a, b ):
  40. """The curve of points satisfying y^2 = x^3 + a*x + b (mod p)."""
  41. self.__p = p
  42. self.__a = a
  43. self.__b = b
  44. def p( self ):
  45. return self.__p
  46. def a( self ):
  47. return self.__a
  48. def b( self ):
  49. return self.__b
  50. def contains_point( self, x, y ):
  51. """Is the point (x,y) on this curve?"""
  52. return ( y * y - ( x * x * x + self.__a * x + self.__b ) ) % self.__p == 0
  53. class Point( object ):
  54. """A point on an elliptic curve. Altering x and y is forbidding,
  55. but they can be read by the x() and y() methods."""
  56. def __init__( self, curve, x, y, order = None ):
  57. """curve, x, y, order; order (optional) is the order of this point."""
  58. self.__curve = curve
  59. self.__x = x
  60. self.__y = y
  61. self.__order = order
  62. # self.curve is allowed to be None only for INFINITY:
  63. if self.__curve: assert self.__curve.contains_point( x, y )
  64. if order: assert self * order == INFINITY
  65. def __eq__( self, other ):
  66. """Return True if the points are identical, False otherwise."""
  67. if self.__curve == other.__curve \
  68. and self.__x == other.__x \
  69. and self.__y == other.__y:
  70. return True
  71. else:
  72. return False
  73. def __add__( self, other ):
  74. """Add one point to another point."""
  75. # X9.62 B.3:
  76. if other == INFINITY: return self
  77. if self == INFINITY: return other
  78. assert self.__curve == other.__curve
  79. if self.__x == other.__x:
  80. if ( self.__y + other.__y ) % self.__curve.p() == 0:
  81. return INFINITY
  82. else:
  83. return self.double()
  84. p = self.__curve.p()
  85. l = ( ( other.__y - self.__y ) * \
  86. numbertheory.inverse_mod( other.__x - self.__x, p ) ) % p
  87. x3 = ( l * l - self.__x - other.__x ) % p
  88. y3 = ( l * ( self.__x - x3 ) - self.__y ) % p
  89. return Point( self.__curve, x3, y3 )
  90. def __mul__( self, other ):
  91. """Multiply a point by an integer."""
  92. def leftmost_bit( x ):
  93. assert x > 0
  94. result = 1
  95. while result <= x: result = 2 * result
  96. return result // 2
  97. e = other
  98. if self.__order: e = e % self.__order
  99. if e == 0: return INFINITY
  100. if self == INFINITY: return INFINITY
  101. assert e > 0
  102. # From X9.62 D.3.2:
  103. e3 = 3 * e
  104. negative_self = Point( self.__curve, self.__x, -self.__y, self.__order )
  105. i = leftmost_bit( e3 ) // 2
  106. result = self
  107. # print_("Multiplying %s by %d (e3 = %d):" % ( self, other, e3 ))
  108. while i > 1:
  109. result = result.double()
  110. if ( e3 & i ) != 0 and ( e & i ) == 0: result = result + self
  111. if ( e3 & i ) == 0 and ( e & i ) != 0: result = result + negative_self
  112. # print_(". . . i = %d, result = %s" % ( i, result ))
  113. i = i // 2
  114. return result
  115. def __rmul__( self, other ):
  116. """Multiply a point by an integer."""
  117. return self * other
  118. def __str__( self ):
  119. if self == INFINITY: return "infinity"
  120. return "(%d,%d)" % ( self.__x, self.__y )
  121. def double( self ):
  122. """Return a new point that is twice the old."""
  123. if self == INFINITY:
  124. return INFINITY
  125. # X9.62 B.3:
  126. p = self.__curve.p()
  127. a = self.__curve.a()
  128. l = ( ( 3 * self.__x * self.__x + a ) * \
  129. numbertheory.inverse_mod( 2 * self.__y, p ) ) % p
  130. x3 = ( l * l - 2 * self.__x ) % p
  131. y3 = ( l * ( self.__x - x3 ) - self.__y ) % p
  132. return Point( self.__curve, x3, y3 )
  133. def x( self ):
  134. return self.__x
  135. def y( self ):
  136. return self.__y
  137. def curve( self ):
  138. return self.__curve
  139. def order( self ):
  140. return self.__order
  141. # This one point is the Point At Infinity for all purposes:
  142. INFINITY = Point( None, None, None )
  143. def __main__():
  144. class FailedTest(Exception): pass
  145. def test_add( c, x1, y1, x2, y2, x3, y3 ):
  146. """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3)."""
  147. p1 = Point( c, x1, y1 )
  148. p2 = Point( c, x2, y2 )
  149. p3 = p1 + p2
  150. print_("%s + %s = %s" % ( p1, p2, p3 ), end=' ')
  151. if p3.x() != x3 or p3.y() != y3:
  152. raise FailedTest("Failure: should give (%d,%d)." % ( x3, y3 ))
  153. else:
  154. print_(" Good.")
  155. def test_double( c, x1, y1, x3, y3 ):
  156. """We expect that on curve c, 2*(x1,y1) = (x3, y3)."""
  157. p1 = Point( c, x1, y1 )
  158. p3 = p1.double()
  159. print_("%s doubled = %s" % ( p1, p3 ), end=' ')
  160. if p3.x() != x3 or p3.y() != y3:
  161. raise FailedTest("Failure: should give (%d,%d)." % ( x3, y3 ))
  162. else:
  163. print_(" Good.")
  164. def test_double_infinity( c ):
  165. """We expect that on curve c, 2*INFINITY = INFINITY."""
  166. p1 = INFINITY
  167. p3 = p1.double()
  168. print_("%s doubled = %s" % ( p1, p3 ), end=' ')
  169. if p3.x() != INFINITY.x() or p3.y() != INFINITY.y():
  170. raise FailedTest("Failure: should give (%d,%d)." % ( INFINITY.x(), INFINITY.y() ))
  171. else:
  172. print_(" Good.")
  173. def test_multiply( c, x1, y1, m, x3, y3 ):
  174. """We expect that on curve c, m*(x1,y1) = (x3,y3)."""
  175. p1 = Point( c, x1, y1 )
  176. p3 = p1 * m
  177. print_("%s * %d = %s" % ( p1, m, p3 ), end=' ')
  178. if p3.x() != x3 or p3.y() != y3:
  179. raise FailedTest("Failure: should give (%d,%d)." % ( x3, y3 ))
  180. else:
  181. print_(" Good.")
  182. # A few tests from X9.62 B.3:
  183. c = CurveFp( 23, 1, 1 )
  184. test_add( c, 3, 10, 9, 7, 17, 20 )
  185. test_double( c, 3, 10, 7, 12 )
  186. test_add( c, 3, 10, 3, 10, 7, 12 ) # (Should just invoke double.)
  187. test_multiply( c, 3, 10, 2, 7, 12 )
  188. test_double_infinity(c)
  189. # From X9.62 I.1 (p. 96):
  190. g = Point( c, 13, 7, 7 )
  191. check = INFINITY
  192. for i in range( 7 + 1 ):
  193. p = ( i % 7 ) * g
  194. print_("%s * %d = %s, expected %s . . ." % ( g, i, p, check ), end=' ')
  195. if p == check:
  196. print_(" Good.")
  197. else:
  198. raise FailedTest("Bad.")
  199. check = check + g
  200. # NIST Curve P-192:
  201. p = 6277101735386680763835789423207666416083908700390324961279
  202. r = 6277101735386680763835789423176059013767194773182842284081
  203. #s = 0x3045ae6fc8422f64ed579528d38120eae12196d5L
  204. c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65
  205. b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1
  206. Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012
  207. Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811
  208. c192 = CurveFp( p, -3, b )
  209. p192 = Point( c192, Gx, Gy, r )
  210. # Checking against some sample computations presented
  211. # in X9.62:
  212. d = 651056770906015076056810763456358567190100156695615665659
  213. Q = d * p192
  214. if Q.x() != 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5:
  215. raise FailedTest("p192 * d came out wrong.")
  216. else:
  217. print_("p192 * d came out right.")
  218. k = 6140507067065001063065065565667405560006161556565665656654
  219. R = k * p192
  220. if R.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD \
  221. or R.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835:
  222. raise FailedTest("k * p192 came out wrong.")
  223. else:
  224. print_("k * p192 came out right.")
  225. u1 = 2563697409189434185194736134579731015366492496392189760599
  226. u2 = 6266643813348617967186477710235785849136406323338782220568
  227. temp = u1 * p192 + u2 * Q
  228. if temp.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD \
  229. or temp.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835:
  230. raise FailedTest("u1 * p192 + u2 * Q came out wrong.")
  231. else:
  232. print_("u1 * p192 + u2 * Q came out right.")
  233. if __name__ == "__main__":
  234. __main__()