def test_rfc6979_tv(self): fname = "rfc6979.json" filename = path.join(path.dirname(__file__), "test_data", fname) with open(filename, "r") as f: test_dict = json.load(f) for ec_name in test_dict: ec = CURVES[ec_name] test_vectors = test_dict[ec_name] for x, x_U, y_U, hf, msg, k, r, s in test_vectors: x = int(x, 16) # test RFC6979 implementation k2 = rfc6979(msg, x, ec, eval("hashlib." + hf)) self.assertEqual(k, hex(k2)) # test RFC6979 usage in DSA sig = dsa.sign(msg, x, k2, ec, eval("hashlib." + hf)) self.assertEqual(r, hex(sig[0])) self.assertIn(s, (hex(sig[1]), hex(ec.n - sig[1]))) # test that RFC6979 is the default nonce for DSA sig = dsa.sign(msg, x, k=None, ec=ec, hf=eval("hashlib." + hf)) self.assertEqual(r, hex(sig[0])) self.assertIn(s, (hex(sig[1]), hex(ec.n - sig[1]))) # test key-pair coherence U = mult(x, ec.G, ec) self.assertEqual((int(x_U, 16), int(y_U, 16)), U) # test signature validity dsa._verify(msg, U, sig, ec, hf=eval("hashlib." + hf))
def test_signature(self): ec = secp256k1 hf = sha256 q = 0x1 Q = mult(ec, q, ec.G) msg = 'Satoshi Nakamoto'.encode() sig = dsa.sign(ec, hf, msg, q) # https://bitcointalk.org/index.php?topic=285142.40 # Deterministic Usage of DSA and ECDSA (RFC 6979) exp_sig = ( 0x934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d8, 0x2442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5) r, s = sig self.assertEqual(sig[0], exp_sig[0]) self.assertIn(sig[1], (exp_sig[1], ec.n - exp_sig[1])) self.assertTrue(dsa.verify(ec, hf, msg, Q, sig)) self.assertTrue(dsa._verify(ec, hf, msg, Q, sig)) # malleability malleated_sig = (r, ec.n - s) self.assertTrue(dsa.verify(ec, hf, msg, Q, malleated_sig)) self.assertTrue(dsa._verify(ec, hf, msg, Q, malleated_sig)) keys = dsa.pubkey_recovery(ec, hf, msg, sig) self.assertTrue(len(keys) == 2) self.assertIn(Q, keys) fmsg = 'Craig Wright'.encode() self.assertFalse(dsa.verify(ec, hf, fmsg, Q, sig)) self.assertFalse(dsa._verify(ec, hf, fmsg, Q, sig)) fdsasig = (sig[0], sig[1], sig[1]) self.assertFalse(dsa.verify(ec, hf, msg, Q, fdsasig)) self.assertRaises(TypeError, dsa._verify, ec, hf, msg, Q, fdsasig) fq = 0x4 fQ = mult(ec, fq, ec.G) self.assertFalse(dsa.verify(ec, hf, msg, fQ, sig)) self.assertFalse(dsa._verify(ec, hf, msg, fQ, sig)) # r not in [1, n-1] invalid_dassig = 0, sig[1] self.assertFalse(dsa.verify(ec, hf, msg, Q, invalid_dassig)) # s not in [1, n-1] invalid_dassig = sig[0], 0 self.assertFalse(dsa.verify(ec, hf, msg, Q, invalid_dassig)) # pubkey = Inf self.assertRaises(ValueError, dsa._verify, ec, hf, msg, (1, 0), sig) #dsa._verify(ec, hf, msg, (1, 0), sig) # private key not in [1, n-1] self.assertRaises(ValueError, dsa.sign, ec, hf, msg, 0) #dsa.sign(ec, hf, msg, 0) # ephemeral key not in [1, n-1] self.assertRaises(ValueError, dsa.sign, ec, hf, msg, 1, 0)
def test_gec(self): """ GEC 2: Test Vectors for SEC 1, section 2 http://read.pudn.com/downloads168/doc/772358/TestVectorsforSEC%201-gec2.pdf """ # 2.1.1 Scheme setup ec = secp160r1 hf = sha1 # 2.1.2 Key Deployment for U dU = 971761939728640320549601132085879836204587084162 self.assertEqual(format(dU, str(ec.psize) + 'x'), 'aa374ffc3ce144e6b073307972cb6d57b2a4e982') QU = mult(ec, dU, ec.G) self.assertEqual(QU, (466448783855397898016055842232266600516272889280, 1110706324081757720403272427311003102474457754220)) self.assertEqual( octets_from_point(ec, QU, True).hex(), '0251b4496fecc406ed0e75a24a3c03206251419dc0') # 2.1.3 Signing Operation for U msg = 'abc'.encode() k = 702232148019446860144825009548118511996283736794 exp_sig = (0xCE2873E5BE449563391FEB47DDCBA2DC16379191, 0x3480EC1371A091A464B31CE47DF0CB8AA2D98B54) sig = dsa.sign(ec, hf, msg, dU, k) r, s = sig self.assertEqual(r, exp_sig[0]) self.assertIn(s, (exp_sig[1], ec.n - exp_sig[1])) # 2.1.4 Verifying Operation for V self.assertTrue(dsa.verify(ec, hf, msg, QU, sig)) self.assertTrue(dsa._verify(ec, hf, msg, QU, sig))
def test_pubkey_recovery(self): ec = secp112r2 hf = sha256 q = 0x1 Q = mult(ec, q, ec.G) msg = 'Satoshi Nakamoto'.encode() sig = dsa.sign(ec, hf, msg, q) self.assertTrue(dsa.verify(ec, hf, msg, Q, sig)) self.assertTrue(dsa._verify(ec, hf, msg, Q, sig)) keys = dsa.pubkey_recovery(ec, hf, msg, sig) self.assertIn(Q, keys) for Q in keys: self.assertTrue(dsa.verify(ec, hf, msg, Q, sig)) self.assertTrue(dsa._verify(ec, hf, msg, Q, sig))
def test_pubkey_recovery(self): ec = secp112r2 q = 0x10 Q = mult(q, ec.G, ec) msg = b'Satoshi Nakamoto' sig = dsa.sign(msg, q, None, ec) self.assertTrue(dsa.verify(msg, Q, sig, ec)) self.assertTrue(dsa._verify(msg, Q, sig, ec)) keys = dsa.pubkey_recovery(msg, sig, ec) self.assertEqual(len(keys), 4) self.assertIn(Q, keys) for Q in keys: self.assertTrue(dsa.verify(msg, Q, sig, ec)) self.assertTrue(dsa._verify(msg, Q, sig, ec))
def test_p2pk(): pubkey = "0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8" signature = "304402200A5C6163F07B8D3B013C4D1D6DBA25E780B39658D79BA37AF7057A3B7F15FFA102201FD9B4EAA9943F734928B99A83592C2E7BF342EA2680F6A2BB705167966B742001" script_pubkey = script.serialize([pubkey, "OP_CHECKSIG"]) script_sig = script.serialize([signature]) founding_tx_script = script.serialize([0, 0]) founding_tx = Tx( 1, 0, vin=[ TxIn(OutPoint(b"\x00" * 32, 0xFFFFFFFF), founding_tx_script, 0xFFFFFFFF, []) ], vout=[TxOut(0, script_pubkey)], ) receiving_tx = Tx( 1, 0, vin=[TxIn(OutPoint(founding_tx.txid, 0), script_sig, 0xFFFFFFFF, [])], vout=[TxOut(0, b"")], ) sighash = get_sighash(receiving_tx, founding_tx.vout[0], 0, 0x01) assert dsa._verify(sighash, bytes.fromhex(pubkey), bytes.fromhex(signature)[:-1])
def test_p2pk_anyonecanpay(): pubkey = "048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf" signature = "304402204710a85181663b32d25c70ec2bbd14adff5ddfff6cb50d09e155ef5f541fc86c0220056b0cc949be9386ecc5f6c2ac0493269031dbb185781db90171b54ac127790281" script_pubkey = script.serialize([pubkey, "OP_CHECKSIG"]) script_sig = script.serialize([signature]) founding_tx_script = script.serialize([0, 0]) founding_tx = Tx( 1, 0, vin=[ TxIn(OutPoint(b"\x00" * 32, 0xFFFFFFFF), founding_tx_script, 0xFFFFFFFF, []) ], vout=[TxOut(0, script_pubkey)], ) receiving_tx = Tx( 1, 0, vin=[TxIn(OutPoint(founding_tx.txid, 0), script_sig, 0xFFFFFFFF, [])], vout=[TxOut(0, b"")], ) sighash = get_sighash(receiving_tx, founding_tx.vout[0], 0, 0x81) assert dsa._verify(sighash, bytes.fromhex(pubkey), bytes.fromhex(signature)[:-1])
def test_p2pkh(): pubkey = "038282263212C609D9EA2A6E3E172DE238D8C39CABD5AC1CA10646E23FD5F51508" signature = "304402206E05A6FE23C59196FFE176C9DDC31E73A9885638F9D1328D47C0C703863B8876022076FEB53811AA5B04E0E79F938EB19906CC5E67548BC555A8E8B8B0FC603D840C01" script_pubkey = script.serialize([ "OP_DUP", "OP_HASH160", "1018853670F9F3B0582C5B9EE8CE93764AC32B93", "OP_EQUALVERIFY", "OP_CHECKSIG", ]) script_sig = script.serialize([signature, pubkey]) founding_tx_script = script.serialize([0, 0]) founding_tx = Tx( 1, 0, vin=[ TxIn(OutPoint(b"\x00" * 32, 0xFFFFFFFF), founding_tx_script, 0xFFFFFFFF, []) ], vout=[TxOut(0, script_pubkey)], ) receiving_tx = Tx( 1, 0, vin=[TxIn(OutPoint(founding_tx.txid, 0), script_sig, 0xFFFFFFFF, [])], vout=[TxOut(0, b"")], ) sighash = get_sighash(receiving_tx, founding_tx.vout[0], 0, 0x01) assert dsa._verify(sighash, bytes.fromhex(pubkey), bytes.fromhex(signature)[:-1])
def test_signtocontract(self): prv = 0x1 pub = mult(prv) m = b"to be signed" c = b"to be committed" dsa_sig, dsa_receipt = ecdsa_commit_sign(c, m, prv, None) self.assertIsNone(dsa._verify(m, pub, dsa_sig, ec, sha256)) self.assertTrue(verify_commit(c, dsa_receipt)) # 32 bytes message for ECSSA m = sha256(m).digest() ssa_sig, ssa_receipt = ecssa_commit_sign(c, m, prv, None) self.assertIsNone(ssa._verify(m, pub, ssa_sig, ec, sha256)) self.assertTrue(verify_commit(c, ssa_receipt))
def test_legacy_p2pkh(): pubkey = "04280c8f66bf2ccaeb3f60a19ad4a06365f8bd6178aab0e709df2173df8f553366549aec336aae8742a84702b6c7c3052d89f5d76d535ec3716e72187956351613" signature = "3045022100ea43c4800d1a860ec89b5273898a146cfb01d34ff4c364d24a110c480d0e3f7502201c82735577f932f1ca8e1c54bf653e0f8e74e408fe83666bc85cac4472ec950801" script_sig = [signature, pubkey] previous_txout = TxOut( value=1051173696, script_pubkey=script.serialize([ "OP_DUP", "OP_HASH160", "82ac30f58baf99ec9d14e6181eee076f4e27f69c", "OP_EQUALVERIFY", "OP_CHECKSIG", ]), ) tx = Tx( 1, 0, vin=[ TxIn( OutPoint( bytes.fromhex( "d8343a35ba951684f2969eafe833d9e6fe436557b9707ae76802875952e860fc" ), 1, ), script_sig, 0xFFFFFFFF, [], ) ], vout=[ TxOut( 2017682, bytes.fromhex( "76a91413bd20236d0da56492c325dce289b4da35b4b5bd88ac"), ), TxOut( 1049154982, bytes.fromhex( "76a914da169b45781ca210f8c11617ba66bd843da76b1688ac"), ), ], ) sighash = get_sighash(tx, previous_txout, 0, 0x01) assert dsa._verify(sighash, bytes.fromhex(pubkey), bytes.fromhex(signature)[:-1])
def test_first_transaction(): transaction = Tx.deserialize( "0100000001c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704000000004847304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901ffffffff0200ca9a3b00000000434104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000" ) previous_txout = TxOut( value=5000000000, script_pubkey=bytes.fromhex( "410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac" ), ) sighash = get_sighash(transaction, previous_txout, 0, 0x01) assert (sighash.hex() == "7a05c6145f10101e9d6325494245adf1297d80f8f38d4d576d57cdba220bcb19") pubkey = "0411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3" signature = "304402204E45E16932B8AF514961A1D3A1A25FDF3F4F7732E9D624C6C61548AB5FB8CD410220181522EC8ECA07DE4860A4ACDD12909D831CC56CBBAC4622082221A8768D1D0901" assert dsa._verify(sighash, bytes.fromhex(pubkey), bytes.fromhex(signature)[:-1])
def test_sighashsingle_bug(): pubkey = "02D5C25ADB51B61339D2B05315791E21BBE80EA470A49DB0135720983C905AACE0" signature = "3045022100C9CDD08798A28AF9D1BAF44A6C77BCC7E279F47DC487C8C899911BC48FEAFFCC0220503C5C50AE3998A733263C5C0F7061B483E2B56C4C41B456E7D2F5A78A74C07703" script_pubkey = script.serialize([ "OP_DUP", "OP_HASH160", "5b6462475454710f3c22f5fdf0b40704c92f25c3", "OP_EQUALVERIFY", "OP_CHECKSIGVERIFY", 1, ]) previous_txout = TxOut(0, script_pubkey) tx = Tx.deserialize( "01000000020002000000000000000000000000000000000000000000000000000000000000000000000151ffffffff0001000000000000000000000000000000000000000000000000000000000000000000006b483045022100c9cdd08798a28af9d1baf44a6c77bcc7e279f47dc487c8c899911bc48feaffcc0220503c5c50ae3998a733263c5c0f7061b483e2b56c4c41b456e7d2f5a78a74c077032102d5c25adb51b61339d2b05315791e21bbe80ea470a49db0135720983c905aace0ffffffff010000000000000000015100000000" ) sighash = get_sighash(tx, previous_txout, 1, 0x03) assert dsa._verify(sighash, bytes.fromhex(pubkey), bytes.fromhex(signature)[:-1])
def test_ledger(self): """Hybrid ECDSA Bitcoin message signature generated by Ledger""" mnemonic = "barely sun snack this snack relief pipe attack disease boss enlist lawsuit" # non-standard leading 31 in DER serialization derivation_path = "m/1" msg = b"\xfb\xa3\x1f\x8cd\x85\xe29#K\xb3{\xfd\xa7<?\x95oL\xee\x19\xb2'oh\xa7]\xd9A\xfeU\xd8" dersig = '3144022012ec0c174936c2a46dc657252340b2e6e6dd8c31dd059b6f9f33a90c21af2fba022030e6305b3ccf88009d419bf7651afcfcc0a30898b93ae9de9aa6ac03cf8ec56b' # pubkey derivation rprv = bip32.rootxprv_from_bip39mnemonic(mnemonic) xprv = bip32.derive(rprv, derivation_path) xpub = bip32.xpub_from_xprv(xprv) # the actual message being signed magic_msg = bms._magic_hash(msg) # save key_id and patch dersig dersig = bytes.fromhex(dersig) key_id = dersig[0] dersig = b'\x30' + dersig[1:] r, s = dsa.deserialize(dersig) # ECDSA signature verification of the patched dersig dsa._verify(magic_msg, xpub, dersig, ec, hf) self.assertTrue(dsa.verify(magic_msg, xpub, dersig)) # compressed address addr = base58address.p2pkh(xpub) # equivalent Bitcoin Message Signature (non-serialized) rec_flag = 27 + 4 + (key_id & 0x01) btcmsgsig = (rec_flag, r, s) # Bitcoin Message Signature verification bms._verify(msg, addr, btcmsgsig) self.assertTrue(bms.verify(msg, addr, btcmsgsig)) self.assertFalse(bms.verify(magic_msg, addr, btcmsgsig)) bms.sign(msg, xprv) # standard leading 30 in DER serialization derivation_path = 'm/0/0' msg = "hello world" dersig = "3045022100967dac3262b4686e89638c8219c5761017f05cd87a855edf034f4a3ec6b59d3d0220108a4ef9682b71a45979d8c75c393382d9ccb8eb561d73b8c5fc0b87a47e7d27" # pubkey derivation rprv = bip32.rootxprv_from_bip39mnemonic(mnemonic) xprv = bip32.derive(rprv, derivation_path) xpub = bip32.xpub_from_xprv(xprv) # the actual message being signed magic_msg = bms._magic_hash(msg) # save key_id and patch dersig dersig = bytes.fromhex(dersig) key_id = dersig[0] dersig = b'\x30' + dersig[1:] r, s = dsa.deserialize(dersig) # ECDSA signature verification of the patched dersig dsa._verify(magic_msg, xpub, dersig, ec, hf) self.assertTrue(dsa.verify(magic_msg, xpub, dersig)) # compressed address addr = base58address.p2pkh(xpub) # equivalent Bitcoin Message Signature (non-serialized) rec_flag = 27 + 4 + (key_id & 0x01) btcmsgsig = (rec_flag, r, s) # Bitcoin Message Signature verification bms._verify(msg, addr, btcmsgsig) self.assertTrue(bms.verify(msg, addr, btcmsgsig)) self.assertFalse(bms.verify(magic_msg, addr, btcmsgsig))