· 9 years ago · Dec 07, 2016, 07:36 AM
1<?php
2
3/**
4 *
5 * @author Jan Moritz Lindemann
6 */
7
8namespace BitcoinPHP\BitcoinECDSA;
9
10if (!extension_loaded('gmp')) {
11 throw new \Exception('GMP extension seems not to be installed');
12}
13
14class BitcoinECDSA
15{
16
17 public $k;
18 public $a;
19 public $b;
20 public $p;
21 public $n;
22 public $G;
23 public $networkPrefix;
24
25 public function __construct()
26 {
27 $this->a = gmp_init('0', 10);
28 $this->b = gmp_init('7', 10);
29 $this->p = gmp_init('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F', 16);
30 $this->n = gmp_init('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16);
31
32 $this->G = array('x' => gmp_init('55066263022277343669578718895168534326250603453777594175500187360389116729240'),
33 'y' => gmp_init('32670510020758816978083085130507043184471273380659243275938904335757337482424'));
34
35 $this->networkPrefix = '00';
36 }
37
38 /***
39 * Set the network prefix, '00' = main network, '6f' = test network.
40 *
41 * @param String Hex $prefix
42 */
43 public function setNetworkPrefix($prefix)
44 {
45 $this->networkPrefix = $prefix;
46 }
47
48 /**
49 * Returns the current network prefix, '00' = main network, '6f' = test network.
50 *
51 * @return String Hex
52 */
53 public function getNetworkPrefix()
54 {
55 return $this->networkPrefix;
56 }
57
58 /***
59 * Permutation table used for Base58 encoding and decoding.
60 *
61 * @param $char
62 * @param bool $reverse
63 * @return null
64 */
65 public function base58_permutation($char, $reverse = false)
66 {
67 $table = array('1','2','3','4','5','6','7','8','9','A','B','C','D',
68 'E','F','G','H','J','K','L','M','N','P','Q','R','S','T','U','V','W',
69 'X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','m','n','o',
70 'p','q','r','s','t','u','v','w','x','y','z'
71 );
72
73 if($reverse)
74 {
75 $reversedTable = array();
76 foreach($table as $key => $element)
77 {
78 $reversedTable[$element] = $key;
79 }
80
81 if(isset($reversedTable[$char]))
82 return $reversedTable[$char];
83 else
84 return null;
85 }
86
87 if(isset($table[$char]))
88 return $table[$char];
89 else
90 return null;
91 }
92
93 /***
94 * encode a hexadecimal string in Base58.
95 *
96 * @param String Hex $data
97 * @param bool $littleEndian
98 * @return String Base58
99 * @throws \Exception
100 */
101 public function base58_encode($data, $littleEndian = true)
102 {
103 $res = '';
104 $dataIntVal = gmp_init($data, 16);
105 while(gmp_cmp($dataIntVal, gmp_init(0, 10)) > 0)
106 {
107 $qr = gmp_div_qr($dataIntVal, gmp_init(58, 10));
108 $dataIntVal = $qr[0];
109 $reminder = gmp_strval($qr[1]);
110 if(!$this->base58_permutation($reminder))
111 {
112 throw new \Exception('Something went wrong during base58 encoding');
113 }
114 $res .= $this->base58_permutation($reminder);
115 }
116
117 //get number of leading zeros
118 $leading = '';
119 $i=0;
120 while(substr($data, $i, 1) == '0')
121 {
122 if($i!= 0 && $i%2)
123 {
124 $leading .= '1';
125 }
126 $i++;
127 }
128
129 if($littleEndian)
130 return strrev($res . $leading);
131 else
132 return $res.$leading;
133 }
134
135 /***
136 * Decode a Base58 encoded string and returns it's value as a hexadecimal string
137 *
138 * @param $encodedData
139 * @param bool $littleEndian
140 * @return String Hex
141 */
142 public function base58_decode($encodedData, $littleEndian = true)
143 {
144 $res = gmp_init(0, 10);
145 $length = strlen($encodedData);
146 if($littleEndian)
147 {
148 $encodedData = strrev($encodedData);
149 }
150
151 for($i = $length - 1; $i >= 0; $i--)
152 {
153 $res = gmp_add(
154 gmp_mul(
155 $res,
156 gmp_init(58, 10)
157 ),
158 $this->base58_permutation(substr($encodedData, $i, 1), true)
159 );
160 }
161
162 $res = gmp_strval($res, 16);
163 $i = $length - 1;
164 while(substr($encodedData, $i, 1) == '1')
165 {
166 $res = '00' . $res;
167 $i--;
168 }
169
170 if(strlen($res)%2 != 0)
171 {
172 $res = '0' . $res;
173 }
174
175 return $res;
176 }
177
178 /***
179 * returns the private key under the Wallet Import Format
180 *
181 * @return String Base58
182 * @throws \Exception
183 */
184 public function getWif()
185 {
186 if(!isset($this->k))
187 {
188 throw new \Exception('No Private Key was defined');
189 }
190
191 $k = $this->k;
192 $secretKey = '80' . $k;
193 $firstSha256 = hash('sha256', hex2bin($secretKey));
194 $secondSha256 = hash('sha256', hex2bin($firstSha256));
195 $secretKey .= substr($secondSha256, 0, 8);
196
197 return $this->base58_encode($secretKey);
198 }
199
200 /***
201 * Computes the result of a point addition and returns the resulting point as an Array.
202 *
203 * @param Array $pt
204 * @return Array Point
205 * @throws \Exception
206 */
207 public function doublePoint(Array $pt)
208 {
209 $a = $this->a;
210 $p = $this->p;
211
212 $gcd = gmp_strval(gmp_gcd(gmp_mod(gmp_mul(gmp_init(2, 10), $pt['y']), $p),$p));
213 if($gcd != '1')
214 {
215 throw new \Exception('This library doesn\'t yet supports point at infinity. See https://github.com/BitcoinPHP/BitcoinECDSA.php/issues/9');
216 }
217
218 // SLOPE = (3 * ptX^2 + a )/( 2*ptY )
219 // Equals (3 * ptX^2 + a ) * ( 2*ptY )^-1
220 $slope = gmp_mod(
221 gmp_mul(
222 gmp_invert(
223 gmp_mod(
224 gmp_mul(
225 gmp_init(2, 10),
226 $pt['y']
227 ),
228 $p
229 ),
230 $p
231 ),
232 gmp_add(
233 gmp_mul(
234 gmp_init(3, 10),
235 gmp_pow($pt['x'], 2)
236 ),
237 $a
238 )
239 ),
240 $p
241 );
242
243 // nPtX = slope^2 - 2 * ptX
244 // Equals slope^2 - ptX - ptX
245 $nPt = array();
246 $nPt['x'] = gmp_mod(
247 gmp_sub(
248 gmp_sub(
249 gmp_pow($slope, 2),
250 $pt['x']
251 ),
252 $pt['x']
253 ),
254 $p
255 );
256
257 // nPtY = slope * (ptX - nPtx) - ptY
258 $nPt['y'] = gmp_mod(
259 gmp_sub(
260 gmp_mul(
261 $slope,
262 gmp_sub(
263 $pt['x'],
264 $nPt['x']
265 )
266 ),
267 $pt['y']
268 ),
269 $p
270 );
271
272 return $nPt;
273 }
274
275 /***
276 * Computes the result of a point addition and returns the resulting point as an Array.
277 *
278 * @param Array $pt1
279 * @param Array $pt2
280 * @return Array Point
281 * @throws \Exception
282 */
283 public function addPoints(Array $pt1, Array $pt2)
284 {
285 $p = $this->p;
286 if(gmp_cmp($pt1['x'], $pt2['x']) == 0 && gmp_cmp($pt1['y'], $pt2['y']) == 0) //if identical
287 {
288 return $this->doublePoint($pt1);
289 }
290
291 $gcd = gmp_strval(gmp_gcd(gmp_sub($pt1['x'], $pt2['x']), $p));
292 if($gcd != '1')
293 {
294 throw new \Exception('This library doesn\'t yet supports point at infinity. See https://github.com/BitcoinPHP/BitcoinECDSA.php/issues/9');
295 }
296
297 // SLOPE = (pt1Y - pt2Y)/( pt1X - pt2X )
298 // Equals (pt1Y - pt2Y) * ( pt1X - pt2X )^-1
299 $slope = gmp_mod(
300 gmp_mul(
301 gmp_sub(
302 $pt1['y'],
303 $pt2['y']
304 ),
305 gmp_invert(
306 gmp_sub(
307 $pt1['x'],
308 $pt2['x']
309 ),
310 $p
311 )
312 ),
313 $p
314 );
315
316 // nPtX = slope^2 - ptX1 - ptX2
317 $nPt = array();
318 $nPt['x'] = gmp_mod(
319 gmp_sub(
320 gmp_sub(
321 gmp_pow($slope, 2),
322 $pt1['x']
323 ),
324 $pt2['x']
325 ),
326 $p
327 );
328
329 // nPtX = slope * (ptX1 - nPtX) - ptY1
330 $nPt['y'] = gmp_mod(
331 gmp_sub(
332 gmp_mul(
333 $slope,
334 gmp_sub(
335 $pt1['x'],
336 $nPt['x']
337 )
338 ),
339 $pt1['y']
340 ),
341 $p
342 );
343
344 return $nPt;
345 }
346
347 /***
348 * Computes the result of a point multiplication and returns the resulting point as an Array.
349 *
350 * @param String Hex $k
351 * @param Array $pG
352 * @param $base
353 * @throws \Exception
354 * @return Array Point
355 */
356 public function mulPoint($k, Array $pG, $base = null)
357 {
358 //in order to calculate k*G
359 if($base == 16 || $base == null || is_resource($base))
360 $k = gmp_init($k, 16);
361 if($base == 10)
362 $k = gmp_init($k, 10);
363 $kBin = gmp_strval($k, 2);
364
365 $lastPoint = $pG;
366 for($i = 1; $i < strlen($kBin); $i++)
367 {
368 if(substr($kBin, $i, 1) == 1 )
369 {
370 $dPt = $this->doublePoint($lastPoint);
371 $lastPoint = $this->addPoints($dPt, $pG);
372 }
373 else
374 {
375 $lastPoint = $this->doublePoint($lastPoint);
376 }
377 }
378 if(!$this->validatePoint(gmp_strval($lastPoint['x'], 16), gmp_strval($lastPoint['y'], 16)))
379 throw new \Exception('The resulting point is not on the curve.');
380 return $lastPoint;
381 }
382
383 /***
384 * Calculates the square root of $a mod p and returns the 2 solutions as an array.
385 *
386 * @param $a
387 * @return array|null
388 * @throws \Exception
389 */
390 public function sqrt($a)
391 {
392 $p = $this->p;
393
394 if(gmp_legendre($a, $p) != 1)
395 {
396 //no result
397 return null;
398 }
399
400 if(gmp_strval(gmp_mod($p, gmp_init(4, 10)), 10) == 3)
401 {
402 $sqrt1 = gmp_powm(
403 $a,
404 gmp_div_q(
405 gmp_add($p, gmp_init(1, 10)),
406 gmp_init(4, 10)
407 ),
408 $p
409 );
410 // there are always 2 results for a square root
411 // In an infinite number field you have -2^2 = 2^2 = 4
412 // In a finite number field you have a^2 = (p-a)^2
413 $sqrt2 = gmp_mod(gmp_sub($p, $sqrt1), $p);
414 return array($sqrt1, $sqrt2);
415 }
416 else
417 {
418 throw new \Exception('P % 4 != 3 , this isn\'t supported yet.');
419 }
420 }
421
422 /***
423 * Calculate the Y coordinates for a given X coordinate.
424 *
425 * @param $x
426 * @param null $derEvenOrOddCode
427 * @return array|null|String
428 */
429 public function calculateYWithX($x, $derEvenOrOddCode = null)
430 {
431 $a = $this->a;
432 $b = $this->b;
433 $p = $this->p;
434
435 $x = gmp_init($x, 16);
436 $y2 = gmp_mod(
437 gmp_add(
438 gmp_add(
439 gmp_powm($x, gmp_init(3, 10), $p),
440 gmp_mul($a, $x)
441 ),
442 $b
443 ),
444 $p
445 );
446
447 $y = $this->sqrt($y2);
448 if(!$derEvenOrOddCode)
449 {
450 return $y;
451 }
452 else if($derEvenOrOddCode == '02') // even
453 {
454 $resY = null;
455 if(!gmp_strval(gmp_mod($y[0], gmp_init(2, 10)), 10))
456 $resY = gmp_strval($y[0], 16);
457 if(!gmp_strval(gmp_mod($y[1], gmp_init(2, 10)), 10))
458 $resY = gmp_strval($y[1], 16);
459 if($resY)
460 {
461 while(strlen($resY) < 64)
462 {
463 $resY = '0' . $resY;
464 }
465
466 }
467 return $resY;
468 }
469 else if($derEvenOrOddCode == '03') // odd
470 {
471 $resY = null;
472 if(gmp_strval(gmp_mod($y[0], gmp_init(2, 10)), 10))
473 $resY = gmp_strval($y[0], 16);
474 if(gmp_strval(gmp_mod($y[1], gmp_init(2, 10)), 10))
475 $resY = gmp_strval($y[1], 16);
476 if($resY)
477 {
478 while(strlen($resY) < 64)
479 {
480 $resY = '0' . $resY;
481 }
482
483 }
484 return $resY;
485 }
486
487 return null;
488 }
489
490 /***
491 * returns the public key coordinates as an array.
492 *
493 * @param $derPubKey
494 * @return array
495 * @throws \Exception
496 */
497 public function getPubKeyPointsWithDerPubKey($derPubKey)
498 {
499 if(substr($derPubKey, 0, 2) == '04' && strlen($derPubKey) == 130)
500 {
501 //uncompressed der encoded public key
502 $x = substr($derPubKey, 2, 64);
503 $y = substr($derPubKey, 66, 64);
504 return array('x' => $x, 'y' => $y);
505 }
506 else if((substr($derPubKey, 0, 2) == '02' || substr($derPubKey, 0, 2) == '03') && strlen($derPubKey) == 66)
507 {
508 //compressed der encoded public key
509 $x = substr($derPubKey, 2, 64);
510 $y = $this->calculateYWithX($x, substr($derPubKey, 0, 2));
511 return array('x' => $x, 'y' => $y);
512 }
513 else
514 {
515 throw new \Exception('Invalid derPubKey format : ' . $derPubKey);
516 }
517 }
518
519 /***
520 * Returns true if the point is on the curve and false if it isn't.
521 *
522 * @param $x
523 * @param $y
524 * @return bool
525 */
526 public function validatePoint($x, $y)
527 {
528 $a = $this->a;
529 $b = $this->b;
530 $p = $this->p;
531
532 $x = gmp_init($x, 16);
533 $y2 = gmp_mod(
534 gmp_add(
535 gmp_add(
536 gmp_powm($x, gmp_init(3, 10), $p),
537 gmp_mul($a, $x)
538 ),
539 $b
540 ),
541 $p
542 );
543 $y = gmp_mod(gmp_pow(gmp_init($y, 16), 2), $p);
544
545 if(gmp_cmp($y2, $y) == 0)
546 return true;
547 else
548 return false;
549 }
550
551 /***
552 * returns the X and Y point coordinates of the public key.
553 *
554 * @return Array Point
555 * @throws \Exception
556 */
557 public function getPubKeyPoints()
558 {
559 $G = $this->G;
560 $k = $this->k;
561
562 if(!isset($this->k))
563 {
564 throw new \Exception('No Private Key was defined');
565 }
566
567 $pubKey = $this->mulPoint($k,
568 array('x' => $G['x'], 'y' => $G['y'])
569 );
570
571 $pubKey['x'] = gmp_strval($pubKey['x'], 16);
572 $pubKey['y'] = gmp_strval($pubKey['y'], 16);
573
574 while(strlen($pubKey['x']) < 64)
575 {
576 $pubKey['x'] = '0' . $pubKey['x'];
577 }
578
579 while(strlen($pubKey['y']) < 64)
580 {
581 $pubKey['y'] = '0' . $pubKey['y'];
582 }
583
584 return $pubKey;
585 }
586
587 /***
588 * returns the uncompressed DER encoded public key.
589 *
590 * @return String Hex
591 */
592 public function getUncompressedPubKey()
593 {
594 $pubKey = $this->getPubKeyPoints();
595 $uncompressedPubKey = '04' . $pubKey['x'] . $pubKey['y'];
596
597 return $uncompressedPubKey;
598 }
599
600 /***
601 * returns the compressed DER encoded public key.
602 *
603 * @return String Hex
604 */
605 public function getPubKey()
606 {
607 $pubKey = $this->getPubKeyPoints();
608
609 if(gmp_strval(gmp_mod(gmp_init($pubKey['y'], 16), gmp_init(2, 10))) == 0)
610 $pubKey = '02' . $pubKey['x']; //if $pubKey['y'] is even
611 else
612 $pubKey = '03' . $pubKey['x']; //if $pubKey['y'] is odd
613
614 return $pubKey;
615 }
616
617 /***
618 * returns the uncompressed Bitcoin address generated from the private key if $compressed is false and
619 * the compressed if $compressed is true.
620 *
621 * @param bool $compressed
622 * @throws \Exception
623 * @return String Base58
624 */
625 public function getUncompressedAddress($compressed = false)
626 {
627 if($compressed) {
628 $address = $this->getPubKey();
629 }
630 else {
631 $address = $this->getUncompressedPubKey();
632 }
633
634 $sha256 = hash('sha256', hex2bin($address));
635 $ripem160 = hash('ripemd160', hex2bin($sha256));
636 $address = $this->getNetworkPrefix() . $ripem160;
637
638 //checksum
639 $sha256 = hash('sha256', hex2bin($address));
640 $sha256 = hash('sha256', hex2bin($sha256));
641 $address = $address.substr($sha256, 0, 8);
642 $address = $this->base58_encode($address);
643
644 if($this->validateAddress($address))
645 return $address;
646 else
647 throw new \Exception('the generated address seems not to be valid.');
648 }
649
650 /***
651 * returns the compressed Bitcoin address generated from the private key.
652 *
653 * @return String Base58
654 */
655 public function getAddress()
656 {
657 return $this->getUncompressedAddress(false);
658 }
659
660 /***
661 * set a private key.
662 *
663 * @param String Hex $k
664 * @throws \Exception
665 */
666 public function setPrivateKey($k)
667 {
668 //private key has to be passed as an hexadecimal number
669 if(gmp_cmp(gmp_init($k, 16), gmp_sub($this->n, gmp_init(1, 10))) == 1)
670 {
671 throw new \Exception('Private Key is not in the 1,n-1 range');
672 }
673 $this->k = $k;
674 }
675
676 /***
677 * return the private key.
678 *
679 * @return String Hex
680 */
681 public function getPrivateKey()
682 {
683 return $this->k;
684 }
685
686
687 /***
688 * Generate a new random private key.
689 * The extra parameter can be some random data typed down by the user or mouse movements to add randomness.
690 *
691 * @param string $extra
692 * @throws \Exception
693 */
694 public function generateRandomPrivateKey($extra = 'FSQF5356dsdsqdfEFEQ3fq4q6dq4s5d')
695 {
696 //private key has to be passed as an hexadecimal number
697 do { //generate a new random private key until to find one that is valid
698 $bytes = openssl_random_pseudo_bytes(256, $cStrong);
699 $hex = bin2hex($bytes);
700 $random = $hex . microtime(true).rand(100000000000, 1000000000000) . $extra;
701 $this->k = hash('sha256', $random);
702
703 if(!$cStrong)
704 {
705 throw new \Exception('Your system is not able to generate strong enough random numbers');
706 }
707
708 } while(gmp_cmp(gmp_init($this->k, 16), gmp_sub($this->n, gmp_init(1, 10))) == 1);
709 }
710
711 /***
712 * Tests if the address is valid or not.
713 *
714 * @param String Base58 $address
715 * @return bool
716 */
717 public function validateAddress($address)
718 {
719 $address = hex2bin($this->base58_decode($address));
720 if(strlen($address) != 25)
721 return false;
722 $checksum = substr($address, 21, 4);
723 $rawAddress = substr($address, 0, 21);
724 $sha256 = hash('sha256', $rawAddress);
725 $sha256 = hash('sha256', hex2bin($sha256));
726
727 if(substr(hex2bin($sha256), 0, 4) == $checksum)
728 return true;
729 else
730 return false;
731 }
732
733 /***
734 * Tests if the Wif key (Wallet Import Format) is valid or not.
735 *
736 * @param String Base58 $wif
737 * @return bool
738 */
739 public function validateWifKey($wif)
740 {
741 $key = $this->base58_decode($wif, false);
742 $length = strlen($key);
743 $firstSha256 = hash('sha256', hex2bin(substr($key, 0, $length - 8)));
744 $secondSha256 = hash('sha256', hex2bin($firstSha256));
745 if(substr($secondSha256, 0, 8) == substr($key, $length - 8, 8))
746 return true;
747 else
748 return false;
749 }
750
751 /***
752 * Sign a hash with the private key that was set and returns signatures as an array (R,S)
753 *
754 * @param $hash
755 * @param null $nonce
756 * @throws \Exception
757 * @return Array
758 */
759 public function getSignatureHashPoints($hash, $nonce = null)
760 {
761
762 //please don't use for now
763 echo "please don't use for now, not working";
764
765 $p = $this->p;
766 $k = $this->k;
767
768 if(empty($k))
769 {
770 throw new \Exception('No Private Key was defined');
771 }
772
773 if(!$nonce)
774 {
775 $random = openssl_random_pseudo_bytes(256, $cStrong);
776 $random = $random . microtime(true).rand(100000000000, 1000000000000);
777 $nonce = gmp_strval(gmp_mod(gmp_init(hash('sha256',$random), 16), $p), 16);
778 }
779
780 //first part of the signature (R).
781
782 $rPt = $this->mulPoint($nonce, $this->G);
783 $R = gmp_strval($rPt ['x'], 16);
784
785 while(strlen($R) < 64)
786 {
787 $R = '0' . $R;
788 }
789
790 //S = nonce^-1 (hash + privKey * R) mod p
791
792 $S = gmp_strval(
793 gmp_mod(
794 gmp_mul(
795 gmp_invert(
796 gmp_init($nonce, 16),
797 $p
798 ),
799 gmp_add(
800 gmp_init($hash, 16),
801 gmp_mul(
802 gmp_init($k, 16),
803 gmp_init($R, 16)
804 )
805 )
806 ),
807 $p
808 ),
809 16
810 );
811
812 if(strlen($S)%2)
813 {
814 $S = '0' . $S;
815 }
816
817 if(strlen($R)%2)
818 {
819 $R = '0' . $R;
820 }
821
822 return array('R' => $R, 'S' => $S);
823 }
824
825 /***
826 * Sign a hash with the private key that was set and returns a DER encoded signature
827 *
828 * @param $hash
829 * @param null $nonce
830 * @return string
831 */
832 public function signHash($hash, $nonce = null)
833 {
834 $points = $this->getSignatureHashPoints($hash, $nonce);
835
836 $signature = '02' . dechex(strlen(hex2bin($points['R']))) . $points['R'] . '02' . dechex(strlen(hex2bin($points['S']))) . $points['S'];
837 $signature = '30' . dechex(strlen(hex2bin($signature))) . $signature;
838
839 return $signature;
840 }
841
842 public function signMessage($message)
843 {
844
845 $points = $this->getSignatureHashPoints(hash('sha256', hash('sha256', $message)));
846
847 $R = $points['R'];
848 $S = $points['S'];
849
850 while(strlen($R) < 64)
851 $R = '0' . $R;
852
853 while(strlen($S) < 64)
854 $S = '0' . $S;
855
856
857 $res = "-----BEGIN BITCOIN SIGNED MESSAGE-----\n";
858 $res .= $message;
859 $res .= "\n-----BEGIN SIGNATURE-----\n";
860 $res .= $this->getAddress()."\n";
861 $res .= base64_encode(hex2bin(dechex(27+4+3) . $R . $S));
862 $res .= "\n-----END BITCOIN SIGNED MESSAGE-----";
863
864 return $res;
865 }
866
867 public function getPubKeyWithRS($R, $S, $hash)
868 {
869
870 }
871
872 public function getPubKeyWithSignature($signature, $hash)
873 {
874
875 }
876
877 public function checkSignaturePoints($pubKey, $R, $S, $hash)
878 {
879 //please don't use for now
880 echo "please don't use for now, not working";
881 $p = $this->p;
882 $G = $this->G;
883
884 $pubKeyPts = $this->getPubKeyPointsWithDerPubKey($pubKey);
885
886 // S^-1* hash * G + S^-1 * R * Qa
887
888 // S^-1* hash
889 $exp1 = gmp_strval(
890 gmp_mul(
891 gmp_invert(
892 gmp_init($S, 16),
893 $p
894 ),
895 gmp_init($hash, 16)
896 ),
897 16
898 );
899 // S^-1* hash * G
900 $exp1Pt = $this->mulPoint($exp1, $G);
901
902
903 // S^-1 * R
904 $exp2 = gmp_strval(
905 gmp_mul(
906 gmp_invert(
907 gmp_init($S, 16),
908 $p
909 ),
910 gmp_init($R, 16)
911 ),
912 16
913 );
914 // S^-1 * R * Qa
915 echo "exp2 : " . $exp2 . "\n";
916
917 $pubKeyPts['x'] = gmp_init($pubKeyPts['x'], 16);
918 $pubKeyPts['y'] = gmp_init($pubKeyPts['y'], 16);
919
920 $exp2Pt = $this->mulPoint($exp2,$pubKeyPts);
921
922 $resultingPt = $this->addPoints($exp1Pt, $exp2Pt);
923
924 $xRes = gmp_strval($resultingPt['x'], 16);
925 echo "GOT : " . $xRes . "\n";
926 echo "Expected : " . $R;
927 }
928
929 public function checkSignatureForMessage($address, $signature, $message)
930 {
931
932 }
933}
934
935?>