Exemplo n.º 1
0
def _get_key_for_payment_id_encryption(destinations: list, change_addr=None):
    """
    Returns destination address public view key to be used for
    payment id encryption.
    """
    from apps.monero.xmr.addresses import addr_eq
    from trezor.messages.MoneroAccountPublicAddress import MoneroAccountPublicAddress

    addr = MoneroAccountPublicAddress(spend_public_key=crypto.NULL_KEY_ENC,
                                      view_public_key=crypto.NULL_KEY_ENC)
    count = 0
    for dest in destinations:
        if dest.amount == 0:
            continue
        if change_addr and addr_eq(dest.addr, change_addr):
            continue
        if addr_eq(dest.addr, addr):
            continue
        if count > 0:
            raise ValueError(
                "Destinations have to have exactly one output to support encrypted payment ids"
            )
        addr = dest.addr
        count += 1

    if count == 0 and change_addr:
        return change_addr.view_public_key

    if addr.view_public_key == crypto.NULL_KEY_ENC:
        raise ValueError("Invalid key")

    return addr.view_public_key
def _check_change(state: State,
                  outputs: List[MoneroTransactionDestinationEntry]):
    """
    Check if the change address in state.output_change (from `tsx_data.outputs`) is
    a) among tx outputs
    b) is equal to our address

    The change output is in `tsx_data.change_dts`, but also has to be in `tsx_data.outputs`.
    This is what Monero does in its cold wallet signing protocol.

    In other words, these structures are built by Monero when generating unsigned transaction set
    and we do not want to modify this logic. We just translate the unsigned tx to the protobuf message.

    So, although we could probably optimize this by having the change output in `change_dts`
    only, we intentionally do not do so.
    """
    from apps.monero.xmr.addresses import addr_eq, get_change_addr_idx

    change_index = get_change_addr_idx(outputs, state.output_change)

    change_addr = state.change_address()
    # if there is no change, there is nothing to check
    if change_addr is None:
        state.mem_trace("No change" if __debug__ else None)
        return
    """
    Sweep tx is just one output and no change.
    To prevent recognition of such transactions another fake output is added
    that spends exactly 0 coins to a random address.
    See https://github.com/monero-project/monero/pull/1415
    """
    if change_index is None and state.output_change.amount == 0 and len(
            outputs) == 2:
        state.mem_trace("Sweep tsx" if __debug__ else None)
        return

    found = False
    for out in outputs:
        if addr_eq(out.addr, change_addr):
            found = True
            break

    if not found:
        raise signing.ChangeAddressError("Change address not found in outputs")

    my_addr = _get_primary_change_address(state)
    if not addr_eq(my_addr, change_addr):
        raise signing.ChangeAddressError("Change address differs from ours")
Exemplo n.º 3
0
def _set_out_derivation(
    state: State,
    dst_entr: MoneroTransactionDestinationEntry,
    additional_txkey_priv: crypto.Scalar,
) -> crypto.Point:
    """
    Calculates derivation which is then used in the one-time address as
    `P = H(derivation)*G + B`.
    For change outputs the derivation equals a*R, because we know the
    private view key. For others it is either `r*A` for traditional
    addresses, or `s*C` for subaddresses. Both `r` and `s` are random
    scalars, `s` is used in the context of subaddresses, but it's
    basically the same thing.
    """
    from apps.monero.xmr.addresses import addr_eq

    change_addr = state.change_address()
    if change_addr and addr_eq(dst_entr.addr, change_addr):
        # sending change to yourself; derivation = a*R
        derivation = crypto_helpers.generate_key_derivation(
            state.tx_pub, state.creds.view_key_private)

    else:
        # sending to the recipient; derivation = r*A (or s*C in the subaddress scheme)
        if dst_entr.is_subaddress and state.need_additional_txkeys:
            deriv_priv = additional_txkey_priv
        else:
            deriv_priv = state.tx_priv
        derivation = crypto_helpers.generate_key_derivation(
            crypto_helpers.decodepoint(dst_entr.addr.view_public_key),
            deriv_priv)
    return derivation
Exemplo n.º 4
0
def _get_key_for_payment_id_encryption(
    tsx_data: MoneroTransactionData,
    change_addr: MoneroAccountPublicAddress | None = None,
    add_dummy_payment_id: bool = False,
) -> bytes:
    """
    Returns destination address public view key to be used for
    payment id encryption. If no encrypted payment ID is chosen,
    dummy payment ID is set for better transaction uniformity if possible.
    """
    from apps.monero.xmr.addresses import addr_eq
    from trezor.messages import MoneroAccountPublicAddress

    addr = MoneroAccountPublicAddress(
        spend_public_key=crypto_helpers.NULL_KEY_ENC,
        view_public_key=crypto_helpers.NULL_KEY_ENC,
    )
    count = 0
    for dest in tsx_data.outputs:
        if dest.amount == 0:
            continue
        if change_addr and addr_eq(dest.addr, change_addr):
            continue
        if addr_eq(dest.addr, addr):
            continue
        if count > 0 and tsx_data.payment_id:
            raise ValueError(
                "Destinations have to have exactly one output to support encrypted payment ids"
            )
        addr = dest.addr
        count += 1

    # Insert dummy payment id for transaction uniformity
    if not tsx_data.payment_id and count <= 1 and add_dummy_payment_id:
        tsx_data.payment_id = bytearray(8)

    if count == 0 and change_addr:
        return change_addr.view_public_key

    if addr.view_public_key == crypto_helpers.NULL_KEY_ENC:
        raise ValueError("Invalid key")

    return addr.view_public_key