feat(tests): Test SegWit cross-coin attack.

release/21.12
Andrew Kozlik 3 years ago committed by Martin Milata
parent 9052133fca
commit de6fab3c1e

@ -18,10 +18,12 @@ import pytest
from trezorlib import btc, device, messages as proto
from trezorlib.exceptions import TrezorFailure
from trezorlib.tools import parse_path
from trezorlib.tools import H_, parse_path
from ..tx_cache import TxCache
from .signtx import request_finished, request_input, request_meta, request_output
B = proto.ButtonRequestType
TX_CACHE_MAINNET = TxCache("Bitcoin")
TX_CACHE_BCASH = TxCache("Bcash")
@ -31,6 +33,12 @@ TXHASH_8cc1f4 = bytes.fromhex(
TXHASH_d5f65e = bytes.fromhex(
"d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882"
)
TXHASH_fa80a9 = bytes.fromhex(
"fa80a9949f1094119195064462f54d0e0eabd3139becd4514ae635b8c7fe3a46"
)
TXHASH_5dfd1b = bytes.fromhex(
"5dfd1b037633adc7f84a17b2df31c9994fe50b3ab3e246c44c4ceff3d326f62e"
)
class TestMsgSigntxInvalidPath:
@ -117,3 +125,94 @@ class TestMsgSigntxInvalidPath:
)
btc.sign_tx(client, "Bcash", [inp1], [out1], prev_txes=TX_CACHE_BCASH)
def test_attack_path_segwit(self, client):
# Scenario: The attacker falsely claims that the transaction uses Testnet paths to
# avoid the path warning dialog, but in step6_sign_segwit_inputs() uses Bitcoin paths
# to get a valid signature.
device.apply_settings(
client, safety_checks=proto.SafetyCheckLevel.PromptTemporarily
)
inp1 = proto.TxInputType(
# The actual input that the attcker wants to get signed.
address_n=parse_path("84'/0'/0'/0/0"),
amount=9426,
prev_hash=TXHASH_fa80a9,
prev_index=0,
script_type=proto.InputScriptType.SPENDWITNESS,
)
inp2 = proto.TxInputType(
# The actual input that the attcker wants to get signed.
# We need this one to be from a different account, so that the match checker
# allows the transaction to pass.
address_n=parse_path("84'/0'/1'/0/1"),
amount=7086,
prev_hash=TXHASH_5dfd1b,
prev_index=0,
script_type=proto.InputScriptType.SPENDWITNESS,
)
out1 = proto.TxOutputType(
# Attacker's Mainnet address encoded as Testnet.
address="tb1q694ccp5qcc0udmfwgp692u2s2hjpq5h407urtu",
script_type=proto.OutputScriptType.PAYTOADDRESS,
amount=9426 + 7086 - 500,
)
attack_count = 6
def attack_processor(msg):
nonlocal attack_count
# Make the inputs look like they are coming from Testnet paths until we reach the
# signing phase.
if attack_count > 0 and msg.tx.inputs and msg.tx.inputs[0] in (inp1, inp2):
attack_count -= 1
msg.tx.inputs[0].address_n[1] = H_(1)
return msg
with client:
client.set_filter(proto.TxAck, attack_processor)
client.set_expected_responses(
[
# Step: process inputs
request_input(0),
# Attacker bypasses warning about non-standard path.
request_input(1),
# Attacker bypasses warning about non-standard path.
# Step: approve outputs
request_output(0),
proto.ButtonRequest(code=B.ConfirmOutput),
proto.ButtonRequest(code=B.SignTx),
# Step: verify inputs
request_input(0),
request_meta(TXHASH_fa80a9),
request_input(0, TXHASH_fa80a9),
request_output(0, TXHASH_fa80a9),
request_output(1, TXHASH_fa80a9),
request_input(1),
request_meta(TXHASH_5dfd1b),
request_input(0, TXHASH_5dfd1b),
request_output(0, TXHASH_5dfd1b),
request_output(1, TXHASH_5dfd1b),
# Step: serialize inputs
request_input(0),
request_input(1),
# Step: serialize outputs
request_output(0),
# Step: sign segwit inputs
request_input(0),
# Trezor must warn about non-standard path before signing.
proto.ButtonRequest(code=B.UnknownDerivationPath),
request_input(1),
# Trezor must warn about non-standard path before signing.
proto.ButtonRequest(code=B.UnknownDerivationPath),
request_finished(),
]
)
btc.sign_tx(
client, "Testnet", [inp1, inp2], [out1], prev_txes=TX_CACHE_MAINNET
)

@ -0,0 +1,22 @@
{
"bin_outputs": [
{
"amount": 7086,
"script_pubkey": "0014f6740e521befc61400662d9489a5f744883ca681"
},
{
"amount": 20120,
"script_pubkey": "001404278d66e85ad1073d6f7a8eeee8b96ca633e6e8"
}
],
"inputs": [
{
"prev_hash": "939cda2b4b609be5f5772dea5885431e18a61834bcbe5e0f895d6dfa05d2cc70",
"prev_index": 0,
"script_sig": "",
"sequence": 4294967293
}
],
"lock_time": 0,
"version": 1
}

@ -0,0 +1,22 @@
{
"bin_outputs": [
{
"amount": 9426,
"script_pubkey": "0014ece6935b2a5a5b5ff997c87370b16fa10f164410"
},
{
"amount": 309896,
"script_pubkey": "a914dfe58cc93d35fb99e15436f47d3bbfce8203280687"
}
],
"inputs": [
{
"prev_hash": "86a6e02943dcd057cfbe349f2c2274478a3a1be908eb788606a6950e727a0d36",
"prev_index": 0,
"script_sig": "16001495f41f5c0e0ec2c7fe27f0ac4bd59a5632a40b5f",
"sequence": 4294967295
}
],
"lock_time": 0,
"version": 1
}

@ -661,6 +661,7 @@
"test_msg_signtx_grs.py-test_send_segwit_native_change": "fd8b04e26d71fad1c59f5e548c35f22f2031cfb99f9077824242e264fcbedfe6",
"test_msg_signtx_grs.py-test_send_segwit_p2sh": "e59018de5c49f902c6880c2347283b6c1830fcb19e8eab9686938a08abd930b3",
"test_msg_signtx_grs.py-test_send_segwit_p2sh_change": "08251e3b7e509264dbd89a5ded2deba51544d0fcfce1dc7466b553b73298d423",
"test_msg_signtx_invalid_path.py-test_attack_path_segwit": "3feaa01d47aa9757e9d74f668927fd1445493c367adff94215405eb8e0a2749b",
"test_msg_signtx_invalid_path.py-test_invalid_path_fail": "1c100ce4b7c1e47e72428f390de0846c1ff933e9f07894872644a369a9422738",
"test_msg_signtx_invalid_path.py-test_invalid_path_pass_forkid": "ef98eb752ec5fa948c952def7599f57a2bc5240b2d6b1eec0e02cc9be5c3040f",
"test_msg_signtx_invalid_path.py-test_invalid_path_prompt": "12e137210397357ed754af0f4618ef03312b3e884930f55727d1b034f969bfd5",

Loading…
Cancel
Save