def scalarmult_key(dst, P, s): dst = _ensure_dst_key(dst) crypto.decodepoint_into(tmp_pt_1, P) crypto.decodeint_into_noreduce(tmp_sc_1, s) crypto.scalarmult_into(tmp_pt_2, tmp_pt_1, tmp_sc_1) crypto.encodepoint_into(dst, tmp_pt_2) return dst
def generate_mlsag_full( message, pubs, in_sk, out_sk_mask, out_pk_commitments, kLRki, index, txn_fee_key, mg_buff, ): cols = len(pubs) if cols == 0: raise ValueError("Empty pubs") rows = 1 # Monero uses only one row if len(out_sk_mask) != len(out_pk_commitments): raise ValueError("Bad outsk/putpk size") sk = _key_vector(rows + 1) M = _key_matrix(rows + 1, cols) tmp_mi_rows = crypto.new_point(None) tmp_pt = crypto.new_point(None) for i in range(cols): crypto.identity_into(tmp_mi_rows) # M[i][rows] # Should iterate over rows, simplified as rows == 1 M[i][0] = pubs[i].dest crypto.point_add_into( tmp_mi_rows, tmp_mi_rows, crypto.decodepoint_into(tmp_pt, pubs[i].commitment), ) pubs[i] = None for j in range(len(out_pk_commitments)): crypto.point_sub_into( tmp_mi_rows, tmp_mi_rows, crypto.decodepoint_into(tmp_pt, out_pk_commitments[j]), ) # subtract output Ci's in last row # Subtract txn fee output in last row crypto.point_sub_into(tmp_mi_rows, tmp_mi_rows, txn_fee_key) M[i][rows] = crypto.encodepoint(tmp_mi_rows) # Simplified as rows == 1 sk[0] = in_sk.dest sk[rows] = in_sk.mask # originally: sum of all in_sk[0..rows] in sk[rows] for j in range(len(out_pk_commitments)): crypto.sc_sub_into( sk[rows], sk[rows], out_sk_mask[j] ) # subtract output masks in last row del (pubs, tmp_mi_rows, tmp_pt) gc.collect() return generate_mlsag(message, M, sk, kLRki, index, rows, mg_buff)
def _acc(self, scalar, point): crypto.decodeint_into_noreduce(tmp_sc_1, scalar) crypto.decodepoint_into(tmp_pt_2, point) crypto.scalarmult_into(tmp_pt_3, tmp_pt_2, tmp_sc_1) crypto.point_add_into(self.acc, self.acc, tmp_pt_3) self.current_idx += 1 self.size += 1
def sub_keys(dst, A, B): dst = _ensure_dst_key(dst) crypto.decodepoint_into(tmp_pt_1, A) crypto.decodepoint_into(tmp_pt_2, B) crypto.point_sub_into(tmp_pt_3, tmp_pt_1, tmp_pt_2) crypto.encodepoint_into(dst, tmp_pt_3) return dst
def add_keys2(dst, a, b, B): dst = _ensure_dst_key(dst) crypto.decodeint_into_noreduce(tmp_sc_1, a) crypto.decodeint_into_noreduce(tmp_sc_2, b) crypto.decodepoint_into(tmp_pt_1, B) crypto.add_keys2_into(tmp_pt_2, tmp_sc_1, tmp_sc_2, tmp_pt_1) crypto.encodepoint_into(dst, tmp_pt_2) return dst
def vector_exponent_custom(A, B, a, b, dst=None): dst = _ensure_dst_key(dst) crypto.identity_into(tmp_pt_2) for i in range(len(a)): crypto.decodeint_into_noreduce(tmp_sc_1, a.to(i)) crypto.decodepoint_into(tmp_pt_3, A.to(i)) crypto.decodeint_into_noreduce(tmp_sc_2, b.to(i)) crypto.decodepoint_into(tmp_pt_4, B.to(i)) crypto.add_keys3_into(tmp_pt_1, tmp_sc_1, tmp_pt_3, tmp_sc_2, tmp_pt_4) crypto.point_add_into(tmp_pt_2, tmp_pt_2, tmp_pt_1) gc_iter(i) crypto.encodepoint_into(dst, tmp_pt_2) return dst
def hadamard_fold(v, a, b, into=None, into_offset=0): """ Folds a curvepoint array using a two way scaled Hadamard product ln = len(v); h = ln // 2 v[i] = a * v[i] + b * v[h + i] """ 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.decodepoint_into(tmp_pt_1, v.to(i)) crypto.decodepoint_into(tmp_pt_2, v.to(h + i)) crypto.add_keys3_into(tmp_pt_3, tmp_sc_1, tmp_pt_1, tmp_sc_2, tmp_pt_2) crypto.encodepoint_into(tmp_bf_0, tmp_pt_3) into.read(i + into_offset, tmp_bf_0) gc_iter(i) return into
def generate_mlsag_simple( message: bytes, pubs: list[MoneroRctKeyPublic], in_sk: CtKey, a: Sc25519, cout: Ge25519, index: int, mg_buff: list[bytes], ) -> list[bytes]: """ MLSAG for RctType.Simple :param message: the full message to be signed (actually its hash) :param pubs: vector of MoneroRctKey; this forms the ring; point values in encoded form; (dest, mask) = (P, C) :param in_sk: CtKey; spending private key with input commitment mask (original); better_name: input_secret_key :param a: mask from the pseudo output commitment; better name: pseudo_out_alpha :param cout: pseudo output commitment; point, decoded; better name: pseudo_out_c :param index: specifies corresponding public key to the `in_sk` in the pubs array :param mg_buff: buffer to store the signature to """ # Monero signs inputs separately, so `rows` always equals 2 (pubkey, commitment) # and `dsRows` is always 1 (denotes where the pubkeys "end") rows = 2 dsRows = 1 cols = len(pubs) if cols == 0: raise ValueError("Empty pubs") sk = _key_vector(rows) M = _key_matrix(rows, cols) sk[0] = in_sk.dest sk[1] = crypto.sc_sub(in_sk.mask, a) tmp_pt = crypto.new_point() for i in range(cols): crypto.point_sub_into( tmp_pt, crypto.decodepoint_into(tmp_pt, pubs[i].commitment), cout ) M[i][0] = pubs[i].dest M[i][1] = crypto.encodepoint(tmp_pt) pubs[i] = None del pubs gc.collect() return generate_mlsag(message, M, sk, index, dsRows, mg_buff)
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 verify_clsag(self, msg, ss, sc1, sI, sD, pubs, C_offset): n = len(pubs) c = crypto.Scalar() D_8 = crypto.Point() tmp_bf = bytearray(32) C_offset_bf = crypto_helpers.encodepoint(C_offset) crypto.sc_copy(c, sc1) point_mul8_into(D_8, sD) hsh_P = crypto_helpers.get_keccak() # domain, I, D, P, C, C_offset hsh_C = crypto_helpers.get_keccak() # domain, I, D, P, C, C_offset hsh_P.update(clsag._HASH_KEY_CLSAG_AGG_0) hsh_C.update(clsag._HASH_KEY_CLSAG_AGG_1) def hsh_PC(x): hsh_P.update(x) hsh_C.update(x) for x in pubs: hsh_PC(x.dest) for x in pubs: hsh_PC(x.commitment) hsh_PC(crypto.encodepoint_into(tmp_bf, sI)) hsh_PC(crypto.encodepoint_into(tmp_bf, sD)) hsh_PC(C_offset_bf) mu_P = crypto_helpers.decodeint(hsh_P.digest()) mu_C = crypto_helpers.decodeint(hsh_C.digest()) c_to_hash = crypto_helpers.get_keccak( ) # domain, P, C, C_offset, message, L, R c_to_hash.update(clsag._HASH_KEY_CLSAG_ROUND) for i in range(len(pubs)): c_to_hash.update(pubs[i].dest) for i in range(len(pubs)): c_to_hash.update(pubs[i].commitment) c_to_hash.update(C_offset_bf) c_to_hash.update(msg) c_p = crypto.Scalar() c_c = crypto.Scalar() L = crypto.Point() R = crypto.Point() tmp_pt = crypto.Point() i = 0 while i < n: crypto.sc_mul_into(c_p, mu_P, c) crypto.sc_mul_into(c_c, mu_C, c) C_P = crypto.point_sub_into( None, crypto.decodepoint_into(tmp_pt, pubs[i].commitment), C_offset) crypto.add_keys2_into( L, ss[i], c_p, crypto.decodepoint_into(tmp_pt, pubs[i].dest)) crypto.point_add_into(L, L, crypto.scalarmult_into(tmp_pt, C_P, c_c)) HP = crypto.hash_to_point_into(None, pubs[i].dest) crypto.add_keys3_into(R, ss[i], HP, c_p, sI) crypto.point_add_into(R, R, crypto.scalarmult_into(tmp_pt, D_8, 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()) i += 1 res = crypto.sc_sub_into(None, c, sc1) if not crypto.sc_eq(res, crypto.Scalar(0)): raise ValueError("Signature error")
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]