PointMathGMP.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. <?php
  2. /*
  3. * Object orieted interface to Helpful Point Math Operations
  4. * Using the GMP library
  5. *
  6. * @author Daniel Morante
  7. * Some parts may contain work based on Jan Moritz Lindemann, Matyas Danter, and Joey Hewitt
  8. */
  9. class PointMathGMP {
  10. /***
  11. * Computes the result of a point addition and returns the resulting point as an Array.
  12. *
  13. * @param Array $pt
  14. * @return Array Point
  15. * @throws \Exception
  16. */
  17. public static function doublePoint(Array $pt, $a, $p)
  18. {
  19. $gcd = gmp_strval(gmp_gcd(gmp_mod(gmp_mul(gmp_init(2, 10), $pt['y']), $p),$p));
  20. if($gcd != '1')
  21. {
  22. throw new \Exception('This library doesn\'t yet supports point at infinity. See https://github.com/BitcoinPHP/BitcoinECDSA.php/issues/9');
  23. }
  24. // SLOPE = (3 * ptX^2 + a )/( 2*ptY )
  25. // Equals (3 * ptX^2 + a ) * ( 2*ptY )^-1
  26. $slope = gmp_mod(
  27. gmp_mul(
  28. gmp_invert(
  29. gmp_mod(
  30. gmp_mul(
  31. gmp_init(2, 10),
  32. $pt['y']
  33. ),
  34. $p
  35. ),
  36. $p
  37. ),
  38. gmp_add(
  39. gmp_mul(
  40. gmp_init(3, 10),
  41. gmp_pow($pt['x'], 2)
  42. ),
  43. $a
  44. )
  45. ),
  46. $p
  47. );
  48. // nPtX = slope^2 - 2 * ptX
  49. // Equals slope^2 - ptX - ptX
  50. $nPt = array();
  51. $nPt['x'] = gmp_mod(
  52. gmp_sub(
  53. gmp_sub(
  54. gmp_pow($slope, 2),
  55. $pt['x']
  56. ),
  57. $pt['x']
  58. ),
  59. $p
  60. );
  61. // nPtY = slope * (ptX - nPtx) - ptY
  62. $nPt['y'] = gmp_mod(
  63. gmp_sub(
  64. gmp_mul(
  65. $slope,
  66. gmp_sub(
  67. $pt['x'],
  68. $nPt['x']
  69. )
  70. ),
  71. $pt['y']
  72. ),
  73. $p
  74. );
  75. return $nPt;
  76. }
  77. /***
  78. * Computes the result of a point addition and returns the resulting point as an Array.
  79. *
  80. * @param Array $pt1
  81. * @param Array $pt2
  82. * @return Array Point
  83. * @throws \Exception
  84. */
  85. public static function addPoints(Array $pt1, Array $pt2, $a, $p)
  86. {
  87. if(gmp_cmp($pt1['x'], $pt2['x']) == 0 && gmp_cmp($pt1['y'], $pt2['y']) == 0) //if identical
  88. {
  89. return self::doublePoint($pt1, $a, $p);
  90. }
  91. $gcd = gmp_strval(gmp_gcd(gmp_sub($pt1['x'], $pt2['x']), $p));
  92. if($gcd != '1')
  93. {
  94. throw new \Exception('This library doesn\'t yet supports point at infinity. See https://github.com/BitcoinPHP/BitcoinECDSA.php/issues/9');
  95. }
  96. // SLOPE = (pt1Y - pt2Y)/( pt1X - pt2X )
  97. // Equals (pt1Y - pt2Y) * ( pt1X - pt2X )^-1
  98. $slope = gmp_mod(
  99. gmp_mul(
  100. gmp_sub(
  101. $pt1['y'],
  102. $pt2['y']
  103. ),
  104. gmp_invert(
  105. gmp_sub(
  106. $pt1['x'],
  107. $pt2['x']
  108. ),
  109. $p
  110. )
  111. ),
  112. $p
  113. );
  114. // nPtX = slope^2 - ptX1 - ptX2
  115. $nPt = array();
  116. $nPt['x'] = gmp_mod(
  117. gmp_sub(
  118. gmp_sub(
  119. gmp_pow($slope, 2),
  120. $pt1['x']
  121. ),
  122. $pt2['x']
  123. ),
  124. $p
  125. );
  126. // nPtX = slope * (ptX1 - nPtX) - ptY1
  127. $nPt['y'] = gmp_mod(
  128. gmp_sub(
  129. gmp_mul(
  130. $slope,
  131. gmp_sub(
  132. $pt1['x'],
  133. $nPt['x']
  134. )
  135. ),
  136. $pt1['y']
  137. ),
  138. $p
  139. );
  140. return $nPt;
  141. }
  142. /***
  143. * Computes the result of a point multiplication and returns the resulting point as an Array.
  144. *
  145. * @param String Hex $k
  146. * @param Array $pG (GMP, GMP)
  147. * @param $base (INT)
  148. * @throws \Exception
  149. * @return Array Point (GMP, GMP)
  150. */
  151. public static function mulPoint($k, Array $pG, $a, $b, $p, $base = null)
  152. {
  153. //in order to calculate k*G
  154. if($base == 16 || $base == null || is_resource($base))
  155. $k = gmp_init($k, 16);
  156. if($base == 10)
  157. $k = gmp_init($k, 10);
  158. $kBin = gmp_strval($k, 2);
  159. $lastPoint = $pG;
  160. for($i = 1; $i < strlen($kBin); $i++)
  161. {
  162. if(substr($kBin, $i, 1) == 1 )
  163. {
  164. $dPt = self::doublePoint($lastPoint, $a, $p);
  165. $lastPoint = self::addPoints($dPt, $pG, $a, $p);
  166. }
  167. else
  168. {
  169. $lastPoint = self::doublePoint($lastPoint, $a, $p);
  170. }
  171. }
  172. if(!self::validatePoint(gmp_strval($lastPoint['x'], 16), gmp_strval($lastPoint['y'], 16), $a, $b, $p)){
  173. throw new \Exception('The resulting point is not on the curve.');
  174. }
  175. return $lastPoint;
  176. }
  177. /***
  178. * Calculates the square root of $a mod p and returns the 2 solutions as an array.
  179. *
  180. * @param $a
  181. * @return array|null
  182. * @throws \Exception
  183. */
  184. public static function sqrt($a, $p)
  185. {
  186. if(gmp_legendre($a, $p) != 1)
  187. {
  188. //no result
  189. return null;
  190. }
  191. if(gmp_strval(gmp_mod($p, gmp_init(4, 10)), 10) == 3)
  192. {
  193. $sqrt1 = gmp_powm(
  194. $a,
  195. gmp_div_q(
  196. gmp_add($p, gmp_init(1, 10)),
  197. gmp_init(4, 10)
  198. ),
  199. $p
  200. );
  201. // there are always 2 results for a square root
  202. // In an infinite number field you have -2^2 = 2^2 = 4
  203. // In a finite number field you have a^2 = (p-a)^2
  204. $sqrt2 = gmp_mod(gmp_sub($p, $sqrt1), $p);
  205. return array($sqrt1, $sqrt2);
  206. }
  207. else
  208. {
  209. throw new \Exception('P % 4 != 3 , this isn\'t supported yet.');
  210. }
  211. }
  212. /***
  213. * Calculate the Y coordinates for a given X coordinate.
  214. *
  215. * @param $x
  216. * @param null $derEvenOrOddCode
  217. * @return array|null|String
  218. */
  219. public static function calculateYWithX($x, $a, $b, $p, $derEvenOrOddCode = null)
  220. {
  221. $x = gmp_init($x, 16);
  222. $y2 = gmp_mod(
  223. gmp_add(
  224. gmp_add(
  225. gmp_powm($x, gmp_init(3, 10), $p),
  226. gmp_mul($a, $x)
  227. ),
  228. $b
  229. ),
  230. $p
  231. );
  232. $y = self::sqrt($y2, $p);
  233. if(!$y) //if there is no result
  234. {
  235. return null;
  236. }
  237. if(!$derEvenOrOddCode)
  238. {
  239. return $y;
  240. }
  241. else if($derEvenOrOddCode == '02') // even
  242. {
  243. $resY = null;
  244. if(false == gmp_strval(gmp_mod($y[0], gmp_init(2, 10)), 10))
  245. $resY = gmp_strval($y[0], 16);
  246. if(false == gmp_strval(gmp_mod($y[1], gmp_init(2, 10)), 10))
  247. $resY = gmp_strval($y[1], 16);
  248. if($resY)
  249. {
  250. while(strlen($resY) < 64)
  251. {
  252. $resY = '0' . $resY;
  253. }
  254. }
  255. return $resY;
  256. }
  257. else if($derEvenOrOddCode == '03') // odd
  258. {
  259. $resY = null;
  260. if(true == gmp_strval(gmp_mod($y[0], gmp_init(2, 10)), 10))
  261. $resY = gmp_strval($y[0], 16);
  262. if(true == gmp_strval(gmp_mod($y[1], gmp_init(2, 10)), 10))
  263. $resY = gmp_strval($y[1], 16);
  264. if($resY)
  265. {
  266. while(strlen($resY) < 64)
  267. {
  268. $resY = '0' . $resY;
  269. }
  270. }
  271. return $resY;
  272. }
  273. return null;
  274. }
  275. /***
  276. * Returns true if the point is on the curve and false if it isn't.
  277. *
  278. * @param $x
  279. * @param $y
  280. * @return bool
  281. */
  282. public static function validatePoint($x, $y, $a, $b, $p)
  283. {
  284. $x = gmp_init($x, 16);
  285. $y2 = gmp_mod(
  286. gmp_add(
  287. gmp_add(
  288. gmp_powm($x, gmp_init(3, 10), $p),
  289. gmp_mul($a, $x)
  290. ),
  291. $b
  292. ),
  293. $p
  294. );
  295. $y = gmp_mod(gmp_pow(gmp_init($y, 16), 2), $p);
  296. if(gmp_cmp($y2, $y) == 0)
  297. return true;
  298. else
  299. return false;
  300. }
  301. /***
  302. * Returns Negated Point (Y).
  303. *
  304. * @param $point Array(GMP, GMP)
  305. * @return Array(GMP, GMP)
  306. */
  307. public static function negatePoint($point) {
  308. return array('x' => $point['x'], 'y' => gmp_neg($point['y']));
  309. }
  310. // These 2 function don't really belong here.
  311. // Checks is the given number (DEC String) is even
  312. public static function isEvenNumber($number) {
  313. return (((int)$number[strlen($number)-1]) & 1) == 0;
  314. }
  315. // Converts BIN to GMP
  316. public static function bin2gmp($binStr) {
  317. $v = gmp_init('0');
  318. for ($i = 0; $i < strlen($binStr); $i++) {
  319. $v = gmp_add(gmp_mul($v, 256), ord($binStr[$i]));
  320. }
  321. return $v;
  322. }
  323. }
  324. ?>