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
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
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
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)
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)
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)
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
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)
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
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
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))
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)
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
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", )
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, )
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
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" ), )
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
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
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
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
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)
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
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
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
def is_reduced(sc): return crypto.encodeint(crypto.decodeint(sc)) == sc
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
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