def ver_borromean(P1, P2, s0, s1, ee): """ Verify range proof signature, Borromean (c.f. gmax/andytoshi's paper) :param P1: :param P2: :param s0: :param s1: :param ee: :return: """ n = len(P1) Lv1 = key_vector(n) for ii in range(n): LL = crypto.add_keys2(s0[ii], ee, P1[ii]) chash = crypto.hash_to_scalar(crypto.encodepoint(LL)) Lv1[ii] = crypto.add_keys2(s1[ii], chash, P2[ii]) kck = crypto.get_keccak() for ii in range(n): kck.update(crypto.encodepoint(Lv1[ii])) # ee_computed = crypto.hash_to_scalar(crypto.encodepoint(Lv1)) ee_computed = crypto.decodeint(kck.digest()) return crypto.sc_eq(ee_computed, ee)
def gen_mlsag_ext(message, pk, xx, kLRki, mscout, index, dsRows): """ Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures) :param message: :param pk: matrix of points, point form (not encoded) :param xx: :param kLRki: :param mscout: :param index: :param dsRows: :return: """ rows, cols = gen_mlsag_assert(pk, xx, kLRki, mscout, index, dsRows) rv = xmrtypes.MgSig() c, L, R, Hi = 0, None, None, None c_old, Ip, alpha = gen_mlsag_rows(message, rv, pk, xx, kLRki, index, dsRows, rows, cols) i = (index + 1) % cols if i == 0: rv.cc = c_old while i != index: rv.ss[i] = scalar_gen_vector(rows) hasher = hasher_message(message) for j in range(dsRows): L = crypto.add_keys2(rv.ss[i][j], c_old, pk[i][j]) Hi = crypto.hash_to_point(crypto.encodepoint( pk[i][j])) # originally hashToPoint() R = crypto.add_keys3(rv.ss[i][j], Hi, c_old, Ip[j]) hasher.update(crypto.encodepoint(pk[i][j])) hasher.update(crypto.encodepoint(L)) hasher.update(crypto.encodepoint(R)) for j in range(dsRows, rows): L = crypto.add_keys2(rv.ss[i][j], c_old, pk[i][j]) hasher.update(crypto.encodepoint(pk[i][j])) hasher.update(crypto.encodepoint(L)) 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]) # alpha[j] - c * xx[j]; sc_mulsub in original does c-ab if mscout: mscout(c) return rv, c
def gen_clsag_sig(self, ring_size=11, index=None): msg = bytearray(random.getrandbits(8) for _ in range(32)) amnt = crypto.sc_init(random.randint(0, 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.randint(0, 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), )) mlsag2.generate_clsag_simple( msg, ring, CtKey(dest=priv, mask=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 ver_mlsag(pk, I, c0, s): """ Verify MLSAG :param pk: :param I: :param c0: :param s: :return: """ rows = len(pk[0]) cols = len(pk) logger.debug("verifying MG sig of dimensions %s x %s" % (rows, cols)) c = [None] * (cols + 1) c[0] = c0 L = key_matrix(rows, cols) R = key_matrix(rows, cols) m = "".join(pk[0]) for i in range(1, cols): m = m + "".join(pk[i]) i = 0 while i < cols: L[i] = [crypto.add_keys2(s[i][j], c[i], pk[i][j]) for j in range(0, rows)] Hi = hash_key_vector(pk[i]) R[i] = [crypto.add_keys3(s[i][j], Hi[j], c[i], I[j]) for j in range(0, rows)] oldi = i i = i + 1 c[i] = crypto.cn_fast_hash(m + "".join(L[oldi]) + "".join(R[oldi])) return c0 == c[cols]
def gen_mlsag(pk, xx, index): """ Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures) These are aka MG signatutes in earlier drafts of the ring ct paper c.f. http://eprint.iacr.org/2015/1098 section 2. keyImageV just does I[i] = xx[i] * Hash(xx[i] * G) for each i Gen creates a signature which proves that for some column in the keymatrix "pk" the signer knows a secret key for each row in that column Ver verifies that the MG sig was created correctly :param pk: :param xx: :param index: :return: """ rows = len(xx) cols = len(pk) logger.debug("Generating MG sig of size %s x %s" % (rows, cols)) logger.debug("index is: %s, %s" % (index, pk[index])) c = [None] * cols alpha = scalar_gen_vector(rows) I = key_image_vector(xx) L = key_matrix(rows, cols) R = key_matrix(rows, cols) s = key_matrix(rows, cols) m = "".join(pk[0]) for i in range(1, cols): m = m + "".join(pk[i]) L[index] = [crypto.scalarmult_base(aa) for aa in alpha] # L = aG Hi = hash_key_vector(pk[index]) R[index] = [crypto.scalarmult(Hi[ii], alpha[ii]) for ii in range(0, rows)] # R = aI oldi = index i = (index + 1) % cols c[i] = crypto.cn_fast_hash(m + "".join(L[oldi]) + "".join(R[oldi])) while i != index: s[i] = scalar_gen_vector(rows) L[i] = [ crypto.add_keys2(s[i][j], c[i], pk[i][j]) for j in range(0, rows) ] Hi = hash_key_vector(pk[i]) R[i] = [ crypto.add_keys3(s[i][j], Hi[j], c[i], I[j]) for j in range(0, rows) ] oldi = i i = (i + 1) % cols c[i] = crypto.cn_fast_hash(m + "".join(L[oldi]) + "".join(R[oldi])) s[index] = [ crypto.sc_mulsub(c[index], xx[j], alpha[j]) for j in range(0, rows) ] # alpha - c * x return I, c[0], s
def ver_mlsag_ext(message, pk, rv, dsRows): """ Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures) c.f. http://eprint.iacr.org/2015/1098 section 2. keyImageV just does I[i] = xx[i] * Hash(xx[i] * G) for each i :param message: :param pk: matrix of EC points, point form. :param rv: :param dsRows: :return: """ rows, cols = ver_mlsag_assert(pk, rv, dsRows) c_old = rv.cc Ip = key_vector(dsRows) for i in range(dsRows): Ip[i] = crypto.precomp(rv.II[i]) i = 0 while i < cols: c = 0 hasher = hasher_message(message) for j in range(dsRows): L = crypto.add_keys2(rv.ss[i][j], c_old, pk[i][j]) Hi = crypto.hash_to_point(crypto.encodepoint( pk[i][j])) # originally hashToPoint() R = crypto.add_keys3(rv.ss[i][j], Hi, c_old, Ip[j]) hasher.update(crypto.encodepoint(pk[i][j])) hasher.update(crypto.encodepoint(L)) hasher.update(crypto.encodepoint(R)) for j in range(dsRows, rows): L = crypto.add_keys2(rv.ss[i][j], c_old, pk[i][j]) hasher.update(crypto.encodepoint(pk[i][j])) hasher.update(crypto.encodepoint(L)) c = crypto.decodeint(hasher.digest()) c_old = c i += 1 c = crypto.sc_sub(c_old, rv.cc) return not crypto.sc_isnonzero(c)
def gen_borromean(x, P1, P2, indices): """ Generates Borromean signature for range proofs. :return: """ n = len(P1) alpha = key_zero_vector(n) s1 = key_zero_vector(n) kck = crypto.get_keccak() # ee computation for ii in range(n): alpha[ii] = crypto.random_scalar() L = crypto.scalarmult_base(alpha[ii]) if indices[ii] == 0: s1[ii] = crypto.random_scalar() c = crypto.hash_to_scalar(crypto.encodepoint(L)) L = crypto.add_keys2(s1[ii], c, P2[ii]) kck.update(crypto.encodepoint(L)) else: kck.update(crypto.encodepoint(L)) ee = crypto.decodeint(kck.digest()) del kck s0 = key_zero_vector(n) for jj in range(n): if not indices[jj]: s0[jj] = crypto.sc_mulsub(x[jj], ee, alpha[jj]) else: s0[jj] = crypto.random_scalar() LL = crypto.add_keys2(s0[jj], ee, P1[jj]) cc = crypto.hash_to_scalar(crypto.encodepoint(LL)) s1[jj] = crypto.sc_mulsub(x[jj], cc, alpha[jj]) return s0, s1, ee
def ecdh_decode_simple(rv, sk, i): """ Decodes ECDH from the transaction, checks mask (decoding validity). """ if i >= len(rv.ecdhInfo): raise ValueError("Bad index") if len(rv.outPk) != len(rv.ecdhInfo): raise ValueError("outPk vs ecdhInfo mismatch") ecdh_info = rv.ecdhInfo[i] ecdh_info = recode_ecdh(ecdh_info, False) ecdh_info = ring_ct.ecdh_decode(ecdh_info, derivation=crypto.encodeint(sk)) c_tmp = crypto.add_keys2(ecdh_info.mask, ecdh_info.amount, crypto.xmr_H()) if not crypto.point_eq(c_tmp, crypto.decodepoint(rv.outPk[i].mask)): raise ValueError("Amount decoded incorrectly") return ecdh_info.amount, ecdh_info.mask
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 :return: sumCi, mask, RangeSig. sumCi is Pedersen commitment on the amount value. sumCi = aG + amount*H mask is "a" from the Pedersent commitment above. """ n = ATOMS bb = d2b(amount, n) # gives binary form of bb in "digits" binary digits ai = [None] * len(bb) Ci = [None] * len(bb) a = crypto.sc_0() C = crypto.identity() alpha = mlsag2.key_zero_vector(n) s1 = mlsag2.key_zero_vector(n) c_H = crypto.xmr_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 == ATOMS - 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: Ci[ii] = crypto.scalarmult_base(ai[ii]) else: Ci[ii] = crypto.point_add(crypto.scalarmult_base(ai[ii]), c_H) C = crypto.point_add(C, Ci[ii]) if bb[ii] == 0: s1[ii] = crypto.random_scalar() c = crypto.hash_to_scalar(crypto.encodepoint(L)) L = crypto.add_keys2(s1[ii], c, crypto.point_sub(Ci[ii], c_H)) kck.update(crypto.encodepoint(L)) else: kck.update(crypto.encodepoint(L)) c_H = crypto.point_double(c_H) # Compute ee, memory cleanup ee = crypto.decodeint(kck.digest()) del kck # Second phase computes: s0, s1 c_H = crypto.xmr_H() s0 = mlsag2.key_zero_vector(n) for jj in range(n): if not bb[jj]: s0[jj] = crypto.sc_mulsub(ai[jj], ee, alpha[jj]) else: s0[jj] = crypto.random_scalar() LL = crypto.add_keys2(s0[jj], ee, Ci[jj]) cc = crypto.hash_to_scalar(crypto.encodepoint(LL)) s1[jj] = crypto.sc_mulsub(ai[jj], cc, alpha[jj]) c_H = crypto.point_double(c_H) A = xmrtypes.BoroSig() A.s0, A.s1, A.ee = s0, s1, ee R = xmrtypes.RangeSig() R.asig = A R.Ci = Ci return C, a, R
async def gen_input(self, value=1, sub_major=None, sub_minor=0, additionals=False): creds = self.src_keys r = self.random_scalar() R = crypto.scalarmult_base(r) additional_keys = [] Additional = None sub_major = sub_major if sub_major is not None else self.account_idx is_sub = sub_major != 0 or sub_minor != 0 if sub_major != self.account_idx: logger.warning( "Generating input with different major subindex, cannot be spent in the resulting " "transaction") kssec = monero.get_subaddress_secret_key(creds.view_key_private, major=sub_major, minor=sub_minor) kssub = crypto.sc_add( kssec, creds.spend_key_private) if is_sub else creds.spend_key_private kvsub = crypto.sc_mul(creds.view_key_private, kssub) if is_sub else creds.view_key_private KSSUB, KVSUB = monero.generate_sub_address_keys( creds.view_key_private, creds.spend_key_public, sub_major, sub_minor) if not crypto.point_eq(KSSUB, crypto.scalarmult_base(kssub)): raise ValueError("Invariant error") oidx = self.prng.randint(0, 12) additionals_cnt = self.prng.randint(oidx + 1, 2 * oidx + 1) deriv = crypto.generate_key_derivation(KVSUB, r) kout = crypto.derive_secret_key(deriv, oidx, kssub) KOUT = crypto.derive_public_key(deriv, oidx, KSSUB) if not crypto.point_eq(KOUT, crypto.scalarmult_base(kout)): raise ValueError("Invariant error") if additionals: if is_sub: Additional = crypto.scalarmult(KSSUB, r) else: Additional = crypto.scalarmult_base(r) else: if is_sub: R = crypto.scalarmult(KSSUB, r) amnt = crypto.sc_init(value) msk = self.random_scalar() # commitment mask C = crypto.add_keys2(msk, amnt, crypto.xmr_H()) ring = [] for i in range(self.ring_size - 1): tk = CtKey( dest=crypto.encodepoint(self.random_pub()), mask=crypto.encodepoint(self.random_pub()), ) ring.append(tk) index = self.prng.randint(0, len(ring)) ring.insert( index, CtKey(dest=crypto.encodepoint(KOUT), mask=crypto.encodepoint(C))) if additionals: additional_keys = [ self.random_pub() for _ in range(additionals_cnt) ] additional_keys[oidx] = Additional src = TxSourceEntry() src.outputs = [(self.random_glob_idx(), x) for x in ring] src.real_output = index src.real_out_tx_key = crypto.encodepoint(R) src.real_out_additional_tx_keys = [ crypto.encodepoint(x) for x in additional_keys ] src.real_output_in_tx_index = oidx src.amount = value src.rct = True src.mask = crypto.encodeint(msk) src.multisig_kLRki = MultisigKLRki(K=crypto.ZERO, L=crypto.ZERO, R=crypto.ZERO, ki=crypto.ZERO) td = TransferDetails() td.m_internal_output_index = oidx td.m_global_output_index = src.outputs[index][0] td.m_mask = src.mask td.m_amount = value td.m_subaddr_index = SubaddressIndex(major=sub_major, minor=sub_minor) td.m_rct = True td.m_txid = self.random_bytes(32) td.m_block_height = self.prng.randint(0, 0xFFFF) td.m_spent = False td.m_spent_height = 0 td.m_key_image_known = True td.m_key_image_requested = False td.m_key_image_partial = False td.m_multisig_k = [] td.m_multisig_info = [] td.m_uses = [] td.m_pk_index = 0 td.m_tx = self.gen_tx_prefix(self.prng.randint(1, 10), additionals_cnt) td.m_tx.vout[oidx].target.key = crypto.encodepoint(KOUT) extras = [] extras.append(TxExtraNonce(nonce=self.random_bytes(8))) extras.append(TxExtraPubKey(pub_key=src.real_out_tx_key)) if src.real_out_additional_tx_keys: extras.append( TxExtraAdditionalPubKeys(data=src.real_out_additional_tx_keys)) td.m_tx.extra = await self.dump_extra_fields(extras) tmpsubs = {} monero.compute_subaddresses(creds, sub_major, [sub_minor], tmpsubs) xi, ki, rderiv = self.check_input(src, creds, tmpsubs) if not crypto.sc_eq(xi, kout): raise ValueError("Invariant error") td.m_key_image = crypto.encodepoint(ki) self.sources.append(src) self.selected_transfers.append(len(self.transfers)) self.transfers.append(td) self.sources_creds.append(creds) if not crypto.point_eq( crypto.decodepoint(src.outputs[src.real_output][1].dest), crypto.scalarmult_base(kout)): raise ValueError("Invariant error") return self