tMerge pull request #5820 from SomberNight/201912_ecdsa_sig_r_grinding - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit a6aa97c3e3a248f24a3e0c5d85f1f48bac230af2 DIR parent 2e4cfd07445bc3f38090fdb3cd52612a55470469 HTML Author: ThomasV <thomasv@electrum.org> Date: Fri, 6 Dec 2019 20:38:46 +0100 Merge pull request #5820 from SomberNight/201912_ecdsa_sig_r_grinding ECDSA signatures: grind low R to match with Bitcoin Core (take 2) Diffstat: M contrib/requirements/requirements.… | 2 +- M electrum/ecc.py | 10 +++++++++- M electrum/ecc_fast.py | 26 ++++++++++++++++++-------- M electrum/tests/test_bitcoin.py | 8 ++++---- M electrum/tests/test_commands.py | 2 +- M electrum/tests/test_lnutil.py | 4 ++-- M electrum/tests/test_wallet_vertica… | 96 ++++++++++++++++---------------- M electrum/transaction.py | 9 +++++++-- 8 files changed, 90 insertions(+), 67 deletions(-) --- DIR diff --git a/contrib/requirements/requirements.txt b/contrib/requirements/requirements.txt t@@ -1,5 +1,5 @@ pyaes>=0.1a1 -ecdsa>=0.13.3 +ecdsa>=0.14 qrcode protobuf dnspython DIR diff --git a/electrum/ecc.py b/electrum/ecc.py t@@ -414,7 +414,15 @@ class ECPrivkey(ECPubkey): if sigdecode is None: sigdecode = get_r_and_s_from_sig_string private_key = _MySigningKey.from_secret_exponent(self.secret_scalar, curve=SECP256k1) - sig = private_key.sign_digest_deterministic(data, hashfunc=hashlib.sha256, sigencode=sigencode) + def sig_encode_r_s(r, s, order): + return r, s + r, s = private_key.sign_digest_deterministic(data, hashfunc=hashlib.sha256, sigencode=sig_encode_r_s) + counter = 0 + while r >= 2**255: # grind for low R value https://github.com/bitcoin/bitcoin/pull/13666 + counter += 1 + extra_entropy = int.to_bytes(counter, 32, 'little') + r, s = private_key.sign_digest_deterministic(data, hashfunc=hashlib.sha256, sigencode=sig_encode_r_s, extra_entropy=extra_entropy) + sig = sigencode(r, s, CURVE_ORDER) public_key = private_key.get_verifying_key() if not public_key.verify_digest(sig, data, sigdecode=sigdecode): raise Exception('Sanity check verifying our own signature failed.') DIR diff --git a/electrum/ecc_fast.py b/electrum/ecc_fast.py t@@ -179,14 +179,24 @@ def _prepare_monkey_patching_of_python_ecdsa_internals_with_libsecp256k1(): nonce_function = None sig = create_string_buffer(64) sig_hash_bytes = hash.to_bytes(32, byteorder="big") - r = _libsecp256k1.secp256k1_ecdsa_sign( - _libsecp256k1.ctx, sig, sig_hash_bytes, secret_exponent.to_bytes(32, byteorder="big"), nonce_function, None) - if not r: - raise Exception('the nonce generation function failed, or the private key was invalid') - compact_signature = create_string_buffer(64) - _libsecp256k1.secp256k1_ecdsa_signature_serialize_compact(_libsecp256k1.ctx, compact_signature, sig) - r = int.from_bytes(compact_signature[:32], byteorder="big") - s = int.from_bytes(compact_signature[32:], byteorder="big") + def sign_with_extra_entropy(extra_entropy): + ret = _libsecp256k1.secp256k1_ecdsa_sign( + _libsecp256k1.ctx, sig, sig_hash_bytes, secret_exponent.to_bytes(32, byteorder="big"), + nonce_function, extra_entropy) + if not ret: + raise Exception('the nonce generation function failed, or the private key was invalid') + compact_signature = create_string_buffer(64) + _libsecp256k1.secp256k1_ecdsa_signature_serialize_compact(_libsecp256k1.ctx, compact_signature, sig) + r = int.from_bytes(compact_signature[:32], byteorder="big") + s = int.from_bytes(compact_signature[32:], byteorder="big") + return r, s + + r, s = sign_with_extra_entropy(extra_entropy=None) + counter = 0 + while r >= 2**255: # grind for low R value https://github.com/bitcoin/bitcoin/pull/13666 + counter += 1 + extra_entropy = counter.to_bytes(32, byteorder="little") + r, s = sign_with_extra_entropy(extra_entropy=extra_entropy) return ecdsa.ecdsa.Signature(r, s) def verify(self: ecdsa.ecdsa.Public_key, hash: int, signature: ecdsa.ecdsa.Signature) -> bool: DIR diff --git a/electrum/tests/test_bitcoin.py b/electrum/tests/test_bitcoin.py t@@ -175,8 +175,8 @@ class Test_bitcoin(ElectrumTestCase): sig1_b64 = base64.b64encode(sig1) sig2_b64 = base64.b64encode(sig2) - self.assertEqual(sig1_b64, b'H/9jMOnj4MFbH3d7t4yCQ9i7DgZU/VZ278w3+ySv2F4yIsdqjsc5ng3kmN8OZAThgyfCZOQxZCWza9V5XzlVY0Y=') - self.assertEqual(sig2_b64, b'G84dmJ8TKIDKMT9qBRhpX2sNmR0y5t+POcYnFFJCs66lJmAs3T8A6Sbpx7KA6yTQ9djQMabwQXRrDomOkIKGn18=') + self.assertEqual(sig1_b64, b'Hzsu0U/THAsPz/MSuXGBKSULz2dTfmrg1NsAhFp+wH5aKfmX4Db7ExLGa7FGn0m6Mf43KsbEOWpvUUUBTM3Uusw=') + self.assertEqual(sig2_b64, b'HBQdYfv7kOrxmRewLJnG7sV6KlU71O04hUnE4tai97p7Pg+D+yKaWXsdGgHTrKw90caQMo/D6b//qX50ge9P9iI=') self.assertTrue(ecc.verify_message_with_address(addr1, sig1, msg1)) self.assertTrue(ecc.verify_message_with_address(addr2, sig2, msg2)) t@@ -211,11 +211,11 @@ class Test_bitcoin(ElectrumTestCase): def test_sign_transaction(self): eckey1 = ecc.ECPrivkey(bfh('7e1255fddb52db1729fc3ceb21a46f95b8d9fe94cc83425e936a6c5223bb679d')) sig1 = eckey1.sign_transaction(bfh('5a548b12369a53faaa7e51b5081829474ebdd9c924b3a8230b69aa0be254cd94')) - self.assertEqual(bfh('3045022100902a288b98392254cd23c0e9a49ac6d7920f171b8249a48e484b998f1874a2010220723d844826828f092cf400cb210c4fa0b8cd1b9d1a7f21590e78e022ff6476b9'), sig1) + self.assertEqual('3044022066e7d6a954006cce78a223f5edece8aaedcf3607142e9677acef1cfcb91cfdde022065cb0b5401bf16959ce7b785ea7fd408be5e4cb7d8f1b1a32c78eac6f73678d9', sig1.hex()) eckey2 = ecc.ECPrivkey(bfh('c7ce8c1462c311eec24dff9e2532ac6241e50ae57e7d1833af21942136972f23')) sig2 = eckey2.sign_transaction(bfh('642a2e66332f507c92bda910158dfe46fc10afbf72218764899d3af99a043fac')) - self.assertEqual(bfh('30440220618513f4cfc87dde798ce5febae7634c23e7b9254a1eabf486be820f6a7c2c4702204fef459393a2b931f949e63ced06888f35e286e446dc46feb24b5b5f81c6ed52'), sig2) + self.assertEqual('30440220618513f4cfc87dde798ce5febae7634c23e7b9254a1eabf486be820f6a7c2c4702204fef459393a2b931f949e63ced06888f35e286e446dc46feb24b5b5f81c6ed52', sig2.hex()) @needs_test_with_all_aes_implementations def test_aes_homomorphic(self): DIR diff --git a/electrum/tests/test_commands.py b/electrum/tests/test_commands.py t@@ -178,5 +178,5 @@ class TestCommandsTestnet(TestCaseForTestnet): } ] } - self.assertEqual("0200000000010139c5375fe9da7bd377c1783002b129f8c57d3e724d62f5eacb9739ca691a229d0100000000feffffff01301b0f0000000000160014ac0e2d229200bffb2167ed6fd196aef9d687d8bb02483045022100fa88a9e7930b2af269fd0a5cb7fbbc3d0a05606f3ac6ea8a40686ebf02fdd85802203dd19603b4ee8fdb81d40185572027686f70ea299c6a3e22bc2545e1396398b20121021f110909ded653828a254515b58498a6bafc96799fb0851554463ed44ca7d9da00000000", + self.assertEqual("0200000000010139c5375fe9da7bd377c1783002b129f8c57d3e724d62f5eacb9739ca691a229d0100000000feffffff01301b0f0000000000160014ac0e2d229200bffb2167ed6fd196aef9d687d8bb0247304402206367fb2ddd723985f5f51e0f2435084c0a66f5c26f4403a75d3dd417b71a20450220545dc3637bcb49beedbbdf5063e05cad63be91af4f839886451c30ecd6edf1d20121021f110909ded653828a254515b58498a6bafc96799fb0851554463ed44ca7d9da00000000", cmds._run('serialize', (jsontx,))) DIR diff --git a/electrum/tests/test_lnutil.py b/electrum/tests/test_lnutil.py t@@ -526,9 +526,9 @@ class TestLNUtil(ElectrumTestCase): TIMEOUT = False output_htlc_tx[0] = (SUCCESS, "020000000001018154ecccf11a5fb56c39654c4deb4d2296f83c69268280b94d021370c94e219700000000000000000001e8030000000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e050047304402206a6e59f18764a5bf8d4fa45eebc591566689441229c918b480fb2af8cc6a4aeb02205248f273be447684b33e3c8d1d85a8e0ca9fa0bae9ae33f0527ada9c162919a60147304402207cb324fa0de88f452ffa9389678127ebcf4cabe1dd848b8e076c1a1962bf34720220116ed922b12311bd602d67e60d2529917f21c5b82f25ff6506c0f87886b4dfd5012000000000000000000000000000000000000000000000000000000000000000008a76a91414011f7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a914b8bcb07f6344b42ab04250c86a6e8b75d3fdbbc688527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae677502f401b175ac686800000000") - output_htlc_tx[2] = (TIMEOUT, "020000000001018154ecccf11a5fb56c39654c4deb4d2296f83c69268280b94d021370c94e219701000000000000000001d0070000000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e0500483045022100d5275b3619953cb0c3b5aa577f04bc512380e60fa551762ce3d7a1bb7401cff9022037237ab0dac3fe100cde094e82e2bed9ba0ed1bb40154b48e56aa70f259e608b01483045022100c89172099507ff50f4c925e6c5150e871fb6e83dd73ff9fbb72f6ce829a9633f02203a63821d9162e99f9be712a68f9e589483994feae2661e4546cd5b6cec007be501008576a91414011f7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122cc726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae67a914b43e1b38138a41b37f7cd9a1d274bc63e3a9b5d188ac6868f6010000") + output_htlc_tx[2] = (TIMEOUT, "020000000001018154ecccf11a5fb56c39654c4deb4d2296f83c69268280b94d021370c94e219701000000000000000001d0070000000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e0500483045022100d5275b3619953cb0c3b5aa577f04bc512380e60fa551762ce3d7a1bb7401cff9022037237ab0dac3fe100cde094e82e2bed9ba0ed1bb40154b48e56aa70f259e608b0147304402205735e9f335dfd123f730ac5bf184fd7d5b672e4d84c51a3f0478cc229bb44936022018b1cec3e3b29e5cc335d7e326bc29d75a7e063216427d081cb83ebdbd828b4d01008576a91414011f7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122cc726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae67a914b43e1b38138a41b37f7cd9a1d274bc63e3a9b5d188ac6868f6010000") - output_htlc_tx[1] = (SUCCESS, "020000000001018154ecccf11a5fb56c39654c4deb4d2296f83c69268280b94d021370c94e219702000000000000000001d0070000000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e050047304402201b63ec807771baf4fdff523c644080de17f1da478989308ad13a58b51db91d360220568939d38c9ce295adba15665fa68f51d967e8ed14a007b751540a80b325f20201483045022100def389deab09cee69eaa1ec14d9428770e45bcbe9feb46468ecf481371165c2f022015d2e3c46600b2ebba8dcc899768874cc6851fd1ecb3fffd15db1cc3de7e10da012001010101010101010101010101010101010101010101010101010101010101018a76a91414011f7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a9144b6b2e5444c2639cc0fb7bcea5afba3f3cdce23988527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae677502f501b175ac686800000000") + output_htlc_tx[1] = (SUCCESS, "020000000001018154ecccf11a5fb56c39654c4deb4d2296f83c69268280b94d021370c94e219702000000000000000001d0070000000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e050047304402201b63ec807771baf4fdff523c644080de17f1da478989308ad13a58b51db91d360220568939d38c9ce295adba15665fa68f51d967e8ed14a007b751540a80b325f202014730440220481a48f83c358ae0f220e37f88e56b3d434cefaded82065b8e7a9fd78fee7a26022022674ab37a4c39e6efba302f760ca05931d8add8d65231c5bf34a6c2a76b15bf012001010101010101010101010101010101010101010101010101010101010101018a76a91414011f7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122cc726e9dded053a2184d88256816826d6231c068d4a5b7c8201208763a9144b6b2e5444c2639cc0fb7bcea5afba3f3cdce23988527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae677502f501b175ac686800000000") output_htlc_tx[3] = (TIMEOUT, "020000000001018154ecccf11a5fb56c39654c4deb4d2296f83c69268280b94d021370c94e219703000000000000000001b80b0000000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e0500483045022100daee1808f9861b6c3ecd14f7b707eca02dd6bdfc714ba2f33bc8cdba507bb182022026654bf8863af77d74f51f4e0b62d461a019561bb12acb120d3f7195d148a554014730440220643aacb19bbb72bd2b635bc3f7375481f5981bace78cdd8319b2988ffcc6704202203d27784ec8ad51ed3bd517a05525a5139bb0b755dd719e0054332d186ac0872701008576a91414011f7254d96b819c76986c277d115efce6f7b58763ac67210394854aa6eab5b2a8122cc726e9dded053a2184d88256816826d6231c068d4a5b7c820120876475527c21030d417a46946384f88d5f3337267c5e579765875dc4daca813e21734b140639e752ae67a9148a486ff2e31d6158bf39e2608864d63fefd09d5b88ac6868f7010000") DIR diff --git a/electrum/tests/test_wallet_vertical.py b/electrum/tests/test_wallet_vertical.py t@@ -603,10 +603,10 @@ class TestWalletSending(TestCaseForTestnet): tx_copy = tx_from_any(tx.serialize()) self.assertTrue(wallet2.is_mine(wallet2.get_txin_address(tx_copy.inputs()[0]))) - self.assertEqual('0100000001e228327e4c0bb80661d258d625f516307e7c127c7f3e2b476a22e89b4dae063c000000006b483045022100d3895b31e7c9766987c6f53794c7394f534f4acecefda5479d963236f9703d0b022026dd4e40700ceb788f136faf54bf85b966648dc7c2a608d8110604f2d22d59070121030b482838721a38d94847699fed8818b5c5f56500ef72f13489e365b65e5749cffeffffff02a0860100000000001600148a28bddb7f61864bdcf58b2ad13d5aeb3abc3c4268360200000000001976a914ca4c60999c46c2108326590b125aefd476dcb11888ac00000000', + self.assertEqual('0100000001e228327e4c0bb80661d258d625f516307e7c127c7f3e2b476a22e89b4dae063c000000006a47304402207d352950df94d1243917abe4fa5c4f74e0ad7eb47ee02232a2939f52d87d6e57022047603b9ffb32a200d530f5c54eed5245f1034e6ed77462d23e3417b8e763f25c0121030b482838721a38d94847699fed8818b5c5f56500ef72f13489e365b65e5749cffeffffff02a0860100000000001600148a28bddb7f61864bdcf58b2ad13d5aeb3abc3c4268360200000000001976a914ca4c60999c46c2108326590b125aefd476dcb11888ac00000000', str(tx_copy)) - self.assertEqual('5f25707571eb776bdf14142f9966bf2a681906e0a79501edbb99a972c2ceb972', tx_copy.txid()) - self.assertEqual('5f25707571eb776bdf14142f9966bf2a681906e0a79501edbb99a972c2ceb972', tx_copy.wtxid()) + self.assertEqual('d0e40c8a8586f7faf33505269df64ebad317e8188f008649faaf48cbeb49dae8', tx_copy.txid()) + self.assertEqual('d0e40c8a8586f7faf33505269df64ebad317e8188f008649faaf48cbeb49dae8', tx_copy.wtxid()) self.assertEqual(tx.wtxid(), tx_copy.wtxid()) wallet1.receive_tx_callback(tx.txid(), tx, TX_HEIGHT_UNCONFIRMED) t@@ -651,7 +651,7 @@ class TestWalletSending(TestCaseForTestnet): outputs = [PartialTxOutput.from_address_and_value(wallet2.get_receiving_address(), 370000)] tx = wallet1a.mktx(outputs=outputs, password=None, fee=5000, tx_version=1) partial_tx = tx.serialize_as_bytes().hex() parazyd.org:70 /git/electrum/commit/a6aa97c3e3a248f24a3e0c5d85f1f48bac230af2.gph:154: line too long