/** * @param string $content * @param Binary $address * @param string $signature * @return bool * @throws \Exception */ public function verify($content, Binary $address, $signature) { if (!strpos($signature, self::$SIGNATURE_GLUE)) { throw new \Exception('Invalid signature.'); } list($r, $s) = explode(self::$SIGNATURE_GLUE, $signature); $math = MathAdapterFactory::getAdapter(); $serializer = new DerPublicKeySerializer($math); $inflatedPublicKey = $this->deserialize($address->getData(), $serializer); $hash = $this->hash($content); $signer = new Signer($math); return $signer->verify($inflatedPublicKey, new Signature($r, $s), $hash); }
/** * {@inheritdoc} */ public function doVerify(string $expected, string $payload, Key $key) : bool { return $this->signer->verify($this->parser->getPublicKey($key), $this->extractSignature($expected), $this->createSigningHash($payload)); }
/** * @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()); }
/** * @dataProvider getAdaptersWithRand */ public function testSignatureValidityWithGeneratedKeys(MathAdapterInterface $math, RandomNumberGeneratorInterface $rng) { $generator = EccFactory::getNistCurves($math)->generator192(); $signer = new Signer($math); $privateKey = $generator->createPrivateKey(); $publicKey = $privateKey->getPublicKey(); $randomK = $rng->generate($privateKey->getPoint()->getOrder()); $hash = $rng->generate($generator->getOrder()); $signature = $signer->sign($privateKey, $hash, $randomK); $this->assertTrue($signer->verify($publicKey, $signature, $hash), 'Correctly validates valid hash.'); $this->assertFalse($signer->verify($publicKey, $signature, $math->sub($hash, 1)), 'Correctly rejects tampered hash.'); }
/** * Check Sig * * This function will check a provided DER encoded $sig, a digest of * the message to be signed - $hash (the output of _create_txin_signature_hash()), * and the $key for the signature to be tested against. * Returns TRUE if the signature is valid for this $hash and $key, * otherwise returns FALSE. * * @param string $sig * @param string $hash * @param string $key * @return boolean */ public static function _check_sig($sig, $hash, $key) { $math = EccFactory::getAdapter(); $generator = EccFactory::getSecgCurves()->generator256k1(); $curve = $generator->getCurve(); $hash = $math->hexDec($hash); $signature = self::decode_signature($sig); $test_signature = new Signature($math->hexDec($signature['r']), $math->hexDec($signature['s'])); if (strlen($key) == '66') { $decompress = BitcoinLib::decompress_public_key($key); $public_key_point = $decompress['point']; } else { $x = $math->hexDec(substr($key, 2, 64)); $y = $math->hexDec(substr($key, 66, 64)); $public_key_point = $curve->getPoint($x, $y); } $signer = new Signer($math); $public_key = new PublicKey($math, $generator, $public_key_point); return $signer->verify($public_key, $test_signature, $hash) == true; }
/** * Check Sig * * This function will check a provided DER encoded $sig, a digest of * the message to be signed - $hash (the output of _create_txin_signature_hash()), * and the $key for the signature to be tested against. * Returns TRUE if the signature is valid for this $hash and $key, * otherwise returns FALSE. * * @param string $sig * @param string $hash * @param string $key * @param bool $allowHighS * @return bool */ public static function _check_sig($sig, $hash, $key, $allowHighS = self::ALLOW_HIGH_S) { $math = EccFactory::getAdapter(); $generator = EccFactory::getSecgCurves()->generator256k1(); $curve = $generator->getCurve(); $hash = $math->hexDec($hash); $decodedSignature = self::decode_signature($sig); $signature = new Signature($math->hexDec($decodedSignature['r']), $math->hexDec($decodedSignature['s'])); if (!$allowHighS) { // if S is > half then someone should have fixed it if (self::check_signature_is_high_s($signature)) { return false; } } if (strlen($key) == '66') { $decompress = BitcoinLib::decompress_public_key($key); $public_key_point = $decompress['point']; } else { $x = $math->hexDec(substr($key, 2, 64)); $y = $math->hexDec(substr($key, 66, 64)); $public_key_point = $curve->getPoint($x, $y); } $signer = new Signer($math); $public_key = new PublicKey($math, $generator, $public_key_point); return $signer->verify($public_key, $signature, $hash) == true; }
/** * @dataProvider getDeterministicSign2Data */ public function testDeterministicSign2($curve, $size, $algo, $privKey, $message, $eK, $eR, $eS) { //echo "Try {$test->curve} / {$test->algorithm} / '{$test->message}'\n"; $G = CurveFactory::getGeneratorByName($curve); // Initialize private key and message hash (decimal) $privateKey = $G->getPrivateKeyFrom($this->math->hexDec($privKey)); $hashHex = hash($algo, $message); $messageHash = $this->math->hexDec($hashHex); // Derive K $drbg = RandomGeneratorFactory::getHmacRandomGenerator($privateKey, $messageHash, $algo); $k = $drbg->generate($G->getOrder()); $this->assertEquals($this->math->hexdec($eK), $k, 'k'); $hexSize = strlen($hashHex); $hashBits = $this->math->baseConvert($messageHash, 10, 2); if (strlen($hashBits) < $hexSize * 4) { $hashBits = str_pad($hashBits, $hexSize * 4, '0', STR_PAD_LEFT); } $messageHash = $this->math->baseConvert(substr($hashBits, 0, $size), 2, 10); $signer = new Signer($this->math); $sig = $signer->sign($privateKey, $messageHash, $k); // R and S should be correct $sR = $this->math->hexDec($eR); $sS = $this->math->hexDec($eS); $this->assertTrue($signer->verify($privateKey->getPublicKey(), $sig, $messageHash)); $this->assertSame($sR, $sig->getR(), 'r'); $this->assertSame($sS, $sig->getS(), 's'); }
/** * 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; }