Exemplo n.º 1
0
def _compute_sec_keys(state: State, tsx_data: MoneroTransactionData) -> None:
    """
    Generate master key H( H(TsxData || tx_priv) || rand )
    """
    from trezor import protobuf
    from apps.monero.xmr.keccak_hasher import get_keccak_writer

    writer = get_keccak_writer()
    writer.write(protobuf.dump_message_buffer(tsx_data))
    writer.write(crypto_helpers.encodeint(state.tx_priv))

    master_key = crypto_helpers.keccak_2hash(
        writer.get_digest() + crypto_helpers.encodeint(crypto.random_scalar()))
    state.key_hmac = crypto_helpers.keccak_2hash(b"hmac" + master_key)
    state.key_enc = crypto_helpers.keccak_2hash(b"enc" + master_key)
Exemplo n.º 2
0
def _compute_tx_keys(
    state: State, dst_entr: MoneroTransactionDestinationEntry
) -> tuple[crypto.Point, crypto.Scalar, crypto.Point]:
    """Computes tx_out_key, amount_key"""

    if state.is_processing_offloaded:
        return None, None, None  # no need to recompute

    # additional tx key if applicable
    additional_txkey_priv = _set_out_additional_keys(state, dst_entr)
    # derivation = a*R or r*A or s*C
    derivation = _set_out_derivation(state, dst_entr, additional_txkey_priv)
    # amount key = H_s(derivation || i)
    amount_key = crypto_helpers.derivation_to_scalar(
        derivation, state.current_output_index)
    # one-time destination address P = H_s(derivation || i)*G + B
    tx_out_key = crypto_helpers.derive_public_key(
        derivation,
        state.current_output_index,
        crypto_helpers.decodepoint(dst_entr.addr.spend_public_key),
    )
    del (additional_txkey_priv, )

    from apps.monero.xmr import monero

    mask = monero.commitment_mask(crypto_helpers.encodeint(amount_key))
    state.output_masks.append(mask)
    return tx_out_key, amount_key, derivation
Exemplo n.º 3
0
def _compute_tx_key(spend_key_private: crypto.Scalar,
                    tx_prefix_hash: bytes) -> tuple[bytes, bytes, bytes]:
    salt = random.bytes(32)

    rand_mult_num = crypto.random_scalar()
    rand_mult = crypto_helpers.encodeint(rand_mult_num)

    tx_key = misc.compute_tx_key(spend_key_private, tx_prefix_hash, salt,
                                 rand_mult_num)
    return tx_key, salt, rand_mult
Exemplo n.º 4
0
def compute_enc_key_host(view_key_private: Scalar,
                         tx_prefix_hash: bytes) -> tuple[bytes, bytes]:
    from trezor.crypto import random
    from apps.monero.xmr import crypto_helpers

    salt = random.bytes(32)
    passwd = crypto_helpers.keccak_2hash(
        crypto_helpers.encodeint(view_key_private) + tx_prefix_hash)
    tx_key = crypto_helpers.compute_hmac(salt, passwd)
    return tx_key, salt
    def test_derivation_to_scalar(self):
        derivation = unhexlify(
            b"e720a09f2e3a0bbf4e4ba7ad93653bb296885510121f806acb2a5f9168fafa01"
        )
        scalar = unhexlify(
            b"25d08763414c379aa9cf989cdcb3cadd36bd5193b500107d6bf5f921f18e470e"
        )

        sc_int = crypto_helpers.derivation_to_scalar(
            crypto_helpers.decodepoint(derivation), 0)
        self.assertEqual(scalar, crypto_helpers.encodeint(sc_int))
 def test_get_subaddress_secret_key(self):
     a = crypto_helpers.decodeint(
         unhexlify(
             b"4ce88c168e0f5f8d6524f712d5f8d7d83233b1e7a2a60b5aba5206cc0ea2bc08"
         ))
     m = monero.get_subaddress_secret_key(secret_key=a, major=0, minor=1)
     self.assertEqual(
         crypto_helpers.encodeint(m),
         unhexlify(
             b"b6ff4d689b95e3310efbf683850c075bcde46361923054e42ef30016b287ff0c"
         ),
     )
    def test_sc_inversion(self):
        res = crypto.Scalar()
        inp = crypto_helpers.decodeint(
            unhexlify(
                b"3482fb9735ef879fcae5ec7721b5d3646e155c4fb58d6cc11c732c9c9b76620a"
            ))

        crypto.sc_inv_into(res, inp)
        self.assertEqual(
            hexlify(crypto_helpers.encodeint(res)),
            b"bcf365a551e6358f3f281a6241d4a25eded60230b60a1d48c67b51a85e33d70e",
        )
