diff --git a/src/apps/common/confirm.py b/src/apps/common/confirm.py index c71f914497..95e091be71 100644 --- a/src/apps/common/confirm.py +++ b/src/apps/common/confirm.py @@ -1,5 +1,5 @@ from trezor import ui, wire -from trezor.messages import ButtonRequestType, FailureType, wire_types +from trezor.messages import ButtonRequestType, wire_types from trezor.messages.ButtonRequest import ButtonRequest from trezor.ui.confirm import CONFIRMED, ConfirmDialog, HoldToConfirmDialog @@ -29,10 +29,10 @@ async def hold_to_confirm(ctx, content, code=None, *args, **kwargs): async def require_confirm(*args, **kwargs): confirmed = await confirm(*args, **kwargs) if not confirmed: - raise wire.FailureError(FailureType.ActionCancelled, 'Cancelled') + raise wire.ActionCancelled('Cancelled') async def require_hold_to_confirm(*args, **kwargs): confirmed = await hold_to_confirm(*args, **kwargs) if not confirmed: - raise wire.FailureError(FailureType.ActionCancelled, 'Cancelled') + raise wire.ActionCancelled('Cancelled') diff --git a/src/apps/common/request_passphrase.py b/src/apps/common/request_passphrase.py index c347c65f79..c7d2ccddef 100644 --- a/src/apps/common/request_passphrase.py +++ b/src/apps/common/request_passphrase.py @@ -1,7 +1,6 @@ from trezor import ui, wire from trezor.messages import ButtonRequestType, wire_types from trezor.messages.ButtonRequest import ButtonRequest -from trezor.messages.FailureType import ActionCancelled, ProcessError from trezor.messages import PassphraseSourceType from trezor.messages.PassphraseRequest import PassphraseRequest from trezor.messages.PassphraseStateRequest import PassphraseStateRequest @@ -24,7 +23,7 @@ async def request_passphrase_entry(ctx): wire_types.ButtonAck, wire_types.Cancel) if ack.MESSAGE_WIRE_TYPE == wire_types.Cancel: - raise wire.FailureError(ActionCancelled, 'Passphrase cancelled') + raise wire.ActionCancelled('Passphrase cancelled') selector = EntrySelector(text) return await ctx.wait(selector) @@ -41,18 +40,18 @@ async def request_passphrase_ack(ctx, on_device): req = PassphraseRequest(on_device=on_device) ack = await ctx.call(req, wire_types.PassphraseAck, wire_types.Cancel) if ack.MESSAGE_WIRE_TYPE == wire_types.Cancel: - raise wire.FailureError(ActionCancelled, 'Passphrase cancelled') + raise wire.ActionCancelled('Passphrase cancelled') if on_device: if ack.passphrase is not None: - raise wire.FailureError(ProcessError, 'Passphrase provided when it should not be') + raise wire.ProcessError('Passphrase provided when it should not be') keyboard = PassphraseKeyboard('Enter passphrase') passphrase = await ctx.wait(keyboard) if passphrase == CANCELLED: - raise wire.FailureError(ActionCancelled, 'Passphrase cancelled') + raise wire.ActionCancelled('Passphrase cancelled') else: if ack.passphrase is None: - raise wire.FailureError(ProcessError, 'Passphrase not provided') + raise wire.ProcessError('Passphrase not provided') passphrase = ack.passphrase req = PassphraseStateRequest(state=get_state(state=ack.state, passphrase=passphrase)) diff --git a/src/apps/common/seed.py b/src/apps/common/seed.py index 9647a22ad2..218ce84e69 100644 --- a/src/apps/common/seed.py +++ b/src/apps/common/seed.py @@ -1,6 +1,5 @@ from trezor import wire from trezor.crypto import bip32, bip39 -from trezor.messages.FailureType import ProcessError from apps.common import cache, storage from apps.common.request_passphrase import protect_by_passphrase @@ -24,7 +23,7 @@ async def _get_seed(ctx: wire.Context) -> bytes: async def _compute_seed(ctx: wire.Context) -> (bytes, str): if not storage.is_initialized(): - raise wire.FailureError(ProcessError, 'Device is not initialized') + raise wire.ProcessError('Device is not initialized') passphrase = await protect_by_passphrase(ctx) return bip39.seed(storage.get_mnemonic(), passphrase), passphrase diff --git a/src/apps/management/apply_settings.py b/src/apps/management/apply_settings.py index 4bf4616448..bc42099fd9 100644 --- a/src/apps/management/apply_settings.py +++ b/src/apps/management/apply_settings.py @@ -1,5 +1,5 @@ from trezor import ui, wire -from trezor.messages import ButtonRequestType, FailureType, PassphraseSourceType +from trezor.messages import ButtonRequestType, PassphraseSourceType from trezor.messages.Success import Success from trezor.ui.text import Text from apps.common import storage @@ -8,11 +8,11 @@ from apps.common.confirm import require_confirm async def apply_settings(ctx, msg): if msg.homescreen is None and msg.label is None and msg.use_passphrase is None and msg.passphrase_source is None: - raise wire.FailureError(FailureType.ProcessError, 'No setting provided') + raise wire.ProcessError('No setting provided') if msg.homescreen is not None: if len(msg.homescreen) > storage.HOMESCREEN_MAXSIZE: - raise wire.FailureError(FailureType.DataError, 'Homescreen is too complex') + raise wire.DataError('Homescreen is too complex') await require_confirm(ctx, Text( 'Change homescreen', ui.ICON_CONFIG, 'Do you really want to', 'change homescreen?'), diff --git a/src/apps/management/backup_device.py b/src/apps/management/backup_device.py index 6eb8bd4c72..175bd171c5 100644 --- a/src/apps/management/backup_device.py +++ b/src/apps/management/backup_device.py @@ -1,16 +1,19 @@ from trezor import wire -from trezor.messages.FailureType import ProcessError from trezor.messages.Success import Success from apps.common import storage -from apps.management.reset_device import (check_mnemonic, show_mnemonic, - show_warning, show_wrong_entry) +from apps.management.reset_device import ( + check_mnemonic, + show_mnemonic, + show_warning, + show_wrong_entry, +) async def backup_device(ctx, msg): if not storage.is_initialized(): - raise wire.FailureError(ProcessError, 'Device is not initialized') + raise wire.ProcessError('Device is not initialized') if not storage.needs_backup(): - raise wire.FailureError(ProcessError, 'Seed already backed up') + raise wire.ProcessError('Seed already backed up') mnemonic = storage.get_mnemonic() diff --git a/src/apps/management/change_pin.py b/src/apps/management/change_pin.py index a921f8c401..44cb2f2ab5 100644 --- a/src/apps/management/change_pin.py +++ b/src/apps/management/change_pin.py @@ -1,8 +1,7 @@ from trezor import config, loop, ui, wire -from trezor.messages import FailureType, wire_types +from trezor.messages import wire_types from trezor.messages.ButtonRequest import ButtonRequest from trezor.messages.ButtonRequestType import Other -from trezor.messages.Failure import Failure from trezor.messages.Success import Success from trezor.pin import pin_to_int, show_pin_timeout from trezor.ui.text import Text @@ -19,7 +18,7 @@ async def change_pin(ctx, msg): if config.has_pin(): curpin = await request_pin_ack(ctx) if not config.check_pin(pin_to_int(curpin), show_pin_timeout): - return Failure(code=FailureType.PinInvalid, message='PIN invalid') + raise wire.PinInvalid('PIN invalid') else: curpin = '' @@ -30,13 +29,13 @@ async def change_pin(ctx, msg): newpin = '' # write into storage - if config.change_pin(pin_to_int(curpin), pin_to_int(newpin), show_pin_timeout): - if newpin: - return Success(message='PIN changed') - else: - return Success(message='PIN removed') + if not config.change_pin(pin_to_int(curpin), pin_to_int(newpin), show_pin_timeout): + raise wire.PinInvalid('PIN invalid') + + if newpin: + return Success(message='PIN changed') else: - return Failure(code=FailureType.PinInvalid, message='PIN invalid') + return Success(message='PIN removed') def require_confirm_change_pin(ctx, msg): @@ -75,7 +74,7 @@ async def request_pin_ack(ctx, *args, **kwargs): await ctx.call(ButtonRequest(code=Other), wire_types.ButtonAck) return await ctx.wait(request_pin(*args, **kwargs)) except PinCancelled: - raise wire.FailureError(FailureType.ActionCancelled, 'Cancelled') + raise wire.ActionCancelled('Cancelled') @ui.layout diff --git a/src/apps/management/load_device.py b/src/apps/management/load_device.py index d4165baaa0..cd2aa461dd 100644 --- a/src/apps/management/load_device.py +++ b/src/apps/management/load_device.py @@ -1,6 +1,5 @@ from trezor import config, ui, wire from trezor.crypto import bip39 -from trezor.messages.FailureType import ProcessError, UnexpectedMessage from trezor.messages.Success import Success from trezor.pin import pin_to_int from trezor.ui.text import Text @@ -11,13 +10,13 @@ from apps.common.confirm import require_confirm async def load_device(ctx, msg): if storage.is_initialized(): - raise wire.FailureError(UnexpectedMessage, 'Already initialized') + raise wire.UnexpectedMessage('Already initialized') if msg.node is not None: - raise wire.FailureError(ProcessError, 'LoadDevice.node is not supported') + raise wire.ProcessError('LoadDevice.node is not supported') if not msg.skip_checksum and not bip39.check(msg.mnemonic): - raise wire.FailureError(ProcessError, 'Mnemonic is not valid') + raise wire.ProcessError('Mnemonic is not valid') await require_confirm(ctx, Text( 'Loading seed', ui.ICON_DEFAULT, diff --git a/src/apps/management/recovery_device.py b/src/apps/management/recovery_device.py index 6c39490174..b4c377805c 100644 --- a/src/apps/management/recovery_device.py +++ b/src/apps/management/recovery_device.py @@ -25,7 +25,7 @@ async def recovery_device(ctx, msg): 5. Save into storage. ''' if not msg.dry_run and storage.is_initialized(): - raise wire.FailureError(UnexpectedMessage, 'Already initialized') + raise wire.UnexpectedMessage('Already initialized') # ask for the number of words wordcount = await request_wordcount(ctx) @@ -36,7 +36,7 @@ async def recovery_device(ctx, msg): # check mnemonic validity if msg.enforce_wordlist or msg.dry_run: if not bip39.check(mnemonic): - raise wire.FailureError(ProcessError, 'Mnemonic is not valid') + raise wire.ProcessError('Mnemonic is not valid') # ask for pin repeatedly if msg.pin_protection: @@ -55,7 +55,7 @@ async def recovery_device(ctx, msg): if storage.get_mnemonic() == mnemonic: return Success(message='The seed is valid and matches the one in the device') else: - raise wire.FailureError(ProcessError, 'The seed is valid but does not match the one in the device') + raise wire.ProcessError('The seed is valid but does not match the one in the device') @ui.layout diff --git a/src/apps/management/reset_device.py b/src/apps/management/reset_device.py index f671edf8ca..5de9d11d18 100644 --- a/src/apps/management/reset_device.py +++ b/src/apps/management/reset_device.py @@ -1,7 +1,7 @@ from micropython import const from trezor import config, ui, wire from trezor.crypto import bip39, hashlib, random -from trezor.messages import ButtonRequestType, FailureType, wire_types +from trezor.messages import ButtonRequestType, wire_types from trezor.messages.ButtonRequest import ButtonRequest from trezor.messages.EntropyRequest import EntropyRequest from trezor.messages.Success import Success @@ -24,13 +24,9 @@ if __debug__: async def reset_device(ctx, msg): # validate parameters and device state if msg.strength not in (128, 192, 256): - raise wire.FailureError( - FailureType.ProcessError, - 'Invalid strength (has to be 128, 192 or 256 bits)') + raise wire.ProcessError('Invalid strength (has to be 128, 192 or 256 bits)') if storage.is_initialized(): - raise wire.FailureError( - FailureType.UnexpectedMessage, - 'Already initialized') + raise wire.UnexpectedMessage('Already initialized') # request new PIN if msg.pin_protection: @@ -62,8 +58,7 @@ async def reset_device(ctx, msg): # write PIN into storage if not config.change_pin(pin_to_int(''), pin_to_int(newpin), None): - raise wire.FailureError( - FailureType.ProcessError, 'Could not change PIN') + raise wire.ProcessError('Could not change PIN') # write settings and mnemonic into storage storage.load_settings( diff --git a/src/apps/management/set_u2f_counter.py b/src/apps/management/set_u2f_counter.py index 3114340340..343f48374d 100644 --- a/src/apps/management/set_u2f_counter.py +++ b/src/apps/management/set_u2f_counter.py @@ -1,5 +1,5 @@ from trezor import ui, wire -from trezor.messages import ButtonRequestType, FailureType +from trezor.messages import ButtonRequestType from trezor.messages.Success import Success from trezor.ui.text import Text from apps.common import storage @@ -8,7 +8,7 @@ from apps.common.confirm import require_confirm async def set_u2f_counter(ctx, msg): if msg.u2f_counter is None: - raise wire.FailureError(FailureType.ProcessError, 'No value provided provided') + raise wire.ProcessError('No value provided') await require_confirm(ctx, Text( 'Set U2F counter', ui.ICON_CONFIG, diff --git a/src/apps/wallet/cipher_key_value.py b/src/apps/wallet/cipher_key_value.py index 7a4d064f73..82adc13ba8 100644 --- a/src/apps/wallet/cipher_key_value.py +++ b/src/apps/wallet/cipher_key_value.py @@ -3,7 +3,6 @@ from trezor.crypto import hmac from trezor.crypto.aes import AES_CBC_Decrypt, AES_CBC_Encrypt from trezor.crypto.hashlib import sha512 from trezor.messages.CipheredKeyValue import CipheredKeyValue -from trezor.messages.FailureType import DataError from trezor.ui.text import Text, TEXT_MARGIN_LEFT from trezor.utils import split_words from apps.common import seed @@ -12,8 +11,7 @@ from apps.common.confirm import require_confirm async def cipher_key_value(ctx, msg): if len(msg.value) % 16 > 0: - raise wire.FailureError( - DataError, 'Value length must be a multiple of 16') + raise wire.DataError('Value length must be a multiple of 16') encrypt = msg.encrypt decrypt = not msg.encrypt diff --git a/src/apps/wallet/sign_message.py b/src/apps/wallet/sign_message.py index b82043171b..83128ba572 100644 --- a/src/apps/wallet/sign_message.py +++ b/src/apps/wallet/sign_message.py @@ -1,5 +1,4 @@ -from trezor import ui -from trezor.wire import FailureError +from trezor import ui, wire from trezor.crypto.curve import secp256k1 from trezor.messages.InputScriptType import SPENDADDRESS, SPENDP2SHWITNESS, SPENDWITNESS from trezor.messages.FailureType import ProcessError @@ -34,7 +33,7 @@ async def sign_message(ctx, msg): elif script_type == SPENDWITNESS: signature = bytes([signature[0] + 8]) + signature[1:] else: - raise FailureError(ProcessError, 'Unsupported script type') + raise wire.ProcessError('Unsupported script type') return MessageSignature(address=address, signature=signature) diff --git a/src/apps/wallet/sign_tx/__init__.py b/src/apps/wallet/sign_tx/__init__.py index 109195a2b0..14269b54a5 100644 --- a/src/apps/wallet/sign_tx/__init__.py +++ b/src/apps/wallet/sign_tx/__init__.py @@ -17,15 +17,15 @@ async def sign_tx(ctx, msg): try: req = signer.send(res) except signing.SigningError as e: - raise wire.FailureError(*e.args) + raise wire.Error(*e.args) except signing.MultisigError as e: - raise wire.FailureError(*e.args) + raise wire.Error(*e.args) except signing.AddressError as e: - raise wire.FailureError(*e.args) + raise wire.Error(*e.args) except signing.ScriptsError as e: - raise wire.FailureError(*e.args) + raise wire.Error(*e.args) except signing.Bip143Error as e: - raise wire.FailureError(*e.args) + raise wire.Error(*e.args) if req.__qualname__ == 'TxRequest': if req.request_type == TXFINISHED: break diff --git a/src/apps/wallet/verify_message.py b/src/apps/wallet/verify_message.py index 7d1705c847..9f33b530c5 100644 --- a/src/apps/wallet/verify_message.py +++ b/src/apps/wallet/verify_message.py @@ -1,7 +1,6 @@ from trezor import ui, wire from trezor.crypto.curve import secp256k1 from trezor.messages.InputScriptType import SPENDADDRESS, SPENDP2SHWITNESS, SPENDWITNESS -from trezor.messages.FailureType import ProcessError from trezor.messages.Success import Success from trezor.ui.text import Text from apps.common import coins @@ -31,12 +30,12 @@ async def verify_message(ctx, msg): script_type = SPENDWITNESS # native segwit signature = bytes([signature[0] - 8]) + signature[1:] else: - raise wire.FailureError(ProcessError, 'Invalid signature') + raise wire.ProcessError('Invalid signature') pubkey = secp256k1.verify_recover(signature, digest) if not pubkey: - raise wire.FailureError(ProcessError, 'Invalid signature') + raise wire.ProcessError('Invalid signature') if script_type == SPENDADDRESS: addr = address_pkh(pubkey, coin.address_type) @@ -45,10 +44,10 @@ async def verify_message(ctx, msg): elif script_type == SPENDWITNESS: addr = address_p2wpkh(pubkey, coin.bech32_prefix) else: - raise wire.FailureError(ProcessError, 'Invalid signature') + raise wire.ProcessError('Invalid signature') if addr != address: - raise wire.FailureError(ProcessError, 'Invalid signature') + raise wire.ProcessError('Invalid signature') await require_confirm_verify_message(ctx, address, message) diff --git a/src/trezor/wire/__init__.py b/src/trezor/wire/__init__.py index b771323c33..4b7de8c30f 100644 --- a/src/trezor/wire/__init__.py +++ b/src/trezor/wire/__init__.py @@ -1,12 +1,7 @@ import protobuf - -from trezor import log -from trezor import loop -from trezor import messages -from trezor import utils -from trezor import workflow - -from . import codec_v1 +from trezor import log, loop, messages, utils, workflow +from trezor.wire import codec_v1 +from trezor.wire.errors import * workflow_handlers = {} @@ -101,13 +96,6 @@ class UnexpectedMessageError(Exception): self.reader = reader -class FailureError(Exception): - def __init__(self, code, message): - super().__init__() - self.code = code - self.message = message - - async def session_handler(iface, sid): reader = None ctx = Context(iface, sid) @@ -135,8 +123,8 @@ async def session_handler(iface, sid): # retry with opened reader from the exception reader = exc.reader continue - except FailureError as exc: - # we log FailureError as warning, not as exception + except Error as exc: + # we log wire.Error as warning, not as exception log.warning(__name__, 'failure: %s', exc.message) except Exception as exc: # sessions are never closed by raised exceptions @@ -148,7 +136,6 @@ async def session_handler(iface, sid): async def protobuf_workflow(ctx, reader, handler, *args): from trezor.messages.Failure import Failure - from trezor.messages.FailureType import FirmwareError req = await protobuf.load_message(reader, messages.get_type(reader.type)) try: @@ -156,13 +143,13 @@ async def protobuf_workflow(ctx, reader, handler, *args): except UnexpectedMessageError: # session handler takes care of this one raise - except FailureError as exc: + except Error as exc: # respond with specific code and message await ctx.write(Failure(code=exc.code, message=exc.message)) raise except Exception as exc: # respond with a generic code and message - await ctx.write(Failure(code=FirmwareError, message='Firmware error')) + await ctx.write(Failure(code=FailureType.FirmwareError, message='Firmware error')) raise if res: # respond with a specific response @@ -171,7 +158,6 @@ async def protobuf_workflow(ctx, reader, handler, *args): async def unexpected_msg(ctx, reader): from trezor.messages.Failure import Failure - from trezor.messages.FailureType import UnexpectedMessage # receive the message and throw it away while reader.size > 0: @@ -179,5 +165,4 @@ async def unexpected_msg(ctx, reader): await reader.areadinto(buf) # respond with an unknown message error - await ctx.write( - Failure(code=UnexpectedMessage, message='Unexpected message')) + await ctx.write(Failure(code=FailureType.UnexpectedMessage, message='Unexpected message')) diff --git a/src/trezor/wire/errors.py b/src/trezor/wire/errors.py new file mode 100644 index 0000000000..4a4e152f5d --- /dev/null +++ b/src/trezor/wire/errors.py @@ -0,0 +1,73 @@ +from trezor.messages import FailureType + + +class Error(Exception): + def __init__(self, code, message): + super().__init__() + self.code = code + self.message = message + + +class UnexpectedMessage(Error): + def __init__(self, message): + super().__init__(FailureType.UnexpectedMessage, message) + + +class ButtonExpected(Error): + def __init__(self, message): + super().__init__(FailureType.ButtonExpected, message) + + +class DataError(Error): + def __init__(self, message): + super().__init__(FailureType.DataError, message) + + +class ActionCancelled(Error): + def __init__(self, message): + super().__init__(FailureType.ActionCancelled, message) + + +class PinExpected(Error): + def __init__(self, message): + super().__init__(FailureType.PinExpected, message) + + +class PinCancelled(Error): + def __init__(self, message): + super().__init__(FailureType.PinCancelled, message) + + +class PinInvalid(Error): + def __init__(self, message): + super().__init__(FailureType.PinInvalid, message) + + +class InvalidSignature(Error): + def __init__(self, message): + super().__init__(FailureType.InvalidSignature, message) + + +class ProcessError(Error): + def __init__(self, message): + super().__init__(FailureType.ProcessError, message) + + +class NotEnoughFunds(Error): + def __init__(self, message): + super().__init__(FailureType.NotEnoughFunds, message) + + +class NotInitialized(Error): + def __init__(self, message): + super().__init__(FailureType.NotInitialized, message) + + +class PinMismatch(Error): + def __init__(self, message): + super().__init__(FailureType.PinMismatch, message) + + +class FirmwareError(Error): + def __init__(self, message): + super().__init__(FailureType.FirmwareError, message)