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: """ image_unp = crypto.ge_frombytes_vartime(image) image_pre = crypto.ge_dsm_precomp(image_unp) buff = b"" + prefix_hash 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]) buff += crypto.encodepoint(tmp2) tmp3 = crypto.hash_to_ec(crypto.encodepoint(pubs[i])) tmp2 = crypto.ge_double_scalarmult_precomp_vartime( sig[i][1], tmp3, sig[i][0], image_pre) buff += crypto.encodepoint(tmp2) 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
async def subadress_outs(self, tx, change_idx=None): change_idx = await self.find_change_idx(tx, change_idx) for ix, ox in enumerate(tx.splitted_dsts): if change_idx is not None and ix == change_idx: continue # if ox.is_subaddress: # continue spkey = bytes(ox.addr.m_spend_public_key) orig_keys = self.dest_keys orig_idx = self.dest_sub_major, 0 if spkey in self.cur_subs: orig_keys = self.cur_keys orig_idx = self.cur_subs[spkey] logger.debug('Out %d, in cur subs, idx: %s' % (ix, orig_idx)) elif spkey in self.dest_subs: orig_idx = self.dest_subs[spkey] logger.debug('Out %d, in dst subs, idx: %s' % (ix, orig_idx)) naddr = monero.generate_sub_address_keys(orig_keys.view_key_private, orig_keys.spend_key_public, orig_idx[0], ix+1) ox.addr.m_spend_public_key = crypto.encodepoint(naddr[0]) ox.addr.m_view_public_key = crypto.encodepoint(naddr[1]) ox.is_subaddress = True return change_idx
def is_out_to_acc_precomp( subaddresses, out_key, derivation, additional_derivations, output_index ): """ Searches subaddresses for the computed subaddress_spendkey. If found, returns (major, minor), derivation. :param subaddresses: :param out_key: :param derivation: :param additional_derivations: :param output_index: :return: """ subaddress_spendkey = crypto.encodepoint( derive_subaddress_public_key(out_key, derivation, output_index) ) if subaddress_spendkey in subaddresses: return subaddresses[subaddress_spendkey], derivation if additional_derivations and len(additional_derivations) > 0: if output_index >= len(additional_derivations): raise ValueError("Wrong number of additional derivations") subaddress_spendkey = derive_subaddress_public_key( out_key, additional_derivations[output_index], output_index ) subaddress_spendkey = crypto.encodepoint(subaddress_spendkey) if subaddress_spendkey in subaddresses: return ( subaddresses[subaddress_spendkey], additional_derivations[output_index], ) return None
def do_keys(self, line): print( "Spend key priv: 0x%s" % binascii.hexlify(crypto.encodeint(self.creds.spend_key_private)).decode( "ascii" ) ) print( "View key priv: 0x%s" % binascii.hexlify(crypto.encodeint(self.creds.view_key_private)).decode( "ascii" ) ) print("") print( "Spend key pub: 0x%s" % binascii.hexlify(crypto.encodepoint(self.creds.spend_key_public)).decode( "ascii" ) ) print( "View key pub: 0x%s" % binascii.hexlify(crypto.encodepoint(self.creds.view_key_public)).decode( "ascii" ) )
async def get_subaddress(self): index = self._fetch(8) major, minor = self._idx_parse(index) D, C = monero.generate_sub_address_keys(self.a, self.B, major, minor) self._insert(crypto.encodepoint(C)) self._insert(crypto.encodepoint(D)) return SW_OK
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 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 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'))
def compute_subaddresses(creds, account, indices, subaddresses=None): """ Computes subaddress public spend key for receiving transactions. :param creds: credentials :param account: major index :param indices: array of minor indices :param subaddresses: subaddress dict. optional. :return: """ if subaddresses is None: subaddresses = {} for idx in indices: if account == 0 and idx == 0: subaddresses[crypto.encodepoint(creds.spend_key_public)] = (0, 0) continue pub = get_subaddress_spend_public_key(creds.view_key_private, creds.spend_key_public, major=account, minor=idx) pub = crypto.encodepoint(pub) subaddresses[pub] = (account, idx) return subaddresses
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 build_address_encode(spend_key, view_key): """ Builds address compatible object from object keys :param spend_key: :param view_key: :return: """ return PubAddress(crypto.encodepoint(spend_key), crypto.encodepoint(view_key))
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 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), )
async def test_ki_sync(self): ki_data = self.get_data_file("ki_sync_01.txt") ki_loaded = await wallet.load_exported_outputs( self.creds.view_key_private, ki_data) self.assertEqual(ki_loaded.m_spend_public_key, crypto.encodepoint(self.creds.spend_key_public)) self.assertEqual(ki_loaded.m_view_public_key, crypto.encodepoint(self.creds.view_key_public)) res = await self.agent.import_outputs(ki_loaded.tds) await self.verify_ki_export(res, ki_loaded)
def test_encoding(self): point = unhexlify( b"2486224797d05cae3cba4be043be2db0df381f3f19cfa113f86ab38e3d8d2bd0" ) self.assertEqual(point, crypto.encodepoint(crypto.decodepoint(point))) self.assertTrue( crypto.point_eq( crypto.decodepoint(point), crypto.decodepoint( crypto.encodepoint(crypto.decodepoint(point))), ))
def test_h_pow(self): hp = crypto.gen_Hpow(10) self.assertEqual(crypto.encodepoint(hp[0]), crypto.encodepoint(crypto.xmr_H())) for i in range(1, 10): crypto.check_ed25519point(hp[i]) self.assertEqual( crypto.encodepoint(hp[i]), crypto.encodepoint( crypto.scalarmult(crypto.xmr_H(), crypto.sc_init(2**i))), )
async def get_key(self): if self.c_p1 == 1: self._insert(crypto.encodepoint(self.A)) self._insert(crypto.encodepoint(self.B)) self._insert(self.creds.address) elif self.c_p1 == 2: # TODO: await layout.require_confirm_watchkey(self.ctx) self._insert(crypto.encodeint(self.a)) else: return SW_WRONG_P1P2 return SW_OK
def gen_sub_address(sd, net_type, major_max, minor_max): for major in range(major_max): for minor in range(minor_max): if major == 0 and minor == 0: continue D, C = monero.generate_sub_address_keys(sd.view_sec, sd.spend_pub, major, minor) addr = encode_addr( net_version(net_type, is_subaddr=True), crypto.encodepoint(D), crypto.encodepoint(C), ) yield major, minor, addr
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 poc(self): print('[+] PoC Ledger-app-Monero 1.4.2 spend key extraction') self.reset() self.set_mode() self.open_tx() # 1. call `monero_apdu_generate_keypair()`, obtain `{enc(x), hmac(enx(x))}`, where `x` is unknown. _, sec = self.gen_kp() print(' 1. rsec: %s' % self.fmtkey(sec).decode('ascii')) # 2. call `sc_sub(enc(x), hmac(enc(x)), enc(x), hmac(enc(x)))`, obtain `{enc(0), hmac(enc(0))}` zero = self.sc_sub(sec, sec) print(' 2. zero: %s' % self.fmtkey(zero).decode('ascii')) # 3. call `monero_apdu_derive_secret_key( # enc(0), hmac(enc(0)), 0, C_FAKE_SEC_SPEND_KEY, hmac(C_FAKE_SEC_SPEND_KEY))`, # obtain `{enc(r), hmac(enc(r))}` encr = self.derive_secret_key(zero, 0, self.fake_b) print(' 3. encr: %s' % self.fmtkey(encr).decode('ascii')) # 4. call `monero_apdu_mlsag_sign(enc(0), hmac(enc(0)), enc(r), hmac(enc(r)))`, obtain `r` r = self.mlsag_sign_s(zero, encr) print(' 4. r: %s' % self.fmtkey(r).decode('ascii')) # 5. compute b: `b = r - H_s(00....00 || varint(0))` hs0 = crypto.hash_to_scalar(bytearray(33)) rsc = crypto.decodeint(r[0]) bsc = crypto.sc_sub(rsc, hs0) 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' % binascii.hexlify(BB).decode('ascii')) if BB == crypto.encodepoint(B): print('[+] PoC successful') else: print('[-] PoC not working') if self.lite: return self.extra_poc(zero, hs0, B)
def export_key_image( creds, subaddresses, pkey, tx_pub_key, additional_tx_pub_keys, out_idx, test=True, verify=True, ): """ Generates key image for the TXO + signature for the key image """ r = monero.generate_key_image_helper(creds, subaddresses, pkey, tx_pub_key, additional_tx_pub_keys, out_idx) xi, ki, recv_derivation = r[:3] phash = crypto.encodepoint(ki) sig = generate_ring_signature(phash, ki, [pkey], xi, 0, test) if verify: if check_ring_singature(phash, ki, [pkey], sig) != 1: raise ValueError("Signature error") return ki, sig
async def _out_proc_range_proof(self, rsig): if self._is_req_bulletproof(): rsig.V = [] batch_size = self.ct.rsig_batches[self.ct.cur_batch_idx] for i in range(batch_size): commitment = self.ct.tx_out_pk[1 + self.ct.cur_output_idx - batch_size + i].mask commitment = crypto.scalarmult(crypto.decodepoint(commitment), crypto.sc_inv_eight()) rsig.V.append(crypto.encodepoint(commitment)) self.ct.tx_out_rsigs.append(rsig) # Rsig verification try: if not ring_ct.ver_range( C=crypto.decodepoint(self.ct.tx_out_pk[-1].mask), rsig=rsig, use_bulletproof=self._is_req_bulletproof(), ): logger.warning("Rsing not valid") except Exception as e: logger.error("Exception rsig: %s" % e) traceback.print_exc()
def check_ring_singature(prefix_hash, image, pubs, sig): """ Checks ring signature generated with generate_ring_signature """ 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 test_clsag_invalid_P(self): res = self.gen_clsag_sig(ring_size=11, index=5) msg, scalars, sc1, sI, sD, ring2, Cp = res with self.assertRaises(ValueError): ring2[5].commitment = crypto.encodepoint( crypto.point_mul8(crypto.decodepoint(ring2[5].dest))) mlsag2.verify_clsag(msg, scalars, sc1, sI, sD, ring2, Cp)
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)
async def sync(self, ctx, tds: MoneroKeyImageSyncStepRequest): self.ctx = ctx if self.blocked: raise ValueError("Blocked") if len(tds.tdis) == 0: raise ValueError("Empty") resp = [] for td in tds.tdis: self.c_idx += 1 if self.c_idx >= self.num: raise ValueError("Too many outputs") hash = key_image.compute_hash(td) self.hasher.update(hash) ki, sig = await key_image.export_key_image(self.creds, self.subaddresses, td) buff = crypto.encodepoint(ki) buff += crypto.encodeint(sig[0][0]) buff += crypto.encodeint(sig[0][1]) nonce, ciph, tag = chacha_poly.encrypt(self.enc_key, buff) eki = MoneroExportedKeyImage(iv=nonce, tag=tag, blob=ciph) resp.append(eki) return MoneroKeyImageSyncStepAck(kis=resp)
async def check_existing_wallet_file(self, key_file): """ Checks existing wallet file correctness :param key_file: :return: """ wl = await wallet.load_keys_file(key_file, self.wallet_password) addr = wl["key_data"]["m_keys"]["m_account_address"] spend_pub = addr["m_spend_public_key"] view_pub = addr["m_view_public_key"] match = spend_pub == crypto.encodepoint( self.pub_spend) and view_pub == crypto.encodepoint(self.pub_view) net_ver = monero.net_version(self.network_type, False) addr = monero.encode_addr(net_ver, spend_pub, view_pub) return addr, match
async def derive_public_key(self): derivation = crypto.decodepoint(self._fetch_decrypt()) output_index = self._fetch_u32() pub = crypto.decodepoint(self._fetch()) drvpub = crypto.derive_public_key(derivation, output_index, pub) self._insert(crypto.encodepoint(drvpub)) return SW_OK
async def get_subaddress_spend_public_key(self): index = self._fetch(8) major, minor = self._idx_parse(index) D = monero.get_subaddress_spend_public_key(self.a, self.B, major, minor) self._insert(crypto.encodepoint(D)) return SW_OK