Exemplo n.º 8
0
async def get_watch_only(
    ctx: Context, msg: MoneroGetWatchKey, keychain: Keychain
) -> MoneroWatchKey:
    await paths.validate_path(ctx, keychain, msg.address_n)

    await layout.require_confirm_watchkey(ctx)

    creds = misc.get_creds(keychain, msg.address_n, msg.network_type)
    address = creds.address
    watch_key = crypto_helpers.encodeint(creds.view_key_private)

    return MoneroWatchKey(watch_key=watch_key, address=address.encode())
Exemplo n.º 9
0
def compute_tx_key(
    spend_key_private: Scalar,
    tx_prefix_hash: bytes,
    salt: bytes,
    rand_mult_num: Scalar,
) -> bytes:
    from apps.monero.xmr import crypto, crypto_helpers

    rand_inp = crypto.sc_add_into(None, spend_key_private, rand_mult_num)
    passwd = crypto_helpers.keccak_2hash(
        crypto_helpers.encodeint(rand_inp) + tx_prefix_hash)
    tx_key = crypto_helpers.compute_hmac(salt, passwd)
    return tx_key
Exemplo n.º 10
0
def generate_monero_keys(
    seed: bytes,
) -> tuple[crypto.Scalar, crypto.Point, crypto.Scalar, crypto.Point]:
    """
    Generates spend key / view key from the seed in the same manner as Monero code does.

    account.cpp:
    crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random).
    """
    spend_sec, spend_pub = generate_keys(crypto_helpers.decodeint(seed))
    hash = crypto.fast_hash_into(None, crypto_helpers.encodeint(spend_sec))
    view_sec, view_pub = generate_keys(crypto_helpers.decodeint(hash))
    return spend_sec, spend_pub, view_sec, view_pub
