Exemplo n.º 1
0
    def prove_setup(self, sv, gamma, proof_v8=False):
        utils.ensure(len(sv) == len(gamma), "|sv| != |gamma|")
        utils.ensure(len(sv) > 0, "sv empty")

        self.proof_sec = crypto.random_bytes(64)
        self._det_mask_init()
        gc.collect()
        sv = [crypto.encodeint(x) for x in sv]
        gamma = [crypto.encodeint(x) for x in gamma]

        M, logM = 1, 0
        while M <= _BP_M and M < len(sv):
            logM += 1
            M = 1 << logM
        MN = M * _BP_N

        V = _ensure_dst_keyvect(None, len(sv))
        for i in range(len(sv)):
            add_keys2(tmp_bf_0, gamma[i], sv[i], _XMR_H)
            if not proof_v8:
                scalarmult_key(tmp_bf_0, tmp_bf_0, _INV_EIGHT)
            V.read(i, tmp_bf_0)

        aL, aR = self.aX_vcts(sv, MN)
        return M, logM, aL, aR, V, gamma
Exemplo n.º 2
0
def _recode_ecdh(ecdh_info):
    """
    In-place ecdh_info tuple recoding
    """
    ecdh_info.mask = crypto.encodeint(ecdh_info.mask)
    ecdh_info.amount = crypto.encodeint(ecdh_info.amount)
    return ecdh_info
Exemplo n.º 3
0
def _compute_tx_key(spend_key_private, tx_prefix_hash):
    salt = crypto.random_bytes(32)

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

    rand_inp = crypto.sc_add(spend_key_private, rand_mult_num)
    passwd = crypto.keccak_2hash(crypto.encodeint(rand_inp) + tx_prefix_hash)
    tx_key = crypto.compute_hmac(salt, passwd)
    return tx_key, salt, rand_mult
Exemplo n.º 4
0
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)
Exemplo n.º 5
0
def _recode_msg(mgs):
    """
    Recodes MGs signatures from raw forms to bytearrays so it works with serialization
    """
    for idx in range(len(mgs)):
        mgs[idx].cc = crypto.encodeint(mgs[idx].cc)
        if hasattr(mgs[idx], "II") and mgs[idx].II:
            for i in range(len(mgs[idx].II)):
                mgs[idx].II[i] = crypto.encodepoint(mgs[idx].II[i])

        for i in range(len(mgs[idx].ss)):
            for j in range(len(mgs[idx].ss[i])):
                mgs[idx].ss[i][j] = crypto.encodeint(mgs[idx].ss[i][j])
    return mgs
async def _compute_sec_keys(state: State, tsx_data: MoneroTransactionData):
    """
    Generate master key H( H(TsxData || tx_priv) || rand )
    """
    import protobuf
    from apps.monero.xmr.keccak_hasher import get_keccak_writer

    writer = get_keccak_writer()
    await protobuf.dump_message(writer, tsx_data)
    writer.write(crypto.encodeint(state.tx_priv))

    master_key = crypto.keccak_2hash(writer.get_digest() +
                                     crypto.encodeint(crypto.random_scalar()))
    state.key_hmac = crypto.keccak_2hash(b"hmac" + master_key)
    state.key_enc = crypto.keccak_2hash(b"enc" + master_key)
