|
3 | 3 | from electrum.bitcoin import hash_encode |
4 | 4 | from electrum.transaction import Transaction |
5 | 5 | from electrum.util import bfh |
6 | | -from electrum.verifier import SPV, InnerNodeOfSpvProofIsValidTx |
| 6 | +from electrum.verifier import SPV, InnerNodeOfSpvProofIsValidTx, LeftSiblingDuplicate |
7 | 7 |
|
8 | 8 | from . import ElectrumTestCase |
9 | 9 |
|
10 | 10 |
|
11 | | -MERKLE_BRANCH = [ |
12 | | - 'f2994fd4546086b21b4916b76cf901afb5c4db1c3ecbfc91d6f4cae1186dfe12', |
13 | | - '6b65935528311901c7acda7db817bd6e3ce2f05d1c62c385b7caadb65fac7520'] |
14 | | - |
15 | | -MERKLE_ROOT = '11dbac015b6969ea75509dd1250f33c04ec4d562c2d895de139a65f62f808254' |
16 | | - |
17 | | -VALID_64_BYTE_TX = ('0200000001cb659c5528311901a7aada7db817bd6e3ce2f05d1c62c385b7caad' |
18 | | - 'b65fac75201234000000fabcdefa01abcd1234010000000405060708fabcdefa') |
19 | | -assert len(VALID_64_BYTE_TX) == 128 |
20 | | - |
21 | | - |
22 | | -class VerifierTestCase(ElectrumTestCase): |
23 | | - # these tests are regarding the attack described in |
| 11 | +class TestVerifier_CVE_2017_12842(ElectrumTestCase): |
| 12 | + # these tests are regarding CVE-2017-12842, the attack described in |
24 | 13 | # https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-June/016105.html |
25 | 14 | TESTNET = True |
26 | 15 |
|
| 16 | + MERKLE_BRANCH = [ |
| 17 | + 'f2994fd4546086b21b4916b76cf901afb5c4db1c3ecbfc91d6f4cae1186dfe12', |
| 18 | + '6b65935528311901c7acda7db817bd6e3ce2f05d1c62c385b7caadb65fac7520', |
| 19 | + ] |
| 20 | + MERKLE_ROOT = '11dbac015b6969ea75509dd1250f33c04ec4d562c2d895de139a65f62f808254' |
| 21 | + VALID_64_BYTE_TX = ('0200000001cb659c5528311901a7aada7db817bd6e3ce2f05d1c62c385b7caad' |
| 22 | + 'b65fac75201234000000fabcdefa01abcd1234010000000405060708fabcdefa') |
| 23 | + assert len(VALID_64_BYTE_TX) == 128 |
| 24 | + |
27 | 25 | def test_verify_ok_t_tx(self): |
28 | 26 | """Actually mined 64 byte tx should not raise.""" |
29 | | - t_tx = Transaction(VALID_64_BYTE_TX) |
| 27 | + t_tx = Transaction(self.VALID_64_BYTE_TX) |
30 | 28 | t_tx_hash = t_tx.txid() |
31 | | - self.assertEqual(MERKLE_ROOT, SPV.hash_merkle_root(MERKLE_BRANCH, t_tx_hash, 3)) |
| 29 | + self.assertEqual(self.MERKLE_ROOT, SPV.hash_merkle_root(self.MERKLE_BRANCH, t_tx_hash, 3)) |
32 | 30 |
|
33 | 31 | def test_verify_fail_f_tx_odd(self): |
34 | 32 | """Raise if inner node of merkle branch is valid tx. ('odd' fake leaf position)""" |
35 | 33 | # first 32 bytes of T encoded as hash |
36 | | - fake_branch_node = hash_encode(bfh(VALID_64_BYTE_TX[:64])) |
37 | | - fake_mbranch = [fake_branch_node] + MERKLE_BRANCH |
| 34 | + fake_branch_node = hash_encode(bfh(self.VALID_64_BYTE_TX[:64])) |
| 35 | + fake_mbranch = [fake_branch_node] + self.MERKLE_BRANCH |
38 | 36 | # last 32 bytes of T encoded as hash |
39 | | - f_tx_hash = hash_encode(bfh(VALID_64_BYTE_TX[64:])) |
| 37 | + f_tx_hash = hash_encode(bfh(self.VALID_64_BYTE_TX[64:])) |
40 | 38 | with self.assertRaises(InnerNodeOfSpvProofIsValidTx): |
41 | 39 | SPV.hash_merkle_root(fake_mbranch, f_tx_hash, 7) |
42 | 40 |
|
43 | 41 | def test_verify_fail_f_tx_even(self): |
44 | 42 | """Raise if inner node of merkle branch is valid tx. ('even' fake leaf position)""" |
45 | 43 | # last 32 bytes of T encoded as hash |
46 | | - fake_branch_node = hash_encode(bfh(VALID_64_BYTE_TX[64:])) |
47 | | - fake_mbranch = [fake_branch_node] + MERKLE_BRANCH |
| 44 | + fake_branch_node = hash_encode(bfh(self.VALID_64_BYTE_TX[64:])) |
| 45 | + fake_mbranch = [fake_branch_node] + self.MERKLE_BRANCH |
48 | 46 | # first 32 bytes of T encoded as hash |
49 | | - f_tx_hash = hash_encode(bfh(VALID_64_BYTE_TX[:64])) |
| 47 | + f_tx_hash = hash_encode(bfh(self.VALID_64_BYTE_TX[:64])) |
50 | 48 | with self.assertRaises(InnerNodeOfSpvProofIsValidTx): |
51 | 49 | SPV.hash_merkle_root(fake_mbranch, f_tx_hash, 6) |
| 50 | + |
| 51 | + |
| 52 | +class TestVerifier_CVE_2012_2459(ElectrumTestCase): |
| 53 | + # These tests are regarding CVE-2012-2459. |
| 54 | + # Bitcoin's Merkle tree duplicates odd nodes to balance the tree. An attacker can |
| 55 | + # exploit this by constructing a tree where a duplicated subtree is treated |
| 56 | + # as containing real leaves, allowing forged proofs for phantom leaf positions. |
| 57 | + # |
| 58 | + # Example with 11 real leaves and forged 16-leaf claim: |
| 59 | + # |
| 60 | + # Real tree (11 leaves): |
| 61 | + # |
| 62 | + # **root** |
| 63 | + # __________/ \_________ |
| 64 | + # / \ |
| 65 | + # 14 c Height 3 |
| 66 | + # _ / \ _ / \ |
| 67 | + # / \ / \ |
| 68 | + # 6 13 b b' Height 2 |
| 69 | + # / \ / \ / \ |
| 70 | + # 2 5 9 12 17 a Height 1 |
| 71 | + # / \ / \ / \ / \ / \ / \ |
| 72 | + # 0 1 3 4 7 8 10 11 15 16 18 18' Height 0 |
| 73 | + # -------------------------------------------------------- |
| 74 | + # 0 1 2 3 4 5 6 7 8 9 10 Leaf index |
| 75 | + # |
| 76 | + # Nodes marked with ' are duplicates to balance the tree. |
| 77 | + # |
| 78 | + # Forged tree (attacker claims 16 leaves): |
| 79 | + # |
| 80 | + # **root** |
| 81 | + # __________/ \________________ |
| 82 | + # / \ |
| 83 | + # 14 c Height 3 |
| 84 | + # _ / \ _ _____/ \_____ |
| 85 | + # / \ / \ |
| 86 | + # 6 13 b b' Height 2 |
| 87 | + # / \ / \ / \ / \ |
| 88 | + # 2 5 9 12 17 a 17' a' Height 1 |
| 89 | + # / \ / \ / \ / \ / \ / \ / \ / \ |
| 90 | + # 0 1 3 4 7 8 10 11 15 16 18 18' 15' 16' 18' 18' Height 0 |
| 91 | + # -------------------------------------------------------------------------- |
| 92 | + # 0 1 2 3 4 5 6 7 8 9 10 11! 12! 13! 14! 15! Leaf index |
| 93 | + # |
| 94 | + # Nodes with ! are phantom leaves. The attacker duplicated the entire |
| 95 | + # subtree under 'b' to create fake leaves 11-15. |
| 96 | + # |
| 97 | + # The attack works because: |
| 98 | + # - Real proof for leaf 10: [18', 17 , b', 14] with b' as RIGHT sibling |
| 99 | + # - Forged proof for leaf 14: [18', 17', b , 14] with b as LEFT sibling |
| 100 | + # |
| 101 | + # We can guard against this: in forged proofs, a duplicate will appear as a LEFT sibling |
| 102 | + # (sibling == current when index bit is 1). |
| 103 | + # Legitimate duplicates for balancing only appear as RIGHT siblings. |
| 104 | + TESTNET = True |
| 105 | + |
| 106 | + # from testnet3 block 4909055 (https://blockstream.info/testnet/block/00000000c4a54b073c224bbf1f7c40cc85498a823e1dd5d20be51e6464a3dab9) |
| 107 | + # but even if it gets reorged, point is: |
| 108 | + # - block has 3 txns total, so valid indices [0,1,2] |
| 109 | + # - next power of 2 is 4, so merkle tree leaves will be [t0,t1,t2,t2'] |
| 110 | + # - TXID is for t2, so its real index is 2, but index 3 could be "forged" for it as well |
| 111 | + MERKLE_BRANCH = [ |
| 112 | + '9b2c7e407188465594832cfbe84c9758029084527c855ea29a16603e5d1c51b6', |
| 113 | + 'a8484ccbaa74ffa060d0a500f7ce3ea4953beace18df8384024dfa9290385b1c', |
| 114 | + ] |
| 115 | + MERKLE_ROOT = '3465af659f6438b133c6d980accbb61b7be43f8ad899e40054e33b37aecba28e' |
| 116 | + TXID = '9b2c7e407188465594832cfbe84c9758029084527c855ea29a16603e5d1c51b6' |
| 117 | + |
| 118 | + def test_valid_right_sibling_duplicate(self): |
| 119 | + leaf_pos_in_tree = 2 |
| 120 | + self.assertEqual(self.MERKLE_ROOT, SPV.hash_merkle_root(self.MERKLE_BRANCH, self.TXID, leaf_pos_in_tree)) |
| 121 | + |
| 122 | + def test_malicious_left_sibling_duplicate(self): |
| 123 | + leaf_pos_in_tree = 3 |
| 124 | + with self.assertRaises(LeftSiblingDuplicate): |
| 125 | + SPV.hash_merkle_root(self.MERKLE_BRANCH, self.TXID, leaf_pos_in_tree) |
0 commit comments