Пример #1
0
def sc_add(dst, a, b):
    dst = _ensure_dst_key(dst)
    crypto.decodeint_into_noreduce(tmp_sc_1, a)
    crypto.decodeint_into_noreduce(tmp_sc_2, b)
    crypto.sc_add_into(tmp_sc_3, tmp_sc_1, tmp_sc_2)
    crypto.encodeint_into(dst, tmp_sc_3)
    return dst
Пример #2
0
def generate_tx_spend_and_key_image(
    ack: AccountCreds,
    out_key: crypto.Point,
    recv_derivation: crypto.Point,
    real_output_index: int,
    received_index: tuple[int, int],
) -> tuple[crypto.Scalar, crypto.Point]:
    """
    Generates UTXO spending key and key image.
    Corresponds to generate_key_image_helper_precomp() in the Monero codebase.

    :param ack: sender credentials
    :type ack: apps.monero.xmr.credentials.AccountCreds
    :param out_key: real output (from input RCT) destination key
    :param recv_derivation:
    :param real_output_index:
    :param received_index: subaddress index this payment was received to
    :return:
    """
    if crypto.sc_iszero(ack.spend_key_private):
        raise ValueError("Watch-only wallet not supported")

    # derive secret key with subaddress - step 1: original CN derivation
    scalar_step1 = crypto_helpers.derive_secret_key(recv_derivation,
                                                    real_output_index,
                                                    ack.spend_key_private)

    # step 2: add Hs(SubAddr || a || index_major || index_minor)
    subaddr_sk = None
    if received_index == (0, 0):
        scalar_step2 = scalar_step1
    else:
        subaddr_sk = get_subaddress_secret_key(ack.view_key_private,
                                               major=received_index[0],
                                               minor=received_index[1])
        scalar_step2 = crypto.sc_add_into(None, scalar_step1, subaddr_sk)

    # When not in multisig, we know the full spend secret key, so the output pubkey can be obtained by scalarmultBase
    pub_ver = crypto.scalarmult_base_into(None, scalar_step2)

    # <Multisig>, branch deactivated until implemented
    # # When in multisig, we only know the partial spend secret key. But we do know the full spend public key,
    # # so the output pubkey can be obtained by using the standard CN key derivation.
    # pub_ver = crypto.derive_public_key(
    #     recv_derivation, real_output_index, ack.spend_key_public
    # )
    #
    # # Add the contribution from the subaddress part
    # if received_index != (0, 0):
    #     subaddr_pk = crypto.scalarmult_base(subaddr_sk)
    #     pub_ver = crypto.point_add(pub_ver, subaddr_pk)
    # </Multisig>

    if not crypto.point_eq(pub_ver, out_key):
        raise ValueError(
            "key image helper precomp: given output pubkey doesn't match the derived one"
        )

    ki = generate_key_image(crypto_helpers.encodepoint(pub_ver), scalar_step2)
    return scalar_step2, ki
