def generate_key_image_ex( creds, subaddresses, out_key, tx_public_key, additional_tx_public_keys, real_output_index, ): recv_derivation = crypto.generate_key_derivation( tx_public_key, creds.view_key_private ) additional_recv_derivations = [] for add_pub_key in additional_tx_public_keys: additional_recv_derivations.append( crypto.generate_key_derivation(add_pub_key, creds.view_key_private) ) subaddr_recv_info = monero.is_out_to_acc_precomp( subaddresses, out_key, recv_derivation, additional_recv_derivations, real_output_index, ) if subaddr_recv_info is None: raise XmrNoSuchAddressException() xi, ki = generate_key_image_helper_precomp( creds, out_key, subaddr_recv_info[1], real_output_index, subaddr_recv_info[0] ) return xi, ki, recv_derivation, subaddr_recv_info
def generate_key_derivation(pub_key, priv_key): """ Generates derivation priv_key * pub_key. Simple ECDH. :param pub_key: :param priv_key: :return: """ return crypto.generate_key_derivation(pub_key, priv_key)
def generate_key_image_helper( creds, subaddresses, out_key, tx_public_key, additional_tx_public_keys, real_output_index, ): """ Generates UTXO spending key and key image. Supports subaddresses. :param creds: :param subaddresses: :param out_key: real output (from input RCT) destination key :param tx_public_key: R, transaction public key :param additional_tx_public_keys: Additional Rs, for subaddress destinations :param real_output_index: index of the real output in the RCT :return: """ recv_derivation = crypto.generate_key_derivation(tx_public_key, creds.view_key_private) additional_recv_derivations = [] for add_pub_key in additional_tx_public_keys: additional_recv_derivations.append( crypto.generate_key_derivation(add_pub_key, creds.view_key_private)) subaddr_recv_info = is_out_to_acc_precomp( subaddresses, out_key, recv_derivation, additional_recv_derivations, real_output_index, ) if subaddr_recv_info is None: raise XmrNoSuchAddressException() xi, ki = generate_key_image_helper_precomp(creds, out_key, subaddr_recv_info[1], real_output_index, subaddr_recv_info[0]) return xi, ki, recv_derivation
async def stealth(self): pub = crypto.decodepoint(self._fetch()) sec = self._fetch_decrypt_key() pay_id = self._fetch(8) drv = crypto.generate_key_derivation(pub, sec) drv += b"\x8d" sec = crypto.keccak_hash(drv) for i in range(8): pay_id[i] ^= sec[i] self._insert(pay_id) return SW_OK
def test_generate_key_derivation(self): key_pub = crypto.decodepoint( unhexlify( b"7739c95d3298e2f87362dba9e0e0b3980a692ae8e2f16796b0e382098cd6bd83" )) key_priv = crypto.decodeint( unhexlify( b"3482fb9735ef879fcae5ec7721b5d3646e155c4fb58d6cc11c732c9c9b76620a" )) deriv_exp = unhexlify( b"fa188a45a0e4daccc0e6d4f6f6858fd46392104be74183ec0047e7e9f4eaf739" ) self.assertEqual( deriv_exp, crypto.encodepoint( crypto.generate_key_derivation(key_pub, key_priv)), )
def encrypt_payment_id(payment_id, public_key, secret_key): """ Encrypts payment_id hex. Used in the transaction extra. Only recipient is able to decrypt. :param payment_id: :param public_key: :param secret_key: :return: """ derivation_p = crypto.generate_key_derivation(public_key, secret_key) derivation = bytearray(33) derivation = crypto.encodepoint_into(derivation_p, derivation) derivation[32] = 0x8b hash = crypto.cn_fast_hash(derivation) pm_copy = bytearray(payment_id) for i in range(8): pm_copy[i] ^= hash[i] return pm_copy
def rekey_input(self, inp, keys, subs=None, new_keys=None, new_subs=None, mixin_change=None): subs = subs if subs else {} real_out_key = inp.outputs[inp.real_output][1] out_key = crypto.decodepoint(real_out_key.dest) tx_key = crypto.decodepoint(inp.real_out_tx_key) additional_keys = [ crypto.decodepoint(x) for x in inp.real_out_additional_tx_keys ] logger.debug("Current out key: %s" % binascii.hexlify(real_out_key.dest)) secs = monero.generate_key_image_helper( keys, subs, out_key, tx_key, additional_keys, inp.real_output_in_tx_index ) xi, ki, di = secs need_additional = additional_keys is not None and len(additional_keys) > 0 is_dst_sub = self.dest_sub_major != 0 and ( self.args.minors[0] != 0 or len(self.args.minors) > 1 ) logger.debug( "Is dst sub: %s, need additional: %s" % (is_dst_sub, need_additional) ) if is_dst_sub and self.add_additionals: need_additional = True if is_dst_sub: rand_minor = random.choice(self.args.minors) m = monero.get_subaddress_secret_key( new_keys.view_key_private, major=self.dest_sub_major, minor=rand_minor ) M = crypto.scalarmult_base(m) d = crypto.sc_add(m, new_keys.spend_key_private) D = crypto.point_add(new_keys.spend_key_public, M) C = crypto.scalarmult(D, new_keys.view_key_private) if not need_additional and not is_dst_sub: # real_out_key.dst = Hs(R*new_a || idx)G + newB r = crypto.random_scalar() tx_key = crypto.scalarmult_base(r) new_deriv = crypto.generate_key_derivation(new_keys.view_key_public, r) new_out_pr = crypto.derive_secret_key( new_deriv, inp.real_output_in_tx_index, new_keys.spend_key_private ) new_out = crypto.scalarmult_base(new_out_pr) real_out_key.dest = crypto.encodepoint(new_out) elif not need_additional and is_dst_sub: # real_out_key.dst = Hs(r*C || idx)G + newB, R=rD r = crypto.random_scalar() tx_key = crypto.scalarmult(D, r) new_deriv = crypto.generate_key_derivation(C, r) new_out_pr = crypto.derive_secret_key( new_deriv, inp.real_output_in_tx_index, d ) new_out = crypto.scalarmult_base(new_out_pr) real_out_key.dest = crypto.encodepoint(new_out) else: r = crypto.random_scalar() tx_key = crypto.scalarmult_base(r) gen_additionals = min(2, inp.real_output_in_tx_index + 1) if additional_keys is None or len(additional_keys) < gen_additionals: additional_keys = [ crypto.scalarmult_base(crypto.random_scalar()) for _ in range(gen_additionals) ] ri = crypto.random_scalar() if is_dst_sub: add_tx = crypto.scalarmult(D, ri) new_deriv = crypto.generate_key_derivation(C, ri) new_out_pr = crypto.derive_secret_key( new_deriv, inp.real_output_in_tx_index, d ) new_out = crypto.scalarmult_base(new_out_pr) if not crypto.point_eq( new_out, crypto.derive_public_key(new_deriv, inp.real_output_in_tx_index, D), ): raise ValueError("Invalid txout computation") else: add_tx = crypto.scalarmult_base(ri) new_deriv = crypto.generate_key_derivation(new_keys.view_key_public, r) new_out_pr = crypto.derive_secret_key( new_deriv, inp.real_output_in_tx_index, new_keys.spend_key_private ) new_out = crypto.scalarmult_base(new_out_pr) additional_keys[inp.real_output_in_tx_index] = add_tx real_out_key.dest = crypto.encodepoint(new_out) # Increasing the size of the mixin if mixin_change and len(inp.outputs) < mixin_change: for i in range(mixin_change - len(inp.outputs)): inp.outputs.append((0, CtKey( mask=crypto.encodepoint(self.random_pub()), dest=crypto.encodepoint(self.random_pub())))) if additional_keys: additional_keys.append(self.random_pub()) inp.real_out_tx_key = crypto.encodepoint(tx_key) inp.real_out_additional_tx_keys = [ crypto.encodepoint(x) for x in additional_keys ] logger.debug("New pub: %s" % binascii.hexlify(real_out_key.dest)) # Self-check self.check_input(inp, new_keys, new_subs) return inp
async def receive(self, tx, all_creds, con_data=None, exp_payment_id=None): """ Test transaction receive with known view/spend keys of destinations. :param tx: :param all_creds: :param con_data: :param exp_payment_id: :return: """ # Unserialize the transaction tx_obj = xmrtypes.Transaction() reader = xmrserialize.MemoryReaderWriter(bytearray(tx)) ar1 = xmrserialize.Archive(reader, False) await ar1.message(tx_obj, msg_type=xmrtypes.Transaction) extras = await monero.parse_extra_fields(tx_obj.extra) tx_pub = monero.find_tx_extra_field_by_type( extras, xmrtypes.TxExtraPubKey ).pub_key additional_pub_keys = monero.find_tx_extra_field_by_type( extras, xmrtypes.TxExtraAdditionalPubKeys ) num_outs = len(tx_obj.vout) num_received = 0 # Try to receive tsx outputs with each account. tx_money_got_in_outs = collections.defaultdict(lambda: 0) outs = [] change_idx = get_change_addr_idx(con_data.tsx_data.outputs, con_data.tsx_data.change_dts) for idx, creds in enumerate(all_creds): wallet_subs = {} for account in range(0, 5): monero.compute_subaddresses(creds, account, range(25), wallet_subs) derivation = crypto.generate_key_derivation( crypto.decodepoint(tx_pub), creds.view_key_private ) additional_derivations = [] if additional_pub_keys and additional_pub_keys.data: for x in additional_pub_keys.data: additional_derivations.append( crypto.generate_key_derivation( crypto.decodepoint(x), creds.view_key_private ) ) for ti, to in enumerate(tx_obj.vout): tx_scan_info = monero.check_acc_out_precomp( to, wallet_subs, derivation, additional_derivations, ti ) if not tx_scan_info.received: continue num_received += 1 tx_scan_info = monero.scan_output( creds, tx_obj, ti, tx_scan_info, tx_money_got_in_outs, outs, False ) # Check spending private key correctness self.assertTrue( crypto.point_eq( crypto.decodepoint(tx_obj.rct_signatures.outPk[ti].mask), crypto.gen_c(tx_scan_info.mask, tx_scan_info.amount), ) ) self.assertTrue( crypto.point_eq( crypto.decodepoint(tx_obj.vout[ti].target.key), crypto.scalarmult_base(tx_scan_info.in_ephemeral), ) ) if exp_payment_id is not None: payment_id = None # Not checking payment id for change transaction if exp_payment_id[0] == 1 and change_idx is not None and ti == change_idx: continue payment_id_type = None extra_nonce = monero.find_tx_extra_field_by_type(extras, xmrtypes.TxExtraNonce) if extra_nonce and monero.has_encrypted_payment_id(extra_nonce.nonce): payment_id_type = 1 payment_id = monero.get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce) payment_id = monero.encrypt_payment_id(payment_id, crypto.decodepoint(tx_pub), creds.view_key_private) elif extra_nonce and monero.has_payment_id(extra_nonce.nonce): payment_id_type = 0 payment_id = monero.get_payment_id_from_tx_extra_nonce(extra_nonce.nonce) self.assertEqual(payment_id_type, exp_payment_id[0]) self.assertEqual(payment_id, exp_payment_id[1]) # All outputs have to be successfully received self.assertEqual(num_outs, num_received)
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
async def generate_key_derivation(self): pub = crypto.decodepoint(self._fetch()) sec = self._fetch_decrypt_key() der = crypto.generate_key_derivation(pub, sec) self._insert_encrypt(crypto.encodepoint(der)) return SW_OK
def test_generate_key_derivation(self): key_pub = crypto.decodepoint( bytes( [ 0x77, 0x39, 0xc9, 0x5d, 0x32, 0x98, 0xe2, 0xf8, 0x73, 0x62, 0xdb, 0xa9, 0xe0, 0xe0, 0xb3, 0x98, 0x0a, 0x69, 0x2a, 0xe8, 0xe2, 0xf1, 0x67, 0x96, 0xb0, 0xe3, 0x82, 0x09, 0x8c, 0xd6, 0xbd, 0x83, ] ) ) key_priv = crypto.decodeint( bytes( [ 0x34, 0x82, 0xfb, 0x97, 0x35, 0xef, 0x87, 0x9f, 0xca, 0xe5, 0xec, 0x77, 0x21, 0xb5, 0xd3, 0x64, 0x6e, 0x15, 0x5c, 0x4f, 0xb5, 0x8d, 0x6c, 0xc1, 0x1c, 0x73, 0x2c, 0x9c, 0x9b, 0x76, 0x62, 0x0a, ] ) ) deriv_exp = bytes( [ 0xfa, 0x18, 0x8a, 0x45, 0xa0, 0xe4, 0xda, 0xcc, 0xc0, 0xe6, 0xd4, 0xf6, 0xf6, 0x85, 0x8f, 0xd4, 0x63, 0x92, 0x10, 0x4b, 0xe7, 0x41, 0x83, 0xec, 0x00, 0x47, 0xe7, 0xe9, 0xf4, 0xea, 0xf7, 0x39, ] ) self.assertEqual( deriv_exp, crypto.encodepoint(crypto.generate_key_derivation(key_pub, key_priv)), )