/** * @param $input * @param GeneratorPoint $G * @param MathAdapterInterface $math */ public function __construct($input, GeneratorPoint $G, MathAdapterInterface $math) { $this->dataType = $this->identify($input); $this->data = $input; $this->math = $math; $this->generator = $G; $this->modMath = $math->getModularArithmetic($G->getOrder()); }
/** * @param GeneratorPoint $G * @param $hash * @return int|string */ public function truncateHash(GeneratorPoint $G, $hash) { $hexSize = strlen($this->adapter->decHex($hash)); $hashBits = $this->adapter->baseConvert($hash, 10, 2); if (strlen($hashBits) < $hexSize * 4) { $hashBits = str_pad($hashBits, $hexSize * 4, '0', STR_PAD_LEFT); } $messageHash = $this->adapter->baseConvert(substr($hashBits, 0, NumberSize::bnNumBits($this->adapter, $G->getOrder())), 2, 10); return $messageHash; }
public function testCurve() { $math = EccFactory::getAdapter(); $G = CurveFactory::getGeneratorByName("nist-p224"); $algo = 'sha256'; // Initialize private key and message hash (decimal) $privateKey = $G->getPrivateKeyFrom($this->math->hexDec('F220266E1105BFE3083E03EC7A3A654651F45E37167E88600BF257C1')); $messageHash = $this->math->hexDec(hash($algo, "sample")); // Derive K $drbg = RandomGeneratorFactory::getHmacRandomGenerator($privateKey, $messageHash, $algo); $k = $drbg->generate($this->G->getOrder()); //$this->assertEquals($this->math->hexdec($test->expectedK), $k); $signer = new Signer($this->math); $sig = $signer->sign($privateKey, $messageHash, $k); // R and S should be correct //$sR = $this->math->hexDec(substr(strtolower($test->expectedRS), 0, 64)); //$sS = $this->math->hexDec(substr(strtolower($test->expectedRS), 64, 64)); //$this->assertSame($sR, $sig->getR()); //$this->assertSame($sS, $sig->getS()); }
/** * Initialize a new instance. * * @param MathAdapterInterface $adapter * @param GeneratorPoint $generator * @param PointInterface $point * @throws \LogicException * @throws \RuntimeException */ public function __construct(MathAdapterInterface $adapter, GeneratorPoint $generator, PointInterface $point) { $this->curve = $generator->getCurve(); $this->generator = $generator; $this->point = $point; $this->adapter = $adapter; $n = $generator->getOrder(); if ($n == null) { throw new \LogicException("Generator must have order."); } if (!$point->mul($n)->isInfinity()) { throw new \RuntimeException("Generator point order is bad."); } if ($adapter->cmp($point->getX(), 0) < 0 || $adapter->cmp($n, $point->getX()) <= 0 || $adapter->cmp($point->getY(), 0) < 0 || $adapter->cmp($n, $point->getY()) <= 0) { throw new \RuntimeException("Generator point has x and y out of range."); } }
/** * @dataProvider getHmacTestSet * @param GeneratorPoint $G * @param integer $size * @param string $privKey * @param string $algo * @param string $message * @param string $eK expected K hex * @param string $eR expected R hex * @param string $eS expected S hex */ public function testHmacSignatures(GeneratorPoint $G, $size, $privKey, $algo, $message, $eK, $eR, $eS) { //echo "Try {$test->curve} / {$test->algorithm} / '{$test->message}'\n"; $math = $G->getAdapter(); // Initialize private key and message hash (decimal) $privateKey = $G->getPrivateKeyFrom($math->hexDec($privKey)); $hashHex = hash($algo, $message); $messageHash = $math->hexDec($hashHex); // Derive K $drbg = RandomGeneratorFactory::getHmacRandomGenerator($privateKey, $messageHash, $algo); $k = $drbg->generate($G->getOrder()); $this->assertEquals($k, $math->hexdec($eK), 'k'); $hexSize = strlen($hashHex); $hashBits = $math->baseConvert($messageHash, 10, 2); if (strlen($hashBits) < $hexSize * 4) { $hashBits = str_pad($hashBits, $hexSize * 4, '0', STR_PAD_LEFT); } $messageHash = $math->baseConvert(substr($hashBits, 0, NumberSize::bnNumBits($math, $G->getOrder())), 2, 10); $signer = new Signer($math); $sig = $signer->sign($privateKey, $messageHash, $k); // Should be consistent $this->assertTrue($signer->verify($privateKey->getPublicKey(), $sig, $messageHash)); // R and S should be correct $sR = $math->hexDec($eR); $sS = $math->hexDec($eS); $this->assertSame($sR, $sig->getR(), "r {$sR} == " . $sig->getR()); $this->assertSame($sS, $sig->getS(), "s {$sR} == " . $sig->getS()); }
/** * Take X, Y, and a generator point, and we can get what we need! * * @param Math $math * @param GeneratorPoint $generator * @param int|string $x * @param int|string $y */ public function __construct(Math $math, GeneratorPoint $generator, $x, $y) { parent::__construct($math, $generator->getCurve(), $x, $y, $generator->getOrder()); }
/** * based on php-bitcoin-signature-routines implementation (which is based on bitcoinjs-lib's implementation) * which is SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public Key Recovery Operation" * http://www.secg.org/sec1-v2.pdf * * @param $r * @param $s * @param $e * @param $recoveryFlags * @param GeneratorPoint $G * @return bool|PublicKey */ private static function recoverPubKey($r, $s, $e, $recoveryFlags, GeneratorPoint $G) { $math = EccFactory::getAdapter(); $isYEven = ($recoveryFlags & 1) != 0; $isSecondKey = ($recoveryFlags & 2) != 0; $curve = $G->getCurve(); $signature = new Signature($r, $s); // Precalculate (p + 1) / 4 where p is the field order $p_over_four = $math->div($math->add($curve->getPrime(), 1), 4); // 1.1 Compute x if (!$isSecondKey) { $x = $r; } else { $x = $math->add($r, $G->getOrder()); } // 1.3 Convert x to point $alpha = $math->mod($math->add($math->add($math->pow($x, 3), $math->mul($curve->getA(), $x)), $curve->getB()), $curve->getPrime()); $beta = $math->powmod($alpha, $p_over_four, $curve->getPrime()); // If beta is even, but y isn't or vice versa, then convert it, // otherwise we're done and y == beta. if (($math->mod($beta, 2) == 0) == $isYEven) { $y = $math->sub($curve->getPrime(), $beta); } else { $y = $beta; } // 1.4 Check that nR is at infinity (implicitly done in constructor) $R = $G->getCurve()->getPoint($x, $y); $point_negate = function (PointInterface $p) use($math, $G) { return $G->getCurve()->getPoint($p->getX(), $math->mul($p->getY(), -1)); }; // 1.6.1 Compute a candidate public key Q = r^-1 (sR - eG) $rInv = $math->inverseMod($r, $G->getOrder()); $eGNeg = $point_negate($G->mul($e)); $Q = $R->mul($s)->add($eGNeg)->mul($rInv); // 1.6.2 Test Q as a public key $signer = new Signer($math); $Qk = new PublicKey($math, $G, $Q); if ($signer->verify($Qk, $signature, $e)) { return $Qk; } return false; }