async def final_msg(state: State): tx_key, salt, rand_mult = _compute_tx_key(state.creds.spend_key_private, state.tx_prefix_hash) key_buff = crypto.encodeint(state.tx_priv) + b"".join( [crypto.encodeint(x) for x in state.additional_tx_private_keys]) tx_enc_keys = chacha_poly.encrypt_pack(tx_key, key_buff) return MoneroTransactionFinalAck(cout_key=None, salt=salt, rand_mult=rand_mult, tx_enc_keys=tx_enc_keys)
async def get_tx_keys(ctx, msg: MoneroGetTxKeyRequest, keychain): await paths.validate_path( ctx, misc.validate_full_path, keychain, msg.address_n, CURVE ) do_deriv = msg.reason == _GET_TX_KEY_REASON_TX_DERIVATION await confirms.require_confirm_tx_key(ctx, export_key=not do_deriv) creds = misc.get_creds(keychain, msg.address_n, msg.network_type) tx_enc_key = misc.compute_tx_key( creds.spend_key_private, msg.tx_prefix_hash, msg.salt1, crypto.decodeint(msg.salt2), ) # the plain_buff first stores the tx_priv_keys as decrypted here # and then is used to store the derivations if applicable plain_buff = chacha_poly.decrypt_pack(tx_enc_key, msg.tx_enc_keys) utils.ensure(len(plain_buff) % 32 == 0, "Tx key buffer has invalid size") del msg.tx_enc_keys # If return only derivations do tx_priv * view_pub if do_deriv: plain_buff = bytearray(plain_buff) view_pub = crypto.decodepoint(msg.view_public_key) tx_priv = crypto.new_scalar() derivation = crypto.new_point() n_keys = len(plain_buff) // 32 for c in range(n_keys): crypto.decodeint_into(tx_priv, plain_buff, 32 * c) crypto.scalarmult_into(derivation, view_pub, tx_priv) crypto.encodepoint_into(plain_buff, derivation, 32 * c) # Encrypt by view-key based password. tx_enc_key_host, salt = misc.compute_enc_key_host( creds.view_key_private, msg.tx_prefix_hash ) res = chacha_poly.encrypt_pack(tx_enc_key_host, plain_buff) res_msg = MoneroGetTxKeyAck(salt=salt) if do_deriv: res_msg.tx_derivations = res return res_msg res_msg.tx_keys = res return res_msg
def final_msg(state: State) -> MoneroTransactionFinalAck: if state.last_step != state.STEP_SIGN: raise ValueError("Invalid state transition") if state.current_input_index != state.input_count - 1: raise ValueError("Invalid input count") tx_key, salt, rand_mult = _compute_tx_key(state.creds.spend_key_private, state.tx_prefix_hash) key_buff = crypto.encodeint(state.tx_priv) + b"".join( [crypto.encodeint(x) for x in state.additional_tx_private_keys]) tx_enc_keys = chacha_poly.encrypt_pack(tx_key, key_buff) state.last_step = None return MoneroTransactionFinalAck( cout_key=None, salt=salt, rand_mult=rand_mult, tx_enc_keys=tx_enc_keys, opening_key=state.opening_key, )
async def _refresh_step(s: LiveRefreshState, ctx, msg: MoneroLiveRefreshStepRequest): buff = bytearray(32 * 3) buff_mv = memoryview(buff) await confirms.live_refresh_step(ctx, s.current_output) s.current_output += 1 if __debug__: log.debug(__name__, "refresh, step i: %d", s.current_output) # Compute spending secret key and the key image # spend_priv = Hs(recv_deriv || real_out_idx) + spend_key_private # If subaddr: # spend_priv += Hs("SubAddr" || view_key_private || major || minor) # out_key = spend_priv * G, KI: spend_priv * Hp(out_key) out_key = crypto.decodepoint(msg.out_key) recv_deriv = crypto.decodepoint(msg.recv_deriv) received_index = msg.sub_addr_major, msg.sub_addr_minor spend_priv, ki = monero.generate_tx_spend_and_key_image( s.creds, out_key, recv_deriv, msg.real_out_idx, received_index) ki_enc = crypto.encodepoint(ki) sig = key_image.generate_ring_signature(ki_enc, ki, [out_key], spend_priv, 0, False) del spend_priv # spend_priv never leaves the device # Serialize into buff buff[0:32] = ki_enc crypto.encodeint_into(buff_mv[32:64], sig[0][0]) crypto.encodeint_into(buff_mv[64:], sig[0][1]) # Encrypt with view key private based key - so host can decrypt and verify HMAC enc_key, salt = misc.compute_enc_key_host(s.creds.view_key_private, msg.out_key) resp = chacha_poly.encrypt_pack(enc_key, buff) return MoneroLiveRefreshStepAck(salt=salt, key_image=resp)
async def set_input( state: State, src_entr: MoneroTransactionSourceEntry ) -> MoneroTransactionSetInputAck: from trezor.messages import MoneroTransactionSetInputAck from apps.monero.xmr.crypto import chacha_poly from apps.monero.xmr.serialize_messages.tx_prefix import TxinToKey from apps.monero.signing import offloading_keys state.current_input_index += 1 await layout.transaction_step(state, state.STEP_INP, state.current_input_index) if state.last_step > state.STEP_INP: raise ValueError("Invalid state transition") if state.current_input_index >= state.input_count: raise ValueError("Too many inputs") # real_output denotes which output in outputs is the real one (ours) if src_entr.real_output >= len(src_entr.outputs): raise ValueError( f"real_output index {src_entr.real_output} bigger than output_keys.size() {len(src_entr.outputs)}" ) state.summary_inputs_money += src_entr.amount # Secrets derivation # the UTXO's one-time address P out_key = crypto.decodepoint( src_entr.outputs[src_entr.real_output].key.dest) # the tx_pub of our UTXO stored inside its transaction tx_key = crypto.decodepoint(src_entr.real_out_tx_key) additional_tx_pub_key = _get_additional_public_key(src_entr) # Calculates `derivation = Ra`, private spend key `x = H(Ra||i) + b` to be able # to spend the UTXO; and key image `I = x*H(P||i)` xi, ki, _di = monero.generate_tx_spend_and_key_image_and_derivation( state.creds, state.subaddresses, out_key, tx_key, additional_tx_pub_key, src_entr.real_output_in_tx_index, state.account_idx, src_entr.subaddr_minor, ) state.mem_trace(1, True) # Construct tx.vin # If multisig is used then ki in vini should be src_entr.multisig_kLRki.ki vini = TxinToKey(amount=src_entr.amount, k_image=crypto.encodepoint(ki)) vini.key_offsets = _absolute_output_offsets_to_relative( [x.idx for x in src_entr.outputs]) if src_entr.rct: vini.amount = 0 # Serialize `vini` with variant code for TxinToKey (prefix = TxinToKey.VARIANT_CODE). # The binary `vini_bin` is later sent to step 4 and 9 with its hmac, # where it is checked and directly used. vini_bin = serialize.dump_msg(vini, preallocate=64, prefix=b"\x02") state.mem_trace(2, True) # HMAC(T_in,i || vin_i) hmac_vini = offloading_keys.gen_hmac_vini(state.key_hmac, src_entr, vini_bin, state.current_input_index) state.mem_trace(3, True) # PseudoOuts commitment, alphas stored to state alpha, pseudo_out = _gen_commitment(state, src_entr.amount) pseudo_out = crypto.encodepoint(pseudo_out) # The alpha is encrypted and passed back for storage pseudo_out_hmac = crypto.compute_hmac( offloading_keys.hmac_key_txin_comm(state.key_hmac, state.current_input_index), pseudo_out, ) alpha_enc = chacha_poly.encrypt_pack( offloading_keys.enc_key_txin_alpha(state.key_enc, state.current_input_index), crypto.encodeint(alpha), ) spend_enc = chacha_poly.encrypt_pack( offloading_keys.enc_key_spend(state.key_enc, state.current_input_index), crypto.encodeint(xi), ) state.last_step = state.STEP_INP if state.current_input_index + 1 == state.input_count: # When we finish the inputs processing, we no longer need # the precomputed subaddresses so we clear them to save memory. state.subaddresses = None state.input_last_amount = src_entr.amount return MoneroTransactionSetInputAck( vini=vini_bin, vini_hmac=hmac_vini, pseudo_out=pseudo_out, pseudo_out_hmac=pseudo_out_hmac, pseudo_out_alpha=alpha_enc, spend_key=spend_enc, )