def generate_first_c_and_key_images(message, 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 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 """ II = _key_vector(dsRows) alpha = _key_vector(rows) tmp_buff = bytearray(32) Hi = crypto.new_point() aGi = crypto.new_point() aHPi = crypto.new_point() 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(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: crypto.hash_to_point_into(Hi, pk[index][i]) alpha[i] = crypto.random_scalar() # L = alpha_i * G crypto.scalarmult_base_into(aGi, alpha[i]) # Ri = alpha_i * H(P_i) crypto.scalarmult_into(aHPi, Hi, alpha[i]) # key image II[i] = crypto.scalarmult(Hi, xx[i]) _hash_point(hasher, aGi, tmp_buff) _hash_point(hasher, aHPi, tmp_buff) for i in range(dsRows, rows): alpha[i] = crypto.random_scalar() # L = alpha_i * G crypto.scalarmult_base_into(aGi, 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 hasher.update(pk[index][i]) _hash_point(hasher, aGi, tmp_buff) # the first c c_old = hasher.digest() c_old = crypto.decodeint(c_old) return c_old, II, alpha
def gen_clsag_sig(self, ring_size=11, index=None): msg = random.bytes(32) amnt = crypto.sc_init(random.uniform(0xFFFFFF) + 12) priv = crypto.random_scalar() msk = crypto.random_scalar() alpha = crypto.random_scalar() P = crypto.scalarmult_base(priv) C = crypto.add_keys2(msk, amnt, crypto.xmr_H()) Cp = crypto.add_keys2(alpha, amnt, crypto.xmr_H()) ring = [] for i in range(ring_size - 1): tk = TmpKey( crypto.encodepoint( crypto.scalarmult_base(crypto.random_scalar())), crypto.encodepoint( crypto.scalarmult_base(crypto.random_scalar())), ) ring.append(tk) index = index if index is not None else random.uniform(len(ring)) ring.insert(index, TmpKey(crypto.encodepoint(P), crypto.encodepoint(C))) ring2 = list(ring) mg_buffer = [] self.assertTrue( crypto.point_eq(crypto.scalarmult_base(priv), crypto.decodepoint(ring[index].dest))) self.assertTrue( crypto.point_eq( crypto.scalarmult_base(crypto.sc_sub(msk, alpha)), crypto.point_sub(crypto.decodepoint(ring[index].commitment), Cp), )) mlsag.generate_clsag_simple( msg, ring, CtKey(priv, msk), alpha, Cp, index, mg_buffer, ) sD = crypto.decodepoint(mg_buffer[-1]) sc1 = crypto.decodeint(mg_buffer[-2]) scalars = [crypto.decodeint(x) for x in mg_buffer[1:-2]] H = crypto.new_point() sI = crypto.new_point() crypto.hash_to_point_into(H, crypto.encodepoint(P)) crypto.scalarmult_into(sI, H, priv) # I = p*H return msg, scalars, sc1, sI, sD, ring2, Cp
def get_exponent(dst, base, idx): dst = _ensure_dst_key(dst) salt = b"bulletproof" idx_size = uvarint_size(idx) final_size = len(salt) + 32 + idx_size buff = tmp_bf_exp_mv memcpy(buff, 0, base, 0, 32) memcpy(buff, 32, salt, 0, len(salt)) dump_uvarint_b_into(idx, buff, 32 + len(salt)) crypto.keccak_hash_into(tmp_bf_1, buff[:final_size]) crypto.hash_to_point_into(tmp_pt_1, tmp_bf_1) crypto.encodepoint_into(dst, tmp_pt_1) return dst
def generate_key_image(public_key: bytes, secret_key: crypto.Scalar) -> crypto.Point: """ Key image: secret_key * H_p(pub_key) """ point = crypto.hash_to_point_into(None, public_key) point2 = crypto.scalarmult_into(None, point, secret_key) return point2
def test_hash_to_point(self): data = unhexlify( b"42f6835bf83114a1f5f6076fe79bdfa0bd67c74b88f127d54572d3910dd09201" ) res = crypto.hash_to_point_into(None, data) res_p = crypto_helpers.encodepoint(res) self.assertEqual( res_p, unhexlify( b"54863a0464c008acc99cffb179bc6cf34eb1bbdf6c29f7a070a7c6376ae30ab5" ), )
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 generate_ring_signature( prefix_hash: bytes, image: crypto.Point, pubs: list[crypto.Point], sec: crypto.Scalar, 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_into(None, sec) if not crypto.point_eq(t, pubs[sec_idx]): raise ValueError("Invalid sec key") k_i = monero.generate_key_image( crypto_helpers.encodepoint(pubs[sec_idx]), sec) if not crypto.point_eq(k_i, image): raise ValueError("Key image invalid") for k in pubs: crypto.ge25519_check(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.Scalar(0) k = crypto.Scalar(0) sig = [] for _ in range(len(pubs)): sig.append([crypto.Scalar(0), crypto.Scalar(0)]) # c, r for i in range(len(pubs)): if i == sec_idx: k = crypto.random_scalar() tmp3 = crypto.scalarmult_base_into(None, k) crypto.encodepoint_into(mvbuff[buff_off:buff_off + 32], tmp3) buff_off += 32 tmp3 = crypto.hash_to_point_into( None, crypto_helpers.encodepoint(pubs[i])) tmp2 = crypto.scalarmult_into(None, 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_vartime_into( None, tmp3, sig[i][0], sig[i][1]) crypto.encodepoint_into(mvbuff[buff_off:buff_off + 32], tmp2) buff_off += 32 tmp3 = crypto.hash_to_point_into(None, crypto_helpers.encodepoint(tmp3)) tmp2 = crypto.add_keys3_into(None, sig[i][1], tmp3, sig[i][0], image) crypto.encodepoint_into(mvbuff[buff_off:buff_off + 32], tmp2) buff_off += 32 crypto.sc_add_into(sum, sum, sig[i][0]) h = crypto.hash_to_scalar_into(None, buff) sig[sec_idx][0] = crypto.sc_sub_into(None, h, sum) sig[sec_idx][1] = crypto.sc_mulsub_into(None, sig[sec_idx][0], sec, k) return sig