Пример #3
0
def _get_ecdh_info_and_out_pk(
        state: State, tx_out_key: Ge25519, amount: int, mask: Sc25519,
        amount_key: Sc25519) -> 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.encodepoint(tx_out_key)
    out_pk_commitment = crypto.encodepoint(crypto.gen_commitment(mask, amount))
    crypto.sc_add_into(state.sumout, state.sumout, mask)
    ecdh_info = _ecdh_encode(amount, crypto.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
Пример #4
0
async def all_inputs_set(state: State):
    state.mem_trace(0)

    await confirms.transaction_step(state.ctx, state.STEP_ALL_IN)

    from trezor.messages.MoneroTransactionAllInputsSetAck import (
        MoneroTransactionAllInputsSetAck, )
    from trezor.messages.MoneroTransactionRsigData import MoneroTransactionRsigData

    # Generate random commitment masks to be used in range proofs.
    # If SimpleRCT is used the sum of the masks must match the input masks sum.
    state.sumout = crypto.sc_init(0)
    for i in range(state.output_count):
        cur_mask = crypto.new_scalar()  # new mask for each output
        is_last = i + 1 == state.output_count
        if is_last and state.rct_type == RctType.Simple:
            # in SimpleRCT the last mask needs to be calculated as an offset of the sum
            crypto.sc_sub_into(cur_mask, state.sumpouts_alphas, state.sumout)
        else:
            crypto.random_scalar(cur_mask)

        crypto.sc_add_into(state.sumout, state.sumout, cur_mask)
        state.output_masks.append(cur_mask)

    if state.rct_type == RctType.Simple:
        utils.ensure(crypto.sc_eq(state.sumout, state.sumpouts_alphas),
                     "Invalid masks sum")  # sum check
    state.sumout = crypto.sc_init(0)

    rsig_data = MoneroTransactionRsigData()
    resp = MoneroTransactionAllInputsSetAck(rsig_data=rsig_data)

    # If range proofs are being offloaded, we send the masks to the host, which uses them
    # to create the range proof. If not, we do not send any and we use them in the following step.
    if state.rsig_offload:
        tmp_buff = bytearray(32)
        rsig_data.mask = bytearray(32 * state.output_count)
        for i in range(state.output_count):
            crypto.encodeint_into(tmp_buff, state.output_masks[i])
            utils.memcpy(rsig_data.mask, 32 * i, tmp_buff, 0, 32)

    return resp
Пример #5
0
def _get_ecdh_info_and_out_pk(state: State, tx_out_key, amount, mask,
                              amount_key):
    """
    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.encodepoint(tx_out_key)
    out_pk_commitment = crypto.encodepoint(crypto.gen_commitment(mask, amount))
    crypto.sc_add_into(state.sumout, state.sumout, mask)

    # masking of mask and amount
    ecdh_info = _ecdh_encode(mask, amount, crypto.encodeint(amount_key),
                             state.is_bulletproof_v2())

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

    return out_pk_dest, out_pk_commitment, ecdh_info_bin
Пример #6
0
def scalar_fold(v, a, b, into=None, into_offset=0):
    """
    ln = len(v); h = ln // 2
    v[i] = v[i] * a + v[h+i] * b)
    """
    h = len(v) // 2
    crypto.decodeint_into_noreduce(tmp_sc_1, a)
    crypto.decodeint_into_noreduce(tmp_sc_2, b)
    into = into if into else v

    for i in range(h):
        crypto.decodeint_into_noreduce(tmp_sc_3, v.to(i))
        crypto.decodeint_into_noreduce(tmp_sc_4, v.to(h + i))
        crypto.sc_mul_into(tmp_sc_3, tmp_sc_3, tmp_sc_1)
        crypto.sc_mul_into(tmp_sc_4, tmp_sc_4, tmp_sc_2)
        crypto.sc_add_into(tmp_sc_3, tmp_sc_3, tmp_sc_4)
        crypto.encodeint_into(tmp_bf_0, tmp_sc_3)
        into.read(i + into_offset, tmp_bf_0)
        gc_iter(i)

    return into
Пример #7
0
def cross_inner_product(l0, r0, l1, r1):
    """
    t1_1 = l0 . r1,     t1_2 = l1 . r0
    t1   = t1_1 + t1_2, t2   = l1 . r1
    """
    sc_t1_1, sc_t1_2, sc_t2 = alloc_scalars(3)
    cl0, cr0, cl1, cr1 = alloc_scalars(4)

    for i in range(len(l0)):
        crypto.decodeint_into_noreduce(cl0, l0.to(i))
        crypto.decodeint_into_noreduce(cr0, r0.to(i))
        crypto.decodeint_into_noreduce(cl1, l1.to(i))
        crypto.decodeint_into_noreduce(cr1, r1.to(i))

        crypto.sc_muladd_into(sc_t1_1, cl0, cr1, sc_t1_1)
        crypto.sc_muladd_into(sc_t1_2, cl1, cr0, sc_t1_2)
        crypto.sc_muladd_into(sc_t2, cl1, cr1, sc_t2)
        gc_iter(i)

    crypto.sc_add_into(sc_t1_1, sc_t1_1, sc_t1_2)
    return crypto.encodeint(sc_t1_1), crypto.encodeint(sc_t2)
Пример #8
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
Пример #9
0
async def _compute_masks(state: State):
    """
    Output masks computed in advance. Used with client_version=0 && HF9.
    After HF10 (included) masks are deterministic, computed from the amount_key.

    After all client update to v1 this code will be removed.
    In order to preserve client_version=0 compatibility the masks have to be adjusted.
    """
    from trezor.messages.MoneroTransactionRsigData import MoneroTransactionRsigData
    from apps.monero.signing import offloading_keys

    rsig_data = MoneroTransactionRsigData()

    # If range proofs are being offloaded, we send the masks to the host, which uses them
    # to create the range proof. If not, we do not send any and we use them in the following step.
    if state.rsig_offload:
        rsig_data.mask = []

    # Deterministic masks, the last one is computed to balance the sums
    for i in range(state.output_count):
        if i + 1 == state.output_count:
            cur_mask = crypto.sc_sub(state.sumpouts_alphas, state.sumout)
            state.output_last_mask = cur_mask
        else:
            cur_mask = offloading_keys.det_comm_masks(state.key_enc, i)

        crypto.sc_add_into(state.sumout, state.sumout, cur_mask)

        if state.rsig_offload:
            rsig_data.mask.append(crypto.encodeint(cur_mask))

    if not crypto.sc_eq(state.sumpouts_alphas, state.sumout):
        raise ValueError("Sum eq error")

    state.sumout = crypto.sc_init(0)
    return rsig_data
Пример #10
0
def _ecdh_encode(mask, amount, amount_key, v2=False):
    """
    Output recipients need be able to reconstruct the amount commitments.
    This means the blinding factor `mask` and `amount` must be communicated
    to the receiver somehow.

    The mask and amount are stored as:
    - mask = mask + Hs(amount_key)
    - amount = amount + Hs(Hs(amount_key))
    Because the receiver can derive the `amount_key` they can
    easily derive both mask and amount as well.
    """
    from apps.monero.xmr.serialize_messages.tx_ecdh import EcdhTuple

    ecdh_info = EcdhTuple(mask=mask, amount=crypto.sc_init(amount))

    if v2:
        amnt = ecdh_info.amount
        ecdh_info.mask = crypto.NULL_KEY_ENC
        ecdh_info.amount = bytearray(32)
        crypto.encodeint_into(ecdh_info.amount, amnt)
        crypto.xor8(ecdh_info.amount, _ecdh_hash(amount_key))
        return ecdh_info

    else:
        amount_key_hash_single = crypto.hash_to_scalar(amount_key)
        amount_key_hash_double = crypto.hash_to_scalar(
            crypto.encodeint(amount_key_hash_single))

        # Not modifying passed mask, is reused in BP.
        ecdh_info.mask = crypto.sc_add(ecdh_info.mask, amount_key_hash_single)
        crypto.sc_add_into(ecdh_info.amount, ecdh_info.amount,
                           amount_key_hash_double)
        ecdh_info.mask = crypto.encodeint(ecdh_info.mask)
        ecdh_info.amount = crypto.encodeint(ecdh_info.amount)
        return ecdh_info
Пример #11
0
def _gen_commitment(state: State,
                    in_amount: int) -> tuple[crypto.Scalar, crypto.Point]:
    """
    Computes Pedersen commitment - pseudo outs
    Here is slight deviation from the original protocol.
    We want that \\sum Alpha = \\sum A_{i,j} where A_{i,j} is a mask from range proof for output i, bit j.

    Previously this was computed in such a way that Alpha_{last} = \\sum A{i,j} - \\sum_{i=0}^{last-1} Alpha
    But we would prefer to compute commitment before range proofs so alphas are generated completely randomly
    and the last A mask is computed in this special way.
    Returns pseudo_out
    """
    alpha = crypto.random_scalar()
    state.sumpouts_alphas = crypto.sc_add_into(None, state.sumpouts_alphas,
                                               alpha)
    return alpha, crypto.gen_commitment_into(None, alpha, in_amount)
Пример #12
0
    def _prove_batch_main(
        self, V, gamma, aL, aR, hash_cache, logM, logN, M, N, proof_v8=False
    ):
        logMN = logM + logN
        MN = M * N
        hash_vct_to_scalar(hash_cache, V)

        # Extended precomputed GiHi
        Gprec = self._gprec_aux(MN)
        Hprec = self._hprec_aux(MN)

        # PAPER LINES 38-39
        alpha = sc_gen()
        ve = _ensure_dst_key()
        A = _ensure_dst_key()
        vector_exponent_custom(Gprec, Hprec, aL, aR, ve)
        add_keys(A, ve, scalarmult_base(tmp_bf_1, alpha))
        if not proof_v8:
            scalarmult_key(A, A, _INV_EIGHT)
        self.gc(11)

        # PAPER LINES 40-42
        sL = self.sL_vct(MN)
        sR = self.sR_vct(MN)
        rho = sc_gen()
        vector_exponent_custom(Gprec, Hprec, sL, sR, ve)
        S = _ensure_dst_key()
        add_keys(S, ve, scalarmult_base(tmp_bf_1, rho))
        if not proof_v8:
            scalarmult_key(S, S, _INV_EIGHT)
        del ve
        self.gc(12)

        # PAPER LINES 43-45
        y = _ensure_dst_key()
        hash_cache_mash(y, hash_cache, A, S)
        if y == _ZERO:
            return (0,)

        z = _ensure_dst_key()
        hash_to_scalar(hash_cache, y)
        copy_key(z, hash_cache)
        if z == _ZERO:
            return (0,)

        # Polynomial construction by coefficients
        zMN = const_vector(z, MN)
        l0 = _ensure_dst_keyvect(None, MN)
        vector_subtract(aL, zMN, l0)
        l1 = sL
        self.gc(13)

        # This computes the ugly sum/concatenation from PAPER LINE 65
        # r0 = aR + z
        r0 = vector_add(aR, zMN)
        del zMN
        self.gc(14)

        # r0 = r0 \odot yMN => r0[i]  = r0[i] * y^i
        # r1 = sR \odot yMN => r1[i]  = sR[i] * y^i
        yMN = vector_powers(y, MN, dynamic=False)
        hadamard(r0, yMN, dst=r0)
        self.gc(15)

        # r0 = r0 + zero_twos
        zpow = vector_powers(z, M + 2)
        twoN = self._two_aux(MN)
        zero_twos = vector_z_two(N, logN, M, zpow, twoN, dynamic=True, raw=True)
        vector_gen(
            r0,
            len(r0),
            lambda i, d: crypto.encodeint_into(
                d,
                crypto.sc_add_into(
                    tmp_sc_1,
                    zero_twos[i],  # noqa: F821
                    crypto.decodeint_into_noreduce(tmp_sc_2, r0.to(i)),  # noqa: F821
                ),
            ),
        )

        del (zero_twos, twoN)
        self.gc(15)

        # Polynomial construction before PAPER LINE 46
        # r1 = KeyVEval(MN, lambda i, d: sc_mul(d, yMN[i], sR[i]))
        # r1 optimization possible, but has clashing sc registers.
        # Moreover, max memory complexity is 4MN as below (while loop).
        r1 = hadamard(yMN, sR, yMN)  # re-use yMN vector for r1
        del (yMN, sR)
        self.gc(16)

        # Inner products
        # l0 = aL - z           r0   = ((aR + z) \cdot ypow) + zt
        # l1 = sL               r1   =   sR      \cdot ypow
        # t1_1 = l0 . r1,       t1_2 = l1 . r0
        # t1   = t1_1 + t1_2,   t2   = l1 . r1
        # l = l0 \odot x*l1     r    = r0 \odot x*r1
        t1, t2 = cross_inner_product(l0, r0, l1, r1)
        self.gc(17)

        # PAPER LINES 47-48
        tau1, tau2 = sc_gen(), sc_gen()
        T1, T2 = _ensure_dst_key(), _ensure_dst_key()

        add_keys(T1, scalarmultH(tmp_bf_1, t1), scalarmult_base(tmp_bf_2, tau1))
        if not proof_v8:
            scalarmult_key(T1, T1, _INV_EIGHT)

        add_keys(T2, scalarmultH(tmp_bf_1, t2), scalarmult_base(tmp_bf_2, tau2))
        if not proof_v8:
            scalarmult_key(T2, T2, _INV_EIGHT)
        del (t1, t2)
        self.gc(17)

        # PAPER LINES 49-51
        x = _ensure_dst_key()
        hash_cache_mash(x, hash_cache, z, T1, T2)
        if x == _ZERO:
            return (0,)

        # PAPER LINES 52-53
        taux = _ensure_dst_key()
        copy_key(taux, _ZERO)
        sc_mul(taux, tau1, x)
        xsq = _ensure_dst_key()
        sc_mul(xsq, x, x)
        sc_muladd(taux, tau2, xsq, taux)
        del (xsq, tau1, tau2)
        for j in range(1, len(V) + 1):
            sc_muladd(taux, zpow.to(j + 1), gamma[j - 1], taux)
        del zpow

        self.gc(18)
        mu = _ensure_dst_key()
        sc_muladd(mu, x, rho, alpha)
        del (rho, alpha)

        # PAPER LINES 54-57
        # l = l0 \odot x*l1, has to evaluated as it becomes aprime in the loop
        l = vector_gen(
            l0,
            len(l0),
            lambda i, d: sc_add(d, d, sc_mul(tmp_bf_1, l1.to(i), x)),  # noqa: F821
        )
        del (l0, l1, sL)
        self.gc(19)

        # r = r0 \odot x*r1, has to evaluated as it becomes bprime in the loop
        r = vector_gen(
            r0,
            len(r0),
            lambda i, d: sc_add(d, d, sc_mul(tmp_bf_1, r1.to(i), x)),  # noqa: F821
        )
        t = inner_product(l, r)
        del (r1, r0)
        self.gc(19)

        # PAPER LINES 32-33
        x_ip = hash_cache_mash(None, hash_cache, x, taux, mu, t)
        if x_ip == _ZERO:
            return 0, None

        # PHASE 2
        # These are used in the inner product rounds
        nprime = MN
        Gprime = _ensure_dst_keyvect(None, MN)
        Hprime = _ensure_dst_keyvect(None, MN)
        aprime = l
        bprime = r
        yinv = invert(None, y)
        yinvpow = init_key(_ONE)
        self.gc(20)

        for i in range(0, MN):
            Gprime.read(i, Gprec.to(i))
            scalarmult_key(tmp_bf_0, Hprec.to(i), yinvpow)
            Hprime.read(i, tmp_bf_0)
            sc_mul(yinvpow, yinvpow, yinv)
            gc_iter(i)
        self.gc(21)

        L = _ensure_dst_keyvect(None, logMN)
        R = _ensure_dst_keyvect(None, logMN)
        cL = _ensure_dst_key()
        cR = _ensure_dst_key()
        winv = _ensure_dst_key()
        w_round = _ensure_dst_key()
        tmp = _ensure_dst_key()

        round = 0
        _tmp_k_1 = _ensure_dst_key()

        # PAPER LINE 13
        while nprime > 1:
            # PAPER LINE 15
            npr2 = nprime
            nprime >>= 1
            self.gc(22)

            # PAPER LINES 16-17
            inner_product(
                aprime.slice_view(0, nprime), bprime.slice_view(nprime, npr2), cL
            )

            inner_product(
                aprime.slice_view(nprime, npr2), bprime.slice_view(0, nprime), cR
            )
            self.gc(23)

            # PAPER LINES 18-19
            vector_exponent_custom(
                Gprime.slice_view(nprime, npr2),
                Hprime.slice_view(0, nprime),
                aprime.slice_view(0, nprime),
                bprime.slice_view(nprime, npr2),
                tmp_bf_0,
            )

            sc_mul(tmp, cL, x_ip)
            add_keys(tmp_bf_0, tmp_bf_0, scalarmultH(_tmp_k_1, tmp))
            if not proof_v8:
                scalarmult_key(tmp_bf_0, tmp_bf_0, _INV_EIGHT)
            L.read(round, tmp_bf_0)
            self.gc(24)

            vector_exponent_custom(
                Gprime.slice_view(0, nprime),
                Hprime.slice_view(nprime, npr2),
                aprime.slice_view(nprime, npr2),
                bprime.slice_view(0, nprime),
                tmp_bf_0,
            )

            sc_mul(tmp, cR, x_ip)
            add_keys(tmp_bf_0, tmp_bf_0, scalarmultH(_tmp_k_1, tmp))
            if not proof_v8:
                scalarmult_key(tmp_bf_0, tmp_bf_0, _INV_EIGHT)
            R.read(round, tmp_bf_0)
            self.gc(25)

            # PAPER LINES 21-22
            hash_cache_mash(w_round, hash_cache, L.to(round), R.to(round))
            if w_round == _ZERO:
                return (0,)

            # PAPER LINES 24-25
            invert(winv, w_round)
            self.gc(26)

            hadamard_fold(Gprime, winv, w_round)
            self.gc(27)

            hadamard_fold(Hprime, w_round, winv, Gprime, nprime)
            Hprime.realloc_init_from(nprime, Gprime, nprime, round < 2)
            self.gc(28)

            # PAPER LINES 28-29
            scalar_fold(aprime, w_round, winv, Gprime, nprime)
            aprime.realloc_init_from(nprime, Gprime, nprime, round < 2)
            self.gc(29)

            scalar_fold(bprime, winv, w_round, Gprime, nprime)
            bprime.realloc_init_from(nprime, Gprime, nprime, round < 2)
            self.gc(30)

            # Finally resize Gprime which was buffer for all ops
            Gprime.resize(nprime, realloc=True)
            round += 1

        from apps.monero.xmr.serialize_messages.tx_rsig_bulletproof import Bulletproof

        return (
            1,
            Bulletproof(
                V=V,
                A=A,
                S=S,
                T1=T1,
                T2=T2,
                taux=taux,
                mu=mu,
                L=L,
                R=R,
                a=aprime.to(0),
                b=bprime.to(0),
                t=t,
            ),
        )
Пример #13
0
async def sign_input(
    state: State,
    src_entr: MoneroTransactionSourceEntry,
    vini_bin: bytes,
    vini_hmac: bytes,
    pseudo_out: bytes,
    pseudo_out_hmac: bytes,
    pseudo_out_alpha_enc: bytes,
    spend_enc: bytes,
    orig_idx: int,
) -> MoneroTransactionSignInputAck:
    """
    :param state: transaction state
    :param src_entr: Source entry
    :param vini_bin: tx.vin[i] for the transaction. Contains key image, offsets, amount (usually zero)
    :param vini_hmac: HMAC for the tx.vin[i] as returned from Trezor
    :param pseudo_out: Pedersen commitment for the current input, uses pseudo_out_alpha
                       as a mask. Only applicable for RCTTypeSimple.
    :param pseudo_out_hmac: HMAC for pseudo_out
    :param pseudo_out_alpha_enc: alpha mask used in pseudo_out, only applicable for RCTTypeSimple. Encrypted.
    :param spend_enc: one time address spending private key. Encrypted.
    :param orig_idx: original index of the src_entr before sorting (HMAC check)
    :return: Generated signature MGs[i]
    """
    await layout.transaction_step(state, state.STEP_SIGN,
                                  state.current_input_index + 1)

    state.current_input_index += 1
    if state.last_step not in (state.STEP_ALL_OUT, state.STEP_SIGN):
        raise ValueError("Invalid state transition")
    if state.current_input_index >= state.input_count:
        raise ValueError("Invalid inputs count")
    if pseudo_out is None:
        raise ValueError("SimpleRCT requires pseudo_out but none provided")
    if pseudo_out_alpha_enc is None:
        raise ValueError(
            "SimpleRCT requires pseudo_out's mask but none provided")

    input_position = orig_idx
    mods = utils.unimport_begin()

    # Check input's HMAC
    from apps.monero.signing import offloading_keys

    vini_hmac_comp = offloading_keys.gen_hmac_vini(state.key_hmac, src_entr,
                                                   vini_bin, input_position)
    if not crypto.ct_equals(vini_hmac_comp, vini_hmac):
        raise ValueError("HMAC is not correct")

    # Key image sorting check - permutation correctness
    cur_ki = offloading_keys.get_ki_from_vini(vini_bin)
    if state.current_input_index > 0 and state.last_ki <= cur_ki:
        raise ValueError("Key image order invalid")

    state.last_ki = cur_ki if state.current_input_index < state.input_count else None
    del (cur_ki, vini_bin, vini_hmac, vini_hmac_comp)

    gc.collect()
    state.mem_trace(1, True)

    from apps.monero.xmr import chacha_poly

    pseudo_out_alpha = crypto_helpers.decodeint(
        chacha_poly.decrypt_pack(
            offloading_keys.enc_key_txin_alpha(state.key_enc, input_position),
            bytes(pseudo_out_alpha_enc),
        ))

    # Last pseudo_out is recomputed so mask sums hold
    if input_position + 1 == state.input_count:
        # Recompute the lash alpha so the sum holds
        state.mem_trace("Correcting alpha")
        alpha_diff = crypto.sc_sub_into(None, state.sumout,
                                        state.sumpouts_alphas)
        crypto.sc_add_into(pseudo_out_alpha, pseudo_out_alpha, alpha_diff)
        pseudo_out_c = crypto.gen_commitment_into(None, pseudo_out_alpha,
                                                  state.input_last_amount)

    else:
        if input_position + 1 == state.input_count:
            utils.ensure(
                crypto.sc_eq(state.sumpouts_alphas, state.sumout) != 0,
                "Sum eq error")

        # both pseudo_out and its mask were offloaded so we need to
        # validate pseudo_out's HMAC and decrypt the alpha
        pseudo_out_hmac_comp = crypto_helpers.compute_hmac(
            offloading_keys.hmac_key_txin_comm(state.key_hmac, input_position),
            pseudo_out,
        )
        if not crypto.ct_equals(pseudo_out_hmac_comp, pseudo_out_hmac):
            raise ValueError("HMAC is not correct")

        pseudo_out_c = crypto_helpers.decodepoint(pseudo_out)

    state.mem_trace(2, True)

    # Spending secret
    spend_key = crypto_helpers.decodeint(
        chacha_poly.decrypt_pack(
            offloading_keys.enc_key_spend(state.key_enc, input_position),
            bytes(spend_enc),
        ))

    del (
        offloading_keys,
        chacha_poly,
        pseudo_out,
        pseudo_out_hmac,
        pseudo_out_alpha_enc,
        spend_enc,
    )
    utils.unimport_end(mods)
    state.mem_trace(3, True)

    # Basic setup, sanity check
    from apps.monero.xmr.serialize_messages.tx_ct_key import CtKey

    index = src_entr.real_output
    input_secret_key = CtKey(spend_key,
                             crypto_helpers.decodeint(src_entr.mask))

    # Private key correctness test
    utils.ensure(
        crypto.point_eq(
            crypto_helpers.decodepoint(
                src_entr.outputs[src_entr.real_output].key.dest),
            crypto.scalarmult_base_into(None, input_secret_key.dest),
        ),
        "Real source entry's destination does not equal spend key's",
    )
    utils.ensure(
        crypto.point_eq(
            crypto_helpers.decodepoint(
                src_entr.outputs[src_entr.real_output].key.commitment),
            crypto.gen_commitment_into(None, input_secret_key.mask,
                                       src_entr.amount),
        ),
        "Real source entry's mask does not equal spend key's",
    )

    state.mem_trace(4, True)

    from apps.monero.xmr import clsag

    mg_buffer = []
    ring_pubkeys = [x.key for x in src_entr.outputs if x]
    utils.ensure(len(ring_pubkeys) == len(src_entr.outputs), "Invalid ring")
    del src_entr

    state.mem_trace(5, True)

    assert state.full_message is not None
    state.mem_trace("CLSAG")
    clsag.generate_clsag_simple(
        state.full_message,
        ring_pubkeys,
        input_secret_key,
        pseudo_out_alpha,
        pseudo_out_c,
        index,
        mg_buffer,
    )

    del (CtKey, input_secret_key, pseudo_out_alpha, clsag, ring_pubkeys)
    state.mem_trace(6, True)

    from trezor.messages import MoneroTransactionSignInputAck

    # Encrypt signature, reveal once protocol finishes OK
    utils.unimport_end(mods)
    state.mem_trace(7, True)
    mg_buffer = _protect_signature(state, mg_buffer)

    state.mem_trace(8, True)
    state.last_step = state.STEP_SIGN
    return MoneroTransactionSignInputAck(
        signature=mg_buffer,
        pseudo_out=crypto_helpers.encodepoint(pseudo_out_c))
 def aR1_fnc(i, d):
     crypto.sc_add_into(aR1_sc1, aR.to(i), zc)
     crypto.sc_muladd_into(aR1_sc1, d_vct[i], ypow_back[MN - i],
                           aR1_sc1)
     return crypto.encodeint_into(d, aR1_sc1)
Пример #15
0
async def sign_input(
    state: State,
    src_entr: MoneroTransactionSourceEntry,
    vini_bin: bytes,
    vini_hmac: bytes,
    pseudo_out: bytes,
    pseudo_out_hmac: bytes,
    pseudo_out_alpha_enc: bytes,
    spend_enc: bytes,
):
    """
    :param state: transaction state
    :param src_entr: Source entry
    :param vini_bin: tx.vin[i] for the transaction. Contains key image, offsets, amount (usually zero)
    :param vini_hmac: HMAC for the tx.vin[i] as returned from Trezor
    :param pseudo_out: Pedersen commitment for the current input, uses pseudo_out_alpha
                       as a mask. Only applicable for RCTTypeSimple.
    :param pseudo_out_hmac: HMAC for pseudo_out
    :param pseudo_out_alpha_enc: alpha mask used in pseudo_out, only applicable for RCTTypeSimple. Encrypted.
    :param spend_enc: one time address spending private key. Encrypted.
    :return: Generated signature MGs[i]
    """
    await confirms.transaction_step(state, state.STEP_SIGN,
                                    state.current_input_index + 1)

    state.current_input_index += 1
    if state.current_input_index >= state.input_count:
        raise ValueError("Invalid inputs count")
    if pseudo_out is None:
        raise ValueError("SimpleRCT requires pseudo_out but none provided")
    if pseudo_out_alpha_enc is None:
        raise ValueError(
            "SimpleRCT requires pseudo_out's mask but none provided")

    input_position = state.source_permutation[state.current_input_index]
    mods = utils.unimport_begin()

    # Check input's HMAC
    from apps.monero.signing import offloading_keys

    vini_hmac_comp = await offloading_keys.gen_hmac_vini(
        state.key_hmac, src_entr, vini_bin, input_position)
    if not crypto.ct_equals(vini_hmac_comp, vini_hmac):
        raise ValueError("HMAC is not correct")

    gc.collect()
    state.mem_trace(1, True)

    from apps.monero.xmr.crypto import chacha_poly

    pseudo_out_alpha = crypto.decodeint(
        chacha_poly.decrypt_pack(
            offloading_keys.enc_key_txin_alpha(state.key_enc, input_position),
            bytes(pseudo_out_alpha_enc),
        ))

    # Last pseud_out is recomputed so mask sums hold
    if state.is_det_mask() and input_position + 1 == state.input_count:
        # Recompute the lash alpha so the sum holds
        state.mem_trace("Correcting alpha")
        alpha_diff = crypto.sc_sub(state.sumout, state.sumpouts_alphas)
        crypto.sc_add_into(pseudo_out_alpha, pseudo_out_alpha, alpha_diff)
        pseudo_out_c = crypto.gen_commitment(pseudo_out_alpha,
                                             state.input_last_amount)

    else:
        if input_position + 1 == state.input_count:
            utils.ensure(crypto.sc_eq(state.sumpouts_alphas, state.sumout),
                         "Sum eq error")

        # both pseudo_out and its mask were offloaded so we need to
        # validate pseudo_out's HMAC and decrypt the alpha
        pseudo_out_hmac_comp = crypto.compute_hmac(
            offloading_keys.hmac_key_txin_comm(state.key_hmac, input_position),
            pseudo_out,
        )
        if not crypto.ct_equals(pseudo_out_hmac_comp, pseudo_out_hmac):
            raise ValueError("HMAC is not correct")

        pseudo_out_c = crypto.decodepoint(pseudo_out)

    state.mem_trace(2, True)

    # Spending secret
    spend_key = crypto.decodeint(
        chacha_poly.decrypt_pack(
            offloading_keys.enc_key_spend(state.key_enc, input_position),
            bytes(spend_enc),
        ))

    del (
        offloading_keys,
        chacha_poly,
        pseudo_out,
        pseudo_out_hmac,
        pseudo_out_alpha_enc,
        spend_enc,
    )
    utils.unimport_end(mods)
    state.mem_trace(3, True)

    from apps.monero.xmr.serialize_messages.ct_keys import CtKey

    # Basic setup, sanity check
    index = src_entr.real_output
    input_secret_key = CtKey(dest=spend_key,
                             mask=crypto.decodeint(src_entr.mask))
    kLRki = None  # for multisig: src_entr.multisig_kLRki

    # Private key correctness test
    utils.ensure(
        crypto.point_eq(
            crypto.decodepoint(
                src_entr.outputs[src_entr.real_output].key.dest),
            crypto.scalarmult_base(input_secret_key.dest),
        ),
        "Real source entry's destination does not equal spend key's",
    )
    utils.ensure(
        crypto.point_eq(
            crypto.decodepoint(
                src_entr.outputs[src_entr.real_output].key.commitment),
            crypto.gen_commitment(input_secret_key.mask, src_entr.amount),
        ),
        "Real source entry's mask does not equal spend key's",
    )

    state.mem_trace(4, True)

    from apps.monero.xmr import mlsag

    mg_buffer = []
    ring_pubkeys = [x.key for x in src_entr.outputs]
    del src_entr

    mlsag.generate_mlsag_simple(
        state.full_message,
        ring_pubkeys,
        input_secret_key,
        pseudo_out_alpha,
        pseudo_out_c,
        kLRki,
        index,
        mg_buffer,
    )

    del (input_secret_key, pseudo_out_alpha, mlsag, ring_pubkeys)
    state.mem_trace(5, True)

    from trezor.messages.MoneroTransactionSignInputAck import (
        MoneroTransactionSignInputAck, )

    return MoneroTransactionSignInputAck(
        signature=mg_buffer, pseudo_out=crypto.encodepoint(pseudo_out_c))
Пример #16
0
def prove_range_borromean(amount, last_mask):
    """Calculates Borromean range proof"""
    # The large chunks allocated first to avoid potential memory fragmentation issues.
    ai = bytearray(32 * 64)
    alphai = bytearray(32 * 64)
    Cis = bytearray(32 * 64)
    s0s = bytearray(32 * 64)
    s1s = bytearray(32 * 64)
    buff = bytearray(32)
    ee_bin = bytearray(32)

    a = crypto.sc_init(0)
    si = crypto.sc_init(0)
    c = crypto.sc_init(0)
    ee = crypto.sc_init(0)
    tmp_ai = crypto.sc_init(0)
    tmp_alpha = crypto.sc_init(0)

    C_acc = crypto.identity()
    C_h = crypto.xmr_H()
    C_tmp = crypto.identity()
    L = crypto.identity()
    kck = crypto.get_keccak()

    for ii in range(64):
        crypto.random_scalar(tmp_ai)
        if last_mask is not None and ii == 63:
            crypto.sc_sub_into(tmp_ai, last_mask, a)

        crypto.sc_add_into(a, a, tmp_ai)
        crypto.random_scalar(tmp_alpha)

        crypto.scalarmult_base_into(L, tmp_alpha)
        crypto.scalarmult_base_into(C_tmp, tmp_ai)

        # if 0: C_tmp += Zero (nothing is added)
        # if 1: C_tmp += 2^i*H
        # 2^i*H is already stored in C_h
        if (amount >> ii) & 1 == 1:
            crypto.point_add_into(C_tmp, C_tmp, C_h)

        crypto.point_add_into(C_acc, C_acc, C_tmp)

        # Set Ci[ii] to sigs
        crypto.encodepoint_into(Cis, C_tmp, ii << 5)
        crypto.encodeint_into(ai, tmp_ai, ii << 5)
        crypto.encodeint_into(alphai, tmp_alpha, ii << 5)

        if ((amount >> ii) & 1) == 0:
            crypto.random_scalar(si)
            crypto.encodepoint_into(buff, L)
            crypto.hash_to_scalar_into(c, buff)

            crypto.point_sub_into(C_tmp, C_tmp, C_h)
            crypto.add_keys2_into(L, si, c, C_tmp)

            crypto.encodeint_into(s1s, si, ii << 5)

        crypto.encodepoint_into(buff, L)
        kck.update(buff)

        crypto.point_double_into(C_h, C_h)

    # Compute ee
    tmp_ee = kck.digest()
    crypto.decodeint_into(ee, tmp_ee)
    del (tmp_ee, kck)

    C_h = crypto.xmr_H()
    gc.collect()

    # Second pass, s0, s1
    for ii in range(64):
        crypto.decodeint_into(tmp_alpha, alphai, ii << 5)
        crypto.decodeint_into(tmp_ai, ai, ii << 5)

        if ((amount >> ii) & 1) == 0:
            crypto.sc_mulsub_into(si, tmp_ai, ee, tmp_alpha)
            crypto.encodeint_into(s0s, si, ii << 5)

        else:
            crypto.random_scalar(si)
            crypto.encodeint_into(s0s, si, ii << 5)

            crypto.decodepoint_into(C_tmp, Cis, ii << 5)
            crypto.add_keys2_into(L, si, ee, C_tmp)
            crypto.encodepoint_into(buff, L)
            crypto.hash_to_scalar_into(c, buff)

            crypto.sc_mulsub_into(si, tmp_ai, c, tmp_alpha)
            crypto.encodeint_into(s1s, si, ii << 5)

        crypto.point_double_into(C_h, C_h)

    crypto.encodeint_into(ee_bin, ee)

    del (ai, alphai, buff, tmp_ai, tmp_alpha, si, c, ee, C_tmp, C_h, L)
    gc.collect()

    return C_acc, a, [s0s, s1s, ee_bin, Cis]
Пример #17
0
def generate_ring_signature(
    prefix_hash: bytes,
    image: crypto.Point,
    pubs: list[crypto.Point],
    sec: crypto.Scalar,
    sec_idx: int,
    test: bool = False,
) -> Sig:
    """
    Generates ring signature with key image.
    void crypto_ops::generate_ring_signature()
    """
    from trezor.utils import memcpy

    if test:
        t = crypto.scalarmult_base_into(None, sec)
        if not crypto.point_eq(t, pubs[sec_idx]):
            raise ValueError("Invalid sec key")

        k_i = monero.generate_key_image(
            crypto_helpers.encodepoint(pubs[sec_idx]), sec)
        if not crypto.point_eq(k_i, image):
            raise ValueError("Key image invalid")
        for k in pubs:
            crypto.ge25519_check(k)

    buff_off = len(prefix_hash)
    buff = bytearray(buff_off + 2 * 32 * len(pubs))
    memcpy(buff, 0, prefix_hash, 0, buff_off)
    mvbuff = memoryview(buff)

    sum = crypto.Scalar(0)
    k = crypto.Scalar(0)
    sig = []

    for _ in range(len(pubs)):
        sig.append([crypto.Scalar(0), crypto.Scalar(0)])  # c, r

    for i in range(len(pubs)):
        if i == sec_idx:
            k = crypto.random_scalar()
            tmp3 = crypto.scalarmult_base_into(None, k)
            crypto.encodepoint_into(mvbuff[buff_off:buff_off + 32], tmp3)
            buff_off += 32

            tmp3 = crypto.hash_to_point_into(
                None, crypto_helpers.encodepoint(pubs[i]))
            tmp2 = crypto.scalarmult_into(None, tmp3, k)
            crypto.encodepoint_into(mvbuff[buff_off:buff_off + 32], tmp2)
            buff_off += 32

        else:
            sig[i] = [crypto.random_scalar(), crypto.random_scalar()]
            tmp3 = pubs[i]
            tmp2 = crypto.ge25519_double_scalarmult_vartime_into(
                None, tmp3, sig[i][0], sig[i][1])
            crypto.encodepoint_into(mvbuff[buff_off:buff_off + 32], tmp2)
            buff_off += 32

            tmp3 = crypto.hash_to_point_into(None,
                                             crypto_helpers.encodepoint(tmp3))
            tmp2 = crypto.add_keys3_into(None, sig[i][1], tmp3, sig[i][0],
                                         image)
            crypto.encodepoint_into(mvbuff[buff_off:buff_off + 32], tmp2)
            buff_off += 32

            crypto.sc_add_into(sum, sum, sig[i][0])

    h = crypto.hash_to_scalar_into(None, buff)
    sig[sec_idx][0] = crypto.sc_sub_into(None, h, sum)
    sig[sec_idx][1] = crypto.sc_mulsub_into(None, sig[sec_idx][0], sec, k)
    return sig