EdDSA.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. <?php
  2. namespace Elliptic;
  3. use Elliptic\EdDSA\KeyPair;
  4. use Elliptic\EdDSA\Signature;
  5. class EdDSA {
  6. public $curve;
  7. public $g;
  8. public $pointClass;
  9. public $encodingLength;
  10. public $hash;
  11. function __construct($curve) {
  12. assert($curve == "ed25519"); //, 'only tested with ed25519 so far');
  13. $curve = \Elliptic\Curves::getCurve($curve)->curve;
  14. $this->curve = $curve;
  15. $this->g = $curve->g;
  16. $this->g->precompute($curve->n->bitLength() + 1);
  17. $this->pointClass = get_class($curve->point());
  18. $this->encodingLength = intval(ceil($curve->n->bitLength() / 8));
  19. // TODO: !!!
  20. $this->hash = [ "algo" => "sha512" ];
  21. }
  22. /**
  23. * @param {Array|String} message - message bytes
  24. * @param {Array|String|KeyPair} secret - secret bytes or a keypair
  25. * @returns {Signature} - signature
  26. */
  27. public function sign($message, $secret) {
  28. $message = Utils::parseBytes($message);
  29. $key = $this->keyFromSecret($secret);
  30. $r = $this->hashInt($key->messagePrefix(), $message);
  31. $R = $this->g->mul($r);
  32. $Rencoded = $this->encodePoint($R);
  33. $s_ = $this->hashInt($Rencoded, $key->pubBytes(), $message)
  34. ->mul($key->priv());
  35. $S = $r->add($s_)->umod($this->curve->n);
  36. return $this->makeSignature([ "R" => $R, "S" => $S, "Rencoded" => $Rencoded ]);
  37. }
  38. /**
  39. * @param {Array} message - message bytes
  40. * @param {Array|String|Signature} sig - sig bytes
  41. * @param {Array|String|Point|KeyPair} pub - public key
  42. * @returns {Boolean} - true if public key matches sig of message
  43. */
  44. public function verify($message, $sig, $pub) {
  45. $message = Utils::parseBytes($message);
  46. $sig = $this->makeSignature($sig);
  47. $key = $this->keyFromPublic($pub);
  48. $h = $this->hashInt($sig->Rencoded(), $key->pubBytes(), $message);
  49. $SG = $this->g->mul($sig->S());
  50. $RplusAh = $sig->R()->add($key->pub()->mul($h));
  51. return $RplusAh->eq($SG);
  52. }
  53. public function hashInt() {
  54. $arguments = func_get_args();
  55. // TODO: refactor when hash-php is ready
  56. $hash = hash_init($this->hash["algo"]);
  57. for ($i = 0; $i < count($arguments); $i++)
  58. hash_update($hash, Utils::toBin($arguments[$i]));
  59. return Utils::intFromLE(hash_final($hash))->umod($this->curve->n);
  60. }
  61. public function keyFromPublic($pub) {
  62. return KeyPair::fromPublic($this, $pub);
  63. }
  64. public function keyFromSecret($secret) {
  65. return KeyPair::fromSecret($this, $secret);
  66. }
  67. public function makeSignature($sig) {
  68. if ($sig instanceof Signature)
  69. return $sig;
  70. return new Signature($this, $sig);
  71. }
  72. /**
  73. * * https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-03#section-5.2
  74. *
  75. * EdDSA defines methods for encoding and decoding points and integers. These are
  76. * helper convenience methods, that pass along to utility functions implied
  77. * parameters.
  78. *
  79. */
  80. public function encodePoint($point) {
  81. $enc = $point->getY()->toArray('le', $this->encodingLength);
  82. $enc[$this->encodingLength - 1] |= $point->getX()->isOdd() ? 0x80 : 0;
  83. return $enc;
  84. }
  85. public function decodePoint($bytes) {
  86. $bytes = Utils::parseBytes($bytes);
  87. $lastIx = count($bytes) - 1;
  88. $normed = $bytes;
  89. $normed[$lastIx] = $bytes[$lastIx] & ~0x80;
  90. $xIsOdd = ($bytes[$lastIx] & 0x80) !== 0;
  91. $y = Utils::intFromLE($normed);
  92. return $this->curve->pointFromY($y, $xIsOdd);
  93. }
  94. public function encodeInt($num) {
  95. return $num->toArray('le', $this->encodingLength);
  96. }
  97. public function decodeInt($bytes) {
  98. return Utils::intFromLE($bytes);
  99. }
  100. public function isPoint($val) {
  101. return is_a($val, $this->pointClass);
  102. }
  103. }