|
|
|
@ -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
|
|
|
|
|
)
|
|
|
|
|