Exemplo n.º 7
0
    def ctest_multiexp(self):
        scalars = [0, 1, 2, 3, 4, 99]
        point_base = [0, 2, 4, 7, 12, 18]
        scalar_sc = [crypto.sc_init(x) for x in scalars]
        points = [
            crypto.scalarmult_base(crypto.sc_init(x)) for x in point_base
        ]

        muex = bp.MultiExp(
            scalars=[crypto.encodeint(x) for x in scalar_sc],
            point_fnc=lambda i, d: crypto.encodepoint(points[i]))

        self.assertEqual(len(muex), len(scalars))
        res = bp.multiexp(None, muex)
        res2 = bp.vector_exponent_custom(
            A=bp.KeyVEval(
                3, lambda i, d: crypto.encodepoint_into(
                    crypto.scalarmult_base(crypto.sc_init(point_base[i])), d)),
            B=bp.KeyVEval(
                3, lambda i, d: crypto.encodepoint_into(
                    crypto.scalarmult_base(crypto.sc_init(point_base[3 + i])),
                    d)),
            a=bp.KeyVEval(
                3,
                lambda i, d: crypto.encodeint_into(crypto.sc_init(scalars[i]),
                                                   d),
            ),
            b=bp.KeyVEval(
                3, lambda i, d: crypto.encodeint_into(
                    crypto.sc_init(scalars[i + 3]), d)),
        )
        self.assertEqual(res, res2)
Exemplo n.º 8
0
def _compute_tx_keys(
        state: State, dst_entr: MoneroTransactionDestinationEntry
) -> Tuple[Ge25519, Sc25519]:
    """Computes tx_out_key, amount_key"""

    if state.is_processing_offloaded:
        return 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.derivation_to_scalar(derivation,
                                             state.current_output_index)
    # one-time destination address P = H_s(derivation || i)*G + B
    tx_out_key = crypto.derive_public_key(
        derivation,
        state.current_output_index,
        crypto.decodepoint(dst_entr.addr.spend_public_key),
    )
    del (derivation, additional_txkey_priv)

    from apps.monero.xmr import monero

    mask = monero.commitment_mask(crypto.encodeint(amount_key))
    state.output_masks.append(mask)
    return tx_out_key, amount_key
Exemplo n.º 9
0
async def get_watch_only(ctx, msg: MoneroGetWatchKey):
    await confirms.require_confirm_watchkey(ctx)

    creds = await misc.get_creds(ctx, msg.address_n, msg.network_type)
    address = creds.address
    watch_key = crypto.encodeint(creds.view_key_private)

    return MoneroWatchKey(watch_key=watch_key, address=address)
Exemplo n.º 10
0
def _compute_tx_key(spend_key_private, tx_prefix_hash):
    salt = crypto.random_bytes(32)

    rand_mult_num = crypto.random_scalar()
    rand_mult = crypto.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.º 11
0
def compute_enc_key_host(view_key_private: Sc25519,
                         tx_prefix_hash: bytes) -> Tuple[bytes, bytes]:
    from apps.monero.xmr import crypto

    salt = crypto.random_bytes(32)
    passwd = crypto.keccak_2hash(
        crypto.encodeint(view_key_private) + tx_prefix_hash)
    tx_key = crypto.compute_hmac(salt, passwd)
    return tx_key, salt
Exemplo n.º 12
0
    def test_derivation_to_scalar(self):
        derivation = unhexlify(
            b"e720a09f2e3a0bbf4e4ba7ad93653bb296885510121f806acb2a5f9168fafa01"
        )
        scalar = unhexlify(
            b"25d08763414c379aa9cf989cdcb3cadd36bd5193b500107d6bf5f921f18e470e"
        )

        sc_int = crypto.derivation_to_scalar(crypto.decodepoint(derivation), 0)
        self.assertEqual(scalar, crypto.encodeint(sc_int))
Exemplo n.º 13
0
async def get_watch_only(ctx, msg: MoneroGetWatchKey, keychain):
    await paths.validate_path(ctx, misc.validate_full_path, keychain, msg.address_n)

    await confirms.require_confirm_watchkey(ctx)

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

    return MoneroWatchKey(watch_key=watch_key, address=address)
