async def bootscreen() -> None: ui.display.orientation(storage.device.get_rotation()) while True: try: if storage.sd_salt.is_enabled() or config.has_pin(): await lockscreen() salt = await request_sd_salt() if not config.has_pin(): config.unlock(pin_to_int(""), salt) storage.init_unlocked() return label = "Enter your PIN" while True: pin = await request_pin(label, config.get_pin_rem()) if config.unlock(pin_to_int(pin), salt): storage.init_unlocked() return else: label = "Wrong PIN, enter again" except (OSError, PinCancelled, SdCardUnavailable) as e: if __debug__: log.exception(__name__, e) except BaseException as e: if __debug__: log.exception(__name__, e) utils.halt(e.__class__.__name__)
def _step(task: Task, value: Any) -> None: try: if isinstance(value, BaseException): result = task.throw(value) # type: ignore # error: Argument 1 to "throw" of "Coroutine" has incompatible type "Exception"; expected "Type[BaseException]" # rationale: In micropython, generator.throw() accepts the exception object directly. else: result = task.send(value) except StopIteration as e: # as e: if __debug__: log.debug(__name__, "finish: %s", task) finalize(task, e.value) except Exception as e: if __debug__: log.exception(__name__, e) finalize(task, e) else: if isinstance(result, Syscall): result.handle(task) elif result is None: schedule(task) else: if __debug__: log.error(__name__, "unknown syscall: %s", result) if after_step_hook: after_step_hook()
async def cardano_get_address(ctx, msg): mnemonic = storage.get_mnemonic() root_node = bip32.from_mnemonic_cardano(mnemonic) try: address, _ = derive_address_and_node(root_node, msg.address_n) except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Deriving address failed") mnemonic = None root_node = None if msg.show_display: if not await show_swipable_with_confirmation(ctx, address, "Export address", icon=ui.ICON_SEND, icon_color=ui.GREEN): raise wire.ActionCancelled("Exporting cancelled") else: lines = _break_address_n_to_lines(msg.address_n) if not await show_swipable_with_confirmation(ctx, lines, "For BIP32 path", icon=ui.ICON_SEND, icon_color=ui.GREEN): raise wire.ActionCancelled("Exporting cancelled") return CardanoAddress(address=address)
async def get_address(ctx, msg): await paths.validate_path(ctx, validate_full_path, path=msg.address_n) mnemonic = storage.get_mnemonic() passphrase = await seed._get_cached_passphrase(ctx) root_node = bip32.from_mnemonic_cardano(mnemonic, passphrase) try: address, _ = derive_address_and_node(root_node, msg.address_n) except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Deriving address failed") mnemonic = None root_node = None if msg.show_display: if not await confirm_with_pagination(ctx, address, "Export address", icon=ui.ICON_SEND, icon_color=ui.GREEN): raise wire.ActionCancelled("Exporting cancelled") return CardanoAddress(address=address)
async def sign_tx(ctx, msg): keychain = await seed.get_keychain(ctx) progress.init(msg.transactions_count, "Loading data") try: attested = len(msg.inputs) * [False] input_coins_sum = 0 # request transactions tx_req = CardanoTxRequest() for index in range(msg.transactions_count): progress.advance() tx_ack = await request_transaction(ctx, tx_req, index) tx_hash = hashlib.blake2b(data=bytes(tx_ack.transaction), outlen=32).digest() tx_decoded = cbor.decode(tx_ack.transaction) for i, input in enumerate(msg.inputs): if not attested[i] and input.prev_hash == tx_hash: attested[i] = True outputs = tx_decoded[1] amount = outputs[input.prev_index][1] input_coins_sum += amount if not all(attested): raise wire.ProcessError("No tx data sent for input " + str(attested.index(False))) transaction = Transaction(msg.inputs, msg.outputs, keychain, msg.protocol_magic, input_coins_sum) # clear progress bar display_homescreen() for i in msg.inputs: await validate_path(ctx, validate_full_path, keychain, i.address_n, CURVE) # sign the transaction bundle and prepare the result tx_body, tx_hash = transaction.serialise_tx() tx = CardanoSignedTx(tx_body=tx_body, tx_hash=tx_hash) except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Signing failed") # display the transaction in UI if not await show_tx( ctx, transaction.output_addresses, transaction.outgoing_coins, transaction.fee, transaction.network_name, transaction.inputs, transaction.outputs, ): raise wire.ActionCancelled("Signing cancelled") return tx
async def _sign_stake_pool_registration_tx( ctx: wire.Context, msg: CardanoSignTx, keychain: seed.Keychain ) -> CardanoSignedTx: """ We have a separate tx signing flow for stake pool registration because it's a transaction where the witnessable entries (i.e. inputs, withdrawals, etc.) in the transaction are not supposed to be controlled by the HW wallet, which means the user is vulnerable to unknowingly supplying a witness for an UTXO or other tx entry they think is external, resulting in the co-signers gaining control over their funds (Something SLIP-0019 is dealing with for BTC but no similar standard is currently available for Cardano). Hence we completely forbid witnessing inputs and other entries of the transaction except the stake pool certificate itself and we provide a witness only to the user's staking key in the list of pool owners. """ try: _validate_stake_pool_registration_tx_structure(msg) _ensure_no_signing_inputs(msg.inputs) _validate_outputs(keychain, msg.outputs, msg.protocol_magic, msg.network_id) _validate_certificates(msg.certificates, msg.protocol_magic, msg.network_id) _validate_metadata(msg.metadata) await _show_stake_pool_registration_tx(ctx, keychain, msg) # sign the transaction bundle and prepare the result serialized_tx, tx_hash = _serialize_tx(keychain, msg) tx = CardanoSignedTx(serialized_tx=serialized_tx, tx_hash=tx_hash) except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Signing failed") return tx
async def _sign_standard_tx(ctx: wire.Context, msg: CardanoSignTx, keychain: seed.Keychain) -> CardanoSignedTx: try: for i in msg.inputs: await validate_path(ctx, keychain, i.address_n, SCHEMA_ADDRESS.match(i.address_n)) _validate_outputs(keychain, msg.outputs, msg.protocol_magic, msg.network_id) _validate_certificates(msg.certificates, msg.protocol_magic, msg.network_id) _validate_withdrawals(msg.withdrawals) _validate_metadata(msg.metadata) # display the transaction in UI await _show_standard_tx(ctx, keychain, msg) # sign the transaction bundle and prepare the result serialized_tx, tx_hash = _serialize_tx(keychain, msg) tx = CardanoSignedTx(serialized_tx=serialized_tx, tx_hash=tx_hash) except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Signing failed") return tx
async def cardano_sign_message(ctx, msg): mnemonic = storage.get_mnemonic() root_node = bip32.from_mnemonic_cardano(mnemonic) try: signature = _sign_message(root_node, msg.message, msg.address_n) except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Signing failed") mnemonic = None root_node = None if not await show_swipable_with_confirmation( ctx, msg.message, "Signing message", ui.ICON_RECEIVE, ui.GREEN ): raise wire.ActionCancelled("Signing cancelled") if not await show_swipable_with_confirmation( ctx, _break_address_n_to_lines(msg.address_n), "With address", ui.ICON_RECEIVE, ui.GREEN, ): raise wire.ActionCancelled("Signing cancelled") return signature
async def sign_tx( ctx: wire.Context, msg: CardanoSignTx, keychain: seed.Keychain ) -> CardanoSignedTx: try: if msg.fee > LOVELACE_MAX_SUPPLY: raise wire.ProcessError("Fee is out of range!") _validate_network_info(msg.network_id, msg.protocol_magic) for i in msg.inputs: await validate_path(ctx, validate_full_path, keychain, i.address_n, CURVE) _validate_outputs(keychain, msg.outputs, msg.protocol_magic, msg.network_id) _validate_certificates(msg.certificates) _validate_withdrawals(msg.withdrawals) _validate_metadata(msg.metadata) # display the transaction in UI await _show_tx(ctx, keychain, msg) # sign the transaction bundle and prepare the result serialized_tx, tx_hash = _serialize_tx(keychain, msg) tx = CardanoSignedTx(serialized_tx=serialized_tx, tx_hash=tx_hash) except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Signing failed") return tx
async def sign_tx(ctx: wire.Context, msg: messages.CardanoSignTxInit, keychain: seed.Keychain) -> messages.CardanoSignTxFinished: signer_type: Type[Signer] if msg.signing_mode == CardanoTxSigningMode.ORDINARY_TRANSACTION: from .ordinary_signer import OrdinarySigner signer_type = OrdinarySigner elif msg.signing_mode == CardanoTxSigningMode.POOL_REGISTRATION_AS_OWNER: from .pool_owner_signer import PoolOwnerSigner signer_type = PoolOwnerSigner elif msg.signing_mode == CardanoTxSigningMode.MULTISIG_TRANSACTION: from .multisig_signer import MultisigSigner signer_type = MultisigSigner elif msg.signing_mode == CardanoTxSigningMode.PLUTUS_TRANSACTION: from .plutus_signer import PlutusSigner signer_type = PlutusSigner else: raise RuntimeError # should be unreachable signer = signer_type(ctx, msg, keychain) try: await signer.sign() except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Signing failed") return messages.CardanoSignTxFinished()
async def session_handler(iface, sid): reader = None ctx = Context(iface, sid) while True: try: # wait for new message, if needed, and find handler if not reader: reader = ctx.getreader() await reader.aopen() try: handler, args = workflow_handlers[reader.type] except KeyError: handler, args = unexpected_msg, () w = handler(ctx, reader, *args) try: workflow.onstart(w) await w finally: workflow.onclose(w) except UnexpectedMessageError as exc: # retry with opened reader from the exception reader = exc.reader continue except FailureError as exc: # we log FailureError as warning, not as exception log.warning(__name__, 'failure: %s', exc.message) except Exception as exc: # sessions are never closed by raised exceptions log.exception(__name__, exc) # read new message in next iteration reader = None
async def get_address( ctx: wire.Context, msg: CardanoGetAddress, keychain: seed.Keychain ) -> CardanoAddress: address_parameters = msg.address_parameters await paths.validate_path( ctx, keychain, address_parameters.address_n, # path must match the ADDRESS schema SCHEMA_ADDRESS.match(address_parameters.address_n), ) validate_network_info(msg.network_id, msg.protocol_magic) try: address = derive_human_readable_address( keychain, address_parameters, msg.protocol_magic, msg.network_id ) except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Deriving address failed") if msg.show_display: await _display_address( ctx, keychain, address_parameters, address, msg.protocol_magic ) return CardanoAddress(address=address)
async def bootscreen() -> None: ui.display.orientation(storage_device.get_rotation()) salt_auth_key = storage_device.get_sd_salt_auth_key() while True: try: if salt_auth_key is not None or config.has_pin(): await lockscreen() if salt_auth_key is not None: salt = await request_sd_salt(None, salt_auth_key ) # type: Optional[bytearray] else: salt = None if not config.has_pin(): config.unlock(pin_to_int(""), salt) storage.init_unlocked() return label = "Enter your PIN" while True: pin = await request_pin(label, config.get_pin_rem()) if config.unlock(pin_to_int(pin), salt): storage.init_unlocked() return else: label = "Wrong PIN, enter again" except (OSError, PinCancelled, SdProtectCancelled) as e: if __debug__: log.exception(__name__, e) except BaseException as e: utils.halt(e.__class__.__name__)
def dispatch(report, open_callback, close_callback, unknown_callback): ''' Dispatches payloads of reports adhering to one of the wire codecs. ''' if codec_v1.detect(report): marker, session_id, report_data = codec_v1.parse_report(report) else: marker, session_id, report_data = codec_v2.parse_report(report) if marker == codec_v2.REP_MARKER_OPEN: log.debug(__name__, 'request for new session') open_callback() return elif marker == codec_v2.REP_MARKER_CLOSE: log.debug(__name__, 'request for closing session %x', session_id) close_callback(session_id) return if session_id not in readers: log.warning(__name__, 'report on unknown session %x', session_id) unknown_callback(session_id, report_data) return log.debug(__name__, 'report on session %x', session_id) reader = readers[session_id] try: reader.send(report_data) except StopIteration: readers.pop(session_id) except Exception as e: log.exception(__name__, e)
async def sign_tx(ctx: wire.Context, msg: CardanoSignTx, keychain: seed.Keychain) -> CardanoSignedTx: if msg.fee > LOVELACE_MAX_SUPPLY: raise wire.ProcessError("Fee is out of range!") validate_network_info(msg.network_id, msg.protocol_magic) try: if _has_stake_pool_registration(msg): cborized_tx, tx_hash = await _sign_stake_pool_registration_tx( ctx, msg, keychain) else: cborized_tx, tx_hash = await _sign_ordinary_tx(ctx, msg, keychain) signed_tx_chunks = cbor.encode_chunked(cborized_tx, MAX_TX_CHUNK_SIZE) for signed_tx_chunk in signed_tx_chunks: response = CardanoSignedTxChunk(signed_tx_chunk=signed_tx_chunk) await ctx.call(response, CardanoSignedTxChunkAck) return CardanoSignedTx(tx_hash=tx_hash, serialized_tx=None) except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Signing failed")
async def handle_session(iface: WireInterface, session_id: int, is_debug_session: bool = False) -> None: if __debug__ and is_debug_session: ctx_buffer = WIRE_BUFFER_DEBUG else: ctx_buffer = WIRE_BUFFER ctx = Context(iface, session_id, ctx_buffer) next_msg: codec_v1.Message | None = None if __debug__ and is_debug_session: import apps.debug apps.debug.DEBUG_CONTEXT = ctx # Take a mark of modules that are imported at this point, so we can # roll back and un-import any others. modules = utils.unimport_begin() while True: try: if next_msg is None: # If the previous run did not keep an unprocessed message for us, # wait for a new one coming from the wire. try: msg = await ctx.read_from_wire() except codec_v1.CodecError as exc: if __debug__: log.exception(__name__, exc) await ctx.write(failure(exc)) continue else: # Process the message from previous run. msg = next_msg next_msg = None try: next_msg = await _handle_single_message( ctx, msg, use_workflow=not is_debug_session) finally: if not __debug__ or not is_debug_session: # Unload modules imported by the workflow. Should not raise. # This is not done for the debug session because the snapshot taken # in a debug session would clear modules which are in use by the # workflow running on wire. utils.unimport_end(modules) if next_msg is None: # Shut down the loop if there is no next message waiting. # Let the session be restarted from `main`. loop.clear() return except Exception as exc: # Log and try again. The session handler can only exit explicitly via # loop.clear() above. if __debug__: log.exception(__name__, exc)
async def _wait(self, child, index): try: result = await child except Exception as e: self._finish(child, index, e) if __debug__: log.exception(__name__, e) else: self._finish(child, index, result)
async def handle_reports(iface: io.HID): while True: try: req = await read_cmd(iface) if req is None: continue resp = dispatch_cmd(req) send_cmd(resp, iface) except Exception as e: log.exception(__name__, e)
def load_icon(self, rp_id_hash: bytes) -> None: from trezor import res from apps.webauthn.knownapps import knownapps try: namepart = knownapps[rp_id_hash]["label"].lower().replace(" ", "_") icon = res.load("apps/webauthn/res/icon_%s.toif" % namepart) except Exception as e: icon = res.load("apps/webauthn/res/icon_webauthn.toif") if __debug__: log.exception(__name__, e) self.app_icon = icon
async def sign_tx(ctx: wire.Context, msg: CardanoSignTxInit, keychain: seed.Keychain) -> CardanoSignTxFinished: await show_transaction_signing_mode(ctx, msg.signing_mode) is_network_id_verifiable = await _validate_tx_signing_request(ctx, msg) # inputs, outputs and fee are mandatory fields, count the number of optional fields present tx_body_map_item_count = 3 + sum(( msg.ttl is not None, msg.certificates_count > 0, msg.withdrawals_count > 0, msg.has_auxiliary_data, msg.validity_interval_start is not None, msg.minting_asset_groups_count > 0, msg.include_network_id, msg.script_data_hash is not None, msg.collateral_inputs_count > 0, msg.required_signers_count > 0, )) account_path_checker = AccountPathChecker() hash_fn = hashlib.blake2b(outlen=32) tx_dict: HashBuilderDict[int, Any] = HashBuilderDict( tx_body_map_item_count, INVALID_TX_SIGNING_REQUEST) tx_dict.start(hash_fn) with tx_dict: await _process_transaction(ctx, msg, keychain, tx_dict, account_path_checker) tx_hash = hash_fn.digest() await _confirm_transaction(ctx, msg, is_network_id_verifiable, tx_hash) try: response_after_witness_requests = await _process_witness_requests( ctx, keychain, tx_hash, msg.witness_requests_count, msg.signing_mode, msg.minting_asset_groups_count > 0, account_path_checker, ) await ctx.call(response_after_witness_requests, CardanoTxHostAck) await ctx.call(CardanoTxBodyHash(tx_hash=tx_hash), CardanoTxHostAck) return CardanoSignTxFinished() except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Signing failed")
def _wrap_protobuf_load( buffer: bytes, expected_type: type[LoadedMessageType], ) -> LoadedMessageType: try: return protobuf.decode(buffer, expected_type, experimental_enabled) except Exception as e: if __debug__: log.exception(__name__, e) if e.args: raise DataError("Failed to decode message: {}".format(e.args[0])) else: raise DataError("Failed to decode message")
async def cardano_get_public_key(ctx, msg): mnemonic = storage.get_mnemonic() root_node = bip32.from_mnemonic_cardano(mnemonic) try: key = _get_public_key(root_node, msg.address_n) except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Deriving public key failed") mnemonic = None root_node = None return key
async def sign_tx(ctx, msg): mnemonic = storage.get_mnemonic() passphrase = await seed._get_cached_passphrase(ctx) root_node = bip32.from_mnemonic_cardano(mnemonic, passphrase) progress.init(msg.transactions_count, "Loading data") try: # request transactions transactions = [] tx_req = CardanoTxRequest() for index in range(msg.transactions_count): progress.advance() tx_ack = await request_transaction(ctx, tx_req, index) transactions.append(tx_ack.transaction) # clear progress bar display_homescreen() for i in msg.inputs: await validate_path(ctx, validate_full_path, path=i.address_n) # sign the transaction bundle and prepare the result transaction = Transaction(msg.inputs, msg.outputs, transactions, root_node, msg.network) tx_body, tx_hash = transaction.serialise_tx() tx = CardanoSignedTx(tx_body=tx_body, tx_hash=tx_hash) except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Signing failed") # display the transaction in UI if not await show_tx( ctx, transaction.output_addresses, transaction.outgoing_coins, transaction.change_derivation_paths, transaction.change_coins, transaction.fee, len(tx_body), transaction.network_name, ): raise wire.ActionCancelled("Signing cancelled") return tx
async def get_public_key(ctx, msg): keychain = await seed.get_keychain(ctx) await paths.validate_path( ctx, paths.validate_path_for_get_public_key, path=msg.address_n, slip44_id=1815 ) try: key = _get_public_key(keychain, msg.address_n) except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Deriving public key failed") if msg.show_display: await layout.show_pubkey(ctx, key.node.public_key) return key
async def get_public_key(ctx, msg): mnemonic = storage.get_mnemonic() passphrase = await seed._get_cached_passphrase(ctx) root_node = bip32.from_mnemonic_cardano(mnemonic, passphrase) try: key = _get_public_key(root_node, msg.address_n) except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Deriving public key failed") mnemonic = None root_node = None if msg.show_display: await layout.show_pubkey(ctx, key.node.public_key) return key
async def bootscreen(): while True: try: if not config.has_pin(): config.unlock(pin_to_int(""), show_pin_timeout) return await lockscreen() label = None while True: pin = await request_pin(label) if config.unlock(pin_to_int(pin), show_pin_timeout): return else: label = "Wrong PIN, enter again" except Exception as e: if __debug__: log.exception(__name__, e)
async def sign_tx(ctx, msg): keychain = await seed.get_keychain(ctx) progress.init(msg.transactions_count, "Loading data") try: # request transactions transactions = [] tx_req = CardanoTxRequest() for index in range(msg.transactions_count): progress.advance() tx_ack = await request_transaction(ctx, tx_req, index) transactions.append(tx_ack.transaction) # clear progress bar display_homescreen() for i in msg.inputs: await validate_path(ctx, validate_full_path, keychain, i.address_n, CURVE) # sign the transaction bundle and prepare the result transaction = Transaction(msg.inputs, msg.outputs, transactions, keychain, msg.protocol_magic) tx_body, tx_hash = transaction.serialise_tx() tx = CardanoSignedTx(tx_body=tx_body, tx_hash=tx_hash) except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Signing failed") # display the transaction in UI if not await show_tx( ctx, transaction.output_addresses, transaction.outgoing_coins, transaction.fee, transaction.network_name, transaction.inputs, transaction.outputs, ): raise wire.ActionCancelled("Signing cancelled") return tx
def _wrap_protobuf_load( buffer: bytes, expected_type: type[LoadedMessageType], ) -> LoadedMessageType: try: msg = protobuf.decode(buffer, expected_type, experimental_enabled) if __debug__ and utils.EMULATOR: log.debug( __name__, "received message contents:\n%s", utils.dump_protobuf(msg) ) return msg except Exception as e: if __debug__: log.exception(__name__, e) if e.args: raise DataError("Failed to decode message: " + " ".join(e.args)) else: raise DataError("Failed to decode message")
async def get_address(ctx, msg, keychain: seed.Keychain): await paths.validate_path(ctx, validate_full_path, keychain, msg.address_n, CURVE) try: address, _ = derive_address_and_node(keychain, msg.address_n) except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Deriving address failed") if msg.show_display: desc = address_n_to_str(msg.address_n) while True: if await show_address(ctx, address, desc=desc): break if await show_qr(ctx, address, desc=desc): break return CardanoAddress(address=address)
async def bootscreen(): while True: try: if not config.has_pin(): config.unlock(pin_to_int("")) storage.init_unlocked() return await lockscreen() label = None while True: pin = await request_pin(label, config.get_pin_rem()) if config.unlock(pin_to_int(pin)): storage.init_unlocked() return else: label = "Wrong PIN, enter again" except Exception as e: if __debug__: log.exception(__name__, e)