curve = \Elliptic\Curves::getCurve($name); $this->assertNotNull($this->curve); $this->ecdsa = new \Elliptic\EC($this->curve); $this->keys = $this->ecdsa->genKeyPair([ "entropy" => self::$entropy ]); return [$this->curve, $this->ecdsa, $this->keys]; } /** * @dataProvider ECDSACurveNames */ public function test_should_generate_proper_key_pair($name) { list($curve, $ecdsa, $keys) = $this->prepare($name); $keylen = 64; if ($name == 'p384') { $keylen = 96; } else if ($name == 'p521') { $keylen = 132; } // Get keys out of pair $this->assertTrue( $keys->getPublic()->x && $keys->getPublic()->y ); $this->assertTrue( $keys->getPrivate()->byteLength() > 0); $this->assertEquals( strlen($keys->getPrivate('hex')), $keylen); $this->assertTrue( strlen($keys->getPublic('hex')) > 0); $this->assertTrue( strlen($keys->getPrivate('hex')) > 0); $this->assertTrue( $keys->validate()["result"], 'key validate' ); } /** * @dataProvider ECDSACurveNames */ public function test_should_sign_and_verify($name) { list($curve, $ecdsa, $keys) = $this->prepare($name); $signature = $ecdsa->sign(self::$msg, $keys); $this->assertTrue($ecdsa->verify(self::$msg, $signature, $keys), 'Normal verify'); } /** * @dataProvider ECDSACurveNames */ public function test_should_sign_and_verify_using_keys_methods($name) { list($curve, $ecdsa, $keys) = $this->prepare($name); $signature = $keys->sign(self::$msg); $this->assertTrue($keys->verify(self::$msg, $signature), 'On-key verify'); } /** * @dataProvider ECDSACurveNames */ public function test_should_load_private_key_from_the_hex_value($name) { list($curve, $ecdsa, $keys) = $this->prepare($name); $copy = $ecdsa->keyFromPrivate($keys->getPrivate('hex'), 'hex'); $signature = $ecdsa->sign(self::$msg, $copy); $this->assertTrue($ecdsa->verify(self::$msg, $signature, $copy), 'hex-private verify'); } /** * @dataProvider ECDSACurveNames */ public function test_should_have_signature_s_leq_keys_ec_nh($name) { list($curve, $ecdsa, $keys) = $this->prepare($name); // key.sign(msg, options) $sign = $keys->sign('deadbeef', [ "canonical" => true ]); $this->assertTrue($sign->s->cmp($keys->ec->nh) <= 0); } /** * @dataProvider ECDSACurveNames */ public function test_should_support_options_k($name) { list($curve, $ecdsa, $keys) = $this->prepare($name); $sign = $keys->sign(self::$msg, [ "k" => function($iter) { $this->assertTrue($iter >= 0); return new BN(1358); } ]); $this->assertTrue($ecdsa->verify(self::$msg, $sign, $keys), 'custom-k verify'); } /** * @dataProvider ECDSACurveNames */ public function test_should_have_another_signature_with_pers($name) { list($curve, $ecdsa, $keys) = $this->prepare($name); $sign1 = $keys->sign(self::$msg); $sign2 = $keys->sign(self::$msg, [ "pers" => '1234', "persEnc" => 'hex' ]); $this->assertNotEquals($sign1->r->toString('hex') . $sign1->s->toString('hex'), $sign2->r->toString('hex') . $sign2->s->toString('hex')); } /** * @dataProvider ECDSACurveNames */ public function test_should_load_public_key_from_compact_hex_value($name) { list($curve, $ecdsa, $keys) = $this->prepare($name); $pub = $keys->getPublic(true, 'hex'); $copy = $ecdsa->keyFromPublic($pub, 'hex'); $this->assertEquals($copy->getPublic(true, 'hex'), $pub); } /** * @dataProvider ECDSACurveNames */ public function test_should_load_public_key_from_hex_value($name) { list($curve, $ecdsa, $keys) = $this->prepare($name); $pub = $keys->getPublic('hex'); $copy = $ecdsa->keyFromPublic($pub, 'hex'); $this->assertEquals($copy->getPublic('hex'), $pub); } /** * @dataProvider ECDSACurveNames */ public function test_should_support_hex_DER_encoding_of_signatures($name) { list($curve, $ecdsa, $keys) = $this->prepare($name); $signature = $ecdsa->sign(self::$msg, $keys); $dsign = $signature->toDER('hex'); $this->assertTrue($ecdsa->verify(self::$msg, $dsign, $keys), 'hex-DER encoded verify'); } /** * @dataProvider ECDSACurveNames */ public function test_should_support_DER_encoding_of_signatures($name) { list($curve, $ecdsa, $keys) = $this->prepare($name); $signature = $ecdsa->sign(self::$msg, $keys); $dsign = $signature->toDER(); $this->assertTrue($ecdsa->verify(self::$msg, $dsign, $keys), 'DER encoded verify'); } /** * @dataProvider ECDSACurveNames */ public function test_should_not_verify_signature_with_wrong_public_key($name) { list($curve, $ecdsa, $keys) = $this->prepare($name); $signature = $ecdsa->sign(self::$msg, $keys); $wrong = $ecdsa->genKeyPair(); $this->assertNotTrue($ecdsa->verify(self::$msg, $signature, $wrong), 'Wrong key verify'); } /** * @dataProvider ECDSACurveNames */ public function test_should_not_verify_signature_with_wrong_private_key($name) { list($curve, $ecdsa, $keys) = $this->prepare($name); $signature = $ecdsa->sign(self::$msg, $keys); $wrong = $ecdsa->keyFromPrivate($keys->getPrivate('hex') . $keys->getPrivate('hex'), 'hex'); $this->assertNotTrue($ecdsa->verify(self::$msg, $signature, $wrong), 'Wrong key verify'); } // TODO: Implement RFC6979 vectors test function MaxwellsTrickVector() { $p256 = \Elliptic\Curves::getCurve("p256"); $p384 = \Elliptic\Curves::getCurve("p384"); $msg = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'; return [ [[ "curve" => $p256, "pub" => '041548fc88953e06cd34d4b300804c5322cb48c24aaaa4d0' . '7a541b0f0ccfeedeb0ae4991b90519ea405588bdf699f5e6' . 'd0c6b2d5217a5c16e8371062737aa1dae1', "message" => $msg, "sig" => '3006020106020104', "result" => true ]], [[ "curve" => $p256, "pub" => '04ad8f60e4ec1ebdb6a260b559cb55b1e9d2c5ddd43a41a2' . 'd11b0741ef2567d84e166737664104ebbc337af3d861d352' . '4cfbc761c12edae974a0759750c8324f9a', "message" => $msg, "sig" => '3006020106020104', "result" => true ]], [[ "curve" => $p256, "pub" => '0445bd879143a64af5746e2e82aa65fd2ea07bba4e355940' . '95a981b59984dacb219d59697387ac721b1f1eccf4b11f43' . 'ddc39e8367147abab3084142ed3ea170e4', "message" => $msg, "sig" => '301502104319055358e8617b0c46353d039cdaae020104', "result" => true ]], [[ "curve" => $p256, "pub" => '040feb5df4cc78b35ec9c180cc0de5842f75f088b4845697' . '8ffa98e716d94883e1e6500b2a1f6c1d9d493428d7ae7d9a' . '8a560fff30a3d14aa160be0c5e7edcd887', "message" => $msg, "sig" => '301502104319055358e8617b0c46353d039cdaae020104', "result" => false ]], [[ "curve" => $p384, "pub" => '0425e299eea9927b39fa92417705391bf17e8110b4615e9e' . 'b5da471b57be0c30e7d89dbdc3e5da4eae029b300344d385' . '1548b59ed8be668813905105e673319d59d32f574e180568' . '463c6186864888f6c0b67b304441f82aab031279e48f047c31', "message" => $msg, "sig" => '3006020103020104', "result" => true ]], [[ "curve" => $p384, "pub" => '04a328f65c22307188b4af65779c1d2ec821c6748c6bd8dc' . '0e6a008135f048f832df501f7f3f79966b03d5bef2f187ec' . '34d85f6a934af465656fb4eea8dd9176ab80fbb4a27a649f' . '526a7dfe616091b78d293552bc093dfde9b31cae69d51d3afb', "message" => $msg, "sig" => '3006020103020104', "result" => true ]], [[ "curve" => $p384, "pub" => '04242e8585eaa7a28cc6062cab4c9c5fd536f46b17be1728' . '288a2cda5951df4941aed1d712defda023d10aca1c5ee014' . '43e8beacd821f7efa27847418ab95ce2c514b2b6b395ee73' . '417c83dbcad631421f360d84d64658c98a62d685b220f5aad4', "message" => $msg, "sig" => '301d0218389cb27e0bc8d21fa7e5f24cb74f58851313e696333ad68e020104', "result" => true ]], [[ "curve" => $p384, "pub" => '04cdf865dd743fe1c23757ec5e65fd5e4038b472ded2af26' . '1e3d8343c595c8b69147df46379c7ca40e60e80170d34a11' . '88dbb2b6f7d3934c23d2f78cfb0db3f3219959fad63c9b61' . '2ef2f20d679777b84192ce86e781c14b1bbb77eacd6e0520e2', "message" => $msg, "sig" => '301d0218389cb27e0bc8d21fa7e5f24cb74f58851313e696333ad68e020104', "result" => false ]] ]; } /** * @dataProvider MaxwellsTrickVector */ public function test_should_pass_on_Maxwells_trick_vectors($vector) { $ecdsa = new \Elliptic\EC($vector["curve"]); $key = $ecdsa->keyFromPublic($vector["pub"], 'hex'); $msg = $vector["message"]; $sig = $vector["sig"]; $actual = $ecdsa->verify($msg, $sig, $key); $this->assertEquals($actual, $vector["result"]); } public function test_should_deterministically_generate_private_key() { $curve = \Elliptic\Curves::getCurve("secp256k1"); $this->assertNotNull($curve); $ecdsa = new \Elliptic\EC($curve); $keys = $ecdsa->genKeyPair(array( "pers" => 'my.pers.string', "entropy" => hash('sha256', 'hello world', true) )); $this->assertEquals( $keys->getPrivate('hex'), '6160edb2b218b7f1394b9ca8eb65a72831032a1f2f3dc2d99291c2f7950ed887'); } public function test_should_recover_the_public_key_from_a_signature() { $ec = new \Elliptic\EC('secp256k1'); $key = $ec->genKeyPair(); $msg = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; $signature = $key->sign($msg); $recid = $ec->getKeyRecoveryParam($msg, $signature, $key->getPublic()); $r = $ec->recoverPubKey($msg, $signature, $recid); $this->assertTrue($key->getPublic()->eq($r), 'the keys should match'); } public function test_should_fail_to_recover_key_when_no_quadratic_residue_available() { $ec = new \Elliptic\EC('secp256k1'); $message = 'f75c6b18a72fabc0f0b888c3da58e004f0af1fe14f7ca5d8c897fe164925d5e9'; $this->expectException(\Exception::class); $ec->recoverPubKey($message, [ "r" => 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', "s" => '8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3' ], 0); } }