Example #1
0
async def load_device(ctx, msg):
    word_count = _validate(msg)
    is_slip39 = backup_types.is_slip39_word_count(word_count)

    if not is_slip39 and not msg.skip_checksum and not bip39.check(
            msg.mnemonics[0]):
        raise wire.ProcessError("Mnemonic is not valid")

    await _warn(ctx)

    if not is_slip39:  # BIP-39
        secret = msg.mnemonics[0].encode()
        backup_type = BackupType.Bip39
    else:
        identifier, iteration_exponent, secret, group_count = slip39.combine_mnemonics(
            msg.mnemonics)
        if group_count == 1:
            backup_type = BackupType.Slip39_Basic
        elif group_count > 1:
            backup_type = BackupType.Slip39_Advanced
        else:
            raise RuntimeError("Invalid group count")
        storage.device.set_slip39_identifier(identifier)
        storage.device.set_slip39_iteration_exponent(iteration_exponent)

    storage.device.store_mnemonic_secret(secret,
                                         backup_type,
                                         needs_backup=True,
                                         no_backup=False)
    storage.device.load_settings(use_passphrase=msg.passphrase_protection,
                                 label=msg.label)
    if msg.pin:
        config.change_pin(pin_to_int(""), pin_to_int(msg.pin), None, None)

    return Success(message="Device loaded")
Example #2
0
async def load_device(ctx, msg):
    # TODO implement SLIP-39
    if storage.is_initialized():
        raise wire.UnexpectedMessage("Already initialized")

    if msg.node is not None:
        raise wire.ProcessError("LoadDevice.node is not supported")

    if not msg.skip_checksum and not bip39.check(msg.mnemonic):
        raise wire.ProcessError("Mnemonic is not valid")

    text = Text("Loading seed")
    text.bold("Loading private seed", "is not recommended.")
    text.normal("Continue only if you", "know what you are doing!")
    await require_confirm(ctx, text)

    secret = bip39.process_all([msg.mnemonic])
    storage.device.store_mnemonic_secret(
        secret=secret,
        mnemonic_type=bip39.get_type(),
        needs_backup=True,
        no_backup=False,
    )
    storage.device.load_settings(use_passphrase=msg.passphrase_protection,
                                 label=msg.label)
    if msg.pin:
        config.change_pin(pin_to_int(""), pin_to_int(msg.pin))

    return Success(message="Device loaded")
Example #3
0
async def recovery_device(ctx, msg):
    """
    Recover BIP39 seed into empty device.

    1. Ask for the number of words in recovered seed.
    2. Let user type in the mnemonic words one by one.
    3. Optionally check the seed validity.
    4. Optionally ask for the PIN, with confirmation.
    5. Save into storage.
    """
    if not msg.dry_run and storage.is_initialized():
        raise wire.UnexpectedMessage("Already initialized")

    text = Text("Device recovery", ui.ICON_RECOVERY)
    text.normal("Do you really want to", "recover the device?", "")

    await require_confirm(ctx, text, code=ProtectCall)

    if msg.dry_run and config.has_pin():
        curpin = await request_pin_ack(ctx, "Enter PIN", config.get_pin_rem())
        if not config.check_pin(pin_to_int(curpin)):
            raise wire.PinInvalid("PIN invalid")

    # ask for the number of words
    wordcount = await request_wordcount(ctx)

    # ask for mnemonic words one by one
    mnemonic = await request_mnemonic(ctx, wordcount)

    # check mnemonic validity
    if msg.enforce_wordlist or msg.dry_run:
        if not bip39.check(mnemonic):
            raise wire.ProcessError("Mnemonic is not valid")

    # ask for pin repeatedly
    if msg.pin_protection:
        newpin = await request_pin_confirm(ctx, cancellable=False)

    # dry run
    if msg.dry_run:
        digest_input = sha256(mnemonic).digest()
        digest_stored = sha256(storage.get_mnemonic()).digest()
        if consteq(digest_stored, digest_input):
            return Success(
                message="The seed is valid and matches the one in the device")
        else:
            raise wire.ProcessError(
                "The seed is valid but does not match the one in the device")

    # save into storage
    if msg.pin_protection:
        config.change_pin(pin_to_int(""), pin_to_int(newpin))
    storage.set_u2f_counter(msg.u2f_counter)
    storage.load_settings(label=msg.label,
                          use_passphrase=msg.passphrase_protection)
    storage.load_mnemonic(mnemonic=mnemonic,
                          needs_backup=False,
                          no_backup=False)

    return Success(message="Device recovered")
