def ver_asnl(P1, P2, L1, s2, s): """ Aggregate Schnorr Non-Linkable :param P1: :param P2: :param L1: :param s2: :param s: :return: """ logger.info("Verifying Aggregate Schnorr Non-linkable Ring Signature") n = len(P1) LHS = crypto.scalarmult_base(crypto.sc_0()) RHS = crypto.scalarmult_base(s) for j in range(0, n): c2 = crypto.hash_to_scalar(crypto.encodepoint(L1[j])) L2 = crypto.point_add(crypto.scalarmult_base(s2[j]), crypto.scalarmult(P2[j], c2)) LHS = crypto.point_add(LHS, L1[j]) c1 = crypto.hash_to_scalar(crypto.encodepoint(L2)) RHS = crypto.point_add(RHS, crypto.scalarmult(P1[j], c1)) if crypto.point_eq(LHS, RHS): return 1 else: logger.warning("Didn't verify L1: %s, L1p: %s" % (crypto.encodepoint(LHS), crypto.encodepoint(RHS))) return 0
def poc2(self): print('[+] PoC Ledger-app-Monero 1.4.2 spend key extraction, v2') self.reset() self.set_mode() self.open_tx() # 1. get A, find x, s.t.: [8*a*x*G]_pt == [8*a*x*G]_sc A = self.scalarmult(self.fake_a) Apt = crypto.decodepoint(A) x, A8x = self.find_confusion(Apt) Gx = crypto.encodepoint(crypto.scalarmult_base(crypto.sc_init(x))) print(' 1. Confusion found, x: %d' % (x, )) print(' 8xA: %s' % binascii.hexlify(A8x).decode('ascii')) print(' A: %s' % binascii.hexlify(A).decode('ascii')) print(' xG: %s' % binascii.hexlify(Gx).decode('ascii')) # 2. gen_deriv (8*a*x*G) = enc(8x*A) = enc(P); we know {P, enc(P)}; # It holds that P=8xA is also a valid scalar value, from the step above. P = self.gen_derivation(Gx, self.fake_a) print(' 2. P: %s' % (self.fmtkey(P).decode('ascii'), )) # 3. get_secret_key: s1 = Hs(P||0) + s sp = self.derive_secret_key(P, 0, self.fake_b) print(' 3. sp: %s' % (self.fmtkey(sp).decode('ascii'), )) # 4. mlsag_hash(p2=1, opt=0x80) = c c = self.mlsag_hash() print(' 4. c: %s' % (binascii.hexlify(c).decode('ascii'), )) # 5. mlsag_sign(s1, enc(P)), r1 = enc(s1 - Pc) = enc(Hs(P||0) + s - Pc); # We have R = Hs(P||0) + s - Pc -> R - Hs(P||0) + Pc = s r = self.mlsag_sign_s(P, sp) print(' 5. r: %s' % (binascii.hexlify(r[0]).decode('ascii'), )) # Extract the spend key hs0 = crypto.hash_to_scalar(bytearray(A8x) + bytearray(1)) rsc = crypto.decodeint(r[0]) rsc = crypto.sc_sub(rsc, hs0) bsc = crypto.sc_add( rsc, crypto.sc_mul(crypto.decodeint(c), crypto.decodeint(A8x))) b = crypto.encodeint(bsc) print(' 5. b: %s' % binascii.hexlify(b).decode('ascii')) B = crypto.scalarmult_base(bsc) print(' 5. B: %s' % binascii.hexlify(crypto.encodepoint(B)).decode('ascii')) # 6. Verify BB = self.scalarmult(self.fake_b) print(' 6. bG: %s\n' % binascii.hexlify(BB).decode('ascii')) if BB == crypto.encodepoint(B): print('[+] PoC successful') else: print('[-] PoC not working') print('\nCommands: ') for x in self.commands: print(' %s' % x)
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 generate_key_image_helper_precomp( ack, out_key, recv_derivation, real_output_index, received_index ): """ Generates UTXO spending key and key image. :param ack: sender credentials :type ack: AccountCreds :param out_key: real output (from input RCT) destination key :param recv_derivation: :param real_output_index: :param received_index: subaddress index this payment was received to :return: """ if ack.spend_key_private == 0: raise ValueError("Watch-only wallet not supported") # derive secret key with subaddress - step 1: original CN derivation scalar_step1 = crypto.derive_secret_key( recv_derivation, real_output_index, ack.spend_key_private ) # step 2: add Hs(SubAddr || a || index_major || index_minor) subaddr_sk = None scalar_step2 = None if received_index == (0, 0): scalar_step2 = scalar_step1 else: subaddr_sk = get_subaddress_secret_key( ack.view_key_private, major=received_index[0], minor=received_index[1] ) scalar_step2 = crypto.sc_add(scalar_step1, subaddr_sk) # when not in multisig, we know the full spend secret key, so the output pubkey can be obtained by scalarmultBase if len(ack.multisig_keys) == 0: pub_ver = crypto.scalarmult_base(scalar_step2) else: # When in multisig, we only know the partial spend secret key. But we do know the full spend public key, # so the output pubkey can be obtained by using the standard CN key derivation. pub_ver = crypto.derive_public_key( recv_derivation, real_output_index, ack.spend_key_public ) # Add the contribution from the subaddress part if received_index != (0, 0): subaddr_pk = crypto.scalarmult_base(subaddr_sk) pub_ver = crypto.point_add(pub_ver, subaddr_pk) if not crypto.point_eq(pub_ver, out_key): raise ValueError( "key image helper precomp: given output pubkey doesn't match the derived one" ) ki = generate_key_image(crypto.encodepoint(pub_ver), scalar_step2) return scalar_step2, ki
def test_ge25519_double_scalarmult_vartime(self): for i in range(10): ap = crypto.random_scalar() A = crypto.scalarmult_base(ap) a = crypto.random_scalar() b = crypto.random_scalar() R = crypto.ge_double_scalarmult_base_vartime(a, A, b) R_exp = crypto.point_add(crypto.scalarmult(A, a), crypto.scalarmult_base(b)) self.assertTrue(crypto.point_eq(R, R_exp))
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
async def put_key(self): sec = crypto.decodeint(self._fetch(32)) pub = crypto.decodepoint(self._fetch(32)) if not crypto.point_eq(pub, crypto.scalarmult_base(sec)): return SW_WRONG_DATA self.a = sec sec = crypto.decodeint(self._fetch(32)) pub = crypto.decodepoint(self._fetch(32)) if not crypto.point_eq(pub, crypto.scalarmult_base(sec)): return SW_WRONG_DATA self.b = sec return SW_OK
def prove_range_orig(amount, last_mask=None, use_asnl=False): """ 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, insecure :return: sumCi, mask, RangeSig. sumCi is Pedersen commitment on the amount value. sumCi = aG + amount*H mask is "a" from the Pedersent commitment above. """ bb = d2b(amount, ATOMS) # gives binary form of bb in "digits" binary digits logger.info("amount, amount in binary %s %s" % (amount, bb)) ai = [None] * len(bb) Ci = [None] * len(bb) CiH = [None] * len(bb) # this is like Ci - 2^i H H2 = crypto.gen_Hpow(ATOMS) a = crypto.sc_0() for i in range(0, ATOMS): ai[i] = crypto.random_scalar() if last_mask is not None and i == ATOMS - 1: ai[i] = crypto.sc_sub(last_mask, a) a = crypto.sc_add( a, ai[i] ) # creating the total mask since you have to pass this to receiver... if bb[i] == 0: Ci[i] = crypto.scalarmult_base(ai[i]) if bb[i] == 1: Ci[i] = crypto.point_add(crypto.scalarmult_base(ai[i]), H2[i]) CiH[i] = crypto.point_sub(Ci[i], H2[i]) A = xmrtypes.BoroSig() if use_asnl: A.s0, A.s1, A.ee = asnl.gen_asnl(ai, Ci, CiH, bb) else: A.s0, A.s1, A.ee = mlsag2.gen_borromean(ai, Ci, CiH, bb) R = xmrtypes.RangeSig() R.asig = A R.Ci = Ci C = sum_Ci(Ci) return C, a, R
def gen_mlsag_rows(message, rv, pk, xx, kLRki, index, dsRows, rows, cols): """ MLSAG computation - the part with secret keys :param message: :param rv: :param pk: :param xx: :param kLRki: :param index: :param dsRows: :param rows: :param cols: :return: """ Ip = key_vector(dsRows) rv.II = key_vector(dsRows) alpha = key_vector(rows) rv.ss = key_matrix(rows, cols) hasher = hasher_message(message) for i in range(dsRows): hasher.update(crypto.encodepoint(pk[index][i])) if kLRki: alpha[i] = kLRki.k rv.II[i] = kLRki.ki hasher.update(crypto.encodepoint(kLRki.L)) hasher.update(crypto.encodepoint(kLRki.R)) else: Hi = crypto.hash_to_point(crypto.encodepoint( pk[index][i])) # originally hashToPoint() alpha[i] = crypto.random_scalar() aGi = crypto.scalarmult_base(alpha[i]) aHPi = crypto.scalarmult(Hi, alpha[i]) rv.II[i] = crypto.scalarmult(Hi, xx[i]) hasher.update(crypto.encodepoint(aGi)) hasher.update(crypto.encodepoint(aHPi)) Ip[i] = crypto.precomp(rv.II[i]) for i in range(dsRows, rows): alpha[i] = crypto.random_scalar() aGi = crypto.scalarmult_base(alpha[i]) hasher.update(crypto.encodepoint(pk[index][i])) hasher.update(crypto.encodepoint(aGi)) c_old = hasher.digest() c_old = crypto.decodeint(c_old) return c_old, Ip, alpha
async def save_exported_outputs(priv_spend, priv_view, transfers): writer = xmrserialize.MemoryReaderWriter() ar = xmrboost.Archive(writer, True) await ar.root() await ar.container(transfers, container_type=ExportedOutputs) trans_bin = writer.get_buffer() buff_dec = bytearray() buff_dec += crypto.encodepoint(crypto.scalarmult_base(priv_spend)) buff_dec += crypto.encodepoint(crypto.scalarmult_base(priv_view)) buff_dec += trans_bin data_enc = chacha.encrypt_xmr(priv_view, buff_dec, authenticated=True) return bytearray(bytes(OUTPUTS_PREFIX) + data_enc)
def extra_poc(self, zero, hs0, B): # --- Extra --- # Extract view key and address reconstruction. # Not needed for the PoC print('\nExtracting view-key...') encr = self.derive_secret_key(zero, 0, self.fake_a) r = self.mlsag_sign_s(zero, encr) rsc = crypto.decodeint(r[0]) asc = crypto.sc_sub(rsc, hs0) a = crypto.encodeint(asc) print(' a: %s' % binascii.hexlify(a).decode('ascii')) A = crypto.scalarmult_base(asc) print(' A: %s' % binascii.hexlify(crypto.encodepoint(A)).decode('ascii')) AA = self.scalarmult(self.fake_a) print(' aG: %s' % binascii.hexlify(AA).decode('ascii')) main_addr = addr.encode_addr( xmr_net.net_version(xmr_net.NetworkTypes.MAINNET), crypto.encodepoint(B), crypto.encodepoint(A), ) test_addr = addr.encode_addr( xmr_net.net_version(xmr_net.NetworkTypes.TESTNET), crypto.encodepoint(B), crypto.encodepoint(A), ) print('Mainnet address: %s' % main_addr.decode('ascii')) print('Testnet address: %s' % test_addr.decode('ascii'))
async def mlsag_prepare(self): Hi = None xin = None options = 0 if len(self.c_msg) > 1: options = 1 Hi = crypto.decodepoint(self._fetch()) if self.options & 0x40: xin = crypto.decodeint(self._fetch()) else: xin = crypto.decodeint(self._fetch_decrypt()) alpha = crypto.random_scalar() self._insert_encrypt(crypto.encodeint(alpha)) # ai.G self._insert(crypto.encodepoint(crypto.scalarmult_base(alpha))) if options: # ai * Hi self._insert(crypto.encodepoint(crypto.scalarmult(Hi, alpha))) # xin * Hi self._insert(crypto.encodepoint(crypto.scalarmult(Hi, xin))) return SW_OK
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 scalar_mult_base_vector(v): """ Creates vector of points from scalars :param v: :return: """ return [crypto.scalarmult_base(a) for a in v]
def gen_r(self): """ Generates a new transaction key pair. :return: """ self.r = crypto.random_scalar() self.r_pub = crypto.scalarmult_base(self.r)
def test_clsag_invalid_Cp(self): res = self.gen_clsag_sig(ring_size=11, index=5) msg, scalars, sc1, sI, sD, ring2, Cp = res with self.assertRaises(ValueError): Cp = crypto.point_add(Cp, crypto.scalarmult_base(crypto.sc_init(1))) mlsag2.verify_clsag(msg, scalars, sc1, sI, sD, ring2, Cp)
def gen_r(self, use_r=None): """ Generates a new transaction key pair. :param use_r: :return: """ self.r = crypto.random_scalar() if use_r is None else use_r self.r_pub = crypto.scalarmult_base(self.r)
def generate_sub_address_keys(view_sec, spend_pub, major, minor): """ Computes generic public sub-address :param view_sec: :param spend_pub: :param major: :param minor: :return: spend public, view public """ if major == 0 and minor == 0: # special case, Monero-defined return spend_pub, crypto.scalarmult_base(view_sec) m = get_subaddress_secret_key(view_sec, major=major, minor=minor) M = crypto.scalarmult_base(m) D = crypto.point_add(spend_pub, M) C = crypto.scalarmult(D, view_sec) return D, C
def generate_keys(recovery_key): """ Wallet gen. :param recovery_key: :return: """ pub = crypto.scalarmult_base(recovery_key) return recovery_key, pub
def verify_tx_key(self, tx_priv, tx_pub, all_creds_subs): if crypto.point_eq(tx_pub, crypto.scalarmult_base(tx_priv)): return True for cred_idx, subs in enumerate(all_creds_subs): for ckey in subs: if crypto.point_eq(tx_pub, crypto.scalarmult(ckey, tx_priv)): return True return False
def derive_subaddress_public_key(out_key, derivation, output_index): """ out_key - H_s(derivation || varint(output_index))G """ crypto.check_ed25519point(out_key) scalar = crypto.derivation_to_scalar(derivation, output_index) point2 = crypto.scalarmult_base(scalar) point4 = crypto.point_sub(out_key, point2) return point4
def generate_keys(recovery_key): """ Wallet gen. :param recovery_key: :return: """ sec = crypto.sc_reduce32(recovery_key) pub = crypto.scalarmult_base(sec) return sec, pub
async def open_tx(self): self.ctx_amount = sha256() self.account = self._fetch_u32() self.r = crypto.random_scalar() self.R = crypto.scalarmult_base(self.r) self._insert(crypto.encodepoint(self.R)) self._insert_encrypt(crypto.encodeint(self.r)) return SW_OK
def new_wallet(cls, priv_view_key, priv_spend_key, network_type=NetworkTypes.MAINNET): pub_view_key = crypto.scalarmult_base(priv_view_key) pub_spend_key = crypto.scalarmult_base(priv_spend_key) addr = encode_addr( net_version(network_type), crypto.encodepoint(pub_spend_key), crypto.encodepoint(pub_view_key), ) return cls( view_key_private=priv_view_key, spend_key_private=priv_spend_key, view_key_public=pub_view_key, spend_key_public=pub_spend_key, address=addr, network_type=network_type, )
def test_range_proof2_back(self): proof = ring_ct.prove_range(123456789, backend_impl=True) res = ring_ct.ver_range(proof[0], proof[2]) self.assertTrue(res) res = ring_ct.ver_range( crypto.point_add(proof[0], crypto.scalarmult_base(crypto.sc_init(4))), proof[2], ) self.assertFalse(res)
async def primary_change_address(self, key, account): D, C = monero.generate_sub_address_keys( key.view_key_private, crypto.scalarmult_base(key.spend_key_private), account, 0, ) return misc.StdObj( view_public_key=crypto.encodepoint(C), spend_public_key=crypto.encodepoint(D), )
def key_image_vector(x): """ Takes as input a keyvector, returns the keyimage-vector TODO: use crypto for generating key images :param x: :return: """ return [ crypto.scalarmult(crypto.hash_to_point(crypto.scalarmult_base(xx)), xx) for xx in x ]
def get_subaddress_spend_public_key(view_private, spend_public, major, minor): """ Generates subaddress spend public key D_{major, minor} """ if major == 0 and minor == 0: return spend_public m = get_subaddress_secret_key(view_private, major=major, minor=minor) M = crypto.scalarmult_base(m) D = crypto.point_add(spend_public, M) return D
def test_range_proof(self): proof = ring_ct.prove_range(0) res = ring_ct.ver_range(proof[0], proof[2]) self.assertTrue(res) self.assertTrue( crypto.point_eq( proof[0], crypto.point_add(crypto.scalarmult_base(proof[1]), crypto.scalarmult_h(0)), )) proof = ring_ct.prove_range(0, mem_opt=False) res = ring_ct.ver_range(proof[0], proof[2]) self.assertTrue(res) self.assertTrue( crypto.point_eq( proof[0], crypto.point_add(crypto.scalarmult_base(proof[1]), crypto.scalarmult_h(0)), ))
def pttest2(N=10000): a = crypto.random_scalar() a8 = crypto.sc_mul(a, crypto.sc_init(8)) for i in range(1, N): ca = crypto.sc_mul(a8, crypto.sc_init(i)) A8 = crypto.scalarmult_base(ca) A8bin = crypto.encodepoint(A8) red = crypto.encodeint(crypto.decodeint(A8bin)) if red == A8bin: return i, red return None