def _return_rsig_data(rsig): if rsig is None: return None from trezor.messages.MoneroTransactionRsigData import MoneroTransactionRsigData if isinstance(rsig, list): return MoneroTransactionRsigData(rsig_parts=rsig) else: return MoneroTransactionRsigData(rsig=rsig)
def _return_rsig_data(rsig=None, mask=None): if rsig is None and mask is None: return None from trezor.messages.MoneroTransactionRsigData import MoneroTransactionRsigData rsig_data = MoneroTransactionRsigData() if mask: rsig_data.mask = mask if rsig: rsig_data.rsig = rsig return rsig_data
async def all_inputs_set(state: State): state.mem_trace(0) await confirms.transaction_step(state.ctx, state.STEP_ALL_IN) from trezor.messages.MoneroTransactionAllInputsSetAck import ( MoneroTransactionAllInputsSetAck, ) from trezor.messages.MoneroTransactionRsigData import MoneroTransactionRsigData # Generate random commitment masks to be used in range proofs. # If SimpleRCT is used the sum of the masks must match the input masks sum. state.sumout = crypto.sc_init(0) for i in range(state.output_count): cur_mask = crypto.new_scalar() # new mask for each output is_last = i + 1 == state.output_count if is_last and state.rct_type == RctType.Simple: # in SimpleRCT the last mask needs to be calculated as an offset of the sum crypto.sc_sub_into(cur_mask, state.sumpouts_alphas, state.sumout) else: crypto.random_scalar(cur_mask) crypto.sc_add_into(state.sumout, state.sumout, cur_mask) state.output_masks.append(cur_mask) if state.rct_type == RctType.Simple: utils.ensure(crypto.sc_eq(state.sumout, state.sumpouts_alphas), "Invalid masks sum") # sum check state.sumout = crypto.sc_init(0) rsig_data = MoneroTransactionRsigData() resp = MoneroTransactionAllInputsSetAck(rsig_data=rsig_data) # If range proofs are being offloaded, we send the masks to the host, which uses them # to create the range proof. If not, we do not send any and we use them in the following step. if state.rsig_offload: tmp_buff = bytearray(32) rsig_data.mask = bytearray(32 * state.output_count) for i in range(state.output_count): crypto.encodeint_into(tmp_buff, state.output_masks[i]) utils.memcpy(rsig_data.mask, 32 * i, tmp_buff, 0, 32) return resp
async def _compute_masks(state: State): """ Output masks computed in advance. Used with client_version=0 && HF9. After HF10 (included) masks are deterministic, computed from the amount_key. After all client update to v1 this code will be removed. In order to preserve client_version=0 compatibility the masks have to be adjusted. """ from trezor.messages.MoneroTransactionRsigData import MoneroTransactionRsigData from apps.monero.signing import offloading_keys rsig_data = MoneroTransactionRsigData() # If range proofs are being offloaded, we send the masks to the host, which uses them # to create the range proof. If not, we do not send any and we use them in the following step. if state.rsig_offload: rsig_data.mask = [] # Deterministic masks, the last one is computed to balance the sums for i in range(state.output_count): if i + 1 == state.output_count: cur_mask = crypto.sc_sub(state.sumpouts_alphas, state.sumout) state.output_last_mask = cur_mask else: cur_mask = offloading_keys.det_comm_masks(state.key_enc, i) crypto.sc_add_into(state.sumout, state.sumout, cur_mask) if state.rsig_offload: rsig_data.mask.append(crypto.encodeint(cur_mask)) if not crypto.sc_eq(state.sumpouts_alphas, state.sumout): raise ValueError("Sum eq error") state.sumout = crypto.sc_init(0) return rsig_data
def _rsig_process_bp(state: State, rsig_data: MoneroTransactionRsigData): from apps.monero.xmr import range_signatures from apps.monero.xmr.serialize_messages.tx_rsig_bulletproof import Bulletproof bp_obj = serialize.parse_msg(rsig_data.rsig, Bulletproof) rsig_data.rsig = None # BP is hashed with raw=False as hash does not contain L, R # array sizes compared to the serialized bulletproof format # thus direct serialization cannot be used. state.full_message_hasher.rsig_val(bp_obj, raw=False) res = range_signatures.verify_bp(bp_obj, state.output_amounts, state.output_masks) utils.ensure(res, "BP verification fail") state.mem_trace("BP verified" if __debug__ else None, collect=True) del (bp_obj, range_signatures) # State cleanup after verification is finished state.output_amounts = [] state.output_masks = []
async def init_transaction( state: State, address_n: list, network_type: int, tsx_data: MoneroTransactionData, keychain, ) -> MoneroTransactionInitAck: from apps.monero.signing import offloading_keys from apps.common import paths await paths.validate_path(state.ctx, misc.validate_full_path, keychain, address_n, CURVE) state.creds = misc.get_creds(keychain, address_n, network_type) state.client_version = tsx_data.client_version or 0 if state.client_version == 0: raise ValueError("Client version not supported") state.fee = state.fee if state.fee > 0 else 0 state.tx_priv = crypto.random_scalar() state.tx_pub = crypto.scalarmult_base(state.tx_priv) state.mem_trace(1) state.input_count = tsx_data.num_inputs state.output_count = len(tsx_data.outputs) state.progress_total = 4 + 3 * state.input_count + state.output_count state.progress_cur = 0 # Ask for confirmation await confirms.require_confirm_transaction(state.ctx, state, tsx_data, state.creds.network_type) state.creds.address = None state.creds.network_type = None gc.collect() state.mem_trace(3) # Basic transaction parameters state.output_change = tsx_data.change_dts state.mixin = tsx_data.mixin state.fee = tsx_data.fee state.account_idx = tsx_data.account state.last_step = state.STEP_INIT if tsx_data.hard_fork: state.hard_fork = tsx_data.hard_fork # Ensure change is correct _check_change(state, tsx_data.outputs) # At least two outpus are required, this applies also for sweep txs # where one fake output is added. See _check_change for more info if state.output_count < 2: raise signing.NotEnoughOutputsError( "At least two outputs are required") _check_rsig_data(state, tsx_data.rsig_data) _check_subaddresses(state, tsx_data.outputs) # Extra processing, payment id _process_payment_id(state, tsx_data) await _compute_sec_keys(state, tsx_data) gc.collect() # Iterative tx_prefix_hash hash computation state.tx_prefix_hasher.uvarint( 2) # current Monero transaction format (RingCT = 2) state.tx_prefix_hasher.uvarint(tsx_data.unlock_time) state.tx_prefix_hasher.uvarint(state.input_count) # ContainerType, size state.mem_trace(10, True) # Final message hasher state.full_message_hasher.init() state.full_message_hasher.set_type_fee(signing.RctType.Bulletproof2, state.fee) # Sub address precomputation if tsx_data.account is not None and tsx_data.minor_indices: _precompute_subaddr(state, tsx_data.account, tsx_data.minor_indices) state.mem_trace(5, True) # HMACs all outputs to disallow tampering. # Each HMAC is then sent alongside the output # and trezor validates it. hmacs = [] for idx in range(state.output_count): c_hmac = await offloading_keys.gen_hmac_tsxdest( state.key_hmac, tsx_data.outputs[idx], idx) hmacs.append(c_hmac) gc.collect() state.mem_trace(6) from trezor.messages.MoneroTransactionInitAck import MoneroTransactionInitAck from trezor.messages.MoneroTransactionRsigData import MoneroTransactionRsigData rsig_data = MoneroTransactionRsigData(offload_type=state.rsig_offload) return MoneroTransactionInitAck(hmacs=hmacs, rsig_data=rsig_data)