Example #4
0
async def layout_load_device(ctx, msg):
    from trezor.crypto import bip39
    from trezor.messages.Success import Success
    from trezor.messages.FailureType import UnexpectedMessage, ProcessError
    from trezor.ui.text import Text
    from ..common.confirm import require_confirm
    from ..common import storage

    if storage.is_initialized():
        raise wire.FailureError(UnexpectedMessage, 'Already initialized')

    if msg.node is not None:
        raise wire.FailureError(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')

    await require_confirm(
        ctx,
        Text('Loading seed', ui.ICON_RESET, ui.BOLD, 'Loading private seed',
             'is not recommended.', ui.NORMAL, 'Continue only if you',
             'know what you are doing!'))

    storage.load_mnemonic(msg.mnemonic)
    storage.load_settings(use_passphrase=msg.passphrase_protection,
                          label=msg.label)
    if msg.pin:
        config.change_pin(pin_to_int(''), pin_to_int(msg.pin), None)

    return Success(message='Device loaded')
Example #5
0
async def load_device(ctx, msg):

    if storage.is_initialized():
        raise wire.FailureError(UnexpectedMessage, 'Already initialized')

    if msg.node is not None:
        raise wire.FailureError(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')

    await require_confirm(
        ctx,
        Text('Loading seed', ui.ICON_DEFAULT, ui.BOLD, 'Loading private seed',
             'is not recommended.', ui.NORMAL, 'Continue only if you',
             'know what you are doing!'))

    storage.load_mnemonic(mnemonic=msg.mnemonic, needs_backup=True)
    storage.load_settings(use_passphrase=msg.passphrase_protection,
                          label=msg.label)
    if msg.pin:
        config.change_pin(pin_to_int(''), pin_to_int(msg.pin), None)

    return Success(message='Device loaded')
Example #6
0
    def test_change_sd_salt(self):
        salt1 = b"0123456789abcdef0123456789abcdef"
        salt2 = b"0123456789ABCDEF0123456789ABCDEF"

        # Enable PIN and SD salt.
        config.init()
        config.wipe()
        self.assertTrue(config.unlock('', None))
        config.set(1, 1, b'value')
        self.assertFalse(config.change_pin('', '', salt1, None))
        self.assertTrue(config.change_pin('', '000', None, salt1))
        self.assertEqual(config.get(1, 1), b'value')

        # Disable PIN and change SD salt.
        config.init()
        self.assertFalse(config.unlock('000', None))
        self.assertIsNone(config.get(1, 1))
        self.assertTrue(config.unlock('000', salt1))
        self.assertTrue(config.change_pin('000', '', salt1, salt2))
        self.assertEqual(config.get(1, 1), b'value')

        # Disable SD salt.
        config.init()
        self.assertFalse(config.unlock('000', salt2))
        self.assertIsNone(config.get(1, 1))
        self.assertTrue(config.unlock('', salt2))
        self.assertTrue(config.change_pin('', '', salt2, None))
        self.assertEqual(config.get(1, 1), b'value')

        # Check that PIN and SD salt are disabled.
        config.init()
        self.assertTrue(config.unlock('', None))
        self.assertEqual(config.get(1, 1), b'value')
Example #7
0
async def reset_device(ctx, msg):
    if __debug__:
        global internal_entropy

    # 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)')
    if storage.is_initialized():
        raise wire.FailureError(
            FailureType.UnexpectedMessage,
            'Already initialized')

    if msg.pin_protection:
        # request new PIN
        newpin = await request_pin_confirm(ctx)
    else:
        # new PIN is empty
        newpin = ''

    # generate and display internal entropy
    internal_entropy = random.bytes(32)
    if msg.display_random:
        await show_entropy(ctx, internal_entropy)

    # request external entropy and compute mnemonic
    ack = await ctx.call(EntropyRequest(), wire_types.EntropyAck)
    mnemonic = generate_mnemonic(
        msg.strength, internal_entropy, ack.entropy)

    if msg.skip_backup:
        # let user backup the mnemonic later
        pass
    else:
        # warn user about mnemonic safety
        await show_warning(ctx)
        while True:
            # show mnemonic and require confirmation of a random word
            await show_mnemonic(ctx, mnemonic)
            if await check_mnemonic(ctx, mnemonic):
                break
            await show_wrong_entry(ctx)

    # 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')

    # write settings and mnemonic into storage
    storage.load_settings(
        label=msg.label, use_passphrase=msg.passphrase_protection)
    storage.load_mnemonic(
        mnemonic=mnemonic, needs_backup=msg.skip_backup)

    # show success message
    if not msg.skip_backup:
        await show_success(ctx)

    return Success(message='Initialized')
Example #8
0
async def sd_protect_disable(ctx: wire.Context, msg: SdProtect) -> Success:
    if not storage.sd_salt.is_enabled():
        raise wire.ProcessError("SD card protection not enabled")

    # Note that the SD card doesn't need to be present in order to disable SD
    # protection. The cleanup will not happen in such case, but that does not matter.

    # Confirm that user wants to proceed with the operation.
    await require_confirm_sd_protect(ctx, msg)

    # Get the current PIN and salt from the SD card.
    pin, salt = await request_pin_and_sd_salt(ctx, "Enter PIN")

    # Check PIN and remove salt.
    if not config.change_pin(pin_to_int(pin), pin_to_int(pin), salt, None):
        await error_pin_invalid(ctx)

    storage.device.set_sd_salt_auth_key(None)

    try:
        # Clean up.
        storage.sd_salt.remove_sd_salt()
    except Exception:
        # The cleanup is not necessary for the correct functioning of
        # SD-protection. If it fails for any reason, we suppress the exception,
        # because overall SD-protection was successfully disabled.
        pass

    await show_success(ctx, ("You have successfully", "disabled SD protection."))
    return Success(message="SD card protection disabled")
Example #9
0
async def sd_protect_refresh(ctx: wire.Context, msg: SdProtect) -> Success:
    if not storage.sd_salt.is_enabled():
        raise wire.ProcessError("SD card protection not enabled")

    # Confirm that user wants to proceed with the operation.
    await require_confirm_sd_protect(ctx, msg)

    # Make sure SD card is present.
    await ensure_sdcard(ctx)

    # Get the current PIN and salt from the SD card.
    pin, old_salt = await request_pin_and_sd_salt(ctx, "Enter PIN")

    # Check PIN and change salt.
    new_salt, new_auth_key, new_salt_tag = _make_salt()
    await _set_salt(ctx, new_salt, new_salt_tag, stage=True)

    if not config.change_pin(pin_to_int(pin), pin_to_int(pin), old_salt, new_salt):
        await error_pin_invalid(ctx)

    storage.device.set_sd_salt_auth_key(new_auth_key)

    try:
        # Clean up.
        storage.sd_salt.commit_sd_salt()
    except Exception:
        # If the cleanup fails, then request_sd_salt() will bring the SD card
        # into a consistent state. We suppress the exception, because overall
        # SD-protection was successfully refreshed.
        pass

    await show_success(ctx, ("You have successfully", "refreshed SD protection."))
    return Success(message="SD card protection refreshed")
Example #10
0
async def change_pin(ctx, msg):

    # confirm that user wants to change the pin
    await require_confirm_change_pin(ctx, msg)

    # get current pin, return failure if invalid
    if config.has_pin():
        curpin = await request_pin_ack(ctx)
        if not config.check_pin(pin_to_int(curpin), show_pin_timeout):
            raise wire.PinInvalid('PIN invalid')
    else:
        curpin = ''

    # get new pin
    if not msg.remove:
        newpin = await request_pin_confirm(ctx)
    else:
        newpin = ''

    # write into storage
    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 Success(message='PIN removed')
Example #11
0
async def sd_protect_enable(ctx: wire.Context, msg: SdProtect) -> Success:
    if storage.sd_salt.is_enabled():
        raise wire.ProcessError("SD card protection already enabled")

    # Confirm that user wants to proceed with the operation.
    await require_confirm_sd_protect(ctx, msg)

    # Make sure SD card is present.
    await ensure_sdcard(ctx)

    # Get the current PIN.
    if config.has_pin():
        pin = pin_to_int(await request_pin(ctx, "Enter PIN", config.get_pin_rem()))
    else:
        pin = pin_to_int("")

    # Check PIN and prepare salt file.
    salt, salt_auth_key, salt_tag = _make_salt()
    await _set_salt(ctx, salt, salt_tag)

    if not config.change_pin(pin, pin, None, salt):
        # Wrong PIN. Clean up the prepared salt file.
        try:
            storage.sd_salt.remove_sd_salt()
        except Exception:
            # The cleanup is not necessary for the correct functioning of
            # SD-protection. If it fails for any reason, we suppress the
            # exception, because primarily we need to raise wire.PinInvalid.
            pass
        await error_pin_invalid(ctx)

    storage.device.set_sd_salt_auth_key(salt_auth_key)

    await show_success(ctx, ("You have successfully", "enabled SD protection."))
    return Success(message="SD card protection enabled")
Example #12
0
async def change_pin(ctx, msg):

    # confirm that user wants to change the pin
    await require_confirm_change_pin(ctx, msg)

    # get current pin, return failure if invalid
    if config.has_pin():
        curpin = await request_pin_ack(ctx, "Enter old PIN",
                                       config.get_pin_rem())
        # if removing, defer check to change_pin()
        if not msg.remove:
            if not config.check_pin(pin_to_int(curpin)):
                raise wire.PinInvalid("PIN invalid")
    else:
        curpin = ""

    # get new pin
    if not msg.remove:
        newpin = await request_pin_confirm(ctx)
    else:
        newpin = ""

    # write into storage
    if not config.change_pin(pin_to_int(curpin), pin_to_int(newpin)):
        raise wire.PinInvalid("PIN invalid")

    if newpin:
        return Success(message="PIN changed")
    else:
        return Success(message="PIN removed")
Example #13
0
async def sd_protect_disable(ctx: wire.Context, msg: SdProtect) -> Success:
    if device.get_sd_salt_auth_key() is None:
        raise wire.ProcessError("SD card protection not enabled")

    # Confirm that user wants to proceed with the operation.
    await require_confirm_sd_protect(ctx, msg)

    # Get the current PIN and salt from the SD card.
    pin, salt = await request_pin_and_sd_salt(ctx, "Enter PIN")

    # Check PIN and remove salt.
    if not config.change_pin(pin_to_int(pin), pin_to_int(pin), salt, None):
        await show_pin_invalid(ctx)
        raise wire.PinInvalid("PIN invalid")

    device.set_sd_salt_auth_key(None)

    try:
        # Clean up.
        await remove_sd_salt(ctx)
    except Exception:
        # The cleanup is not necessary for the correct functioning of
        # SD-protection. If it fails for any reason, we suppress the exception,
        # because overall SD-protection was successfully disabled.
        pass

    await show_success(ctx,
                       ("You have successfully", "disabled SD protection."))
    return Success(message="SD card protection disabled")
Example #14
0
async def load_device(ctx, msg):
    if storage.is_initialized():
        raise wire.UnexpectedMessage("Already initialized")

    if msg.node is not None:
        raise wire.ProcessError("LoadDevice.node is not supported")

    if not msg.mnemonics:
        raise wire.ProcessError("No mnemonic provided")

    word_count = len(msg.mnemonics[0].split(" "))
    for m in msg.mnemonics[1:]:
        if word_count != len(m.split(" ")):
            raise wire.ProcessError(
                "All shares are required to have the same number of words")

    mnemonic_type = mnemonic.type_from_word_count(word_count)

    if (mnemonic_type == mnemonic.TYPE_BIP39 and not msg.skip_checksum
            and not bip39.check(msg.mnemonics[0])):
        raise wire.ProcessError("Mnemonic is not valid")

    text = Text("Loading seed")
    text.bold("Loading private seed", "is not recommended.")
    text.normal("Continue only if you", "know what you are doing!")
    await require_confirm(ctx, text)

    if mnemonic_type == mnemonic.TYPE_BIP39:
        secret = msg.mnemonics[0].encode()
    elif mnemonic_type == mnemonic.TYPE_SLIP39:
        identifier, iteration_exponent, secret = slip39.combine_mnemonics(
            msg.mnemonics)
        storage.device.set_slip39_identifier(identifier)
        storage.device.set_slip39_iteration_exponent(iteration_exponent)
    else:
        raise RuntimeError("Unknown mnemonic type")

    storage.device.store_mnemonic_secret(secret,
                                         mnemonic_type,
                                         needs_backup=True,
                                         no_backup=False)
    storage.device.load_settings(use_passphrase=msg.passphrase_protection,
                                 label=msg.label)
    if msg.pin:
        config.change_pin(pin_to_int(""), pin_to_int(msg.pin))

    return Success(message="Device loaded")
Example #15
0
async def recovery_device(ctx: wire.Context, msg: RecoveryDevice) -> Success:
    """
    Recover BIP39/SLIP39 seed into empty device.
    Recovery is also possible with replugged Trezor. We call this process Persistance.
    User starts the process here using the RecoveryDevice msg and then they can unplug
    the device anytime and continue without a computer.
    """
    _check_state(msg)

    if not msg.dry_run:
        title = "Recovery mode"
        text = Text(title, ui.ICON_RECOVERY)
        text.normal("Do you really want to", "recover a wallet?", "")
    else:
        title = "Seed check"
        text = Text(title, ui.ICON_RECOVERY)
        text.normal("Do you really want to", "check the recovery", "seed?")
    await require_confirm(ctx, text, code=ButtonRequestType.ProtectCall)

    # for dry run pin needs to entered
    if msg.dry_run:
        if config.has_pin():
            curpin = await request_pin_ack(ctx, "Enter PIN",
                                           config.get_pin_rem())
        else:
            curpin = ""
        if not config.check_pin(pin_to_int(curpin)):
            raise wire.PinInvalid("PIN invalid")

    # set up pin if requested
    if msg.pin_protection:
        if msg.dry_run:
            raise wire.ProcessError("Can't setup PIN during dry_run recovery.")
        newpin = await request_pin_confirm(ctx, allow_cancel=False)
        config.change_pin(pin_to_int(""), pin_to_int(newpin))

    if msg.u2f_counter:
        storage.device.set_u2f_counter(msg.u2f_counter)
    storage.device.load_settings(label=msg.label,
                                 use_passphrase=msg.passphrase_protection)
    storage.recovery.set_in_progress(True)
    if msg.dry_run:
        storage.recovery.set_dry_run(msg.dry_run)

    result = await recovery_process(ctx)

    return result
Example #16
0
async def reset_device(ctx, msg):
    # validate parameters and device state
    if msg.strength not in (128, 192, 256):
        raise wire.ProcessError(
            "Invalid strength (has to be 128, 192 or 256 bits)")
    if msg.display_random and (msg.skip_backup or msg.no_backup):
        raise wire.ProcessError(
            "Can't show internal entropy when backup is skipped")
    if storage.is_initialized():
        raise wire.UnexpectedMessage("Already initialized")

    # request new PIN
    if msg.pin_protection:
        newpin = await request_pin_confirm(ctx)
    else:
        newpin = ""

    # generate and display internal entropy
    internal_ent = random.bytes(32)
    if __debug__:
        debug.reset_internal_entropy = internal_ent
    if msg.display_random:
        await show_entropy(ctx, internal_ent)

    # request external entropy and compute mnemonic
    ent_ack = await ctx.call(EntropyRequest(), MessageType.EntropyAck)
    mnemonic = generate_mnemonic(msg.strength, internal_ent, ent_ack.entropy)

    if not msg.skip_backup and not msg.no_backup:
        # require confirmation of the mnemonic safety
        await show_warning(ctx)

        # show mnemonic and require confirmation of a random word
        while True:
            await show_mnemonic(ctx, mnemonic)
            if await check_mnemonic(ctx, mnemonic):
                break
            await show_wrong_entry(ctx)

    # write PIN into storage
    if not config.change_pin(pin_to_int(""), pin_to_int(newpin), None):
        raise wire.ProcessError("Could not change PIN")

    # write settings and mnemonic into storage
    storage.load_settings(label=msg.label,
                          use_passphrase=msg.passphrase_protection)
    storage.load_mnemonic(mnemonic=mnemonic,
                          needs_backup=msg.skip_backup,
                          no_backup=msg.no_backup)

    # show success message.  if we skipped backup, it's possible that homescreen
    # is still running, uninterrupted.  restart it to pick up new label.
    if not msg.skip_backup and not msg.no_backup:
        await show_success(ctx)
    else:
        workflow.restartdefault()

    return Success(message="Initialized")
Example #17
0
async def recovery_device(ctx, msg):
    """
    Recover BIP39 seed into empty device.

    1. Ask for the number of words in recovered seed.
    2. Let user type in the mnemonic words one by one.
    3. Optionally check the seed validity.
    4. Optionally ask for the PIN, with confirmation.
    5. Save into storage.
    """
    if not msg.dry_run and storage.is_initialized():
        raise wire.UnexpectedMessage("Already initialized")

    # ask for the number of words
    wordcount = await request_wordcount(ctx)

    # ask for mnemonic words one by one
    mnemonic = await request_mnemonic(ctx, wordcount)

    # check mnemonic validity
    if msg.enforce_wordlist or msg.dry_run:
        if not bip39.check(mnemonic):
            raise wire.ProcessError("Mnemonic is not valid")

    # ask for pin repeatedly
    if msg.pin_protection:
        newpin = await request_pin_confirm(ctx, cancellable=False)

    # save into storage
    if not msg.dry_run:
        if msg.pin_protection:
            config.change_pin(pin_to_int(""), pin_to_int(newpin), None)
        storage.load_settings(label=msg.label,
                              use_passphrase=msg.passphrase_protection)
        storage.load_mnemonic(mnemonic=mnemonic,
                              needs_backup=False,
                              no_backup=False)
        return Success(message="Device recovered")
    else:
        if storage.get_mnemonic() == mnemonic:
            return Success(
                message="The seed is valid and matches the one in the device")
        else:
            raise wire.ProcessError(
                "The seed is valid but does not match the one in the device")
Example #18
0
    def test_change_pin(self):
        config.init()
        config.wipe()
        self.assertTrue(config.unlock('', None))
        config.set(1, 1, b'value')
        PINS = ('123', '123', 'Trezor T', '3141592653589793238462643383279502884197', '')
        old_pin = ''
        for new_pin in PINS:
            self.assertTrue(config.unlock(old_pin, None))

            # The APP namespace which is reserved for storage related values is inaccessible even
            # when unlocked.
            with self.assertRaises(ValueError):
                config.set(PINAPP, PINKEY, b'value')

            self.assertTrue(config.change_pin(old_pin, new_pin, None, None))

            # Old PIN cannot be used to change the current PIN.
            if old_pin != new_pin:
                self.assertFalse(config.change_pin(old_pin, '666', None, None))

            # Storage remains unlocked.
            self.assertEqual(config.get(1, 1), b'value')

            # The APP namespace which is reserved for storage related values is inaccessible even
            # when unlocked.
            with self.assertRaises(ValueError):
                config.get(PINAPP, PINKEY)

            # Old PIN cannot be used to unlock storage.
            if old_pin != new_pin:
                config.init()
                self.assertFalse(config.unlock(old_pin, None))
                self.assertEqual(config.get(1, 1), None)
                with self.assertRaises(RuntimeError):
                    config.set(1, 1, b'new value')

            # New PIN unlocks the storage.
            self.assertTrue(config.unlock(new_pin, None))
            self.assertEqual(config.get(1, 1), b'value')

            # Lock the storage.
            config.init()
            old_pin = new_pin
 def test_change_pin(self):
     config.init()
     config.wipe()
     self.assertEqual(config.unlock(pin_to_int('')), True)
     with self.assertRaises(RuntimeError):
         config.set(PINAPP, PINKEY, b'value')
     self.assertEqual(
         config.change_pin(pin_to_int('000'), pin_to_int('666')), False)
     self.assertEqual(config.change_pin(pin_to_int(''), pin_to_int('000')),
                      True)
     self.assertEqual(config.get(PINAPP, PINKEY), None)
     config.set(1, 1, b'value')
     config.init()
     self.assertEqual(config.unlock(pin_to_int('000')), True)
     config.change_pin(pin_to_int('000'), pin_to_int(''))
     config.init()
     self.assertEqual(config.unlock(pin_to_int('000')), False)
     self.assertEqual(config.unlock(pin_to_int('')), True)
     self.assertEqual(config.get(1, 1), b'value')
Example #20
0
async def recovery_device(ctx: wire.Context, msg: RecoveryDevice) -> Success:
    """
    Recover BIP39/SLIP39 seed into empty device.
    Recovery is also possible with replugged Trezor. We call this process Persistence.
    User starts the process here using the RecoveryDevice msg and then they can unplug
    the device anytime and continue without a computer.
    """
    _validate(msg)

    if storage.recovery.is_in_progress():
        return await recovery_process(ctx)

    await _continue_dialog(ctx, msg)

    if not msg.dry_run:
        # wipe storage to make sure the device is in a clear state
        storage.reset()

    # for dry run pin needs to be entered
    if msg.dry_run:
        curpin, salt = await request_pin_and_sd_salt(ctx, "Enter PIN")
        if not config.check_pin(curpin, salt):
            await error_pin_invalid(ctx)

    if not msg.dry_run:
        # set up pin if requested
        if msg.pin_protection:
            newpin = await request_pin_confirm(ctx, allow_cancel=False)
            config.change_pin("", newpin, None, None)

        storage.device.set_passphrase_enabled(bool(msg.passphrase_protection))
        if msg.u2f_counter is not None:
            storage.device.set_u2f_counter(msg.u2f_counter)
        if msg.label is not None:
            storage.device.set_label(msg.label)

    storage.recovery.set_in_progress(True)
    storage.recovery.set_dry_run(bool(msg.dry_run))

    workflow.set_default(recovery_homescreen)
    return await recovery_process(ctx)
Example #21
0
async def load_device(ctx, msg):
    word_count = _validate(msg)
    is_slip39 = backup_types.is_slip39_word_count(word_count)

    if not is_slip39 and not msg.skip_checksum and not bip39.check(
            msg.mnemonics[0]):
        raise wire.ProcessError("Mnemonic is not valid")

    await _warn(ctx)

    if not is_slip39:  # BIP-39
        secret = msg.mnemonics[0].encode()
        backup_type = BackupType.Bip39
    else:
        identifier, iteration_exponent, secret = slip39.recover_ems(
            msg.mnemonics)

        # this must succeed if the recover_ems call succeeded
        share = slip39.decode_mnemonic(msg.mnemonics[0])
        if share.group_count == 1:
            backup_type = BackupType.Slip39_Basic
        elif share.group_count > 1:
            backup_type = BackupType.Slip39_Advanced
        else:
            raise wire.ProcessError("Invalid group count")

        storage.device.set_slip39_identifier(identifier)
        storage.device.set_slip39_iteration_exponent(iteration_exponent)

    storage.device.store_mnemonic_secret(
        secret,
        backup_type,
        needs_backup=msg.needs_backup is True,
        no_backup=msg.no_backup is True,
    )
    storage.device.set_passphrase_enabled(msg.passphrase_protection)
    storage.device.set_label(msg.label or "")
    if msg.pin:
        config.change_pin(pin_to_int(""), pin_to_int(msg.pin), None, None)

    return Success(message="Device loaded")
Example #22
0
async def recovery_device(ctx: wire.Context, msg: RecoveryDevice) -> Success:
    """
    Recover BIP39/SLIP39 seed into empty device.
    Recovery is also possible with replugged Trezor. We call this process Persistance.
    User starts the process here using the RecoveryDevice msg and then they can unplug
    the device anytime and continue without a computer.
    """
    _check_state(msg)

    if storage.recovery.is_in_progress():
        return await recovery_process(ctx)

    await _continue_dialog(ctx, msg)

    # for dry run pin needs to be entered
    if msg.dry_run:
        curpin, salt = await request_pin_and_sd_salt(ctx, "Enter PIN")
        if not config.check_pin(pin_to_int(curpin), salt):
            await show_pin_invalid(ctx)
            raise wire.PinInvalid("PIN invalid")

    # set up pin if requested
    if msg.pin_protection:
        if msg.dry_run:
            raise wire.ProcessError("Can't setup PIN during dry_run recovery.")
        newpin = await request_pin_confirm(ctx, allow_cancel=False)
        config.change_pin(pin_to_int(""), pin_to_int(newpin), None, None)

    if msg.u2f_counter:
        storage.device.set_u2f_counter(msg.u2f_counter)
    storage.device.load_settings(label=msg.label,
                                 use_passphrase=msg.passphrase_protection)
    storage.recovery.set_in_progress(True)
    if msg.dry_run:
        storage.recovery.set_dry_run(msg.dry_run)

    workflow.replace_default(recovery_homescreen)
    return await recovery_process(ctx)
Example #23
0
async def recovery_device(ctx, msg):
    '''
    Recover BIP39 seed into empty device.

    1. Ask for the number of words in recovered seed.
    2. Let user type in the mnemonic words one by one.
    3. Optionally check the seed validity.
    4. Optionally ask for the PIN, with confirmation.
    5. Save into storage.
    '''
    if storage.is_initialized():
        raise wire.FailureError(UnexpectedMessage, 'Already initialized')

    # ask for the number of words
    wordcount = await request_wordcount(ctx)

    # ask for mnemonic words one by one
    mnemonic = await request_mnemonic(ctx, wordcount)

    # check mnemonic validity
    if msg.enforce_wordlist:
        if not bip39.check(mnemonic):
            raise wire.FailureError(ProcessError, 'Mnemonic is not valid')

    # ask for pin repeatedly
    if msg.pin_protection:
        newpin = await request_pin_confirm(ctx, cancellable=False)

    # save into storage
    if not msg.dry_run:
        if msg.pin_protection:
            config.change_pin(pin_to_int(''), pin_to_int(newpin), None)
        storage.load_settings(
            label=msg.label, use_passphrase=msg.passphrase_protection)
        storage.load_mnemonic(
            mnemonic=mnemonic, needs_backup=False)

    return Success()
Example #24
0
async def recovery_device(ctx, msg):
    '''
    Recover BIP39 seed into empty device.

    1. Ask for the number of words in recovered seed.
    2. Let user type in the mnemonic words one by one.
    3. Optionally check the seed validity.
    4. Optionally ask for the PIN, with confirmation.
    5. Save into storage.
    '''
    from trezor import config
    from trezor.crypto import bip39
    from trezor.messages.FailureType import UnexpectedMessage, ProcessError
    from trezor.messages.Success import Success
    from trezor.ui.text import Text
    from apps.common import storage
    from apps.common.request_pin import request_pin
    from apps.common.request_words import request_words

    if storage.is_initialized():
        raise wire.FailureError(UnexpectedMessage, 'Already initialized')

    wordcount = await request_words(
        ctx, Text('Device recovery', ui.ICON_RECOVERY, 'Number of words?'))
    mnemonic = await request_mnemonic(wordcount, 'Type %s. word')

    if msg.enforce_wordlist and not bip39.check(mnemonic):
        raise wire.FailureError(ProcessError, 'Mnemonic is not valid')

    if msg.pin_protection:
        curpin = ''
        newpin = await request_pin(ctx)
        config.change_pin(curpin, newpin)

    storage.load_settings(label=msg.label,
                          use_passphrase=msg.passphrase_protection)
    storage.load_mnemonic(mnemonic)
    return Success()
 def test_lock(self):
     for _ in range(128):
         config.init()
         config.wipe()
         self.assertEqual(config.unlock(pin_to_int(''), None), True)
         appid, key = random_entry()
         value = random.bytes(16)
         config.set(appid, key, value)
         config.init()
         self.assertEqual(config.get(appid, key), bytes())
         with self.assertRaises(RuntimeError):
             config.set(appid, key, bytes())
     config.init()
     config.wipe()
     self.assertEqual(
         config.change_pin(pin_to_int(''), pin_to_int('000'), None), False)
Example #26
0
async def change_pin(ctx: wire.Context, msg: ChangePin) -> Success:
    if not is_initialized():
        raise wire.NotInitialized("Device is not initialized")

    # confirm that user wants to change the pin
    await require_confirm_change_pin(ctx, msg)

    # get old pin
    curpin, salt = await request_pin_and_sd_salt(ctx, "Enter old PIN")

    # if changing pin, pre-check the entered pin before getting new pin
    if curpin and not msg.remove:
        if not config.check_pin(pin_to_int(curpin), salt):
            await show_pin_invalid(ctx)
            raise wire.PinInvalid("PIN invalid")

    # get new pin
    if not msg.remove:
        newpin = await request_pin_confirm(ctx)
    else:
        newpin = ""

    # write into storage
    if not config.change_pin(pin_to_int(curpin), pin_to_int(newpin), salt,
                             salt):
        if newpin:
            await show_pin_matches_wipe_code(ctx)
        else:
            await show_pin_invalid(ctx)
        raise wire.PinInvalid("PIN invalid")

    if newpin:
        if curpin:
            msg_screen = "changed your PIN."
            msg_wire = "PIN changed"
        else:
            msg_screen = "enabled PIN protection."
            msg_wire = "PIN enabled"
    else:
        msg_screen = "disabled PIN protection."
        msg_wire = "PIN removed"

    await show_success(ctx, ("You have successfully", msg_screen))
    return Success(message=msg_wire)
Example #27
0
async def sd_protect_enable(ctx: wire.Context, msg: SdProtect) -> Success:
    salt_auth_key = device.get_sd_salt_auth_key()
    if salt_auth_key is not None:
        raise wire.ProcessError("SD card protection already enabled")

    # Confirm that user wants to proceed with the operation.
    await require_confirm_sd_protect(ctx, msg)

    # Get the current PIN.
    if config.has_pin():
        pin = pin_to_int(await request_pin_ack(ctx, "Enter PIN",
                                               config.get_pin_rem()))
    else:
        pin = pin_to_int("")

    # Check PIN and prepare salt file.
    salt = random.bytes(SD_SALT_LEN_BYTES)
    salt_auth_key = random.bytes(SD_SALT_AUTH_KEY_LEN_BYTES)
    salt_tag = hmac.new(salt_auth_key, salt,
                        sha256).digest()[:SD_SALT_AUTH_TAG_LEN_BYTES]
    try:
        await set_sd_salt(ctx, salt, salt_tag)
    except Exception:
        raise wire.ProcessError("Failed to write to SD card")

    if not config.change_pin(pin, pin, None, salt):
        # Wrong PIN. Clean up the prepared salt file.
        try:
            await remove_sd_salt(ctx)
        except Exception:
            # The cleanup is not necessary for the correct functioning of
            # SD-protection. If it fails for any reason, we suppress the
            # exception, because primarily we need to raise wire.PinInvalid.
            pass
        await show_pin_invalid(ctx)
        raise wire.PinInvalid("PIN invalid")

    device.set_sd_salt_auth_key(salt_auth_key)

    await show_success(ctx,
                       ("You have successfully", "enabled SD protection."))
    return Success(message="SD card protection enabled")
Example #28
0
async def sd_protect_refresh(ctx: wire.Context, msg: SdProtect) -> Success:
    if device.get_sd_salt_auth_key() is None:
        raise wire.ProcessError("SD card protection not enabled")

    # Confirm that user wants to proceed with the operation.
    await require_confirm_sd_protect(ctx, msg)

    # Get the current PIN and salt from the SD card.
    pin, old_salt = await request_pin_and_sd_salt(ctx, "Enter PIN")

    # Check PIN and change salt.
    new_salt = random.bytes(SD_SALT_LEN_BYTES)
    new_salt_auth_key = random.bytes(SD_SALT_AUTH_KEY_LEN_BYTES)
    new_salt_tag = hmac.new(new_salt_auth_key, new_salt,
                            sha256).digest()[:SD_SALT_AUTH_TAG_LEN_BYTES]
    try:
        await stage_sd_salt(ctx, new_salt, new_salt_tag)
    except Exception:
        raise wire.ProcessError("Failed to write to SD card")

    if not config.change_pin(pin_to_int(pin), pin_to_int(pin), old_salt,
                             new_salt):
        await show_pin_invalid(ctx)
        raise wire.PinInvalid("PIN invalid")

    device.set_sd_salt_auth_key(new_salt_auth_key)

    try:
        # Clean up.
        await commit_sd_salt(ctx)
    except Exception:
        # If the cleanup fails, then request_sd_salt() will bring the SD card
        # into a consistent state. We suppress the exception, because overall
        # SD-protection was successfully refreshed.
        pass

    await show_success(ctx,
                       ("You have successfully", "refreshed SD protection."))
    return Success(message="SD card protection refreshed")
Example #29
0
async def layout_change_pin(ctx, msg):
    from trezor.messages.Success import Success
    from trezor.messages.Failure import Failure
    from trezor.messages import FailureType, PinMatrixRequestType

    await confirm_change_pin(ctx, msg)
    if config.has_pin():
        curr_pin = await request_pin(ctx, PinMatrixRequestType.Current)
    else:
        curr_pin = ''
    if msg.remove:
        new_pin = ''
    else:
        new_pin = await request_pin_confirm(ctx)

    if config.change_pin(pin_to_int(curr_pin), pin_to_int(new_pin), show_pin_timeout):
        if new_pin:
            return Success(message='PIN changed')
        else:
            return Success(message='PIN removed')
    else:
        return Failure(code=FailureType.PinInvalid, message='PIN invalid')
Example #30
0
async def reset_device(ctx: wire.Context, msg: ResetDevice) -> Success:
    # validate parameters and device state
    _validate_reset_device(msg)

    # make sure user knows they're setting up a new wallet
    if msg.backup_type == BackupType.Slip39_Basic:
        prompt = "Create a new wallet\nwith Shamir Backup?"
    elif msg.backup_type == BackupType.Slip39_Advanced:
        prompt = "Create a new wallet\nwith Super Shamir?"
    else:
        prompt = "Do you want to create\na new wallet?"
    await confirm_reset_device(ctx, prompt)
    await LoadingAnimation()

    # wipe storage to make sure the device is in a clear state
    storage.reset()

    # request and set new PIN
    if msg.pin_protection:
        newpin = await request_pin_confirm(ctx)
        if not config.change_pin("", newpin, None, None):
            raise wire.ProcessError("Failed to set PIN")

    # generate and display internal entropy
    int_entropy = random.bytes(32)
    if __debug__:
        debug.reset_internal_entropy = int_entropy
    if msg.display_random:
        await layout.show_internal_entropy(ctx, int_entropy)

    # request external entropy and compute the master secret
    entropy_ack = await ctx.call(EntropyRequest(), EntropyAck)
    ext_entropy = entropy_ack.entropy
    # For SLIP-39 this is the Encrypted Master Secret
    secret = _compute_secret_from_entropy(int_entropy, ext_entropy, msg.strength)

    # Check backup type, perform type-specific handling
    if msg.backup_type == BackupType.Bip39:
        # in BIP-39 we store mnemonic string instead of the secret
        secret = bip39.from_data(secret).encode()
    elif msg.backup_type in (BackupType.Slip39_Basic, BackupType.Slip39_Advanced):
        # generate and set SLIP39 parameters
        storage.device.set_slip39_identifier(slip39.generate_random_identifier())
        storage.device.set_slip39_iteration_exponent(slip39.DEFAULT_ITERATION_EXPONENT)
    else:
        # Unknown backup type.
        raise RuntimeError

    # If either of skip_backup or no_backup is specified, we are not doing backup now.
    # Otherwise, we try to do it.
    perform_backup = not msg.no_backup and not msg.skip_backup

    # If doing backup, ask the user to confirm.
    if perform_backup:
        perform_backup = await confirm_backup(ctx)

    # generate and display backup information for the master secret
    if perform_backup:
        await backup_seed(ctx, msg.backup_type, secret)

    # write settings and master secret into storage
    if msg.label is not None:
        storage.device.set_label(msg.label)
    storage.device.set_passphrase_enabled(bool(msg.passphrase_protection))
    storage.device.store_mnemonic_secret(
        secret,  # for SLIP-39, this is the EMS
        msg.backup_type,
        needs_backup=not perform_backup,
        no_backup=msg.no_backup,
    )

    # if we backed up the wallet, show success message
    if perform_backup:
        await layout.show_backup_success(ctx)

    return Success(message="Initialized")