public function _doCompressionConsistence(MathAdapterInterface $adapter, NumberTheory $theory) { foreach ($this->compression_data as $o) { // Try and regenerate the y coordinate from the parity byte // '04' . $x_coordinate . determined y coordinate should equal $o->decompressed // Tests squareRootModP which touches most functions in NumberTheory $y_byte = substr($o->compressed, 0, 2); $x_coordinate = substr($o->compressed, 2); $x = $adapter->hexDec($x_coordinate); // x^3 $x3 = $adapter->powmod($x, 3, $this->generator->getCurve()->getPrime()); // y^2 $y2 = $adapter->add($x3, $this->generator->getCurve()->getB()); // y0 = sqrt(y^2) $y0 = $theory->squareRootModP($y2, $this->generator->getCurve()->getPrime()); if ($y_byte == '02') { $y_coordinate = $adapter->mod($y0, 2) == '0' ? gmp_strval(gmp_init($y0, 10), 16) : gmp_strval(gmp_sub($this->generator->getCurve()->getPrime(), $y0), 16); } else { $y_coordinate = $adapter->mod($y0, 2) == '0' ? gmp_strval(gmp_sub($this->generator->getCurve()->getPrime(), $y0), 16) : gmp_strval(gmp_init($y0, 10), 16); } $y_coordinate = str_pad($y_coordinate, 64, '0', STR_PAD_LEFT); // Successfully regenerated uncompressed ECDSA key from the x coordinate and the parity byte. $this->assertTrue('04' . $x_coordinate . $y_coordinate == $o->decompressed); } }
/** * @param NamedCurveFp $c * @param GeneratorPoint $G * @return string */ public function serialize(NamedCurveFp $c, GeneratorPoint $G) { $math = $G->getAdapter(); $fieldID = $this->getFieldIdAsn($math, $c); $curve = $this->getCurveAsn($math, $c); $domain = new Sequence(new Integer(1), $fieldID, $curve, new OctetString($this->pointSerializer->serialize($G)), new Integer($G->getOrder()), new Integer(1)); $payload = $domain->getBinary(); $content = self::HEADER . PHP_EOL . trim(chunk_split(base64_encode($payload), 64, PHP_EOL)) . PHP_EOL . self::FOOTER; return $content; }
/** * @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; }
/** * @param MathAdapterInterface $math * @param NamedCurveFp $curve * @param GeneratorPoint $generatorPoint * @param Hasher $hasher - must be a known hash algorithm */ public function __construct(MathAdapterInterface $math, NamedCurveFp $curve, GeneratorPoint $generatorPoint, Hasher $hasher) { if (!$curve->contains($generatorPoint->getX(), $generatorPoint->getY())) { throw new \RuntimeException('Provided generator point does not exist on curve'); } $this->hasher = $hasher; $this->curve = $curve; $this->generator = $generatorPoint; $this->math = $math; }
/** * 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."); } }
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()); }
/** * {@inheritDoc} * @see \Mdanter\Ecc\EcMathInterface::add() */ public function add($addend) { $type = $this->identify($addend); if ($this->dataType == 'point' && $type == 'point') { $this->data = $this->data->add($addend); return $this; } if ($this->dataType == 'int' && $type == 'int') { $this->data = $this->modMath->add($this->data, $addend); return $this; } $this->handleOppositeTypes($addend, function (PointInterface $data, $addendInt) { // Multiply by generator and return a regular point to add to $data $point = $this->generator->mul($addendInt); //$point = $this->generator->getCurve()->getPoint($point->getX(), $point->getY(), $this->generator->getOrder()); return $data->add($point); }); return $this; }
/** * @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()); }
/** * {@inheritDoc} * @see \Mdanter\Ecc\Crypto\Key\PrivateKeyInterface::getCurve() */ public function getCurve() { return $this->generator->getCurve(); }
/** * 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()); }
public function parseKey(GeneratorPoint $generator, $data) { $point = $this->pointSerializer->unserialize($generator->getCurve(), $data); return new PublicKey($this->adapter, $generator, $point); }
/** * 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; }