Exemplo n.º 11
0
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_helpers.encodeint(state.tx_priv) + b"".join([
        crypto_helpers.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,
    )
Exemplo n.º 12
0
def _get_ecdh_info_and_out_pk(
    state: State,
    tx_out_key: crypto.Point,
    amount: int,
    mask: crypto.Scalar,
    amount_key: crypto.Scalar,
) -> tuple[bytes, bytes, bytes]:
    """
    Calculates the Pedersen commitment C = aG + bH and returns it as CtKey.
    Also encodes the two items - `mask` and `amount` - into ecdh info,
    so the recipient is able to reconstruct the commitment.
    """
    out_pk_dest = crypto_helpers.encodepoint(tx_out_key)
    out_pk_commitment = crypto.gen_commitment_into(None, mask, amount)

    out_pk_commitment = crypto_helpers.encodepoint(out_pk_commitment)
    crypto.sc_add_into(state.sumout, state.sumout, mask)
    ecdh_info = _ecdh_encode(amount, crypto_helpers.encodeint(amount_key))

    # Manual ECDH info serialization
    ecdh_info_bin = _serialize_ecdh(ecdh_info)
    gc.collect()

    return out_pk_dest, out_pk_commitment, ecdh_info_bin
Exemplo n.º 13
0
def _range_proof(
    state: State, rsig_data: MoneroTransactionRsigData
) -> tuple[MoneroTransactionRsigData, crypto.Scalar]:
    """
    Computes rangeproof and handles range proof offloading logic.

    Since HF10 the commitments are deterministic.
    The range proof is incrementally hashed to the final_message.
    """
    provided_rsig = None
    if rsig_data and rsig_data.rsig and len(rsig_data.rsig) > 0:
        provided_rsig = rsig_data.rsig
    if not state.rsig_offload and provided_rsig:
        raise signing.Error("Provided unexpected rsig")

    # Batching & validation
    bidx = _get_rsig_batch(state, state.current_output_index)
    last_in_batch = _is_last_in_batch(state, state.current_output_index, bidx)
    if state.rsig_offload and provided_rsig and not last_in_batch:
        raise signing.Error("Provided rsig too early")

    if (state.rsig_offload and last_in_batch and not provided_rsig
            and state.is_processing_offloaded):
        raise signing.Error("Rsig expected, not provided")

    # Batch not finished, skip range sig generation now
    mask = state.output_masks[-1] if not state.is_processing_offloaded else None
    offload_mask = mask and state.rsig_offload

    # If not last, do not proceed to the BP processing.
    if not last_in_batch:
        rsig_data_new = (_return_rsig_data(
            mask=crypto_helpers.encodeint(mask)) if offload_mask else None)
        return rsig_data_new, mask

    # Rangeproof
    # Pedersen commitment on the value, mask from the commitment, range signature.
    rsig = None

    state.mem_trace("pre-rproof" if __debug__ else None, collect=True)
    if not state.rsig_offload:
        # Bulletproof calculation in Trezor
        rsig = _rsig_bp(state)

    elif not state.is_processing_offloaded:
        # Bulletproof offloaded to the host, deterministic masks. Nothing here, waiting for offloaded BP.
        pass

    else:
        # Bulletproof offloaded to the host, check BP, hash it.
        _rsig_process_bp(state, rsig_data)

    state.mem_trace("rproof" if __debug__ else None, collect=True)

    # Construct new rsig data to send back to the host.
    rsig_data_new = _return_rsig_data(
        rsig,
        crypto_helpers.encodeint(mask) if offload_mask else None)

    if state.current_output_index + 1 == state.output_count and (
            not state.rsig_offload or state.is_processing_offloaded):
        # output masks and amounts are not needed anymore
        state.output_amounts = None
        state.output_masks = None

    return rsig_data_new, mask
Exemplo n.º 14
0
async def set_input(
        state: State, src_entr: MoneroTransactionSourceEntry
) -> MoneroTransactionSetInputAck:
    from trezor.messages import MoneroTransactionSetInputAck
    from apps.monero.xmr 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_helpers.decodepoint(
        src_entr.outputs[src_entr.real_output].key.dest)
    # the tx_pub of our UTXO stored inside its transaction
    tx_key = crypto_helpers.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_helpers.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_helpers.encodepoint(pseudo_out)

    # The alpha is encrypted and passed back for storage
    pseudo_out_hmac = crypto_helpers.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_helpers.encodeint(alpha),
    )

    spend_enc = chacha_poly.encrypt_pack(
        offloading_keys.enc_key_spend(state.key_enc,
                                      state.current_input_index),
        crypto_helpers.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,
    )