Exemplo n.º 14
0
def generate_monero_keys(seed):
    """
    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.decodeint(seed))
    hash = crypto.cn_fast_hash(crypto.encodeint(spend_sec))
    view_sec, view_pub = generate_keys(crypto.decodeint(hash))
    return spend_sec, spend_pub, view_sec, view_pub
Exemplo n.º 15
0
    def test_sc_inversion(self):
        res = crypto.new_scalar()
        inp = crypto.decodeint(
            unhexlify(
                b"3482fb9735ef879fcae5ec7721b5d3646e155c4fb58d6cc11c732c9c9b76620a"
            ))

        crypto.sc_inv_into(res, inp)
        self.assertEqual(
            hexlify(crypto.encodeint(res)),
            b"bcf365a551e6358f3f281a6241d4a25eded60230b60a1d48c67b51a85e33d70e",
        )
Exemplo n.º 16
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)
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,
    )
Exemplo n.º 18
0
def compute_tx_key(
    spend_key_private: Sc25519,
    tx_prefix_hash: bytes,
    salt: bytes,
    rand_mult_num: Sc25519,
) -> bytes:
    from apps.monero.xmr import crypto

    rand_inp = crypto.sc_add(spend_key_private, rand_mult_num)
    passwd = crypto.keccak_2hash(crypto.encodeint(rand_inp) + tx_prefix_hash)
    tx_key = crypto.compute_hmac(salt, passwd)
    return tx_key
Exemplo n.º 19
0
 def test_get_subaddress_secret_key(self):
     a = crypto.decodeint(
         unhexlify(
             b"4ce88c168e0f5f8d6524f712d5f8d7d83233b1e7a2a60b5aba5206cc0ea2bc08"
         ))
     m = monero.get_subaddress_secret_key(secret_key=a, major=0, minor=1)
     self.assertEqual(
         crypto.encodeint(m),
         unhexlify(
             b"b6ff4d689b95e3310efbf683850c075bcde46361923054e42ef30016b287ff0c"
         ),
     )
Exemplo n.º 20
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
Exemplo n.º 21
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
Exemplo n.º 22
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
Exemplo n.º 23
0
def ecdh_decode(masked, receiver_sk=None, derivation=None):
    """
    Elliptic Curve Diffie-Helman: encodes and decodes the amount b and mask a
    where C= aG + bH
    :param masked:
    :param receiver_sk:
    :param derivation:
    :return:
    """
    from apps.monero.xmr.serialize_messages.tx_ecdh import EcdhTuple

    rv = EcdhTuple()

    if derivation is None:
        derivation = crypto.scalarmult(masked.senderPk, receiver_sk)

    sharedSec1 = crypto.hash_to_scalar(derivation)
    sharedSec2 = crypto.hash_to_scalar(crypto.encodeint(sharedSec1))

    rv.mask = crypto.sc_sub(masked.mask, sharedSec1)
    rv.amount = crypto.sc_sub(masked.amount, sharedSec2)
    return rv
Exemplo n.º 24
0
def _ecdh_encode(mask, amount, amount_key):
    """
    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))
    amount_key_hash_single = crypto.hash_to_scalar(amount_key)
    amount_key_hash_double = crypto.hash_to_scalar(
        crypto.encodeint(amount_key_hash_single))

    ecdh_info.mask = crypto.sc_add(ecdh_info.mask, amount_key_hash_single)
    ecdh_info.amount = crypto.sc_add(ecdh_info.amount, amount_key_hash_double)
    return _recode_ecdh(ecdh_info)
Exemplo n.º 25
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))

    state.sumout = crypto.sc_add(state.sumout, mask)
    state.output_sk_masks.append(mask)

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

    # Manual ECDH info serialization
    ecdh_info_bin = bytearray(64)
    utils.memcpy(ecdh_info_bin, 0, ecdh_info.mask, 0, 32)
    utils.memcpy(ecdh_info_bin, 32, ecdh_info.amount, 0, 32)
    gc.collect()

    return out_pk_dest, out_pk_commitment, ecdh_info_bin
