From 1f677306a16b5bcc6bd1234fc2d81cf286e5b675 Mon Sep 17 00:00:00 2001 From: Tomas Susanka Date: Thu, 21 Dec 2017 15:59:38 +0100 Subject: [PATCH] ethereum/signing: streaming; all tests passing --- src/apps/ethereum/ethereum_sign_tx.py | 43 +++++++++++++++++++++------ src/trezor/crypto/rlp.py | 21 +++++++++++-- 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/apps/ethereum/ethereum_sign_tx.py b/src/apps/ethereum/ethereum_sign_tx.py index ce8851ab..7378acad 100644 --- a/src/apps/ethereum/ethereum_sign_tx.py +++ b/src/apps/ethereum/ethereum_sign_tx.py @@ -45,24 +45,49 @@ async def ethereum_sign_tx(ctx, msg): data += msg.data_initial_chunk data_left = data_total - len(msg.data_initial_chunk) + total_length = get_total_length(msg, data_total) + + sha = sha3_256() + sha.update(rlp.encode_length(total_length, True)) # total length + + for field in [msg.nonce, msg.gas_price, msg.gas_limit, msg.to, msg.value]: + sha.update(rlp.encode(field)) + + if data_left == 0: + sha.update(rlp.encode(data)) + else: + sha.update(rlp.encode_length(data_total, False)) + sha.update(rlp.encode(data, False)) + while data_left > 0: resp = await send_request_chunk(ctx, data_left, data_total) - data += resp.data_chunk data_left -= len(resp.data_chunk) - # todo stream + sha.update(resp.data_chunk) + # eip 155 replay protection if msg.chain_id: - fields = [msg.nonce, msg.gas_price, msg.gas_limit, msg.to, msg.value, data, msg.chain_id, 0, 0] - else: - fields = [msg.nonce, msg.gas_price, msg.gas_limit, msg.to, msg.value, data] - rlp_encoded = rlp.encode(fields) - sha256 = sha3_256() - sha256.update(rlp_encoded) - digest = sha256.digest(True) + sha.update(rlp.encode(msg.chain_id)) + sha.update(rlp.encode(0)) + sha.update(rlp.encode(0)) + digest = sha.digest(True) return await send_signature(ctx, msg, digest) +def get_total_length(msg: EthereumSignTx, data_total: int) -> int: + length = 0 + for field in [msg.nonce, msg.gas_price, msg.gas_limit, msg.to, msg.value]: + length += rlp.field_length(len(field), field[:1]) + + if msg.chain_id: # forks replay protection + length += rlp.field_length(1, [msg.chain_id]) + length += rlp.field_length(0, 0) + length += rlp.field_length(0, 0) + + length += rlp.field_length(data_total, msg.data_initial_chunk) + return length + + async def send_request_chunk(ctx, data_left: int, data_total: int): from trezor.messages.wire_types import EthereumTxAck # todo layoutProgress ? diff --git a/src/trezor/crypto/rlp.py b/src/trezor/crypto/rlp.py index 94340510..1f13bd7b 100644 --- a/src/trezor/crypto/rlp.py +++ b/src/trezor/crypto/rlp.py @@ -20,13 +20,13 @@ def encode_length(l: int, is_list: bool) -> bytes: raise ValueError('Input too long') -def encode(data) -> bytes: +def encode(data, include_length=True) -> bytes: if isinstance(data, int): return encode(int_to_bytes(data)) if isinstance(data, bytearray): data = bytes(data) if isinstance(data, bytes): - if len(data) == 1 and ord(data) < 128: + if (len(data) == 1 and ord(data) < 128) or not include_length: return data else: return encode_length(len(data), is_list=False) + data @@ -34,6 +34,21 @@ def encode(data) -> bytes: output = b'' for item in data: output += encode(item) - return encode_length(len(output), is_list=True) + output + if include_length: + return encode_length(len(output), is_list=True) + output + else: + return output else: raise TypeError('Invalid input of type ' + str(type(data))) + + +def field_length(length: int, first_byte: bytearray) -> int: + if length == 1 and first_byte[0] <= 0x7f: + return 1 + elif length <= 55: + return 1 + length + elif length <= 0xff: + return 2 + length + elif length <= 0xffff: + return 3 + length + return 4 + length