Exemplo n.º 15
0
def _generate_clsag(
    message: bytes,
    P: list[bytes],
    p: crypto.Scalar,
    C_nonzero: list[bytes],
    z: crypto.Scalar,
    Cout: crypto.Point,
    index: int,
    mg_buff: list[bytearray],
) -> list[bytes]:
    sI = crypto.Point()  # sig.I
    sD = crypto.Point()  # sig.D
    sc1 = crypto.Scalar()  # sig.c1
    a = crypto.random_scalar()
    H = crypto.Point()
    D = crypto.Point()
    Cout_bf = crypto_helpers.encodepoint(Cout)

    tmp_sc = crypto.Scalar()
    tmp = crypto.Point()
    tmp_bf = bytearray(32)

    crypto.hash_to_point_into(H, P[index])
    crypto.scalarmult_into(sI, H, p)  # I = p*H
    crypto.scalarmult_into(D, H, z)  # D = z*H
    crypto.sc_mul_into(tmp_sc, z, crypto_helpers.INV_EIGHT_SC)  # 1/8*z
    crypto.scalarmult_into(sD, H, tmp_sc)  # sig.D = 1/8*z*H
    sD = crypto_helpers.encodepoint(sD)

    hsh_P = crypto_helpers.get_keccak()  # domain, I, D, P, C, C_offset
    hsh_C = crypto_helpers.get_keccak()  # domain, I, D, P, C, C_offset
    hsh_P.update(_HASH_KEY_CLSAG_AGG_0)
    hsh_C.update(_HASH_KEY_CLSAG_AGG_1)

    def hsh_PC(x):
        nonlocal hsh_P, hsh_C
        hsh_P.update(x)
        hsh_C.update(x)

    for x in P:
        hsh_PC(x)

    for x in C_nonzero:
        hsh_PC(x)

    hsh_PC(crypto.encodepoint_into(tmp_bf, sI))
    hsh_PC(sD)
    hsh_PC(Cout_bf)
    mu_P = crypto_helpers.decodeint(hsh_P.digest())
    mu_C = crypto_helpers.decodeint(hsh_C.digest())

    del (hsh_PC, hsh_P, hsh_C)
    c_to_hash = crypto_helpers.get_keccak()  # domain, P, C, C_offset, message, aG, aH
    c_to_hash.update(_HASH_KEY_CLSAG_ROUND)
    for i in range(len(P)):
        c_to_hash.update(P[i])
    for i in range(len(P)):
        c_to_hash.update(C_nonzero[i])
    c_to_hash.update(Cout_bf)
    c_to_hash.update(message)

    chasher = c_to_hash.copy()
    crypto.scalarmult_base_into(tmp, a)
    chasher.update(crypto.encodepoint_into(tmp_bf, tmp))  # aG
    crypto.scalarmult_into(tmp, H, a)
    chasher.update(crypto.encodepoint_into(tmp_bf, tmp))  # aH
    c = crypto_helpers.decodeint(chasher.digest())
    del (chasher, H)

    L = crypto.Point()
    R = crypto.Point()
    c_p = crypto.Scalar()
    c_c = crypto.Scalar()
    i = (index + 1) % len(P)
    if i == 0:
        crypto.sc_copy(sc1, c)

    mg_buff.append(int_serialize.dump_uvarint_b(len(P)))
    for _ in range(len(P)):
        mg_buff.append(bytearray(32))

    while i != index:
        crypto.random_scalar(tmp_sc)
        crypto.encodeint_into(mg_buff[i + 1], tmp_sc)

        crypto.sc_mul_into(c_p, mu_P, c)
        crypto.sc_mul_into(c_c, mu_C, c)

        # L = tmp_sc * G + c_P * P[i] + c_c * C[i]
        crypto.add_keys2_into(L, tmp_sc, c_p, crypto.decodepoint_into(tmp, P[i]))
        crypto.decodepoint_into(tmp, C_nonzero[i])  # C = C_nonzero - Cout
        crypto.point_sub_into(tmp, tmp, Cout)
        crypto.scalarmult_into(tmp, tmp, c_c)
        crypto.point_add_into(L, L, tmp)

        # R = tmp_sc * HP + c_p * I + c_c * D
        crypto.hash_to_point_into(tmp, P[i])
        crypto.add_keys3_into(R, tmp_sc, tmp, c_p, sI)
        crypto.point_add_into(R, R, crypto.scalarmult_into(tmp, D, c_c))

        chasher = c_to_hash.copy()
        chasher.update(crypto.encodepoint_into(tmp_bf, L))
        chasher.update(crypto.encodepoint_into(tmp_bf, R))
        crypto.decodeint_into(c, chasher.digest())

        P[i] = None  # type: ignore
        C_nonzero[i] = None  # type: ignore

        i = (i + 1) % len(P)
        if i == 0:
            crypto.sc_copy(sc1, c)

        if i & 3 == 0:
            gc.collect()

    # Final scalar = a - c * (mu_P * p + mu_c * Z)
    crypto.sc_mul_into(tmp_sc, mu_P, p)
    crypto.sc_muladd_into(tmp_sc, mu_C, z, tmp_sc)
    crypto.sc_mulsub_into(tmp_sc, c, tmp_sc, a)
    crypto.encodeint_into(mg_buff[index + 1], tmp_sc)

    if TYPE_CHECKING:
        assert list_of_type(mg_buff, bytes)

    mg_buff.append(crypto_helpers.encodeint(sc1))
    mg_buff.append(sD)
    return mg_buff