async def process_unknown_action(ctx: wire.Context, w: Writer, action: EosTxActionAck) -> None: checksum = HashWriter(sha256()) writers.write_variant32(checksum, action.unknown.data_size) checksum.extend(action.unknown.data_chunk) writers.write_bytes_unchecked(w, action.unknown.data_chunk) bytes_left = action.unknown.data_size - len(action.unknown.data_chunk) while bytes_left != 0: action = await ctx.call(EosTxActionRequest(data_size=bytes_left), EosTxActionAck) if action.unknown is None: raise ValueError("Bad response. Unknown struct expected.") checksum.extend(action.unknown.data_chunk) writers.write_bytes_unchecked(w, action.unknown.data_chunk) bytes_left -= len(action.unknown.data_chunk) if bytes_left < 0: raise ValueError("Bad response. Buffer overflow.") await layout.confirm_action_unknown(ctx, action.common, checksum.get_digest())
async def sign_tx(ctx: wire.Context, msg: EthereumSignTx, keychain: Keychain) -> EthereumTxRequest: check(msg) await paths.validate_path(ctx, keychain, msg.address_n) # Handle ERC20s token, address_bytes, recipient, value = await handle_erc20(ctx, msg) data_total = msg.data_length await require_confirm_tx(ctx, recipient, value, msg.chain_id, token) 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, ) 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)) rlp.write_header(sha, total_length, rlp.LIST_HEADER_BYTE) if msg.tx_type is not None: rlp.write(sha, msg.tx_type) for field in (msg.nonce, msg.gas_price, msg.gas_limit, address_bytes, msg.value): rlp.write(sha, field) if data_left == 0: rlp.write(sha, data) else: rlp.write_header(sha, data_total, rlp.STRING_HEADER_BYTE, data) sha.extend(data) 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 rlp.write(sha, msg.chain_id) rlp.write(sha, 0) rlp.write(sha, 0) digest = sha.get_digest() result = sign_digest(msg, keychain, digest) return result
def message_digest(message): h = HashWriter(sha256) signed_message_header = "Lisk Signed Message:\n" write_varint(h, len(signed_message_header)) h.extend(signed_message_header) write_varint(h, len(message)) h.extend(message) return sha256(h.get_digest()).digest()
def message_digest(coin, message): h = HashWriter(sha256) write_varint(h, len(coin.signed_message_header)) h.extend(coin.signed_message_header) write_varint(h, len(message)) h.extend(message) ret = h.get_digest() if coin.sign_hash_double: ret = sha256(ret).digest() return ret
def write_leftpad32(w: HashWriter, value: bytes, signed: bool = False) -> None: assert len(value) <= 32 # Values need to be sign-extended, so accounting for negative ints if signed and value[0] & 0x80: pad_value = 0xFF else: pad_value = 0x00 for _ in range(32 - len(value)): w.append(pad_value) w.extend(value)
def message_digest(message): from apps.wallet.sign_tx.signing import write_varint from trezor.crypto.hashlib import sha3_256 from trezor.utils import HashWriter h = HashWriter(sha3_256) signed_message_header = 'Ethereum Signed Message:\n' write_varint(h, len(signed_message_header)) h.extend(signed_message_header) write_varint(h, len(message)) h.extend(message) return h.get_digest(True)
def message_digest(coin: CoinType, message: bytes) -> bytes: if coin.decred: h = HashWriter(blake256()) else: h = HashWriter(sha256()) write_varint(h, len(coin.signed_message_header)) h.extend(coin.signed_message_header) write_varint(h, len(message)) h.extend(message) ret = h.get_digest() if coin.sign_hash_double: ret = sha256(ret).digest() return ret
async def sign_tx(ctx, msg): pubkey, seckey = await _get_keys(ctx, msg) transaction = _update_raw_tx(msg.transaction, pubkey) try: await _require_confirm_by_type(ctx, transaction) except AttributeError: raise wire.DataError("The transaction has invalid asset data field") await layout.require_confirm_fee(ctx, transaction.amount, transaction.fee) txbytes = _get_transaction_bytes(transaction) txhash = HashWriter(sha256()) for field in txbytes: txhash.extend(field) digest = txhash.get_digest() signature = ed25519.sign(seckey, digest) return LiskSignedTx(signature=signature)
def message_digest(message): h = HashWriter(sha3_256(keccak=True)) signed_message_header = "\x19Ethereum Signed Message:\n" h.extend(signed_message_header) h.extend(str(len(message))) h.extend(message) return h.get_digest()
def message_digest(message): h = HashWriter(sha256()) signed_message_header = 'Beam Signed Message:\n' h.extend(signed_message_header) h.extend(str(len(message))) h.extend(message) return sha256(h.get_digest()).digest()
def encode_field( w: HashWriter, field: EthereumFieldType, value: bytes, ) -> None: """ SPEC: Atomic types: - Boolean false and true are encoded as uint256 values 0 and 1 respectively - Addresses are encoded as uint160 - Integer values are sign-extended to 256-bit and encoded in big endian order - Bytes1 to bytes31 are arrays with a beginning (index 0) and an end (index length - 1), they are zero-padded at the end to bytes32 and encoded in beginning to end order Dynamic types: - Bytes and string are encoded as a keccak256 hash of their contents Reference types: - Array values are encoded as the keccak256 hash of the concatenated encodeData of their contents - Struct values are encoded recursively as hashStruct(value) """ data_type = field.data_type if data_type == EthereumDataType.BYTES: if field.size is None: w.extend(keccak256(value)) else: write_rightpad32(w, value) elif data_type == EthereumDataType.STRING: w.extend(keccak256(value)) elif data_type == EthereumDataType.INT: write_leftpad32(w, value, signed=True) elif data_type in ( EthereumDataType.UINT, EthereumDataType.BOOL, EthereumDataType.ADDRESS, ): write_leftpad32(w, value) else: raise ValueError # Unsupported data type for field encoding
async def sign_tx(ctx, msg, keychain): msg = sanitize(msg) check(msg) await paths.validate_path(ctx, keychain, 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") if token is tokens.UNKNOWN_TOKEN: await require_confirm_unknown_token(ctx, address_bytes) 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
async def sign_tx(ctx, msg, keychain): await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n, CURVE) address_bytes = recipient = address.bytes_from_address(msg.to) value = int.from_bytes(msg.value, "big") 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) 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
async def ethereum_sign_tx(ctx, msg): from trezor.crypto.hashlib import sha3_256 msg = sanitize(msg) check(msg) data_total = msg.data_length # detect ERC - 20 token token = None recipient = msg.to value = int.from_bytes(msg.value, "big") if (len(msg.to) == 20 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, msg.to) 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) 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, msg.to, 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(True) # True -> use keccak mode return await send_signature(ctx, msg, digest)
def hash_type(self, w: HashWriter, primary_type: str) -> None: """Create a representation of a type.""" result = keccak256(self.encode_type(primary_type)) w.extend(result)
async def sign_tx_eip1559(ctx: wire.Context, msg: EthereumSignTxEIP1559, keychain: Keychain) -> EthereumTxRequest: check(msg) await paths.validate_path(ctx, keychain, msg.address_n) # Handle ERC20s token, address_bytes, recipient, value = await handle_erc20(ctx, msg) data_total = msg.data_length await require_confirm_tx(ctx, recipient, value, msg.chain_id, token) if token is None and msg.data_length > 0: await require_confirm_data(ctx, msg.data_initial_chunk, data_total) await require_confirm_eip1559_fee( ctx, int.from_bytes(msg.max_priority_fee, "big"), int.from_bytes(msg.max_gas_fee, "big"), int.from_bytes(msg.gas_limit, "big"), msg.chain_id, ) 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)) rlp.write(sha, TX_TYPE) rlp.write_header(sha, total_length, rlp.LIST_HEADER_BYTE) fields: Tuple[rlp.RLPItem, ...] = ( msg.chain_id, msg.nonce, msg.max_priority_fee, msg.max_gas_fee, msg.gas_limit, address_bytes, msg.value, ) for field in fields: rlp.write(sha, field) if data_left == 0: rlp.write(sha, data) else: rlp.write_header(sha, data_total, rlp.STRING_HEADER_BYTE, data) sha.extend(data) while data_left > 0: resp = await send_request_chunk(ctx, data_left) data_left -= len(resp.data_chunk) sha.extend(resp.data_chunk) write_access_list(sha, msg.access_list) digest = sha.get_digest() result = sign_digest(msg, keychain, digest) return result
async def sign_tx(ctx, msg, keychain): msg = sanitize(msg) #TODO refine `sanitize` to support more fields check(msg) #TODO refine check to support ~ await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n, CURVE) node = keychain.derive(msg.address_n) seckey = node.private_key() public_key = secp256k1.publickey(seckey, False) # uncompressed sender_address = sha3_256(public_key[1:], keccak=True).digest()[12:] recipient = address.bytes_from_address(msg.to) # TODO- check sender and real sender addr value = int.from_bytes(msg.value, "big") await require_confirm_tx(ctx, recipient, value, msg.chain_id, None, msg.tx_type) # TODO if fee delegation tx? -> require_confirm_fee_delegation await require_confirm_fee( ctx, value, int.from_bytes(msg.gas_price, "big"), int.from_bytes(msg.gas_limit, "big"), msg.chain_id, msg.fee_ratio, None, msg.tx_type, ) data_total = msg.data_length data = bytearray() data += msg.data_initial_chunk data_left = data_total - len(msg.data_initial_chunk) total_length = get_total_length(msg, data_total) print("total length: ", total_length) sha = HashWriter(sha3_256(keccak=True)) if msg.tx_type is None: sha.extend(rlp.encode_length(total_length, True)) # total length for field in (msg.nonce, msg.gas_price, msg.gas_limit, recipient, 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)) if msg.chain_id: sha.extend(rlp.encode(msg.chain_id)) sha.extend(rlp.encode(0)) sha.extend(rlp.encode(0)) else: basic_type = to_basic_type(msg.tx_type) attributes = [msg.tx_type, msg.nonce, msg.gas_price, msg.gas_limit] # TxTypeValueTransfer(0x08) if basic_type == 0x08: attributes += [recipient, msg.value, sender_address] if to_fee_type(msg.tx_type) == TX_TYPE_PARTIAL_FEE_DELEGATION: attributes.append(msg.fee_ratio) # TxTypeValueTransferMemo(0x10), TxTypeSmartContractExecution(0x30) elif basic_type == 0x10 or basic_type == 0x30: attributes += [recipient, msg.value, sender_address, data] if to_fee_type(msg.tx_type) == TX_TYPE_PARTIAL_FEE_DELEGATION: attributes.append(msg.fee_ratio) # TxTypeSmartContractDeploy(0x28) elif basic_type == 0x28: human_readable = 0x00 if msg.human_readable: human_readable = 0x01 attributes += [ recipient, msg.value, sender_address, data, human_readable ] if to_fee_type(msg.tx_type) == TX_TYPE_PARTIAL_FEE_DELEGATION: attributes.append(msg.fee_ratio) attributes.append(msg.code_format) # TxTypeCancel(0x38) elif basic_type == 0x38: attributes.append(sender_address) if to_fee_type(msg.tx_type) == TX_TYPE_PARTIAL_FEE_DELEGATION: attributes.append(msg.fee_ratio) # not supported tx type else: raise wire.DataError("Not supported transaction type") encoded_out = rlp.encode(attributes) sha.extend(rlp.encode([encoded_out, msg.chain_id, 0, 0], True)) digest = sha.get_digest() result = sign_digest(msg, keychain, digest) return result
def write_rightpad32(w: HashWriter, value: bytes) -> None: assert len(value) <= 32 w.extend(value) for _ in range(32 - len(value)): w.append(0x00)
async def get_and_encode_data( self, w: HashWriter, primary_type: str, member_path: list[int], show_data: bool, parent_objects: list[str], ) -> None: """ Gradually fetch data from client and encode the whole struct. SPEC: The encoding of a struct instance is enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ), i.e. the concatenation of the encoded member values in the order that they appear in the type. Each encoded member value is exactly 32-byte long. """ type_members = self.types[primary_type].members member_value_path = member_path + [0] current_parent_objects = parent_objects + [""] for member_index, member in enumerate(type_members): member_value_path[-1] = member_index field_name = member.name field_type = member.type # Arrays and structs need special recursive handling if field_type.data_type == EthereumDataType.STRUCT: assert field_type.struct_name is not None # validate_field_type struct_name = field_type.struct_name current_parent_objects[-1] = field_name if show_data: show_struct = await should_show_struct( ctx=self.ctx, description=struct_name, data_members=self.types[struct_name].members, title=".".join(current_parent_objects), ) else: show_struct = False res = await self.hash_struct( primary_type=struct_name, member_path=member_value_path, show_data=show_struct, parent_objects=current_parent_objects, ) w.extend(res) elif field_type.data_type == EthereumDataType.ARRAY: # Getting the length of the array first, if not fixed if field_type.size is None: array_size = await get_array_size(self.ctx, member_value_path) else: array_size = field_type.size assert field_type.entry_type is not None # validate_field_type entry_type = field_type.entry_type current_parent_objects[-1] = field_name if show_data: show_array = await should_show_array( ctx=self.ctx, parent_objects=current_parent_objects, data_type=get_type_name(entry_type), size=array_size, ) else: show_array = False arr_w = get_hash_writer() el_member_path = member_value_path + [0] for i in range(array_size): el_member_path[-1] = i # TODO: we do not support arrays of arrays, check if we should if entry_type.data_type == EthereumDataType.STRUCT: assert entry_type.struct_name is not None # validate_field_type struct_name = entry_type.struct_name # Metamask V4 implementation has a bug, that causes the # behavior of structs in array be different from SPEC # Explanation at https://github.com/MetaMask/eth-sig-util/pull/107 # encode_data() is the way to process structs in arrays, but # Metamask V4 is using hash_struct() even in this case if self.metamask_v4_compat: res = await self.hash_struct( primary_type=struct_name, member_path=el_member_path, show_data=show_array, parent_objects=current_parent_objects, ) arr_w.extend(res) else: await self.get_and_encode_data( w=arr_w, primary_type=struct_name, member_path=el_member_path, show_data=show_array, parent_objects=current_parent_objects, ) else: value = await get_value(self.ctx, entry_type, el_member_path) encode_field(arr_w, entry_type, value) if show_array: await confirm_typed_value( ctx=self.ctx, name=field_name, value=value, parent_objects=parent_objects, field=entry_type, array_index=i, ) w.extend(arr_w.get_digest()) else: value = await get_value(self.ctx, field_type, member_value_path) encode_field(w, field_type, value) if show_data: await confirm_typed_value( ctx=self.ctx, name=field_name, value=value, parent_objects=parent_objects, field=field_type, )