def get_total_length(msg: EthereumSignTx, data_total: int) -> int: length = 0 if msg.tx_type is not None: length += rlp.field_length(1, [msg.tx_type]) length += rlp.field_length(len(msg.nonce), msg.nonce[:1]) length += rlp.field_length(len(msg.gas_price), msg.gas_price) length += rlp.field_length(len(msg.gas_limit), msg.gas_limit) to = address.bytes_from_address(msg.to) length += rlp.field_length(len(to), to) length += rlp.field_length(len(msg.value), msg.value) if msg.chain_id: # forks replay protection if msg.chain_id < 0x100: l = 1 elif msg.chain_id < 0x10000: l = 2 elif msg.chain_id < 0x1000000: l = 3 else: l = 4 length += rlp.field_length(l, [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 verify_message(ctx, msg): digest = message_digest(msg.message) if len(msg.signature) != 65: raise wire.DataError("Invalid signature") sig = bytearray([msg.signature[64]]) + msg.signature[:64] try: pubkey = secp256k1.verify_recover(sig, digest) except ValueError: raise wire.DataError("Invalid signature") if not pubkey: raise wire.DataError("Invalid signature") pkh = sha3_256(pubkey[1:], keccak=True).digest()[-20:] address_bytes = bytes_from_address(msg.address) if address_bytes != pkh: raise wire.DataError("Invalid signature") address = address_from_bytes(address_bytes) await require_confirm_verify_message(ctx, address, msg.message) return Success(message="Message verified")
async def sign_tx(ctx, msg, keychain): msg = sanitize(msg) check(msg) await paths.validate_path(ctx, validate_full_path, path=msg.address_n) data_total = msg.data_length # detect ERC - 20 token token = None address_bytes = recipient = address.bytes_from_address(msg.to) value = int.from_bytes(msg.value, "big") if (len(msg.to) in (40, 42) and len(msg.value) == 0 and data_total == 68 and len(msg.data_initial_chunk) == 68 and msg.data_initial_chunk[:16] == b"\xa9\x05\x9c\xbb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ): token = tokens.token_by_chain_address(msg.chain_id, address_bytes) recipient = msg.data_initial_chunk[16:36] value = int.from_bytes(msg.data_initial_chunk[36:68], "big") await require_confirm_tx(ctx, recipient, value, msg.chain_id, token, msg.tx_type) if token is None and msg.data_length > 0: await require_confirm_data(ctx, msg.data_initial_chunk, data_total) await require_confirm_fee( ctx, value, int.from_bytes(msg.gas_price, "big"), int.from_bytes(msg.gas_limit, "big"), msg.chain_id, token, msg.tx_type, ) data = bytearray() data += msg.data_initial_chunk data_left = data_total - len(msg.data_initial_chunk) total_length = get_total_length(msg, data_total) sha = HashWriter(sha3_256(keccak=True)) sha.extend(rlp.encode_length(total_length, True)) # total length if msg.tx_type is not None: sha.extend(rlp.encode(msg.tx_type)) for field in (msg.nonce, msg.gas_price, msg.gas_limit, address_bytes, msg.value): sha.extend(rlp.encode(field)) if data_left == 0: sha.extend(rlp.encode(data)) else: sha.extend(rlp.encode_length(data_total, False)) sha.extend(rlp.encode(data, False)) while data_left > 0: resp = await send_request_chunk(ctx, data_left) data_left -= len(resp.data_chunk) sha.extend(resp.data_chunk) # eip 155 replay protection if msg.chain_id: sha.extend(rlp.encode(msg.chain_id)) sha.extend(rlp.encode(0)) sha.extend(rlp.encode(0)) digest = sha.get_digest() result = sign_digest(msg, keychain, digest) return result