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 set_input(self, src_entr): """ :param src_entr: :type src_entr: xmrtypes.TxSourceEntry :return: """ self.inp_idx += 1 if src_entr.real_output >= len(src_entr.outputs): raise ValueError( "real_output index %s bigger than output_keys.size()" % (src_entr.real_output, len(src_entr.outputs)) ) self.summary_inputs_money += src_entr.amount out_key = crypto.decodepoint(src_entr.outputs[src_entr.real_output][1].dest) tx_key = crypto.decodepoint(src_entr.real_out_tx_key) additional_keys = [ crypto.decodepoint(x) for x in src_entr.real_out_additional_tx_keys ] secs = monero.generate_key_image_helper( self.trezor.creds, self.subaddresses, out_key, tx_key, additional_keys, src_entr.real_output_in_tx_index, ) xi, ki, di = secs self.input_secrets.append((xi,)) self.input_rcts.append(src_entr.rct) # Construct tx.vin vini = xmrtypes.TxinToKey( amount=src_entr.amount, k_image=crypto.encodepoint(ki) ) vini.key_offsets = [x[0] for x in src_entr.outputs] vini.key_offsets = monero.absolute_output_offsets_to_relative(vini.key_offsets) self.tx.vin.append(vini) # HMAC(T_in,i || vin_i) kwriter = monero.get_keccak_writer() ar = xmrserialize.Archive(kwriter, True) await ar.message(src_entr, xmrtypes.TxSourceEntry) await ar.message(vini, xmrtypes.TxinToKey) hmac_key_vini = crypto.keccak_hash( self.key_hmac + b"txin" + xmrserialize.dump_uvarint_b(self.inp_idx) ) hmac_vini = crypto.compute_hmac(hmac_key_vini, kwriter.get_digest()) return vini, hmac_vini
def check_input(self, inp, new_keys, new_subs): real_out_key = inp.outputs[inp.real_output][1] tx_key = crypto.decodepoint(inp.real_out_tx_key) additional_keys = [ crypto.decodepoint(x) for x in inp.real_out_additional_tx_keys ] _ = monero.generate_key_image_helper( new_keys, new_subs, crypto.decodepoint(real_out_key.dest), tx_key, additional_keys, inp.real_output_in_tx_index, )
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 set_input(self, src_entr): """ Sets UTXO one by one. Computes spending secret key, key image. tx.vin[i] + HMAC, Pedersen commitment on amount. If number of inputs is small, in-memory mode is used = alpha, pseudo_outs are kept in the Trezor. Otherwise pseudo_outs are offloaded with HMAC, alpha is offloaded encrypted under Chacha20Poly1305() with key derived for exactly this purpose. :param src_entr: :type src_entr: monero_glue.xmr.serialize_messages.tx_construct.TxSourceEntry :return: """ from monero_glue.messages.MoneroTransactionSetInputAck import ( MoneroTransactionSetInputAck ) from monero_glue.xmr.enc import chacha_poly from monero_glue.xmr.sub import tsx_helper from monero_serialize.xmrtypes import TxinToKey self.state.input() self.inp_idx += 1 await self.trezor.iface.transaction_step( self.STEP_INP, self.inp_idx, self.num_inputs() ) if self.inp_idx >= self.num_inputs(): raise ValueError("Too many inputs") if src_entr.real_output >= len(src_entr.outputs): raise ValueError( "real_output index %s bigger than output_keys.size() %s" % (src_entr.real_output, len(src_entr.outputs)) ) self.summary_inputs_money += src_entr.amount # Secrets derivation out_key = crypto.decodepoint(src_entr.outputs[src_entr.real_output][1].dest) tx_key = crypto.decodepoint(src_entr.real_out_tx_key) additional_keys = [ crypto.decodepoint(x) for x in src_entr.real_out_additional_tx_keys ] secs = monero.generate_key_image_helper( self.creds, self.subaddresses, out_key, tx_key, additional_keys, src_entr.real_output_in_tx_index, ) xi, ki, di = secs # Construct tx.vin ki_real = src_entr.multisig_kLRki.ki if self.multi_sig else ki vini = TxinToKey(amount=src_entr.amount, k_image=crypto.encodepoint(ki_real)) vini.key_offsets = [x[0] for x in src_entr.outputs] vini.key_offsets = tsx_helper.absolute_output_offsets_to_relative( vini.key_offsets ) if src_entr.rct: vini.amount = 0 if self.in_memory(): self.tx.vin.append(vini) # HMAC(T_in,i || vin_i) hmac_vini = await self.gen_hmac_vini(src_entr, vini, self.inp_idx) # PseudoOuts commitment, alphas stored to state pseudo_out = None pseudo_out_hmac = None alpha_enc = None spend_enc = None if self.use_simple_rct: alpha, pseudo_out = await self.commitment(src_entr.amount) pseudo_out = crypto.encodepoint(pseudo_out) # In full version the alpha is encrypted and passed back for storage if self.in_memory(): self.input_alphas.append(alpha) self.input_pseudo_outs.append(pseudo_out) else: pseudo_out_hmac = crypto.compute_hmac( self.hmac_key_txin_comm(self.inp_idx), pseudo_out ) alpha_enc = chacha_poly.encrypt_pack( self.enc_key_txin_alpha(self.inp_idx), crypto.encodeint(alpha) ) if self.many_inputs(): spend_enc = chacha_poly.encrypt_pack( self.enc_key_spend(self.inp_idx), crypto.encodeint(xi) ) else: self.input_secrets.append(xi) # All inputs done? if self.inp_idx + 1 == self.num_inputs(): await self.tsx_inputs_done() return MoneroTransactionSetInputAck( vini=await misc.dump_msg(vini, preallocate=64), vini_hmac=hmac_vini, pseudo_out=pseudo_out, pseudo_out_hmac=pseudo_out_hmac, alpha_enc=alpha_enc, spend_enc=spend_enc, )