EC.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. <?php
  2. namespace Elliptic;
  3. use Elliptic\Curve\PresetCurve;
  4. use Elliptic\EC\KeyPair;
  5. use Elliptic\EC\Signature;
  6. use BN\BN;
  7. class EC
  8. {
  9. public $curve;
  10. public $n;
  11. public $nh;
  12. public $g;
  13. public $hash;
  14. function __construct($options)
  15. {
  16. if( is_string($options) )
  17. {
  18. $options = Curves::getCurve($options);
  19. }
  20. if( $options instanceof PresetCurve )
  21. $options = array("curve" => $options);
  22. $this->curve = $options["curve"]->curve;
  23. $this->n = $this->curve->n;
  24. $this->nh = $this->n->ushrn(1);
  25. //Point on curve
  26. $this->g = $options["curve"]->g;
  27. $this->g->precompute($options["curve"]->n->bitLength() + 1);
  28. //Hash for function for DRBG
  29. if( isset($options["hash"]) )
  30. $this->hash = $options["hash"];
  31. else
  32. $this->hash = $options["curve"]->hash;
  33. }
  34. public function keyPair($options) {
  35. return new KeyPair($this, $options);
  36. }
  37. public function keyFromPrivate($priv, $enc = false) {
  38. return KeyPair::fromPrivate($this, $priv, $enc);
  39. }
  40. public function keyFromPublic($pub, $enc = false) {
  41. return KeyPair::fromPublic($this, $pub, $enc);
  42. }
  43. public function genKeyPair($options = null)
  44. {
  45. // Instantiate HmacDRBG
  46. $drbg = new HmacDRBG(array(
  47. "hash" => $this->hash,
  48. "pers" => isset($options["pers"]) ? $options["pers"] : "",
  49. "entropy" => isset($options["entropy"]) ? $options["entropy"] : Utils::randBytes($this->hash["hmacStrength"]),
  50. "nonce" => $this->n->toArray()
  51. ));
  52. $bytes = $this->n->byteLength();
  53. $ns2 = $this->n->sub(new BN(2));
  54. while(true)
  55. {
  56. $priv = new BN($drbg->generate($bytes));
  57. if( $priv->cmp($ns2) > 0 )
  58. continue;
  59. $priv->iaddn(1);
  60. return $this->keyFromPrivate($priv);
  61. }
  62. }
  63. private function _truncateToN($msg, $truncOnly = false)
  64. {
  65. $delta = intval(($msg->byteLength() * 8) - $this->n->bitLength());
  66. if( $delta > 0 ) {
  67. $msg = $msg->ushrn($delta);
  68. }
  69. if( $truncOnly || $msg->cmp($this->n) < 0 )
  70. return $msg;
  71. return $msg->sub($this->n);
  72. }
  73. public function sign($msg, $key, $enc = null, $options = null)
  74. {
  75. if( !is_string($enc) )
  76. {
  77. $options = $enc;
  78. $enc = null;
  79. }
  80. $key = $this->keyFromPrivate($key, $enc);
  81. $msg = $this->_truncateToN(new BN($msg, 16));
  82. // Zero-extend key to provide enough entropy
  83. $bytes = $this->n->byteLength();
  84. $bkey = $key->getPrivate()->toArray("be", $bytes);
  85. // Zero-extend nonce to have the same byte size as N
  86. $nonce = $msg->toArray("be", $bytes);
  87. $kFunc = null;
  88. if( isset($options["k"]) )
  89. $kFunc = $options["k"];
  90. else
  91. {
  92. // Instatiate HmacDRBG
  93. $drbg = new HmacDRBG(array(
  94. "hash" => $this->hash,
  95. "entropy" => $bkey,
  96. "nonce" => $nonce,
  97. "pers" => isset($options["pers"]) ? $options["pers"] : "",
  98. "persEnc" => isset($options["persEnc"]) ? $options["persEnc"] : false
  99. ));
  100. $kFunc = function($iter) use ($drbg, $bytes) {
  101. return new BN($drbg->generate($bytes));
  102. };
  103. }
  104. // Number of bytes to generate
  105. $ns1 = $this->n->sub(new BN(1));
  106. $canonical = isset($options["canonical"]) ? $options["canonical"] : false;
  107. for($iter = 0; true; $iter++)
  108. {
  109. $k = $kFunc($iter);
  110. $k = $this->_truncateToN($k, true);
  111. if( $k->cmpn(1) <= 0 || $k->cmp($ns1) >= 0 )
  112. continue;
  113. // Fix the bit-length of the random nonce,
  114. // so that it doesn't leak via timing.
  115. // This does not change that ks = k mod k
  116. $ks = $k->add($this->n);
  117. $kt = $ks->add($this->n);
  118. if ($ks->bitLength() === $this->n->bitLength()) {
  119. $kp = $this->g->mul($kt);
  120. } else {
  121. $kp = $this->g->mul($ks);
  122. }
  123. if( $kp->isInfinity() )
  124. continue;
  125. $kpX = $kp->getX();
  126. $r = $kpX->umod($this->n);
  127. if( $r->isZero() )
  128. continue;
  129. $s = $k->invm($this->n)->mul($r->mul($key->getPrivate())->iadd($msg));
  130. $s = $s->umod($this->n);
  131. if( $s->isZero() )
  132. continue;
  133. $recoveryParam = ($kp->getY()->isOdd() ? 1 : 0) | ($kpX->cmp($r) !== 0 ? 2 : 0);
  134. // Use complement of `s`, if it is > `n / 2`
  135. if( $canonical && $s->cmp($this->nh) > 0 )
  136. {
  137. $s = $this->n->sub($s);
  138. $recoveryParam ^= 1;
  139. }
  140. return new Signature(array(
  141. "r" => $r,
  142. "s" => $s,
  143. "recoveryParam" => $recoveryParam
  144. ));
  145. }
  146. }
  147. public function verify($msg, $signature, $key, $enc = false)
  148. {
  149. $msg = $this->_truncateToN(new BN($msg, 16));
  150. $key = $this->keyFromPublic($key, $enc);
  151. $signature = new Signature($signature, "hex");
  152. // Perform primitive values validation
  153. $r = $signature->r;
  154. $s = $signature->s;
  155. if( $r->cmpn(1) < 0 || $r->cmp($this->n) >= 0 )
  156. return false;
  157. if( $s->cmpn(1) < 0 || $s->cmp($this->n) >= 0 )
  158. return false;
  159. // Validate signature
  160. $sinv = $s->invm($this->n);
  161. $u1 = $sinv->mul($msg)->umod($this->n);
  162. $u2 = $sinv->mul($r)->umod($this->n);
  163. if( !$this->curve->_maxwellTrick )
  164. {
  165. $p = $this->g->mulAdd($u1, $key->getPublic(), $u2);
  166. if( $p->isInfinity() )
  167. return false;
  168. return $p->getX()->umod($this->n)->cmp($r) === 0;
  169. }
  170. // NOTE: Greg Maxwell's trick, inspired by:
  171. // https://git.io/vad3K
  172. $p = $this->g->jmulAdd($u1, $key->getPublic(), $u2);
  173. if( $p->isInfinity() )
  174. return false;
  175. // Compare `p.x` of Jacobian point with `r`,
  176. // this will do `p.x == r * p.z^2` instead of multiplying `p.x` by the
  177. // inverse of `p.z^2`
  178. return $p->eqXToP($r);
  179. }
  180. public function recoverPubKey($msg, $signature, $j, $enc = false)
  181. {
  182. assert((3 & $j) === $j); //, "The recovery param is more than two bits");
  183. $signature = new Signature($signature, $enc);
  184. $e = new BN($msg, 16);
  185. $r = $signature->r;
  186. $s = $signature->s;
  187. // A set LSB signifies that the y-coordinate is odd
  188. $isYOdd = ($j & 1) == 1;
  189. $isSecondKey = $j >> 1;
  190. if ($r->cmp($this->curve->p->umod($this->curve->n)) >= 0 && $isSecondKey)
  191. throw new \Exception("Unable to find second key candinate");
  192. // 1.1. Let x = r + jn.
  193. if( $isSecondKey )
  194. $r = $this->curve->pointFromX($r->add($this->curve->n), $isYOdd);
  195. else
  196. $r = $this->curve->pointFromX($r, $isYOdd);
  197. $eNeg = $this->n->sub($e);
  198. // 1.6.1 Compute Q = r^-1 (sR - eG)
  199. // Q = r^-1 (sR + -eG)
  200. $rInv = $signature->r->invm($this->n);
  201. return $this->g->mulAdd($eNeg, $r, $s)->mul($rInv);
  202. }
  203. public function getKeyRecoveryParam($e, $signature, $Q, $enc = false)
  204. {
  205. $signature = new Signature($signature, $enc);
  206. if( $signature->recoveryParam != null )
  207. return $signature->recoveryParam;
  208. for($i = 0; $i < 4; $i++)
  209. {
  210. $Qprime = null;
  211. try {
  212. $Qprime = $this->recoverPubKey($e, $signature, $i);
  213. }
  214. catch(\Exception $e) {
  215. continue;
  216. }
  217. if( $Qprime->eq($Q))
  218. return $i;
  219. }
  220. throw new \Exception("Unable to find valid recovery factor");
  221. }
  222. }
  223. ?>