BaseCurve.php 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. <?php
  2. namespace Elliptic\Curve;
  3. use Elliptic\Utils;
  4. use \Exception;
  5. use BN\BN;
  6. abstract class BaseCurve
  7. {
  8. public $type;
  9. public $p;
  10. public $red;
  11. public $zero;
  12. public $one;
  13. public $two;
  14. public $n;
  15. public $g;
  16. protected $_wnafT1;
  17. protected $_wnafT2;
  18. protected $_wnafT3;
  19. protected $_wnafT4;
  20. public $redN;
  21. public $_maxwellTrick;
  22. function __construct($type, $conf)
  23. {
  24. $this->type = $type;
  25. $this->p = new BN($conf["p"], 16);
  26. //Use Montgomery, when there is no fast reduction for the prime
  27. $this->red = isset($conf["prime"]) ? BN::red($conf["prime"]) : BN::mont($this->p);
  28. //Useful for many curves
  29. $this->zero = (new BN(0))->toRed($this->red);
  30. $this->one = (new BN(1))->toRed($this->red);
  31. $this->two = (new BN(2))->toRed($this->red);
  32. //Curve configuration, optional
  33. $this->n = isset($conf["n"]) ? new BN($conf["n"], 16) : null;
  34. $this->g = isset($conf["g"]) ? $this->pointFromJSON($conf["g"], isset($conf["gRed"]) ? $conf["gRed"] : null) : null;
  35. //Temporary arrays
  36. $this->_wnafT1 = array(0,0,0,0);
  37. $this->_wnafT2 = array(0,0,0,0);
  38. $this->_wnafT3 = array(0,0,0,0);
  39. $this->_wnafT4 = array(0,0,0,0);
  40. //Generalized Greg Maxwell's trick
  41. $adjustCount = $this->n != null ? $this->p->div($this->n) : null;
  42. if( $adjustCount == null || $adjustCount->cmpn(100) > 0 )
  43. {
  44. $this->redN = null;
  45. $this->_maxwellTrick = false;
  46. }
  47. else
  48. {
  49. $this->redN = $this->n->toRed($this->red);
  50. $this->_maxwellTrick = true;
  51. }
  52. }
  53. abstract public function point($x, $z);
  54. abstract public function validate($point);
  55. public function _fixedNafMul($p, $k)
  56. {
  57. assert(isset($p->precomputed));
  58. $doubles = $p->_getDoubles();
  59. $naf = Utils::getNAF($k, 1);
  60. $I = (1 << ($doubles["step"] + 1)) - ($doubles["step"] % 2 == 0 ? 2 : 1);
  61. $I = $I / 3;
  62. //Translate to more windowed form
  63. $repr = array();
  64. for($j = 0; $j < count($naf); $j += $doubles["step"])
  65. {
  66. $nafW = 0;
  67. for($k = $j + $doubles["step"] - 1; $k >= $j; $k--)
  68. $nafW = ($nafW << 1) + (isset($naf[$k]) ? $naf[$k] : 0);
  69. array_push($repr, $nafW);
  70. }
  71. $a = $this->jpoint(null, null, null);
  72. $b = $this->jpoint(null, null, null);
  73. for($i = $I; $i > 0; $i--)
  74. {
  75. for($j = 0; $j < count($repr); $j++)
  76. {
  77. $nafW = $repr[$j];
  78. if ($nafW == $i) {
  79. $b = $b->mixedAdd($doubles["points"][$j]);
  80. } else if($nafW == -$i) {
  81. $b = $b->mixedAdd($doubles["points"][$j]->neg());
  82. }
  83. }
  84. $a = $a->add($b);
  85. }
  86. return $a->toP();
  87. }
  88. public function _wnafMul($p, $k)
  89. {
  90. $w = 4;
  91. //Precompute window
  92. $nafPoints = $p->_getNAFPoints($w);
  93. $w = $nafPoints["wnd"];
  94. $wnd = $nafPoints["points"];
  95. //Get NAF form
  96. $naf = Utils::getNAF($k, $w);
  97. //Add `this`*(N+1) for every w-NAF index
  98. $acc = $this->jpoint(null, null, null);
  99. for($i = count($naf) - 1; $i >= 0; $i--)
  100. {
  101. //Count zeros
  102. for($k = 0; $i >= 0 && $naf[$i] == 0; $i--)
  103. $k++;
  104. if($i >= 0)
  105. $k++;
  106. $acc = $acc->dblp($k);
  107. if($i < 0)
  108. break;
  109. $z = $naf[$i];
  110. assert($z != 0);
  111. if( $p->type == "affine" )
  112. {
  113. //J +- P
  114. if( $z > 0 )
  115. $acc = $acc->mixedAdd($wnd[($z - 1) >> 1]);
  116. else
  117. $acc = $acc->mixedAdd($wnd[(-$z - 1) >> 1]->neg());
  118. }
  119. else
  120. {
  121. //J +- J
  122. if( $z > 0 )
  123. $acc = $acc->add($wnd[($z - 1) >> 1]);
  124. else
  125. $acc = $acc->add($wnd[(-$z - 1) >> 1]->neg());
  126. }
  127. }
  128. return $p->type == "affine" ? $acc->toP() : $acc;
  129. }
  130. public function _wnafMulAdd($defW, $points, $coeffs, $len, $jacobianResult = false)
  131. {
  132. $wndWidth = &$this->_wnafT1;
  133. $wnd = &$this->_wnafT2;
  134. $naf = &$this->_wnafT3;
  135. //Fill all arrays
  136. $max = 0;
  137. for($i = 0; $i < $len; $i++)
  138. {
  139. $p = $points[$i];
  140. $nafPoints = $p->_getNAFPoints($defW);
  141. $wndWidth[$i] = $nafPoints["wnd"];
  142. $wnd[$i] = $nafPoints["points"];
  143. }
  144. //Comb all window NAFs
  145. for($i = $len - 1; $i >= 1; $i -= 2)
  146. {
  147. $a = $i - 1;
  148. $b = $i;
  149. if( $wndWidth[$a] != 1 || $wndWidth[$b] != 1 )
  150. {
  151. $naf[$a] = Utils::getNAF($coeffs[$a], $wndWidth[$a]);
  152. $naf[$b] = Utils::getNAF($coeffs[$b], $wndWidth[$b]);
  153. $max = max(count($naf[$a]), $max);
  154. $max = max(count($naf[$b]), $max);
  155. continue;
  156. }
  157. $comb = array(
  158. $points[$a], /* 1 */
  159. null, /* 3 */
  160. null, /* 5 */
  161. $points[$b] /* 7 */
  162. );
  163. //Try to avoid Projective points, if possible
  164. if( $points[$a]->y->cmp($points[$b]->y) == 0 )
  165. {
  166. $comb[1] = $points[$a]->add($points[$b]);
  167. $comb[2] = $points[$a]->toJ()->mixedAdd($points[$b]->neg());
  168. }
  169. elseif( $points[$a]->y->cmp($points[$b]->y->redNeg()) == 0 )
  170. {
  171. $comb[1] = $points[$a]->toJ()->mixedAdd($points[$b]);
  172. $comb[2] = $points[$a]->add($points[$b]->neg());
  173. }
  174. else
  175. {
  176. $comb[1] = $points[$a]->toJ()->mixedAdd($points[$b]);
  177. $comb[2] = $points[$a]->toJ()->mixedAdd($points[$b]->neg());
  178. }
  179. $index = array(
  180. -3, /* -1 -1 */
  181. -1, /* -1 0 */
  182. -5, /* -1 1 */
  183. -7, /* 0 -1 */
  184. 0, /* 0 0 */
  185. 7, /* 0 1 */
  186. 5, /* 1 -1 */
  187. 1, /* 1 0 */
  188. 3 /* 1 1 */
  189. );
  190. $jsf = Utils::getJSF($coeffs[$a], $coeffs[$b]);
  191. $max = max(count($jsf[0]), $max);
  192. if ($max > 0) {
  193. $naf[$a] = array_fill(0, $max, 0);
  194. $naf[$b] = array_fill(0, $max, 0);
  195. } else {
  196. $naf[$a] = [];
  197. $naf[$b] = [];
  198. }
  199. for($j = 0; $j < $max; $j++)
  200. {
  201. $ja = isset($jsf[0][$j]) ? $jsf[0][$j] : 0;
  202. $jb = isset($jsf[1][$j]) ? $jsf[1][$j] : 0;
  203. $naf[$a][$j] = $index[($ja + 1) * 3 + ($jb + 1)];
  204. $naf[$b][$j] = 0;
  205. $wnd[$a] = $comb;
  206. }
  207. }
  208. $acc = $this->jpoint(null, null, null);
  209. $tmp = &$this->_wnafT4;
  210. for($i = $max; $i >= 0; $i--)
  211. {
  212. $k = 0;
  213. while($i >= 0)
  214. {
  215. $zero = true;
  216. for($j = 0; $j < $len; $j++)
  217. {
  218. $tmp[$j] = isset($naf[$j][$i]) ? $naf[$j][$i] : 0;
  219. if( $tmp[$j] != 0 )
  220. $zero = false;
  221. }
  222. if( !$zero )
  223. break;
  224. $k++;
  225. $i--;
  226. }
  227. if( $i >=0 )
  228. $k++;
  229. $acc = $acc->dblp($k);
  230. if( $i < 0 )
  231. break;
  232. for($j = 0; $j < $len; $j++)
  233. {
  234. $z = $tmp[$j];
  235. $p = null;
  236. if( $z == 0 )
  237. continue;
  238. elseif( $z > 0 )
  239. $p = $wnd[$j][($z - 1) >> 1];
  240. elseif( $z < 0 )
  241. $p = $wnd[$j][(-$z - 1) >> 1]->neg();
  242. if( $p->type == "affine" )
  243. $acc = $acc->mixedAdd($p);
  244. else
  245. $acc = $acc->add($p);
  246. }
  247. }
  248. //Zeroify references
  249. for($i = 0; $i < $len; $i++)
  250. $wnd[$i] = null;
  251. if( $jacobianResult )
  252. return $acc;
  253. else
  254. return $acc->toP();
  255. }
  256. public function decodePoint($bytes, $enc = false)
  257. {
  258. $bytes = Utils::toArray($bytes, $enc);
  259. $len = $this->p->byteLength();
  260. $count = count($bytes);
  261. //uncompressed, hybrid-odd, hybrid-even
  262. if(($bytes[0] == 0x04 || $bytes[0] == 0x06 || $bytes[0] == 0x07) && ($count - 1) == (2 * $len) )
  263. {
  264. if( $bytes[0] == 0x06 )
  265. assert($bytes[$count - 1] % 2 == 0);
  266. elseif( $bytes[0] == 0x07 )
  267. assert($bytes[$count - 1] % 2 == 1);
  268. return $this->point(array_slice($bytes, 1, $len), array_slice($bytes, 1 + $len, $len));
  269. }
  270. if( ($bytes[0] == 0x02 || $bytes[0] == 0x03) && ($count - 1) == $len )
  271. return $this->pointFromX(array_slice($bytes, 1, $len), $bytes[0] == 0x03);
  272. throw new Exception("Unknown point format");
  273. }
  274. }
  275. ?>