Exemplo n.º 1
0
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())
Exemplo n.º 2
0
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
Exemplo n.º 3
0
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()
Exemplo n.º 4
0
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
Exemplo n.º 5
0
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)
Exemplo n.º 6
0
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)
Exemplo n.º 7
0
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
Exemplo n.º 8
0
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)
Exemplo n.º 9
0
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()
Exemplo n.º 10
0
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()
Exemplo n.º 11
0
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
Exemplo n.º 12
0
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
Exemplo n.º 13
0
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
Exemplo n.º 14
0
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)
Exemplo n.º 15
0
 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)
Exemplo n.º 16
0
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
Exemplo n.º 17
0
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
Exemplo n.º 18
0
def write_rightpad32(w: HashWriter, value: bytes) -> None:
    assert len(value) <= 32

    w.extend(value)
    for _ in range(32 - len(value)):
        w.append(0x00)
Exemplo n.º 19
0
    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,
                    )