From c6e6ffa838a8834147ff61fb3b12da24555eff70 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Wed, 12 Sep 2018 16:08:04 +0200 Subject: [PATCH] Merge commit 'ba500bf4ec1ef9cd953bdf5a47888c5226db8d0b' into xmr # Conflicts: # src/apps/monero/__init__.py # src/apps/monero/key_image_sync.py # src/apps/monero/protocol/tsx_sign_builder.py # src/apps/monero/sign_tx.py --- src/apps/monero/__init__.py | 4 +- src/apps/monero/key_image_sync.py | 68 +++---- src/apps/monero/protocol/tsx_sign_builder.py | 26 +-- src/apps/monero/sign_tx.py | 187 ++++++++++++++++--- 4 files changed, 204 insertions(+), 81 deletions(-) diff --git a/src/apps/monero/__init__.py b/src/apps/monero/__init__.py index 495df2263..41489f00b 100644 --- a/src/apps/monero/__init__.py +++ b/src/apps/monero/__init__.py @@ -16,8 +16,8 @@ def __init__(self): def boot(): wire.add(MessageType.MoneroGetAddress, __name__, "get_address") wire.add(MessageType.MoneroGetWatchKey, __name__, "get_watch_only") - wire.add(MessageType.MoneroTransactionSignRequest, __name__, "sign_tx", STATE) - wire.add(MessageType.MoneroKeyImageSyncRequest, __name__, "key_image_sync", STATE) + wire.add(MessageType.MoneroTransactionSignRequest, __name__, "sign_tx") + wire.add(MessageType.MoneroKeyImageSyncRequest, __name__, "key_image_sync") if hasattr(MessageType, "MoneroLiteInitRequest"): wire.add(MessageType.MoneroLiteInitRequest, "lite_protocol", STATE, 1) diff --git a/src/apps/monero/key_image_sync.py b/src/apps/monero/key_image_sync.py index 78c61b5d3..210d261d7 100644 --- a/src/apps/monero/key_image_sync.py +++ b/src/apps/monero/key_image_sync.py @@ -1,57 +1,43 @@ import gc -import micropython from trezor import log +from trezor.messages import MessageType -async def key_image_sync(ctx, msg, state): - log.debug( - __name__, - "### KI SYNC. Free: {} Allocated: {}".format(gc.mem_free(), gc.mem_alloc()), - ) - log.debug(__name__, "KI sync state: %s", state.ctx_ki) +async def key_image_sync(ctx, msg): + state = None - from apps.monero.protocol import key_image_sync + while True: + res, state = await key_image_sync_step(ctx, msg, state) + if msg.final_msg: + break + msg = await ctx.call(res, MessageType.MoneroKeyImageSyncRequest) - log.debug( - __name__, - "### KI sync imported. Free: {} Allocated: {}".format( - gc.mem_free(), gc.mem_alloc() - ), - ) + return res - gc.collect() - micropython.mem_info() - micropython.mem_info(1) - try: - if msg.init: - log.debug(__name__, "ki_sync, init") - from apps.monero.controller import iface +async def key_image_sync_step(ctx, msg, state): + if __debug__: + log.debug(__name__, "f: %s a: %s", gc.mem_free(), gc.mem_alloc()) + log.debug(__name__, "s: %s", state) - state.ctx_ki = key_image_sync.KeyImageSync( - ctx=ctx, iface=iface.get_iface(ctx) - ) - return await state.ctx_ki.init(ctx, msg.init) + from apps.monero.protocol import key_image_sync - elif msg.step: - log.debug(__name__, "ki_sync, step") - return await state.ctx_ki.sync(ctx, msg.step) + if __debug__: + log.debug(__name__, "f: %s a: %s", gc.mem_free(), gc.mem_alloc()) + gc.collect() - elif msg.final_msg: - log.debug(__name__, "ki_sync, final") - res = await state.ctx_ki.final(ctx, msg.final_msg) - state.ctx_ki = None - return res + if msg.init: + from apps.monero.controller import iface - else: - raise ValueError("Unknown error") + state = key_image_sync.KeyImageSync(ctx=ctx, iface=iface.get_iface(ctx)) + return await state.init(ctx, msg.init), state - except Exception as e: - state.ctx_ki = None + elif msg.step: + return await state.sync(ctx, msg.step), state - log.debug(__name__, "KI error, %s: %s", type(e), e) - raise - # from trezor.messages.Failure import Failure + elif msg.final_msg: + return await state.final(ctx, msg.final_msg), None - # return Failure() + else: + raise ValueError("Unknown error") diff --git a/src/apps/monero/protocol/tsx_sign_builder.py b/src/apps/monero/protocol/tsx_sign_builder.py index 70fc97ec0..91ec5c094 100644 --- a/src/apps/monero/protocol/tsx_sign_builder.py +++ b/src/apps/monero/protocol/tsx_sign_builder.py @@ -29,8 +29,8 @@ class TTransactionBuilder(object): STEP_MLSAG = const(600) STEP_SIGN = const(700) - def __init__(self, trezor=None, creds=None, state=None, **kwargs): - self.trezor = trezor + def __init__(self, iface=None, creds=None, state=None, **kwargs): + self.iface = iface self.creds = creds self.key_master = None self.key_hmac = None @@ -485,7 +485,7 @@ async def init_transaction(self, tsx_data, tsx_ctr): self._log_trace(1) # Ask for confirmation - confirmation = await self.trezor.iface.confirm_transaction(tsx_data, self.creds) + confirmation = await self.iface.confirm_transaction(tsx_data, self.creds) if not confirmation: from trezor.messages import FailureType from trezor.messages.Failure import Failure @@ -671,7 +671,7 @@ async def set_input(self, src_entr): self.state.input() self.inp_idx += 1 - await self.trezor.iface.transaction_step( + await self.iface.transaction_step( self.STEP_INP, self.inp_idx, self.num_inputs() ) @@ -797,7 +797,7 @@ async def tsx_inputs_permutation(self, permutation): MoneroTransactionInputsPermutationAck ) - await self.trezor.iface.transaction_step(self.STEP_PERM) + await self.iface.transaction_step(self.STEP_PERM) if self.in_memory(): return @@ -858,7 +858,7 @@ async def input_vini(self, src_entr, vini, hmac, pseudo_out, pseudo_out_hmac): MoneroTransactionInputViniAck ) - await self.trezor.iface.transaction_step( + await self.iface.transaction_step( self.STEP_VINI, self.inp_idx + 1, self.num_inputs() ) @@ -921,7 +921,7 @@ async def all_in_set(self, rsig_data): """ self._log_trace(0) self.state.input_all_done() - await self.trezor.iface.transaction_step(self.STEP_ALL_IN) + await self.iface.transaction_step(self.STEP_ALL_IN) from trezor.messages.MoneroTransactionAllInputsSetAck import ( MoneroTransactionAllInputsSetAck @@ -1249,7 +1249,7 @@ async def set_out1(self, dst_entr, dst_entr_hmac, rsig_data=None): self._log_trace(0, True) mods = utils.unimport_begin() - await self.trezor.iface.transaction_step( + await self.iface.transaction_step( self.STEP_OUT, self.out_idx + 1, self.num_dests() ) self._log_trace(1) @@ -1366,7 +1366,7 @@ async def all_out1_set(self): """ self._log_trace(0) self.state.set_output_done() - await self.trezor.iface.transaction_step(self.STEP_ALL_OUT) + await self.iface.transaction_step(self.STEP_ALL_OUT) self._log_trace(1) if self.out_idx + 1 != self.num_dests(): @@ -1461,7 +1461,7 @@ async def mlsag_done(self): ) self.state.set_final_message_done() - await self.trezor.iface.transaction_step(self.STEP_MLSAG) + await self.iface.transaction_step(self.STEP_MLSAG) await self.tsx_mlsag_ecdh_info() await self.tsx_mlsag_out_pk() @@ -1499,7 +1499,7 @@ async def sign_input( :return: Generated signature MGs[i] """ self.state.set_signature() - await self.trezor.iface.transaction_step( + await self.iface.transaction_step( self.STEP_SIGN, self.inp_idx + 1, self.num_inputs() ) @@ -1644,7 +1644,7 @@ async def sign_input( # Final state transition if self.inp_idx + 1 == self.num_inputs(): self.state.set_signature_done() - await self.trezor.iface.transaction_signed() + await self.iface.transaction_signed() gc.collect() self._log_trace() @@ -1684,7 +1684,7 @@ async def final_msg(self, *args, **kwargs): ) tx_enc_keys = chacha_poly.encrypt_pack(tx_key, key_buff) - await self.trezor.iface.transaction_finished() + await self.iface.transaction_finished() gc.collect() return MoneroTransactionFinalAck( diff --git a/src/apps/monero/sign_tx.py b/src/apps/monero/sign_tx.py index 6437c68e3..f85061efd 100644 --- a/src/apps/monero/sign_tx.py +++ b/src/apps/monero/sign_tx.py @@ -1,39 +1,176 @@ import gc -import micropython -from trezor import log +from trezor import wire +from trezor.messages import MessageType -async def sign_tx(ctx, msg, state): +async def sign_tx(ctx, msg): + state = None + + while True: + res, state = await sign_tx_step(ctx, msg, state) + if msg.final_msg: + break + msg = await ctx.call(res, MessageType.MoneroTransactionSignRequest) + + return res + + +async def sign_tx_step(ctx, msg, state): gc.threshold(gc.mem_free() // 4 + gc.mem_alloc()) - log.debug( - __name__, - "############################ TSX. F: {} A: {} thr: {}".format( - gc.mem_free(), gc.mem_alloc(), gc.mem_free() // 4 + gc.mem_alloc() - ), - ) gc.collect() - micropython.mem_info() - from apps.monero.protocol.tsx_sign import TsxSigner + from apps.monero.controller import iface, wrapper + from apps.monero.protocol.tsx_sign_builder import TTransactionBuilder - log.debug(__name__, "TsxSigner. F: {} A: {}".format(gc.mem_free(), gc.mem_alloc())) - log.debug(__name__, "TsxState: %s", state.ctx_sign) + if msg.init: + init = msg.init + creds = await wrapper.monero_get_creds(ctx, init.address_n, init.network_type) + state = None + else: + creds = None + tsx = TTransactionBuilder(iface.get_iface(ctx), creds, state) + del creds + del state + + gc.collect() + res = await sign_tx_dispatch(tsx, msg) gc.collect() + if tsx.is_terminal(): + state = None + else: + state = tsx.state_save() + return res, state + + +async def sign_tx_dispatch(tsx, msg): + if msg.init: + return await tsx_init(tsx, msg.init.tsx_data) + elif msg.set_input: + return await tsx_set_input(tsx, msg.set_input) + elif msg.input_permutation: + return await tsx_inputs_permutation(tsx, msg.input_permutation) + elif msg.input_vini: + return await tsx_input_vini(tsx, msg.input_vini) + elif msg.all_in_set: + return await tsx_all_in_set(tsx, msg.all_in_set) + elif msg.set_output: + return await tsx_set_output1(tsx, msg.set_output) + elif msg.all_out_set: + return await tsx_all_out1_set(tsx, msg.all_out_set) + elif msg.mlsag_done: + return await tsx_mlsag_done(tsx) + elif msg.sign_input: + return await tsx_sign_input(tsx, msg.sign_input) + elif msg.final_msg: + return await tsx_sign_final(tsx) + else: + raise wire.DataError("Unknown message") + + +async def tsx_init(tsx, tsx_data): + return await tsx.init_transaction(tsx_data, 1) # TODO: remove counter + + +async def tsx_set_input(tsx, msg): + """ + 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 AES-GCM() with + key derived for exactly this purpose. + """ + return await tsx.set_input(msg.src_entr) + + +async def tsx_inputs_permutation(tsx, msg): + """ + Set permutation on the inputs - sorted by key image on host. + """ + return await tsx.tsx_inputs_permutation(msg.perm) + + +async def tsx_input_vini(tsx, msg): + """ + Set tx.vin[i] for incremental tx prefix hash computation. + After sorting by key images on host. + """ + from apps.monero.controller.misc import parse_vini + + vini = await parse_vini(msg.vini) + del msg.vini + + return await tsx.input_vini( + msg.src_entr, vini, msg.vini_hmac, msg.pseudo_out, msg.pseudo_out_hmac + ) + + +async def tsx_all_in_set(tsx, msg): + """ + All inputs set. Defining rsig parameters. + """ + return await tsx.all_in_set(msg.rsig_data) + + +async def tsx_set_output1(tsx, msg): + """ + Set destination entry one by one. + Computes destination stealth address, amount key, range proof + HMAC, out_pk, ecdh_info. + """ + dst, dst_hmac, rsig_data = msg.dst_entr, msg.dst_entr_hmac, msg.rsig_data + del (msg) + + return await tsx.set_out1(dst, dst_hmac, rsig_data) + + +async def tsx_all_out1_set(tsx, msg): + """ + All outputs were set in this phase. Computes additional public keys (if needed), tx.extra and + transaction prefix hash. + Adds additional public keys to the tx.extra + + :return: tx.extra, tx_prefix_hash + """ + from apps.monero.controller.misc import TrezorTxPrefixHashNotMatchingError + try: - signer = TsxSigner() - await signer.wake_up(ctx, state.ctx_sign, msg) - state.ctx_sign = None + return await tsx.all_out1_set() + except TrezorTxPrefixHashNotMatchingError as e: + raise wire.NotEnoughFunds(e.message) + + +async def tsx_mlsag_done(tsx): + """ + MLSAG message computed. + """ + return await tsx.mlsag_done() - res = await signer.sign(msg) - gc.collect() - if not await signer.should_purge(): - state.ctx_sign = await signer.state_save() - return res +async def tsx_sign_input(tsx, msg): + """ + Generates a signature for one input. + """ + from apps.monero.controller.misc import parse_vini + + vini = await parse_vini(msg.vini) + del msg.vini + + return await tsx.sign_input( + msg.src_entr, + vini, + msg.vini_hmac, + msg.pseudo_out, + msg.pseudo_out_hmac, + msg.alpha_enc, + msg.spend_enc, + ) + - except Exception as e: - state.ctx_sign = None - log.error(__name__, "Tsx exception: %s %s", type(e), e) - raise +async def tsx_sign_final(tsx): + """ + Final message. + Offloading tx related data, encrypted. + """ + return await tsx.final_msg()