/** * * @dataProvider getAdapters */ public function testSecp256r1EquivalenceToNistP192(MathAdapterInterface $adapter) { $secpFactory = EccFactory::getSecgCurves($adapter); $nistFactory = EccFactory::getNistCurves($adapter); $signer = new Signer($adapter); $secret = $adapter->hexDec('DC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F'); $secpKey = $secpFactory->generator256r1()->getPrivateKeyFrom($secret); $nistKey = $nistFactory->generator256()->getPrivateKeyFrom($secret); $randomK = RandomGeneratorFactory::getRandomGenerator()->generate($secpKey->getPoint()->getOrder()); $message = RandomGeneratorFactory::getRandomGenerator()->generate($secpKey->getPoint()->getOrder()); $sigSecp = $signer->sign($secpKey, $message, $randomK); $sigNist = $signer->sign($nistKey, $message, $randomK); $this->assertEquals($sigNist->getR(), $sigSecp->getR()); $this->assertEquals($sigNist->getS(), $sigSecp->getS()); }
/** * @param string $content * @param Binary $key * @return string */ public function sign($content, Binary $key) { $math = MathAdapterFactory::getAdapter(); $inflatedPrivateKey = $this->deserializePrivate($key->getData(), $math); $rng = RandomGeneratorFactory::getRandomGenerator(); $hash = $this->hash($content); $signer = new Signer($math); $signature = $signer->sign($inflatedPrivateKey, $hash, $rng->generate($inflatedPrivateKey->getPoint()->getOrder())); return $signature->getR() . self::$SIGNATURE_GLUE . $signature->getS(); }
/** * {@inheritdoc} */ public function createHash(string $payload, Key $key, RandomNumberGeneratorInterface $generator = null) : string { $privateKey = $this->parser->getPrivateKey($key); $generator = $generator ?: RandomGeneratorFactory::getRandomGenerator(); return $this->createSignatureHash($this->signer->sign($privateKey, $this->createSigningHash($payload), $generator->generate($privateKey->getPoint()->getOrder()))); }
/** * @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.'); }
/** * Sign * * This function accepts the same parameters as signrawtransaction. * $raw_transaction is a hex encoded string for an unsigned/partially * signed transaction. $inputs is an array, containing the txid/vout/ * scriptPubKey/redeemscript. $priv_keys contains WIF keys. * * The function looks at each TxIn and tries to sign, if the hash160 * belongs to a key specified in the wallet. * * @param array $wallet * @param string $raw_transaction * @param string $inputs * @param string $magic_byte * @param string $magic_p2sh_byte * @return array */ public static function sign($wallet, $raw_transaction, $inputs, $magic_byte = null, $magic_p2sh_byte = null) { $math = EccFactory::getAdapter(); $generator = EccFactory::getSecgCurves($math)->generator256k1(); $magic_byte = BitcoinLib::magicByte($magic_byte); $magic_p2sh_byte = BitcoinLib::magicP2SHByte($magic_p2sh_byte); // Generate digests of inputs to sign. $message_hash = self::_create_txin_signature_hash($raw_transaction, $inputs); $inputs_arr = (array) json_decode($inputs); // Generate an association of expected hash160's and related information. $decode = self::decode($raw_transaction); $req_sigs = 0; $sign_count = 0; foreach ($decode['vin'] as $vin => $input) { $scriptPubKey = self::_decode_scriptPubKey($inputs_arr[$vin]->scriptPubKey); $tx_info = self::_get_transaction_type($scriptPubKey, $magic_byte, $magic_p2sh_byte); if (isset($wallet[$tx_info['hash160']])) { $key_info = $wallet[$tx_info['hash160']]; $message_hash_dec = $math->hexDec($message_hash[$vin]); if ($key_info['type'] == 'scripthash') { $signatures = self::extract_input_signatures_p2sh($input, $message_hash[$vin], $key_info); $sign_count += count($signatures); // Create Signature foreach ($key_info['keys'] as $key) { $key_dec = $math->hexDec($key['private_key']); $k = $math->hexDec((string) bin2hex(mcrypt_create_iv(32, \MCRYPT_DEV_URANDOM))); $signer = new Signer($math); $_private_key = $generator->getPrivateKeyFrom($key_dec); $sign = $signer->sign($_private_key, $message_hash_dec, $k); if ($sign !== false) { $sign_count++; $signatures[$key['public_key']] = self::encode_signature($sign); } } $decode['vin'][$vin]['scriptSig']['hex'] = self::_apply_sig_scripthash_multisig($signatures, $key_info); // Increase required # signature counter. $req_sigs += $key_info['required_signature_count']; } if ($key_info['type'] == 'pubkeyhash') { $key_dec = $math->hexDec($key_info['private_key']); $signer = new Signer($math); $_private_key = $generator->getPrivateKeyFrom($key_dec); $sign = $signer->sign($_private_key, $message_hash_dec, $math->hexDec((string) bin2hex(mcrypt_create_iv(32, \MCRYPT_DEV_URANDOM)))); if ($sign !== false) { $sign_count++; $decode['vin'][$vin]['scriptSig']['hex'] = self::_apply_sig_pubkeyhash(self::encode_signature($sign), $key_info['public_key']); } $req_sigs++; } } else { $req_sigs++; } } $new_raw = self::encode($decode); // If the transaction isn't fully signed, return false. // If it's fully signed, perform signature verification, return true if valid, or invalid if signatures are incorrect. $complete = $req_sigs - $sign_count <= 0 ? self::validate_signed_transaction($new_raw, $inputs, $magic_byte, $magic_p2sh_byte) == true ? 'true' : 'false' : 'false'; return array('hex' => $new_raw, 'complete' => $complete, 'sign_count' => $sign_count, 'req_sigs' => $req_sigs); }
/** * @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'); }
/** * sign a message with specified private key * * @param $message * @param $privateKey * @param null $k used for testing, don't use it! * @return string * @throws \Exception */ public static function signMessage($message, $privateKey, $k = null) { $math = EccFactory::getAdapter(); $generator = EccFactory::getSecgCurves($math)->generator256k1(); $messageHash = "Bitcoin Signed Message:\n" . hex2bin(RawTransaction::_encode_vint(strlen($message))) . $message; $messageHash = hash('sha256', hash('sha256', $messageHash, true), true); $messageHash = $math->hexDec(bin2hex($messageHash)); $key_dec = $math->hexDec($privateKey['key']); $pubKey = self::private_key_to_public_key($privateKey['key'], false); $x = $math->hexDec(substr($pubKey, 2, 64)); $y = $math->hexDec(substr($pubKey, 66, 64)); $point = $generator->getCurve()->getPoint($x, $y); $_publicKey = new PublicKey($math, $generator, $point); $_privateKey = $generator->getPrivateKeyFrom($key_dec); $signer = new Signer($math); $sign = $signer->sign($_privateKey, $messageHash, $k ?: $math->hexDec((string) bin2hex(mcrypt_create_iv(32, \MCRYPT_DEV_URANDOM)))); // calculate the recovery param // there should be a way to get this when signing too, but idk how ... $i = self::calcPubKeyRecoveryParam($sign->getR(), $sign->getS(), $messageHash, $_publicKey->getPoint()); return base64_encode(self::encodeMessageSignature($sign, $i, true)); }