From 249b976219cf3e06c485c0217790600d03a1c59e Mon Sep 17 00:00:00 2001 From: Coline Date: Thu, 21 Apr 2022 18:01:18 +0200 Subject: [PATCH] feat: simple sign test --- .../boilerplate_client/boilerplate_cmd.py | 100 +++++++----------- .../boilerplate_cmd_builder.py | 16 +-- .../boilerplate_client/transaction.py | 45 ++++---- tests/speculos/test_sign_cmd.py | 45 ++++++-- 4 files changed, 106 insertions(+), 100 deletions(-) diff --git a/tests/speculos/boilerplate_client/boilerplate_cmd.py b/tests/speculos/boilerplate_client/boilerplate_cmd.py index f491f1f..252668c 100644 --- a/tests/speculos/boilerplate_client/boilerplate_cmd.py +++ b/tests/speculos/boilerplate_client/boilerplate_cmd.py @@ -1,6 +1,7 @@ from ast import List from contextlib import contextmanager from ctypes.wintypes import INT +from re import A import struct from typing import Tuple @@ -77,44 +78,25 @@ class BoilerplateCommand: result.append(eth_addr) result.append(chain_code) - @contextmanager - def simple_sign_tx(self, bip32_path: str, transaction, result: List = list()) -> Tuple[bytes, bytes, bytes]: - try: - chunk: bytes = self.builder.simple_sign_tx(bip32_path=bip32_path, transaction=transaction) - - - with self.client.apdu_exchange_nowait(cla=chunk[0], ins=chunk[1], - p1=chunk[2], p2=chunk[3], - data=chunk[5:]) as exchange: - yield exchange - response: bytes = exchange.receive() - - except ApduException as error: - raise DeviceException(error_code=error.sw, ins=InsType.INS_SIGN_TX) - - print("HERE: ", response) - - # response = V (1) || R (32) || S (32) - assert len(response) == 65 - - v, r, s = struct.unpack("BII", response) - - result.append(v) - result.append(r) - result.append(s) - - def test_zemu_hard_apdu_sign(self) -> Tuple[int, int, int]: + def test_zemu_hard_apdu_sign(self, transaction: Transaction) -> Tuple[int, int, int]: sign: bytes = b'\xe0\x04\x00\x00\x80\x05\x80\x00\x00\x2c\x80\x00\x00\x3c\x80\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x69\x46\x85\x06\xa8\xb1\x5e\x00\x82\xeb\xeb\x94\x6b\x17\x54\x74\xe8\x90\x94\xc4\x4d\xa9\x8b\x95\x4e\xed\xea\xc4\x95\x27\x1d\x0f\x80\xb8\x44\x09\x5e\xa7\xb3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7d\x27\x68\xde\x32\xb0\xb8\x0b\x7a\x34\x54\xc0\x6b\xda\xc9\x4a\x69\xdd\xc7\xa9\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x80\x80' + simple_eth: bytes = b'\xe0\x04\x00\x00\x41\x05\x80\x00\x00\x2c\x80\x00\x00\x3c\x80\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\xeb\x44\x85\x03\x06\xdc\x42\x00\x82\x52\x08\x94\x5a\x32\x17\x44\x66\x70\x52\xaf\xfa\x83\x86\xed\x49\xe0\x0e\xf2\x23\xcb\xff\xc3\x87\x6f\x9c\x9e\x7b\xf6\x18\x18\x80\x01\x80\x80' provide_erc20: bytes = b'\xe0\x0a\x00\x00\x67\x03\x44\x41\x49\x6b\x17\x54\x74\xe8\x90\x94\xc4\x4d\xa9\x8b\x95\x4e\xed\xea\xc4\x95\x27\x1d\x0f\x00\x00\x00\x12\x00\x00\x00\x01\x30\x45\x02\x21\x00\xb3\xaa\x97\x96\x33\x28\x4e\xb0\xf5\x54\x59\x09\x93\x33\xab\x92\xcf\x06\xfd\xd5\x8d\xc9\x0e\x9c\x07\x00\x00\xc8\xe9\x68\x86\x4c\x02\x20\x7b\x10\xec\x7d\x66\x09\xf5\x1d\xda\x53\xd0\x83\xa6\xe1\x65\xa0\xab\xf3\xa7\x7e\x13\x25\x0e\x6f\x26\x07\x72\x80\x9b\x49\xaf\xf5' + b: bytes = b'\xe0\x04\x00\x00\x56\x05\x80\x00\x00\x2c\x80\x00\x00\x3c\x80\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x3f\x26\x8e\x02\xcc\x9b\xe5\xc5\x3e\xa4\x4b\xd4\x3c\x28\x9d\xcd\xdc\x82\x52\x08\x94\xda\xc1\x7f\x95\x8d\x2e\xe5\x23\xa2\x20\x62\x06\x99\x45\x97\xc1\x3d\x83\x1e\xc7\x92\x8d\xb8\xb0\x86\x1b\x8f\x7f\xe5\xdf\x83\xcd\x55\x3a\x82\x98\x78\x00\x00\x80\x01\x80\x80' + + a = self.builder.simple_sign_tx(bip32_path="44'/60'/1'/0/0", transaction=transaction) + + print(a.hex()) + try: response = self.client._apdu_exchange( - provide_erc20 + a ) # type: int, bytes - response = self.client._apdu_exchange( - sign - ) + #response = self.client._apdu_exchange( + # sign + #) except ApduException as error: raise DeviceException(error_code=error.sw, ins=InsType.INS_SIGN_TX) @@ -135,42 +117,34 @@ class BoilerplateCommand: return v, r, s + @contextmanager + def simple_sign_tx(self, bip32_path: str, transaction: Transaction, result: List = list()) -> Tuple[int, bytes]: + try: + chunk: bytes = self.builder.simple_sign_tx(bip32_path=bip32_path, transaction=transaction) - def sign_tx(self, bip32_path: str, transaction: Transaction) -> Tuple[int, bytes]: - sw: int - response: bytes = b"" + with self.client.apdu_exchange_nowait(cla=chunk[0], ins=chunk[1], + p1=chunk[2], p2=chunk[3], + data=chunk[5:]) as exchange: + yield exchange + response: bytes = exchange.receive() + + except ApduException as error: + raise DeviceException(error_code=error.sw, ins=InsType.INS_SIGN_TX) - for is_last, chunk in self.builder.sign_tx(bip32_path=bip32_path, transaction=transaction): - if is_last: - with self.client.apdu_exchange_nowait(cla=chunk[0], ins=chunk[1], - p1=chunk[2], p2=chunk[3], - data=chunk[5:]) as exchange: - # Review Transaction - self.client.press_and_release('right') - # Address 1/3, 2/3, 3/3 - self.client.press_and_release('right') - self.client.press_and_release('right') - self.client.press_and_release('right') - # Amount - self.client.press_and_release('right') - # Approve - self.client.press_and_release('both') - response = exchange.receive() - else: - response = self.client._apdu_exchange(chunk) - print(response) + # response = V (1) || R (32) || S (32) + assert len(response) == 65 + print(response.hex()) - # response = der_sig_len (1) || - # der_sig (var) || - # v (1) offset: int = 0 - der_sig_len: int = response[offset] - offset += 1 - der_sig: bytes = response[offset:offset + der_sig_len] - offset += der_sig_len - v: int = response[offset] + + v: bytes = response[offset] offset += 1 - assert len(response) == 1 + der_sig_len + 1 + r: bytes = response[offset:offset + 32] + offset += 32 - return v, der_sig + s: bytes = response[offset:] + + result.append(v) + result.append(r) + result.append(s) diff --git a/tests/speculos/boilerplate_client/boilerplate_cmd_builder.py b/tests/speculos/boilerplate_client/boilerplate_cmd_builder.py index cb50d91..4508cdb 100644 --- a/tests/speculos/boilerplate_client/boilerplate_cmd_builder.py +++ b/tests/speculos/boilerplate_client/boilerplate_cmd_builder.py @@ -197,7 +197,7 @@ class BoilerplateCommandBuilder: p2=0x00, cdata=chunk) - def simple_sign_tx(self, bip32_path: str, transaction) -> bytes: + def simple_sign_tx(self, bip32_path: str, transaction: Transaction) -> bytes: """Command builder for INS_SIGN_TX. Parameters @@ -214,17 +214,17 @@ class BoilerplateCommandBuilder: """ bip32_paths: List[bytes] = bip32_path_from_string(bip32_path) - tx_encode = rlp.encode(transaction) - lc = len(bip32_paths) + len(tx_encode) - hard_length = 5 - fake_data = [0xcc, 0x85, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x85, 0x77, 0x6f, 0x72, 0x6c, 0x64] - cdata: bytes = b"".join([ - hard_length.to_bytes(1, byteorder="big"), - *bip32_paths, + len(bip32_paths).to_bytes(1, byteorder="big"), + *bip32_paths ]) + + tx: bytes = transaction.serialize() + + cdata = cdata + tx + return self.serialize(cla=self.CLA, ins=InsType.INS_SIGN_TX, p1=0x00, diff --git a/tests/speculos/boilerplate_client/transaction.py b/tests/speculos/boilerplate_client/transaction.py index ce43f47..82c3049 100644 --- a/tests/speculos/boilerplate_client/transaction.py +++ b/tests/speculos/boilerplate_client/transaction.py @@ -10,11 +10,14 @@ class TransactionError(Exception): class Transaction: - def __init__(self, nonce: int, to: Union[str, bytes], value: int, memo: str) -> None: + def __init__(self, txType: int, nonce: int, gasPrice: int, gasLimit: int, to: Union[str, bytes], value: int, memo: Union[str, bytes]) -> None: + self.txType: int = txType self.nonce: int = nonce + self.gasPrice: int = gasPrice + self.gasLimit: int = gasLimit self.to: bytes = bytes.fromhex(to[2:]) if isinstance(to, str) else to self.value: int = value - self.memo: bytes = memo.encode("ascii") + self.memo: bytes = bytes.fromhex(memo[2:]) if isinstance(memo, str) else memo if not (0 <= self.nonce <= UINT64_MAX): raise TransactionError(f"Bad nonce: '{self.nonce}'!") @@ -25,23 +28,29 @@ class Transaction: if len(self.to) != 20: raise TransactionError(f"Bad address: '{self.to}'!") + self.lenGP = int((len(hex(self.gasPrice)) - 1) / 2) + self.lenGL = int((len(hex(self.gasLimit)) - 1) / 2) + self.lenValue = int((len(hex(self.value)) - 1) / 2) + + def serialize(self) -> bytes: return b"".join([ - self.nonce.to_bytes(8, byteorder="big"), + self.txType.to_bytes(1, byteorder="big"), + + self.nonce.to_bytes(1, byteorder="big"), + + write_varint(self.lenGP + 0x80), + self.gasPrice.to_bytes(self.lenGP, byteorder="big"), + + write_varint(self.lenGL + 0x80), + self.gasLimit.to_bytes(self.lenGL, byteorder="big"), + + write_varint(len(self.to) + 0x80), self.to, - self.value.to_bytes(8, byteorder="big"), - write_varint(len(self.memo)), - self.memo + + write_varint(self.lenValue + 0x80), + self.value.to_bytes(self.lenValue, byteorder="big"), + + self.memo, + ]) - - @classmethod - def from_bytes(cls, hexa: Union[bytes, BytesIO]): - buf: BytesIO = BytesIO(hexa) if isinstance(hexa, bytes) else hexa - - nonce: int = read_uint(buf, 64, byteorder="big") - to: bytes = read(buf, 20) - value: int = read_uint(buf, 64, byteorder="big") - memo_len: int = read_varint(buf) - memo: str = read(buf, memo_len).decode("ascii") - - return cls(nonce=nonce, to=to, value=value, memo=memo) diff --git a/tests/speculos/test_sign_cmd.py b/tests/speculos/test_sign_cmd.py index 8905cdb..786543c 100644 --- a/tests/speculos/test_sign_cmd.py +++ b/tests/speculos/test_sign_cmd.py @@ -2,18 +2,41 @@ from urllib import response import boilerplate_client import struct +from boilerplate_client.transaction import Transaction + def test_sign(cmd): - # result: list = [] - # transaction = "dog" - # with cmd.simple_sign_tx(bip32_path="44'/60'/1'/0/0", transaction=transaction, result=result) as ex: - # pass - # v, r, s = result - # print("HERE: ", result) - # assert 2 == 1 - v, r, s = cmd.test_zemu_hard_apdu_sign() + result: list = [] + + transaction = Transaction( + txType=0xEB, + nonce=68, + gasPrice=0x0306dc4200, + gasLimit=0x5208, + to="0x5a321744667052affa8386ed49e00ef223cbffc3", + value=0x6f9c9e7bf61818, + memo="0x80018080", + ) - assert v == 0x25 - assert r.hex() == "92243511396b65a4faa735a5472ea99b3ce0f7f2338eab426206730bc0ddc57f" - assert s.hex() == "161bc0f861064d840de4f4304cfd19a571017e62df7d8f70cf605c0f025593b6" + with cmd.simple_sign_tx(bip32_path="44'/60'/1'/0/0", transaction=transaction, result=result) as ex: + # Review transaction + cmd.client.press_and_release('right') + # Amount 1/3, 2/3, 3/3 + cmd.client.press_and_release('right') + cmd.client.press_and_release('right') + cmd.client.press_and_release('right') + # Address 1/3, 2/3, 3/3 + cmd.client.press_and_release('right') + cmd.client.press_and_release('right') + cmd.client.press_and_release('right') + # Max Fees + cmd.client.press_and_release('right') + #Accept and send + cmd.client.press_and_release('both') + + v, r, s = result + + assert v == 0x26 + assert r.hex() == "6f389d15320f0501383526ed03de917c14212716f09a262dbc98431086a5db49" + assert s.hex() == "0dc994b7b97230bb35fdf6fec2f4d8ff4cfb8bfeb2a652c364c738ff033c05dd" \ No newline at end of file