def _set_out_additional_keys(state: State, dst_entr): """ If needed (decided in step 1), additional tx keys are calculated for this particular output. """ if not state.need_additional_txkeys: return None additional_txkey_priv = crypto.random_scalar() if dst_entr.is_subaddress: # R=r*D additional_txkey = crypto.scalarmult( crypto.decodepoint(dst_entr.addr.spend_public_key), additional_txkey_priv) else: # R=r*G additional_txkey = crypto.scalarmult_base(additional_txkey_priv) state.additional_tx_public_keys.append( crypto.encodepoint(additional_txkey)) state.additional_tx_private_keys.append(additional_txkey_priv) return additional_txkey_priv
def ecdh_encode(unmasked, receiver_pk=None, derivation=None): """ Elliptic Curve Diffie-Helman: encodes and decodes the amount b and mask a where C= aG + bH :param unmasked: :param receiver_pk: :param derivation: :return: """ from apps.monero.xmr.serialize_messages.tx_ecdh import EcdhTuple rv = EcdhTuple() if derivation is None: esk = crypto.random_scalar() rv.senderPk = crypto.scalarmult_base(esk) derivation = crypto.encodepoint(crypto.scalarmult(receiver_pk, esk)) sharedSec1 = crypto.hash_to_scalar(derivation) sharedSec2 = crypto.hash_to_scalar(crypto.encodeint(sharedSec1)) rv.mask = crypto.sc_add(unmasked.mask, sharedSec1) rv.amount = crypto.sc_add(unmasked.amount, sharedSec2) return rv
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 sc_gen(dst=None): dst = _ensure_dst_key(dst) crypto.random_scalar(tmp_sc_1) crypto.encodeint_into(dst, tmp_sc_1) return dst
def generate_ring_signature( prefix_hash: bytes, image: Ge25519, pubs: list[Ge25519], sec: Sc25519, 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(sec) if not crypto.point_eq(t, pubs[sec_idx]): raise ValueError("Invalid sec key") k_i = monero.generate_key_image(crypto.encodepoint(pubs[sec_idx]), sec) if not crypto.point_eq(k_i, image): raise ValueError("Key image invalid") for k in pubs: crypto.check_ed25519point(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.sc_0() k = crypto.sc_0() sig = [] for _ in range(len(pubs)): sig.append([crypto.sc_0(), crypto.sc_0()]) # c, r for i in range(len(pubs)): if i == sec_idx: k = crypto.random_scalar() tmp3 = crypto.scalarmult_base(k) crypto.encodepoint_into(mvbuff[buff_off:buff_off + 32], tmp3) buff_off += 32 tmp3 = crypto.hash_to_point(crypto.encodepoint(pubs[i])) tmp2 = crypto.scalarmult(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_base_vartime( sig[i][0], tmp3, sig[i][1]) crypto.encodepoint_into(mvbuff[buff_off:buff_off + 32], tmp2) buff_off += 32 tmp3 = crypto.hash_to_point(crypto.encodepoint(tmp3)) tmp2 = crypto.ge25519_double_scalarmult_vartime2( sig[i][1], tmp3, sig[i][0], image) crypto.encodepoint_into(mvbuff[buff_off:buff_off + 32], tmp2) buff_off += 32 sum = crypto.sc_add(sum, sig[i][0]) h = crypto.hash_to_scalar(buff) sig[sec_idx][0] = crypto.sc_sub(h, sum) sig[sec_idx][1] = crypto.sc_mulsub(sig[sec_idx][0], sec, k) return sig
async def init_transaction( state: State, address_n: list, network_type: int, tsx_data: MoneroTransactionData, keychain, ) -> MoneroTransactionInitAck: from apps.monero.signing import offloading_keys from apps.common import paths await paths.validate_path(state.ctx, misc.validate_full_path, keychain, address_n, CURVE) state.creds = misc.get_creds(keychain, address_n, network_type) state.client_version = tsx_data.client_version or 0 if state.client_version == 0: raise ValueError("Client version not supported") state.fee = state.fee if state.fee > 0 else 0 state.tx_priv = crypto.random_scalar() state.tx_pub = crypto.scalarmult_base(state.tx_priv) state.mem_trace(1) state.input_count = tsx_data.num_inputs state.output_count = len(tsx_data.outputs) state.progress_total = 4 + 3 * state.input_count + state.output_count state.progress_cur = 0 # Ask for confirmation await confirms.require_confirm_transaction(state.ctx, state, tsx_data, state.creds.network_type) state.creds.address = None state.creds.network_type = None gc.collect() state.mem_trace(3) # Basic transaction parameters state.output_change = tsx_data.change_dts state.mixin = tsx_data.mixin state.fee = tsx_data.fee state.account_idx = tsx_data.account state.last_step = state.STEP_INIT if tsx_data.hard_fork: state.hard_fork = tsx_data.hard_fork # Ensure change is correct _check_change(state, tsx_data.outputs) # At least two outpus are required, this applies also for sweep txs # where one fake output is added. See _check_change for more info if state.output_count < 2: raise signing.NotEnoughOutputsError( "At least two outputs are required") _check_rsig_data(state, tsx_data.rsig_data) _check_subaddresses(state, tsx_data.outputs) # Extra processing, payment id _process_payment_id(state, tsx_data) await _compute_sec_keys(state, tsx_data) gc.collect() # Iterative tx_prefix_hash hash computation state.tx_prefix_hasher.uvarint( 2) # current Monero transaction format (RingCT = 2) state.tx_prefix_hasher.uvarint(tsx_data.unlock_time) state.tx_prefix_hasher.uvarint(state.input_count) # ContainerType, size state.mem_trace(10, True) # Final message hasher state.full_message_hasher.init() state.full_message_hasher.set_type_fee(signing.RctType.Bulletproof2, state.fee) # Sub address precomputation if tsx_data.account is not None and tsx_data.minor_indices: _precompute_subaddr(state, tsx_data.account, tsx_data.minor_indices) state.mem_trace(5, True) # HMACs all outputs to disallow tampering. # Each HMAC is then sent alongside the output # and trezor validates it. hmacs = [] for idx in range(state.output_count): c_hmac = await offloading_keys.gen_hmac_tsxdest( state.key_hmac, tsx_data.outputs[idx], idx) hmacs.append(c_hmac) gc.collect() state.mem_trace(6) from trezor.messages.MoneroTransactionInitAck import MoneroTransactionInitAck from trezor.messages.MoneroTransactionRsigData import MoneroTransactionRsigData rsig_data = MoneroTransactionRsigData(offload_type=state.rsig_offload) return MoneroTransactionInitAck(hmacs=hmacs, rsig_data=rsig_data)
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
def generate_mlsag( message: bytes, pk: KeyM, xx: List[Sc25519], index: int, dsRows: int, mg_buff: List[bytes], ) -> List[bytes]: """ Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures) :param message: the full message to be signed (actually its hash) :param pk: matrix of public keys and commitments :param xx: input secret array composed of a private key and commitment mask :param index: specifies corresponding public key to the `xx`'s private key in the `pk` array :param dsRows: separates pubkeys from commitment :param mg_buff: mg signature buffer """ rows, cols = gen_mlsag_assert(pk, xx, index, dsRows) rows_b_size = int_serialize.uvarint_size(rows) # Preallocation of the chunked buffer, len + cols + cc for _ in range(1 + cols + 1): mg_buff.append(None) mg_buff[0] = int_serialize.dump_uvarint_b(cols) cc = crypto.new_scalar() # rv.cc c = crypto.new_scalar() L = crypto.new_point() R = crypto.new_point() Hi = crypto.new_point() # calculates the "first" c, key images and random scalars alpha c_old, II, alpha = generate_first_c_and_key_images(message, pk, xx, index, dsRows, rows, cols) i = (index + 1) % cols if i == 0: crypto.sc_copy(cc, c_old) ss = [crypto.new_scalar() for _ in range(rows)] tmp_buff = bytearray(32) while i != index: hasher = _hasher_message(message) # Serialize size of the row mg_buff[i + 1] = bytearray(rows_b_size + 32 * rows) int_serialize.dump_uvarint_b_into(rows, mg_buff[i + 1]) for x in ss: crypto.random_scalar(x) for j in range(dsRows): # L = rv.ss[i][j] * G + c_old * pk[i][j] crypto.add_keys2_into(L, ss[j], c_old, crypto.decodepoint_into(Hi, pk[i][j])) crypto.hash_to_point_into(Hi, pk[i][j]) # R = rv.ss[i][j] * H(pk[i][j]) + c_old * Ip[j] crypto.add_keys3_into(R, ss[j], Hi, c_old, II[j]) hasher.update(pk[i][j]) _hash_point(hasher, L, tmp_buff) _hash_point(hasher, R, tmp_buff) for j in range(dsRows, rows): # again, omitting R here as discussed above crypto.add_keys2_into(L, ss[j], c_old, crypto.decodepoint_into(Hi, pk[i][j])) hasher.update(pk[i][j]) _hash_point(hasher, L, tmp_buff) for si in range(rows): crypto.encodeint_into(mg_buff[i + 1], ss[si], rows_b_size + 32 * si) crypto.decodeint_into(c, hasher.digest()) crypto.sc_copy(c_old, c) pk[i] = None i = (i + 1) % cols if i == 0: crypto.sc_copy(cc, c_old) gc.collect() del II # Finalizing rv.ss by processing rv.ss[index] mg_buff[index + 1] = bytearray(rows_b_size + 32 * rows) int_serialize.dump_uvarint_b_into(rows, mg_buff[index + 1]) for j in range(rows): crypto.sc_mulsub_into(ss[j], c, xx[j], alpha[j]) crypto.encodeint_into(mg_buff[index + 1], ss[j], rows_b_size + 32 * j) # rv.cc mg_buff[-1] = crypto.encodeint(cc) return mg_buff
def prove_range_mem(amount, last_mask=None): """ Memory optimized range proof. Gives C, and mask such that \sumCi = C c.f. http:#eprint.iacr.org/2015/1098 section 5.1 Ci is a commitment to either 0 or 2^i, i=0,...,63 thus this proves that "amount" is in [0, 2^ATOMS] mask is a such that C = aG + bH, and b = amount :param amount: :param last_mask: ai[ATOMS-1] will be computed as \sum_{i=0}^{ATOMS-2} a_i - last_mask :param use_asnl: use ASNL, used before Borromean :return: sumCi, mask, RangeSig. sumCi is Pedersen commitment on the amount value. sumCi = aG + amount*H mask is "a" from the Pedersent commitment above. """ res = bytearray(32 * (64 + 64 + 64 + 1)) mv = memoryview(res) gc.collect() def as0(mv, x, i): crypto.encodeint_into(x, mv[32 * i:]) def as1(mv, x, i): crypto.encodeint_into(x, mv[32 * 64 + 32 * i:]) def aci(mv, x, i): crypto.encodepoint_into(x, mv[32 * 64 * 2 + 32 + 32 * i:]) n = 64 bb = d2b(amount, n) # gives binary form of bb in "digits" binary digits ai = key_zero_vector(n) a = crypto.sc_0() C = crypto.identity() alpha = key_zero_vector(n) c_H = crypto.gen_H() kck = crypto.get_keccak() # ee computation # First pass, generates: ai, alpha, Ci, ee, s1 for ii in range(n): ai[ii] = crypto.random_scalar() if last_mask is not None and ii == 64 - 1: ai[ii] = crypto.sc_sub(last_mask, a) a = crypto.sc_add( a, ai[ii] ) # creating the total mask since you have to pass this to receiver... alpha[ii] = crypto.random_scalar() L = crypto.scalarmult_base(alpha[ii]) if bb[ii] == 0: Ctmp = crypto.scalarmult_base(ai[ii]) else: Ctmp = crypto.point_add(crypto.scalarmult_base(ai[ii]), c_H) C = crypto.point_add(C, Ctmp) aci(mv, Ctmp, ii) if bb[ii] == 0: si = crypto.random_scalar() c = crypto.hash_to_scalar(crypto.encodepoint(L)) L = crypto.add_keys2(si, c, crypto.point_sub(Ctmp, c_H)) kck.update(crypto.encodepoint(L)) as1(mv, si, ii) else: kck.update(crypto.encodepoint(L)) c_H = crypto.point_double(c_H) # Compute ee, memory cleanup ee = crypto.sc_reduce32(crypto.decodeint(kck.digest())) crypto.encodeint_into(ee, mv[64 * 32 * 2:]) del kck gc.collect() # Second phase computes: s0, s1 c_H = crypto.gen_H() for jj in range(n): if not bb[jj]: s0 = crypto.sc_mulsub(ai[jj], ee, alpha[jj]) else: s0 = crypto.random_scalar() Ctmp = crypto.decodepoint( mv[32 * 64 * 2 + 32 + 32 * jj:32 * 64 * 2 + 32 + 32 * jj + 32]) LL = crypto.add_keys2(s0, ee, Ctmp) cc = crypto.hash_to_scalar(crypto.encodepoint(LL)) si = crypto.sc_mulsub(ai[jj], cc, alpha[jj]) as1(mv, si, jj) as0(mv, s0, jj) c_H = crypto.point_double(c_H) gc.collect() return C, a, res
def generate_ring_signature(prefix_hash, image, pubs, sec, sec_idx, test=False): """ Generates ring signature with key image. void crypto_ops::generate_ring_signature() :param prefix_hash: :param image: :param pubs: :param sec: :param sec_idx: :param test: :return: """ from apps.monero.xmr.common import memcpy if test: from apps.monero.xmr import monero t = crypto.scalarmult_base(sec) if not crypto.point_eq(t, pubs[sec_idx]): raise ValueError("Invalid sec key") k_i = monero.generate_key_image(crypto.encodepoint(pubs[sec_idx]), sec) if not crypto.point_eq(k_i, image): raise ValueError("Key image invalid") for k in pubs: crypto.ge_frombytes_vartime_check(k) image_unp = crypto.ge_frombytes_vartime(image) image_pre = crypto.ge_dsm_precomp(image_unp) 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.sc_0() k = crypto.sc_0() sig = [] for i in range(len(pubs)): sig.append([crypto.sc_0(), crypto.sc_0()]) # c, r for i in range(len(pubs)): if i == sec_idx: k = crypto.random_scalar() tmp3 = crypto.scalarmult_base(k) crypto.encodepoint_into(tmp3, mvbuff[buff_off:buff_off + 32]) buff_off += 32 tmp3 = crypto.hash_to_ec(crypto.encodepoint(pubs[i])) tmp2 = crypto.scalarmult(tmp3, k) crypto.encodepoint_into(tmp2, mvbuff[buff_off:buff_off + 32]) buff_off += 32 else: sig[i] = [crypto.random_scalar(), crypto.random_scalar()] tmp3 = crypto.ge_frombytes_vartime(pubs[i]) tmp2 = crypto.ge_double_scalarmult_base_vartime( sig[i][0], tmp3, sig[i][1]) crypto.encodepoint_into(tmp2, mvbuff[buff_off:buff_off + 32]) buff_off += 32 tmp3 = crypto.hash_to_ec(crypto.encodepoint(tmp3)) tmp2 = crypto.ge_double_scalarmult_precomp_vartime( sig[i][1], tmp3, sig[i][0], image_pre) crypto.encodepoint_into(tmp2, mvbuff[buff_off:buff_off + 32]) buff_off += 32 sum = crypto.sc_add(sum, sig[i][0]) h = crypto.hash_to_scalar(buff) sig[sec_idx][0] = crypto.sc_sub(h, sum) sig[sec_idx][1] = crypto.sc_mulsub(sig[sec_idx][0], sec, k) return sig
async def diag(ctx, msg, **kwargs) -> Failure: log.debug(__name__, "----diagnostics") gc.collect() if msg.ins == 0: check_mem(0) return retit() elif msg.ins == 1: check_mem(1) micropython.mem_info(1) return retit() elif msg.ins == 2: log.debug(__name__, "_____________________________________________") log.debug(__name__, "_____________________________________________") log.debug(__name__, "_____________________________________________") return retit() elif msg.ins == 3: pass elif msg.ins == 4: total = 0 monero = 0 for k, v in sys.modules.items(): log.info(__name__, "Mod[%s]: %s", k, v) total += 1 if k.startswith("apps.monero"): monero += 1 log.info(__name__, "Total modules: %s, Monero modules: %s", total, monero) return retit() elif msg.ins in [5, 6, 7]: check_mem() from apps.monero.xmr import bulletproof as bp check_mem("BP Imported") from apps.monero.xmr import crypto check_mem("Crypto Imported") bpi = bp.BulletProofBuilder() bpi.gc_fnc = gc.collect bpi.gc_trace = log_trace vals = [crypto.Scalar((1 << 30) - 1 + 16), crypto.Scalar(22222)] masks = [crypto.random_scalar(), crypto.random_scalar()] check_mem("BP pre input") if msg.ins == 5: bp_res = bpi.prove_testnet(vals[0], masks[0]) check_mem("BP post prove") bpi.verify_testnet(bp_res) check_mem("BP post verify") elif msg.ins == 6: bp_res = bpi.prove(vals[0], masks[0]) check_mem("BP post prove") bpi.verify(bp_res) check_mem("BP post verify") elif msg.ins == 7: bp_res = bpi.prove_batch(vals, masks) check_mem("BP post prove") bpi.verify(bp_res) check_mem("BP post verify") return retit() return retit()
def _generate_random_vector(n): """ Generates vector of random scalars """ return [crypto.random_scalar() for _ in range(0, n)]
def generate_first_c_and_key_images(message, rv, pk, xx, kLRki, index, dsRows, rows, cols): """ MLSAG computation - the part with secret keys :param message: the full message to be signed (actually its hash) :param rv: MgSig :param pk: matrix of public keys and commitments :param xx: input secret array composed of a private key and commitment mask :param kLRki: used only in multisig, currently not implemented :param index: specifies corresponding public key to the `xx`'s private key in the `pk` array :param dsRows: row number where the pubkeys "end" (and commitments follow) :param rows: total number of rows :param cols: size of ring """ Ip = _key_vector(dsRows) rv.II = _key_vector(dsRows) alpha = _key_vector(rows) rv.ss = _key_matrix(rows, cols) tmp_buff = bytearray(32) hasher = _hasher_message(message) for i in range(dsRows): # this is somewhat extra as compared to the Ring Confidential Tx paper # see footnote in From Zero to Monero section 3.3 hasher.update(crypto.encodepoint(pk[index][i])) if kLRki: raise NotImplementedError("Multisig not implemented") # alpha[i] = kLRki.k # rv.II[i] = kLRki.ki # hash_point(hasher, kLRki.L, tmp_buff) # hash_point(hasher, kLRki.R, tmp_buff) else: Hi = crypto.hash_to_point(crypto.encodepoint(pk[index][i])) alpha[i] = crypto.random_scalar() # L = alpha_i * G aGi = crypto.scalarmult_base(alpha[i]) # Ri = alpha_i * H(P_i) aHPi = crypto.scalarmult(Hi, alpha[i]) # key image rv.II[i] = crypto.scalarmult(Hi, xx[i]) _hash_point(hasher, aGi, tmp_buff) _hash_point(hasher, aHPi, tmp_buff) Ip[i] = rv.II[i] for i in range(dsRows, rows): alpha[i] = crypto.random_scalar() # L = alpha_i * G aGi = crypto.scalarmult_base(alpha[i]) # for some reasons we omit calculating R here, which seems # contrary to the paper, but it is in the Monero official client # see https://github.com/monero-project/monero/blob/636153b2050aa0642ba86842c69ac55a5d81618d/src/ringct/rctSigs.cpp#L191 _hash_point(hasher, pk[index][i], tmp_buff) _hash_point(hasher, aGi, tmp_buff) # the first c c_old = hasher.digest() c_old = crypto.decodeint(c_old) return c_old, Ip, alpha
def gen_clsag_sig(self, ring_size=11, index=None): msg = random.bytes(32) amnt = crypto.Scalar(random.uniform(0xFFFFFF) + 12) priv = crypto.random_scalar() msk = crypto.random_scalar() alpha = crypto.random_scalar() P = crypto.scalarmult_base_into(None, priv) C = crypto.add_keys2_into(None, msk, amnt, crypto.xmr_H()) Cp = crypto.add_keys2_into(None, alpha, amnt, crypto.xmr_H()) ring = [] for i in range(ring_size - 1): tk = TmpKey( crypto_helpers.encodepoint( crypto.scalarmult_base_into(None, crypto.random_scalar())), crypto_helpers.encodepoint( crypto.scalarmult_base_into(None, crypto.random_scalar())), ) ring.append(tk) index = index if index is not None else random.uniform(len(ring)) ring.insert( index, TmpKey(crypto_helpers.encodepoint(P), crypto_helpers.encodepoint(C))) ring2 = list(ring) mg_buffer = [] self.assertTrue( crypto.point_eq( crypto.scalarmult_base_into(None, priv), crypto_helpers.decodepoint(ring[index].dest), )) self.assertTrue( crypto.point_eq( crypto.scalarmult_base_into( None, crypto.sc_sub_into(None, msk, alpha)), crypto.point_sub_into( None, crypto_helpers.decodepoint(ring[index].commitment), Cp), )) clsag.generate_clsag_simple( msg, ring, CtKey(priv, msk), alpha, Cp, index, mg_buffer, ) sD = crypto_helpers.decodepoint(mg_buffer[-1]) sc1 = crypto_helpers.decodeint(mg_buffer[-2]) scalars = [crypto_helpers.decodeint(x) for x in mg_buffer[1:-2]] H = crypto.Point() sI = crypto.Point() crypto.hash_to_point_into(H, crypto_helpers.encodepoint(P)) crypto.scalarmult_into(sI, H, priv) # I = p*H return msg, scalars, sc1, sI, sD, ring2, Cp
def test_clsag_invalid_sD(self): res = self.gen_clsag_sig(ring_size=11, index=5) msg, scalars, sc1, sI, sD, ring2, Cp = res with self.assertRaises(ValueError): sD = crypto.scalarmult_base_into(None, crypto.random_scalar()) self.verify_clsag(msg, scalars, sc1, sI, sD, ring2, Cp)
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]