async def final_msg(state: State):
    tx_key, salt, rand_mult = _compute_tx_key(state.creds.spend_key_private,
                                              state.tx_prefix_hash)

    key_buff = crypto.encodeint(state.tx_priv) + b"".join(
        [crypto.encodeint(x) for x in state.additional_tx_private_keys])
    tx_enc_keys = chacha_poly.encrypt_pack(tx_key, key_buff)

    return MoneroTransactionFinalAck(cout_key=None,
                                     salt=salt,
                                     rand_mult=rand_mult,
                                     tx_enc_keys=tx_enc_keys)
Exemple #2
0
async def get_tx_keys(ctx, msg: MoneroGetTxKeyRequest, keychain):
    await paths.validate_path(
        ctx, misc.validate_full_path, keychain, msg.address_n, CURVE
    )

    do_deriv = msg.reason == _GET_TX_KEY_REASON_TX_DERIVATION
    await confirms.require_confirm_tx_key(ctx, export_key=not do_deriv)

    creds = misc.get_creds(keychain, msg.address_n, msg.network_type)

    tx_enc_key = misc.compute_tx_key(
        creds.spend_key_private,
        msg.tx_prefix_hash,
        msg.salt1,
        crypto.decodeint(msg.salt2),
    )

    # the plain_buff first stores the tx_priv_keys as decrypted here
    # and then is used to store the derivations if applicable
    plain_buff = chacha_poly.decrypt_pack(tx_enc_key, msg.tx_enc_keys)
    utils.ensure(len(plain_buff) % 32 == 0, "Tx key buffer has invalid size")
    del msg.tx_enc_keys

    # If return only derivations do tx_priv * view_pub
    if do_deriv:
        plain_buff = bytearray(plain_buff)
        view_pub = crypto.decodepoint(msg.view_public_key)
        tx_priv = crypto.new_scalar()
        derivation = crypto.new_point()
        n_keys = len(plain_buff) // 32
        for c in range(n_keys):
            crypto.decodeint_into(tx_priv, plain_buff, 32 * c)
            crypto.scalarmult_into(derivation, view_pub, tx_priv)
            crypto.encodepoint_into(plain_buff, derivation, 32 * c)

    # Encrypt by view-key based password.
    tx_enc_key_host, salt = misc.compute_enc_key_host(
        creds.view_key_private, msg.tx_prefix_hash
    )

    res = chacha_poly.encrypt_pack(tx_enc_key_host, plain_buff)
    res_msg = MoneroGetTxKeyAck(salt=salt)
    if do_deriv:
        res_msg.tx_derivations = res
        return res_msg

    res_msg.tx_keys = res
    return res_msg
def final_msg(state: State) -> MoneroTransactionFinalAck:
    if state.last_step != state.STEP_SIGN:
        raise ValueError("Invalid state transition")
    if state.current_input_index != state.input_count - 1:
        raise ValueError("Invalid input count")

    tx_key, salt, rand_mult = _compute_tx_key(state.creds.spend_key_private,
                                              state.tx_prefix_hash)

    key_buff = crypto.encodeint(state.tx_priv) + b"".join(
        [crypto.encodeint(x) for x in state.additional_tx_private_keys])
    tx_enc_keys = chacha_poly.encrypt_pack(tx_key, key_buff)
    state.last_step = None

    return MoneroTransactionFinalAck(
        cout_key=None,
        salt=salt,
        rand_mult=rand_mult,
        tx_enc_keys=tx_enc_keys,
        opening_key=state.opening_key,
    )
Exemple #4
0
async def _refresh_step(s: LiveRefreshState, ctx,
                        msg: MoneroLiveRefreshStepRequest):
    buff = bytearray(32 * 3)
    buff_mv = memoryview(buff)

    await confirms.live_refresh_step(ctx, s.current_output)
    s.current_output += 1

    if __debug__:
        log.debug(__name__, "refresh, step i: %d", s.current_output)

    # Compute spending secret key and the key image
    # spend_priv = Hs(recv_deriv || real_out_idx) + spend_key_private
    # If subaddr:
    #   spend_priv += Hs("SubAddr" || view_key_private || major || minor)
    # out_key = spend_priv * G, KI: spend_priv * Hp(out_key)
    out_key = crypto.decodepoint(msg.out_key)
    recv_deriv = crypto.decodepoint(msg.recv_deriv)
    received_index = msg.sub_addr_major, msg.sub_addr_minor
    spend_priv, ki = monero.generate_tx_spend_and_key_image(
        s.creds, out_key, recv_deriv, msg.real_out_idx, received_index)

    ki_enc = crypto.encodepoint(ki)
    sig = key_image.generate_ring_signature(ki_enc, ki, [out_key], spend_priv,
                                            0, False)
    del spend_priv  # spend_priv never leaves the device

    # Serialize into buff
    buff[0:32] = ki_enc
    crypto.encodeint_into(buff_mv[32:64], sig[0][0])
    crypto.encodeint_into(buff_mv[64:], sig[0][1])

    # Encrypt with view key private based key - so host can decrypt and verify HMAC
    enc_key, salt = misc.compute_enc_key_host(s.creds.view_key_private,
                                              msg.out_key)
    resp = chacha_poly.encrypt_pack(enc_key, buff)

    return MoneroLiveRefreshStepAck(salt=salt, key_image=resp)