Exemplo n.º 26
0
def _compute_tx_keys(state: State, dst_entr):
    """Computes tx_out_key, amount_key"""

    if state.is_processing_offloaded:
        return 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.derivation_to_scalar(derivation,
                                             state.current_output_index)
    # one-time destination address P = H_s(derivation || i)*G + B
    tx_out_key = crypto.derive_public_key(
        derivation,
        state.current_output_index,
        crypto.decodepoint(dst_entr.addr.spend_public_key),
    )
    del (derivation, additional_txkey_priv)

    # Computes the newest mask if applicable
    if state.is_det_mask():
        from apps.monero.xmr import monero

        mask = monero.commitment_mask(crypto.encodeint(amount_key))

    elif state.current_output_index + 1 < state.output_count:
        mask = offloading_keys.det_comm_masks(state.key_enc,
                                              state.current_output_index)

    else:
        mask = state.output_last_mask
        state.output_last_mask = None

    state.output_masks.append(mask)
    return tx_out_key, amount_key
Exemplo n.º 27
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
Exemplo n.º 28
0
def is_reduced(sc):
    return crypto.encodeint(crypto.decodeint(sc)) == sc
Exemplo n.º 29
0
    def verify_batch(self, proofs, single_optim=True, proof_v8=False):
        """
        BP batch verification
        :param proofs:
        :param single_optim: single proof memory optimization
        :param proof_v8: previous testnet version
        :return:
        """
        max_length = 0
        for proof in proofs:
            utils.ensure(is_reduced(proof.taux), "Input scalar not in range")
            utils.ensure(is_reduced(proof.mu), "Input scalar not in range")
            utils.ensure(is_reduced(proof.a), "Input scalar not in range")
            utils.ensure(is_reduced(proof.b), "Input scalar not in range")
            utils.ensure(is_reduced(proof.t), "Input scalar not in range")
            utils.ensure(len(proof.V) >= 1, "V does not have at least one element")
            utils.ensure(len(proof.L) == len(proof.R), "|L| != |R|")
            utils.ensure(len(proof.L) > 0, "Empty proof")
            max_length = max(max_length, len(proof.L))

        utils.ensure(max_length < 32, "At least one proof is too large")

        maxMN = 1 << max_length
        logN = 6
        N = 1 << logN
        tmp = _ensure_dst_key()

        # setup weighted aggregates
        is_single = len(proofs) == 1 and single_optim  # ph4
        z1 = init_key(_ZERO)
        z3 = init_key(_ZERO)
        m_z4 = vector_dup(_ZERO, maxMN) if not is_single else None
        m_z5 = vector_dup(_ZERO, maxMN) if not is_single else None
        m_y0 = init_key(_ZERO)
        y1 = init_key(_ZERO)
        muex_acc = init_key(_ONE)

        Gprec = self._gprec_aux(maxMN)
        Hprec = self._hprec_aux(maxMN)

        for proof in proofs:
            M = 1
            logM = 0
            while M <= _BP_M and M < len(proof.V):
                logM += 1
                M = 1 << logM

            utils.ensure(len(proof.L) == 6 + logM, "Proof is not the expected size")
            MN = M * N
            weight_y = crypto.encodeint(crypto.random_scalar())
            weight_z = crypto.encodeint(crypto.random_scalar())

            # Reconstruct the challenges
            hash_cache = hash_vct_to_scalar(None, proof.V)
            y = hash_cache_mash(None, hash_cache, proof.A, proof.S)
            utils.ensure(y != _ZERO, "y == 0")
            z = hash_to_scalar(None, y)
            copy_key(hash_cache, z)
            utils.ensure(z != _ZERO, "z == 0")

            x = hash_cache_mash(None, hash_cache, z, proof.T1, proof.T2)
            utils.ensure(x != _ZERO, "x == 0")
            x_ip = hash_cache_mash(None, hash_cache, x, proof.taux, proof.mu, proof.t)
            utils.ensure(x_ip != _ZERO, "x_ip == 0")

            # PAPER LINE 61
            sc_mulsub(m_y0, proof.taux, weight_y, m_y0)
            zpow = vector_powers(z, M + 3)

            k = _ensure_dst_key()
            ip1y = vector_power_sum(y, MN)
            sc_mulsub(k, zpow[2], ip1y, _ZERO)
            for j in range(1, M + 1):
                utils.ensure(j + 2 < len(zpow), "invalid zpow index")
                sc_mulsub(k, zpow.to(j + 2), _BP_IP12, k)

            # VERIFY_line_61rl_new
            sc_muladd(tmp, z, ip1y, k)
            sc_sub(tmp, proof.t, tmp)

            sc_muladd(y1, tmp, weight_y, y1)
            weight_y8 = init_key(weight_y)
            if not proof_v8:
                weight_y8 = sc_mul(None, weight_y, _EIGHT)

            muex = MultiExpSequential(points=[pt for pt in proof.V])
            for j in range(len(proof.V)):
                sc_mul(tmp, zpow[j + 2], weight_y8)
                muex.add_scalar(init_key(tmp))

            sc_mul(tmp, x, weight_y8)
            muex.add_pair(init_key(tmp), proof.T1)

            xsq = _ensure_dst_key()
            sc_mul(xsq, x, x)

            sc_mul(tmp, xsq, weight_y8)
            muex.add_pair(init_key(tmp), proof.T2)

            weight_z8 = init_key(weight_z)
            if not proof_v8:
                weight_z8 = sc_mul(None, weight_z, _EIGHT)

            muex.add_pair(weight_z8, proof.A)
            sc_mul(tmp, x, weight_z8)
            muex.add_pair(init_key(tmp), proof.S)

            multiexp(tmp, muex, False)
            add_keys(muex_acc, muex_acc, tmp)
            del muex

            # Compute the number of rounds for the inner product
            rounds = logM + logN
            utils.ensure(rounds > 0, "Zero rounds")

            # PAPER LINES 21-22
            # The inner product challenges are computed per round
            w = _ensure_dst_keyvect(None, rounds)
            for i in range(rounds):
                hash_cache_mash(tmp_bf_0, hash_cache, proof.L[i], proof.R[i])
                w.read(i, tmp_bf_0)
                utils.ensure(w[i] != _ZERO, "w[i] == 0")

            # Basically PAPER LINES 24-25
            # Compute the curvepoints from G[i] and H[i]
            yinvpow = init_key(_ONE)
            ypow = init_key(_ONE)
            yinv = invert(None, y)
            self.gc(61)

            winv = _ensure_dst_keyvect(None, rounds)
            for i in range(rounds):
                invert(tmp_bf_0, w.to(i))
                winv.read(i, tmp_bf_0)
                self.gc(62)

            g_scalar = _ensure_dst_key()
            h_scalar = _ensure_dst_key()
            twoN = self._two_aux(N)
            for i in range(MN):
                copy_key(g_scalar, proof.a)
                sc_mul(h_scalar, proof.b, yinvpow)

                for j in range(rounds - 1, -1, -1):
                    J = len(w) - j - 1

                    if (i & (1 << j)) == 0:
                        sc_mul(g_scalar, g_scalar, winv.to(J))
                        sc_mul(h_scalar, h_scalar, w.to(J))
                    else:
                        sc_mul(g_scalar, g_scalar, w.to(J))
                        sc_mul(h_scalar, h_scalar, winv.to(J))

                # Adjust the scalars using the exponents from PAPER LINE 62
                sc_add(g_scalar, g_scalar, z)
                utils.ensure(2 + i // N < len(zpow), "invalid zpow index")
                utils.ensure(i % N < len(twoN), "invalid twoN index")
                sc_mul(tmp, zpow.to(2 + i // N), twoN.to(i % N))
                sc_muladd(tmp, z, ypow, tmp)
                sc_mulsub(h_scalar, tmp, yinvpow, h_scalar)

                if not is_single:  # ph4
                    sc_mulsub(m_z4[i], g_scalar, weight_z, m_z4[i])
                    sc_mulsub(m_z5[i], h_scalar, weight_z, m_z5[i])
                else:
                    sc_mul(tmp, g_scalar, weight_z)
                    sub_keys(muex_acc, muex_acc, scalarmult_key(tmp, Gprec.to(i), tmp))

                    sc_mul(tmp, h_scalar, weight_z)
                    sub_keys(muex_acc, muex_acc, scalarmult_key(tmp, Hprec.to(i), tmp))

                if i != MN - 1:
                    sc_mul(yinvpow, yinvpow, yinv)
                    sc_mul(ypow, ypow, y)
                if i & 15 == 0:
                    self.gc(62)

            del (g_scalar, h_scalar, twoN)
            self.gc(63)

            sc_muladd(z1, proof.mu, weight_z, z1)
            muex = MultiExpSequential(
                point_fnc=lambda i, d: proof.L[i // 2]
                if i & 1 == 0
                else proof.R[i // 2]
            )
            for i in range(rounds):
                sc_mul(tmp, w[i], w[i])
                sc_mul(tmp, tmp, weight_z8)
                muex.add_scalar(tmp)
                sc_mul(tmp, winv[i], winv[i])
                sc_mul(tmp, tmp, weight_z8)
                muex.add_scalar(tmp)

            acc = multiexp(None, muex, False)
            add_keys(muex_acc, muex_acc, acc)

            sc_mulsub(tmp, proof.a, proof.b, proof.t)
            sc_mul(tmp, tmp, x_ip)
            sc_muladd(z3, tmp, weight_z, z3)

        sc_sub(tmp, m_y0, z1)
        z3p = sc_sub(None, z3, y1)

        check2 = crypto.encodepoint(
            crypto.ge25519_double_scalarmult_base_vartime(
                crypto.decodeint(z3p), crypto.xmr_H(), crypto.decodeint(tmp)
            )
        )
        add_keys(muex_acc, muex_acc, check2)

        if not is_single:  # ph4
            muex = MultiExpSequential(
                point_fnc=lambda i, d: Gprec.to(i // 2)
                if i & 1 == 0
                else Hprec.to(i // 2)
            )
            for i in range(maxMN):
                muex.add_scalar(m_z4[i])
                muex.add_scalar(m_z5[i])
            add_keys(muex_acc, muex_acc, multiexp(None, muex, True))

        if muex_acc != _ONE:
            raise ValueError("Verification failure at step 2")
        return True
Exemplo n.º 30
0
def _generate_clsag(
    message: bytes,
    P: List[bytes],
    p: Sc25519,
    C_nonzero: List[bytes],
    z: Sc25519,
    Cout: Ge25519,
    index: int,
    mg_buff: List[bytes],
) -> List[bytes]:
    sI = crypto.new_point()  # sig.I
    sD = crypto.new_point()  # sig.D
    sc1 = crypto.new_scalar()  # sig.c1
    a = crypto.random_scalar()
    H = crypto.new_point()
    D = crypto.new_point()
    Cout_bf = crypto.encodepoint(Cout)

    tmp_sc = crypto.new_scalar()
    tmp = crypto.new_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.sc_inv_eight())  # 1/8*z
    crypto.scalarmult_into(sD, H, tmp_sc)  # sig.D = 1/8*z*H
    sD = crypto.encodepoint(sD)

    hsh_P = crypto.get_keccak()  # domain, I, D, P, C, C_offset
    hsh_C = crypto.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.decodeint(hsh_P.digest())
    mu_C = crypto.decodeint(hsh_C.digest())

    del (hsh_PC, hsh_P, hsh_C)
    c_to_hash = crypto.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.decodeint(chasher.digest())
    del (chasher, H)

    L = crypto.new_point()
    R = crypto.new_point()
    c_p = crypto.new_scalar()
    c_c = crypto.new_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
        C_nonzero[i] = None

        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)

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