def sc_mul(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_mul_into(tmp_sc_3, tmp_sc_1, tmp_sc_2) crypto.encodeint_into(dst, tmp_sc_3) return dst
async def _sync_step(s, ctx, tds): if not tds.tdis: raise wire.DataError("Empty") kis = [] buff = bytearray(32 * 3) buff_mv = memoryview(buff) await confirms.keyimage_sync_step(ctx, s.current_output, s.num_outputs) for td in tds.tdis: s.current_output += 1 if s.current_output >= s.num_outputs: raise wire.DataError("Too many outputs") if __debug__: log.debug(__name__, "ki_sync, step i: %d", s.current_output) # Update the control hash s.hasher.update(key_image.compute_hash(td)) # Compute keyimage + signature ki, sig = key_image.export_key_image(s.creds, s.subaddresses, td) # Serialize into buff crypto.encodepoint_into(buff_mv[0:32], ki) crypto.encodeint_into(buff_mv[32:64], sig[0][0]) crypto.encodeint_into(buff_mv[64:], sig[0][1]) # Encrypt with enc_key nonce, ciph, _ = chacha_poly.encrypt(s.enc_key, buff) kis.append(MoneroExportedKeyImage(iv=nonce, blob=ciph)) return MoneroKeyImageSyncStepAck(kis=kis)
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 __getitem__(self, item): vector_z_two_i(self.logN, self.zpow, self.twoN, self.idxize(item), self.sc) if self.raw: return self.sc crypto.encodeint_into(self.cur, self.sc) return self.cur
def sc_mulsub(dst, a, b, c): dst = _ensure_dst_key(dst) crypto.decodeint_into_noreduce(tmp_sc_1, a) crypto.decodeint_into_noreduce(tmp_sc_2, b) crypto.decodeint_into_noreduce(tmp_sc_3, c) crypto.sc_mulsub_into(tmp_sc_4, tmp_sc_1, tmp_sc_2, tmp_sc_3) crypto.encodeint_into(dst, tmp_sc_4) return dst
def sX_gen(self, ln=_BP_N): gc.collect() buff = bytearray(ln * 32) buff_mv = memoryview(buff) sc = crypto.new_scalar() for i in range(ln): crypto.random_scalar(sc) crypto.encodeint_into(buff_mv[i * 32 : (i + 1) * 32], sc) gc_iter(i) return KeyV(buffer=buff)
def _det_mask(self, i, is_sL=True, dst=None): dst = _ensure_dst_key(dst) if self.fnc_det_mask: return self.fnc_det_mask(i, is_sL, dst) self.tmp_det_buff[64] = int(is_sL) memcpy(self.tmp_det_buff, 65, _ZERO, 0, 4) dump_uvarint_b_into(i, self.tmp_det_buff, 65) crypto.hash_to_scalar_into(self.tmp_sc_1, self.tmp_det_buff) crypto.encodeint_into(dst, self.tmp_sc_1) return dst
def _ecdh_encode(amount: int, amount_key: bytes) -> EcdhTuple: """ Output recipients decode amounts from EcdhTuple structure. """ from apps.monero.xmr.serialize_messages.tx_ecdh import EcdhTuple ecdh_info = EcdhTuple(mask=crypto.NULL_KEY_ENC, amount=bytearray(32)) amnt = crypto.sc_init(amount) crypto.encodeint_into(ecdh_info.amount, amnt) crypto.xor8(ecdh_info.amount, _ecdh_hash(amount_key)) return ecdh_info
def hash_vct_to_scalar(dst, data): dst = _ensure_dst_key(dst) ctx = crypto.get_keccak() for x in data: ctx.update(x) hsh = ctx.digest() crypto.decodeint_into(tmp_sc_1, hsh) crypto.encodeint_into(tmp_bf_1, tmp_sc_1) copy_key(dst, tmp_bf_1) return dst
def test_square_multiply(self): for x in [2, 3, 16, 17, 31, 32]: ss = crypto.random_scalar() s1 = crypto.sc_copy(None, ss) s2 = crypto.sc_copy(None, ss) for i in range(1, x): crypto.sc_mul_into(s1, s1, ss) bp._sc_square_mult(s2, ss, x) self.assertEqual(crypto.encodeint_into(None, s1), crypto.encodeint_into(None, s2))
def inner_product(a, b, dst=None): if len(a) != len(b): raise ValueError("Incompatible sizes of a and b") dst = _ensure_dst_key(dst) crypto.sc_init_into(tmp_sc_1, 0) for i in range(len(a)): crypto.decodeint_into_noreduce(tmp_sc_2, a.to(i)) crypto.decodeint_into_noreduce(tmp_sc_3, b.to(i)) crypto.sc_muladd_into(tmp_sc_1, tmp_sc_2, tmp_sc_3, tmp_sc_1) gc_iter(i) crypto.encodeint_into(dst, tmp_sc_1) return dst
def hash_cache_mash(dst, hash_cache, *args): dst = _ensure_dst_key(dst) ctx = crypto.get_keccak() ctx.update(hash_cache) for x in args: if x is None: break ctx.update(x) hsh = ctx.digest() crypto.decodeint_into(tmp_sc_1, hsh) crypto.encodeint_into(tmp_bf_1, tmp_sc_1) copy_key(dst, tmp_bf_1) copy_key(hash_cache, tmp_bf_1) return dst
def vector_powers(x, n, dst=None, dynamic=False, **kwargs): if dynamic: return KeyVPowers(n, x, **kwargs) dst = _ensure_dst_keyvect(dst, n) if n == 0: return dst dst.read(0, _ONE) if n == 1: return dst dst.read(1, x) crypto.decodeint_into_noreduce(tmp_sc_1, x) crypto.decodeint_into_noreduce(tmp_sc_2, x) for i in range(2, n): crypto.sc_mul_into(tmp_sc_1, tmp_sc_1, tmp_sc_2) crypto.encodeint_into(tmp_bf_0, tmp_sc_1) dst.read(i, tmp_bf_0) gc_iter(i) return dst
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
def test_bpp_bprime(self): N, M = 64, 4 MN = N * M y = unhexlify( b'60421950bee0aab949e63336db1eb9532dba6b4599c5cd9fb1dbde909114100e' ) z = unhexlify( b'e0408b528e9d35ccb8386b87f39b85c724740644f4db412483a8852cdb3ceb00' ) zc = crypto.decodeint_into(None, z) z_sq = bp._sc_mul(None, z, z) sv = [1234, 8789, 4455, 6697] sv = [crypto.encodeint_into(None, crypto.Scalar(x)) for x in sv] num_inp = len(sv) sc_zero = crypto.decodeint_into_noreduce(None, bp._ZERO) sc_mone = crypto.decodeint_into_noreduce(None, bp._MINUS_ONE) def e_xL(idx, d=None): j, i = idx // bp._BP_N, idx % bp._BP_N r = None if j >= num_inp: r = sc_mone elif sv[j][i // 8] & (1 << i % 8): r = sc_zero else: r = sc_mone if d: return crypto.sc_copy(d, r) return r aR = bp.KeyVEval(MN, lambda i, d: e_xL(i, d), raw=True) d_vct = bp.VctD(N, M, z_sq, raw=True) ypow_back = bp.KeyVPowersBackwards(MN + 1, y, raw=True) aR1_sc1 = crypto.Scalar() 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) bprime = bp.KeyVEval(MN, aR1_fnc, raw=False) # aR1 b64 = bp._copy_key(None, bprime.to(64)) b65 = bp._copy_key(None, bprime.to(65)) b128 = bp._copy_key(None, bprime.to(128)) b65_2 = bp._copy_key(None, bprime.to(65)) b64_2 = bp._copy_key(None, bprime.to(64)) _ = bprime[89] b128_2 = bp._copy_key(None, bprime.to(128)) self.assertEqual(b64, b64_2) self.assertEqual(b65, b65_2) self.assertEqual(b128, b128_2)
async def _refresh_step( s: LiveRefreshState, ctx: Context, msg: MoneroLiveRefreshStepRequest) -> MoneroLiveRefreshStepAck: assert s.creds is not None buff = bytearray(32 * 3) buff_mv = memoryview(buff) await layout.live_refresh_step(ctx, s.current_output) s.current_output += 1 if __debug__: log.debug(__name__, "refresh, step i: %d", s.current_output) # Compute spending secret key and the key image # spend_priv = Hs(recv_deriv || real_out_idx) + spend_key_private # If subaddr: # spend_priv += Hs("SubAddr" || view_key_private || major || minor) # out_key = spend_priv * G, KI: spend_priv * Hp(out_key) out_key = crypto_helpers.decodepoint(msg.out_key) recv_deriv = crypto_helpers.decodepoint(msg.recv_deriv) received_index = msg.sub_addr_major, msg.sub_addr_minor spend_priv, ki = monero.generate_tx_spend_and_key_image( s.creds, out_key, recv_deriv, msg.real_out_idx, received_index) ki_enc = crypto_helpers.encodepoint(ki) sig = key_image.generate_ring_signature(ki_enc, ki, [out_key], spend_priv, 0, False) del spend_priv # spend_priv never leaves the device # Serialize into buff buff[0:32] = ki_enc crypto.encodeint_into(buff_mv[32:64], sig[0][0]) crypto.encodeint_into(buff_mv[64:], sig[0][1]) # Encrypt with view key private based key - so host can decrypt and verify HMAC enc_key, salt = misc.compute_enc_key_host(s.creds.view_key_private, msg.out_key) resp = chacha_poly.encrypt_pack(enc_key, buff) return MoneroLiveRefreshStepAck(salt=salt, key_image=resp)
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
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 _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 invert(dst, x): dst = _ensure_dst_key(dst) crypto.decodeint_into_noreduce(tmp_sc_1, x) crypto.sc_inv_into(tmp_sc_2, tmp_sc_1) crypto.encodeint_into(dst, tmp_sc_2) return dst
def test_pow_back_skips(self): MN = 128 y = unhexlify( '60421950bee0aab949e63336db1eb9532dba6b4599c5cd9fb1dbde909114100e') y_sc = crypto.decodeint_into(None, y) yinv = bp._invert(None, y) y_to_MN_1 = bp._sc_square_mult(None, y_sc, MN - 1) ymax = crypto.sc_mul_into(None, y_to_MN_1, y_sc) ## y**MN ymax2 = bp._sc_square_mult(None, y_sc, MN) self.assertEqual(crypto.encodeint_into(None, ymax), crypto.encodeint_into(None, ymax2)) size = MN + 1 ypow_back = bp.KeyVPowersBackwards(size, y, x_inv=yinv, x_max=ymax, raw=True) self.assertEqual(crypto.encodeint_into(None, ymax), crypto.encodeint_into(None, ypow_back[MN])) for i in range(10): _ = ypow_back[MN - i] self.assertEqual( crypto.encodeint_into(None, ypow_back[MN - 9]), crypto.encodeint_into(None, bp._sc_square_mult(None, y_sc, MN - 9))) self.assertEqual( crypto.encodeint_into(None, ypow_back[MN - 19]), crypto.encodeint_into(None, bp._sc_square_mult(None, y_sc, MN - 19))) self.assertEqual( crypto.encodeint_into(None, ypow_back[MN - 65]), crypto.encodeint_into(None, bp._sc_square_mult(None, y_sc, MN - 65))) self.assertEqual( crypto.encodeint_into(None, ypow_back[MN - 14]), crypto.encodeint_into(None, bp._sc_square_mult(None, y_sc, MN - 14))) tmp = crypto.sc_copy(None, ypow_back[MN - 64]) # another jump back and forth _ = ypow_back[MN - 127] self.assertEqual(crypto.encodeint_into(None, ypow_back[MN - 64]), crypto.encodeint_into(None, tmp)) self.assertEqual( crypto.encodeint_into(None, ypow_back[MN - 64]), crypto.encodeint_into(None, bp._sc_square_mult(None, y_sc, MN - 64)))
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]
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 as0(mv, x, i): crypto.encodeint_into(x, mv[32 * i:])
def hash_to_scalar(dst, data): dst = _ensure_dst_key(dst) crypto.hash_to_scalar_into(tmp_sc_1, data) crypto.encodeint_into(dst, tmp_sc_1) return dst
def as1(mv, x, i): crypto.encodeint_into(x, mv[32 * 64 + 32 * i:])
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 _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, ), )
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 test_dvct_skips(self): z_sq = unhexlify( b'e0408b528e9d35ccb8386b87f39b85c724740644f4db412483a8852cdb3ceb00' ) d_vct0 = bp.VctD(64, 8, z_sq, raw=True) d_vct1 = bp.VctD(64, 8, z_sq, raw=True) tmp = crypto.Scalar() # Linear scan vs jump for i in range(65): tmp = d_vct0[i] self.assertEqual(crypto.encodeint_into(None, tmp), crypto.encodeint_into(None, d_vct1[64])) # Jumping around _ = d_vct0[128] self.assertEqual(crypto.encodeint_into(None, d_vct0[64]), crypto.encodeint_into(None, d_vct1[64])) # Sync on the same jump self.assertEqual(crypto.encodeint_into(None, d_vct0[65]), crypto.encodeint_into(None, d_vct1[65])) self.assertEqual(crypto.encodeint_into(None, d_vct0[65]), crypto.encodeint_into(None, d_vct1[65])) # Jump vs linear again, move_one vs move_more for i in range(1, 10): tmp = d_vct0[65 + i] self.assertEqual(crypto.encodeint_into(None, tmp), crypto.encodeint_into(None, d_vct1[74])) _ = d_vct0[85] _ = d_vct1[89] # different jump sizes, internal state management test self.assertEqual(crypto.encodeint_into(None, d_vct0[95]), crypto.encodeint_into(None, d_vct1[95])) _ = d_vct0[ 319] # move_one mults by z_sq then; enforce z component updates self.assertEqual(crypto.encodeint_into(None, d_vct0[320]), crypto.encodeint_into(None, d_vct1[320])) tmp = crypto.sc_copy(None, d_vct0[64]) # another jump back and forth _ = d_vct0[127] self.assertEqual(crypto.encodeint_into(None, d_vct0[64]), crypto.encodeint_into(None, tmp)) _ = d_vct0[0] _ = d_vct1[0] _ = d_vct0[64] self.assertEqual(crypto.encodeint_into(None, d_vct0[5]), crypto.encodeint_into(None, d_vct1[5]))