async def set_input(
        state: State, src_entr: MoneroTransactionSourceEntry
) -> MoneroTransactionSetInputAck:
    from trezor.messages import MoneroTransactionSetInputAck
    from apps.monero.xmr.crypto import chacha_poly
    from apps.monero.xmr.serialize_messages.tx_prefix import TxinToKey
    from apps.monero.signing import offloading_keys

    state.current_input_index += 1

    await layout.transaction_step(state, state.STEP_INP,
                                  state.current_input_index)

    if state.last_step > state.STEP_INP:
        raise ValueError("Invalid state transition")
    if state.current_input_index >= state.input_count:
        raise ValueError("Too many inputs")
    # real_output denotes which output in outputs is the real one (ours)
    if src_entr.real_output >= len(src_entr.outputs):
        raise ValueError(
            f"real_output index {src_entr.real_output} bigger than output_keys.size() {len(src_entr.outputs)}"
        )
    state.summary_inputs_money += src_entr.amount

    # Secrets derivation
    # the UTXO's one-time address P
    out_key = crypto.decodepoint(
        src_entr.outputs[src_entr.real_output].key.dest)
    # the tx_pub of our UTXO stored inside its transaction
    tx_key = crypto.decodepoint(src_entr.real_out_tx_key)
    additional_tx_pub_key = _get_additional_public_key(src_entr)

    # Calculates `derivation = Ra`, private spend key `x = H(Ra||i) + b` to be able
    # to spend the UTXO; and key image `I = x*H(P||i)`
    xi, ki, _di = monero.generate_tx_spend_and_key_image_and_derivation(
        state.creds,
        state.subaddresses,
        out_key,
        tx_key,
        additional_tx_pub_key,
        src_entr.real_output_in_tx_index,
        state.account_idx,
        src_entr.subaddr_minor,
    )
    state.mem_trace(1, True)

    # Construct tx.vin
    # If multisig is used then ki in vini should be src_entr.multisig_kLRki.ki
    vini = TxinToKey(amount=src_entr.amount, k_image=crypto.encodepoint(ki))
    vini.key_offsets = _absolute_output_offsets_to_relative(
        [x.idx for x in src_entr.outputs])

    if src_entr.rct:
        vini.amount = 0

    # Serialize `vini` with variant code for TxinToKey (prefix = TxinToKey.VARIANT_CODE).
    # The binary `vini_bin` is later sent to step 4 and 9 with its hmac,
    # where it is checked and directly used.
    vini_bin = serialize.dump_msg(vini, preallocate=64, prefix=b"\x02")
    state.mem_trace(2, True)

    # HMAC(T_in,i || vin_i)
    hmac_vini = offloading_keys.gen_hmac_vini(state.key_hmac, src_entr,
                                              vini_bin,
                                              state.current_input_index)
    state.mem_trace(3, True)

    # PseudoOuts commitment, alphas stored to state
    alpha, pseudo_out = _gen_commitment(state, src_entr.amount)
    pseudo_out = crypto.encodepoint(pseudo_out)

    # The alpha is encrypted and passed back for storage
    pseudo_out_hmac = crypto.compute_hmac(
        offloading_keys.hmac_key_txin_comm(state.key_hmac,
                                           state.current_input_index),
        pseudo_out,
    )

    alpha_enc = chacha_poly.encrypt_pack(
        offloading_keys.enc_key_txin_alpha(state.key_enc,
                                           state.current_input_index),
        crypto.encodeint(alpha),
    )

    spend_enc = chacha_poly.encrypt_pack(
        offloading_keys.enc_key_spend(state.key_enc,
                                      state.current_input_index),
        crypto.encodeint(xi),
    )

    state.last_step = state.STEP_INP
    if state.current_input_index + 1 == state.input_count:
        # When we finish the inputs processing, we no longer need
        # the precomputed subaddresses so we clear them to save memory.
        state.subaddresses = None
        state.input_last_amount = src_entr.amount

    return MoneroTransactionSetInputAck(
        vini=vini_bin,
        vini_hmac=hmac_vini,
        pseudo_out=pseudo_out,
        pseudo_out_hmac=pseudo_out_hmac,
        pseudo_out_alpha=alpha_enc,
        spend_key=spend_enc,
    )