def generate_key_image(public_key: bytes, secret_key: Sc25519) -> Ge25519: """ Key image: secret_key * H_p(pub_key) """ point = crypto.hash_to_point(public_key) point2 = crypto.scalarmult(point, secret_key) return point2
def generate_mlsag(message, pk, xx, kLRki, index, dsRows): """ 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 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: separates pubkeys from commitment :return MgSig """ from apps.monero.xmr.serialize_messages.tx_full import MgSig rows, cols = gen_mlsag_assert(pk, xx, kLRki, index, dsRows) rv = MgSig() c, L, R, Hi = 0, None, None, None # calculates the "first" c, key images and random scalars alpha c_old, Ip, alpha = generate_first_c_and_key_images(message, rv, pk, xx, kLRki, index, dsRows, rows, cols) i = (index + 1) % cols if i == 0: rv.cc = c_old tmp_buff = bytearray(32) while i != index: rv.ss[i] = _generate_random_vector(rows) hasher = _hasher_message(message) for j in range(dsRows): # L = rv.ss[i][j] * G + c_old * pk[i][j] L = crypto.add_keys2(rv.ss[i][j], c_old, pk[i][j]) Hi = crypto.hash_to_point(crypto.encodepoint(pk[i][j])) # R = rv.ss[i][j] * H(pk[i][j]) + c_old * Ip[j] R = crypto.add_keys3(rv.ss[i][j], Hi, c_old, rv.II[j]) _hash_point(hasher, pk[i][j], tmp_buff) _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 L = crypto.add_keys2(rv.ss[i][j], c_old, pk[i][j]) _hash_point(hasher, pk[i][j], tmp_buff) _hash_point(hasher, L, tmp_buff) c = crypto.decodeint(hasher.digest()) c_old = c i = (i + 1) % cols if i == 0: rv.cc = c_old for j in range(rows): rv.ss[index][j] = crypto.sc_mulsub(c, xx[j], alpha[j]) return rv
def generate_key_image(public_key, secret_key): """ Key image: secret_key * H_p(pub_key) """ point = crypto.hash_to_point(public_key) point2 = crypto.scalarmult(point, secret_key) return point2
def test_hash_to_point(self): data = unhexlify( b"42f6835bf83114a1f5f6076fe79bdfa0bd67c74b88f127d54572d3910dd09201" ) res = crypto.hash_to_point(data) res_p = crypto.encodepoint(res) self.assertEqual( res_p, unhexlify( b"54863a0464c008acc99cffb179bc6cf34eb1bbdf6c29f7a070a7c6376ae30ab5" ), )
def check_ring_singature(prefix_hash, image, pubs, sig): """ Checks ring signature generated with generate_ring_signature :param prefix_hash: :param image: :param pubs: :param sig: :return: """ from apps.monero.xmr.common import memcpy 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() for i in range(len(pubs)): if crypto.sc_check(sig[i][0]) != 0 or crypto.sc_check(sig[i][1]) != 0: return False tmp3 = crypto.ge_frombytes_vartime(pubs[i]) tmp2 = crypto.ge_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(pubs[i])) tmp2 = crypto.ge_double_scalarmult_precomp_vartime( sig[i][1], tmp3, sig[i][0], image_pre ) 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) h = crypto.sc_sub(h, sum) return crypto.sc_isnonzero(h) == 0
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
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(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 = crypto.ge_frombytes_vartime(pubs[i]) tmp2 = crypto.ge_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.ge_double_scalarmult_precomp_vartime( sig[i][1], tmp3, sig[i][0], image_pre ) 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
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 verify_clsag(self, msg, ss, sc1, sI, sD, pubs, C_offset): n = len(pubs) c = crypto.new_scalar() D_8 = crypto.new_point() tmp_bf = bytearray(32) C_offset_bf = crypto.encodepoint(C_offset) crypto.sc_copy(c, sc1) crypto.point_mul8_into(D_8, 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(mlsag._HASH_KEY_CLSAG_AGG_0) hsh_C.update(mlsag._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.decodeint(hsh_P.digest()) mu_C = crypto.decodeint(hsh_C.digest()) c_to_hash = crypto.get_keccak( ) # domain, P, C, C_offset, message, L, R c_to_hash.update(mlsag._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.new_scalar() c_c = crypto.new_scalar() L = crypto.new_point() R = crypto.new_point() tmp_pt = crypto.new_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( 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(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(c, sc1) if not crypto.sc_eq(res, crypto.sc_0()): raise ValueError("Signature error")