From 3084d1196d1db33e9ab57ffdb77d94481ea47378 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Thu, 13 Aug 2020 23:29:50 +0200 Subject: [PATCH] feat(core): Support 50 digit PIN and wipe code. --- core/CHANGELOG.md | 2 + .../extmod/modtrezorconfig/modtrezorconfig.c | 48 +++++++---- .../extmod/modtrezorconfig/norcow_config.h | 2 +- core/mocks/generated/trezorconfig.pyi | 14 +-- core/src/apps/common/request_pin.py | 9 +- core/src/apps/debug/load_device.py | 3 +- core/src/apps/management/change_pin.py | 5 +- core/src/apps/management/change_wipe_code.py | 5 +- .../management/recovery_device/__init__.py | 5 +- .../apps/management/reset_device/__init__.py | 3 +- core/src/apps/management/sd_protect.py | 9 +- core/src/trezor/pin.py | 4 - core/src/trezor/ui/components/tt/pin.py | 36 ++++++-- core/tests/test_storage.py | 1 - core/tests/test_trezor.config.py | 85 ++++++++++++------- 15 files changed, 141 insertions(+), 90 deletions(-) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index eae87420f..a146c7fd5 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Allow decreasing the output value in RBF transactions. [#1491] - Cardano: Allow stake pool registrations with zero margin. [#1502] - Cardano: Assets are now shown as CIP-0014. [#1510] +- Support PIN of unlimited length. [#1167] ### Deprecated @@ -358,6 +359,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). [#1139]: https://github.com/trezor/trezor-firmware/issues/1139 [#1159]: https://github.com/trezor/trezor-firmware/issues/1159 [#1165]: https://github.com/trezor/trezor-firmware/pull/1165 +[#1167]: https://github.com/trezor/trezor-firmware/issues/1167 [#1173]: https://github.com/trezor/trezor-firmware/pull/1173 [#1184]: https://github.com/trezor/trezor-firmware/issues/1184 [#1188]: https://github.com/trezor/trezor-firmware/issues/1188 diff --git a/core/embed/extmod/modtrezorconfig/modtrezorconfig.c b/core/embed/extmod/modtrezorconfig/modtrezorconfig.c index 1c2518ddc..44bb8c9be 100644 --- a/core/embed/extmod/modtrezorconfig/modtrezorconfig.c +++ b/core/embed/extmod/modtrezorconfig/modtrezorconfig.c @@ -66,13 +66,15 @@ STATIC mp_obj_t mod_trezorconfig_init(size_t n_args, const mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorconfig_init_obj, 0, 1, mod_trezorconfig_init); -/// def unlock(pin: int, ext_salt: Optional[bytes]) -> bool: +/// def unlock(pin: str, ext_salt: Optional[bytes]) -> bool: /// """ /// Attempts to unlock the storage with the given PIN and external salt. /// Returns True on success, False on failure. /// """ STATIC mp_obj_t mod_trezorconfig_unlock(mp_obj_t pin, mp_obj_t ext_salt) { - uint32_t pin_i = trezor_obj_get_uint(pin); + mp_buffer_info_t pin_b = {0}; + mp_get_buffer_raise(pin, &pin_b, MP_BUFFER_READ); + mp_buffer_info_t ext_salt_b = {0}; ext_salt_b.buf = NULL; if (ext_salt != mp_const_none) { @@ -81,7 +83,7 @@ STATIC mp_obj_t mod_trezorconfig_unlock(mp_obj_t pin, mp_obj_t ext_salt) { mp_raise_msg(&mp_type_ValueError, "Invalid length of external salt."); } - if (sectrue != storage_unlock(pin_i, ext_salt_b.buf)) { + if (sectrue != storage_unlock(pin_b.buf, pin_b.len, ext_salt_b.buf)) { return mp_const_false; } return mp_const_true; @@ -89,7 +91,7 @@ STATIC mp_obj_t mod_trezorconfig_unlock(mp_obj_t pin, mp_obj_t ext_salt) { STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorconfig_unlock_obj, mod_trezorconfig_unlock); -/// def check_pin(pin: int, ext_salt: Optional[bytes]) -> bool: +/// def check_pin(pin: str, ext_salt: Optional[bytes]) -> bool: /// """ /// Check the given PIN with the given external salt. /// Returns True on success, False on failure. @@ -148,8 +150,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_get_pin_rem_obj, mod_trezorconfig_get_pin_rem); /// def change_pin( -/// oldpin: int, -/// newpin: int, +/// oldpin: str, +/// newpin: str, /// old_ext_salt: Optional[bytes], /// new_ext_salt: Optional[bytes], /// ) -> bool: @@ -158,8 +160,12 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_get_pin_rem_obj, /// """ STATIC mp_obj_t mod_trezorconfig_change_pin(size_t n_args, const mp_obj_t *args) { - uint32_t oldpin = trezor_obj_get_uint(args[0]); - uint32_t newpin = trezor_obj_get_uint(args[1]); + mp_buffer_info_t oldpin = {0}; + mp_get_buffer_raise(args[0], &oldpin, MP_BUFFER_READ); + + mp_buffer_info_t newpin = {0}; + mp_get_buffer_raise(args[1], &newpin, MP_BUFFER_READ); + mp_buffer_info_t ext_salt_b = {0}; const uint8_t *old_ext_salt = NULL; if (args[2] != mp_const_none) { @@ -176,8 +182,8 @@ STATIC mp_obj_t mod_trezorconfig_change_pin(size_t n_args, new_ext_salt = ext_salt_b.buf; } - if (sectrue != - storage_change_pin(oldpin, newpin, old_ext_salt, new_ext_salt)) { + if (sectrue != storage_change_pin(oldpin.buf, oldpin.len, newpin.buf, + newpin.len, old_ext_salt, new_ext_salt)) { return mp_const_false; } return mp_const_true; @@ -185,13 +191,14 @@ STATIC mp_obj_t mod_trezorconfig_change_pin(size_t n_args, STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorconfig_change_pin_obj, 4, 4, mod_trezorconfig_change_pin); -/// def ensure_not_wipe_code(pin: int) -> None: +/// def ensure_not_wipe_code(pin: str) -> None: /// """ /// Wipes the device if the entered PIN is the wipe code. /// """ STATIC mp_obj_t mod_trezorconfig_ensure_not_wipe_code(mp_obj_t pin) { - uint32_t pin_i = trezor_obj_get_uint(pin); - storage_ensure_not_wipe_code(pin_i); + mp_buffer_info_t pin_b = {0}; + mp_get_buffer_raise(pin, &pin_b, MP_BUFFER_READ); + storage_ensure_not_wipe_code(pin_b.buf, pin_b.len); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorconfig_ensure_not_wipe_code_obj, @@ -211,17 +218,21 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorconfig_has_wipe_code_obj, mod_trezorconfig_has_wipe_code); /// def change_wipe_code( -/// pin: int, +/// pin: str, /// ext_salt: Optional[bytes], -/// wipe_code: int, +/// wipe_code: str, /// ) -> bool: /// """ /// Change wipe code. Returns True on success, False on failure. /// """ STATIC mp_obj_t mod_trezorconfig_change_wipe_code(size_t n_args, const mp_obj_t *args) { - uint32_t pin = trezor_obj_get_uint(args[0]); - uint32_t wipe_code = trezor_obj_get_uint(args[2]); + mp_buffer_info_t pin_b = {0}; + mp_get_buffer_raise(args[0], &pin_b, MP_BUFFER_READ); + + mp_buffer_info_t wipe_code_b = {0}; + mp_get_buffer_raise(args[2], &wipe_code_b, MP_BUFFER_READ); + mp_buffer_info_t ext_salt_b = {0}; const uint8_t *ext_salt = NULL; if (args[1] != mp_const_none) { @@ -231,7 +242,8 @@ STATIC mp_obj_t mod_trezorconfig_change_wipe_code(size_t n_args, ext_salt = ext_salt_b.buf; } - if (sectrue != storage_change_wipe_code(pin, ext_salt, wipe_code)) { + if (sectrue != storage_change_wipe_code(pin_b.buf, pin_b.len, ext_salt, + wipe_code_b.buf, wipe_code_b.len)) { return mp_const_false; } return mp_const_true; diff --git a/core/embed/extmod/modtrezorconfig/norcow_config.h b/core/embed/extmod/modtrezorconfig/norcow_config.h index 4b42b2aa5..422a81d09 100644 --- a/core/embed/extmod/modtrezorconfig/norcow_config.h +++ b/core/embed/extmod/modtrezorconfig/norcow_config.h @@ -39,6 +39,6 @@ /* * Current storage version. */ -#define NORCOW_VERSION ((uint32_t)0x00000002) +#define NORCOW_VERSION ((uint32_t)0x00000003) #endif diff --git a/core/mocks/generated/trezorconfig.pyi b/core/mocks/generated/trezorconfig.pyi index ccc7063ba..e0060b3cf 100644 --- a/core/mocks/generated/trezorconfig.pyi +++ b/core/mocks/generated/trezorconfig.pyi @@ -12,7 +12,7 @@ def init( # extmod/modtrezorconfig/modtrezorconfig.c -def unlock(pin: int, ext_salt: Optional[bytes]) -> bool: +def unlock(pin: str, ext_salt: Optional[bytes]) -> bool: """ Attempts to unlock the storage with the given PIN and external salt. Returns True on success, False on failure. @@ -20,7 +20,7 @@ def unlock(pin: int, ext_salt: Optional[bytes]) -> bool: # extmod/modtrezorconfig/modtrezorconfig.c -def check_pin(pin: int, ext_salt: Optional[bytes]) -> bool: +def check_pin(pin: str, ext_salt: Optional[bytes]) -> bool: """ Check the given PIN with the given external salt. Returns True on success, False on failure. @@ -57,8 +57,8 @@ def get_pin_rem() -> int: # extmod/modtrezorconfig/modtrezorconfig.c def change_pin( - oldpin: int, - newpin: int, + oldpin: str, + newpin: str, old_ext_salt: Optional[bytes], new_ext_salt: Optional[bytes], ) -> bool: @@ -68,7 +68,7 @@ def change_pin( # extmod/modtrezorconfig/modtrezorconfig.c -def ensure_not_wipe_code(pin: int) -> None: +def ensure_not_wipe_code(pin: str) -> None: """ Wipes the device if the entered PIN is the wipe code. """ @@ -83,9 +83,9 @@ def has_wipe_code() -> bool: # extmod/modtrezorconfig/modtrezorconfig.c def change_wipe_code( - pin: int, + pin: str, ext_salt: Optional[bytes], - wipe_code: int, + wipe_code: str, ) -> bool: """ Change wipe code. Returns True on success, False on failure. diff --git a/core/src/apps/common/request_pin.py b/core/src/apps/common/request_pin.py index bb734eab1..340786774 100644 --- a/core/src/apps/common/request_pin.py +++ b/core/src/apps/common/request_pin.py @@ -3,7 +3,6 @@ import utime import storage.sd_salt from trezor import config, ui, wire from trezor.messages import ButtonRequestType -from trezor.pin import pin_to_int from trezor.ui.components.tt.pin import CANCELLED, PinDialog from trezor.ui.components.tt.text import Text from trezor.ui.popup import Popup @@ -71,7 +70,7 @@ async def request_pin_and_sd_salt( ) -> Tuple[str, Optional[bytearray]]: if config.has_pin(): pin = await request_pin(ctx, prompt, config.get_pin_rem(), allow_cancel) - config.ensure_not_wipe_code(pin_to_int(pin)) + config.ensure_not_wipe_code(pin) else: pin = "" @@ -98,7 +97,7 @@ async def verify_user_pin( if config.has_pin(): pin = await request_pin(ctx, prompt, config.get_pin_rem(), allow_cancel) - config.ensure_not_wipe_code(pin_to_int(pin)) + config.ensure_not_wipe_code(pin) else: pin = "" @@ -106,7 +105,7 @@ async def verify_user_pin( salt = await request_sd_salt(ctx) except SdCardUnavailable: raise wire.PinCancelled("SD salt is unavailable") - if config.unlock(pin_to_int(pin), salt): + if config.unlock(pin, salt): _last_successful_unlock = utime.ticks_ms() return elif not config.has_pin(): @@ -116,7 +115,7 @@ async def verify_user_pin( pin = await request_pin( ctx, "Wrong PIN, enter again", config.get_pin_rem(), allow_cancel ) - if config.unlock(pin_to_int(pin), salt): + if config.unlock(pin, salt): _last_successful_unlock = utime.ticks_ms() return diff --git a/core/src/apps/debug/load_device.py b/core/src/apps/debug/load_device.py index a295d452d..91933033a 100644 --- a/core/src/apps/debug/load_device.py +++ b/core/src/apps/debug/load_device.py @@ -4,7 +4,6 @@ from trezor import config, wire from trezor.crypto import bip39, slip39 from trezor.messages import BackupType from trezor.messages.Success import Success -from trezor.pin import pin_to_int from trezor.ui.layouts import confirm_action, require from apps.management import backup_types @@ -46,7 +45,7 @@ async def load_device(ctx, msg): 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) + config.change_pin("", msg.pin, None, None) return Success(message="Device loaded") diff --git a/core/src/apps/management/change_pin.py b/core/src/apps/management/change_pin.py index 527a93e8f..3d52d86bf 100644 --- a/core/src/apps/management/change_pin.py +++ b/core/src/apps/management/change_pin.py @@ -1,7 +1,6 @@ from storage.device import is_initialized from trezor import config, ui, wire from trezor.messages.Success import Success -from trezor.pin import pin_to_int from trezor.ui.components.tt.text import Text from trezor.ui.layouts import require, show_success @@ -29,7 +28,7 @@ async def change_pin(ctx: wire.Context, msg: ChangePin) -> Success: # 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): + if not config.check_pin(curpin, salt): await error_pin_invalid(ctx) # get new pin @@ -39,7 +38,7 @@ async def change_pin(ctx: wire.Context, msg: ChangePin) -> Success: newpin = "" # write into storage - if not config.change_pin(pin_to_int(curpin), pin_to_int(newpin), salt, salt): + if not config.change_pin(curpin, newpin, salt, salt): if newpin: await error_pin_matches_wipe_code(ctx) else: diff --git a/core/src/apps/management/change_wipe_code.py b/core/src/apps/management/change_wipe_code.py index d0b429334..2088e89f4 100644 --- a/core/src/apps/management/change_wipe_code.py +++ b/core/src/apps/management/change_wipe_code.py @@ -1,7 +1,6 @@ from storage.device import is_initialized from trezor import config, ui, wire from trezor.messages.Success import Success -from trezor.pin import pin_to_int from trezor.ui.components.tt.text import Text from trezor.ui.layouts import require, show_success from trezor.ui.popup import Popup @@ -30,7 +29,7 @@ async def change_wipe_code(ctx: wire.Context, msg: ChangeWipeCode) -> Success: if not msg.remove: # Pre-check the entered PIN. - if config.has_pin() and not config.check_pin(pin_to_int(pin), salt): + if config.has_pin() and not config.check_pin(pin, salt): await error_pin_invalid(ctx) # Get new wipe code. @@ -39,7 +38,7 @@ async def change_wipe_code(ctx: wire.Context, msg: ChangeWipeCode) -> Success: wipe_code = "" # Write into storage. - if not config.change_wipe_code(pin_to_int(pin), salt, pin_to_int(wipe_code)): + if not config.change_wipe_code(pin, salt, wipe_code): await error_pin_invalid(ctx) if wipe_code: diff --git a/core/src/apps/management/recovery_device/__init__.py b/core/src/apps/management/recovery_device/__init__.py index 25274a6b7..2d2c973e6 100644 --- a/core/src/apps/management/recovery_device/__init__.py +++ b/core/src/apps/management/recovery_device/__init__.py @@ -4,7 +4,6 @@ import storage.recovery from trezor import config, ui, wire, workflow from trezor.messages import ButtonRequestType from trezor.messages.Success import Success -from trezor.pin import pin_to_int from trezor.ui.components.tt.text import Text from apps.common.confirm import require_confirm @@ -47,14 +46,14 @@ async def recovery_device(ctx: wire.Context, msg: RecoveryDevice) -> Success: # 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): + 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(pin_to_int(""), pin_to_int(newpin), None, None) + config.change_pin("", newpin, None, None) storage.device.set_passphrase_enabled(bool(msg.passphrase_protection)) if msg.u2f_counter is not None: diff --git a/core/src/apps/management/reset_device/__init__.py b/core/src/apps/management/reset_device/__init__.py index e6da3257e..d8e7aafd7 100644 --- a/core/src/apps/management/reset_device/__init__.py +++ b/core/src/apps/management/reset_device/__init__.py @@ -6,7 +6,6 @@ from trezor.messages import BackupType from trezor.messages.EntropyAck import EntropyAck from trezor.messages.EntropyRequest import EntropyRequest from trezor.messages.Success import Success -from trezor.pin import pin_to_int from trezor.ui.layouts import confirm_backup, confirm_reset_device, require from trezor.ui.loader import LoadingAnimation @@ -43,7 +42,7 @@ async def reset_device(ctx: wire.Context, msg: ResetDevice) -> Success: # request and set new PIN if msg.pin_protection: newpin = await request_pin_confirm(ctx) - if not config.change_pin(pin_to_int(""), pin_to_int(newpin), None, None): + if not config.change_pin("", newpin, None, None): raise wire.ProcessError("Failed to set PIN") # generate and display internal entropy diff --git a/core/src/apps/management/sd_protect.py b/core/src/apps/management/sd_protect.py index 283053d9e..eb477ef0a 100644 --- a/core/src/apps/management/sd_protect.py +++ b/core/src/apps/management/sd_protect.py @@ -4,7 +4,6 @@ from trezor import config, ui, wire from trezor.crypto import random from trezor.messages import SdProtectOperationType from trezor.messages.Success import Success -from trezor.pin import pin_to_int from trezor.ui.components.tt.text import Text from trezor.ui.layouts import require, show_success @@ -66,9 +65,9 @@ async def sd_protect_enable(ctx: wire.Context, msg: SdProtect) -> Success: # Get the current PIN. if config.has_pin(): - pin = pin_to_int(await request_pin(ctx, "Enter PIN", config.get_pin_rem())) + pin = await request_pin(ctx, "Enter PIN", config.get_pin_rem()) else: - pin = pin_to_int("") + pin = "" # Check PIN and prepare salt file. salt, salt_auth_key, salt_tag = _make_salt() @@ -107,7 +106,7 @@ async def sd_protect_disable(ctx: wire.Context, msg: SdProtect) -> Success: 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): + if not config.change_pin(pin, pin, salt, None): await error_pin_invalid(ctx) storage.device.set_sd_salt_auth_key(None) @@ -144,7 +143,7 @@ async def sd_protect_refresh(ctx: wire.Context, msg: SdProtect) -> Success: 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): + if not config.change_pin(pin, pin, old_salt, new_salt): await error_pin_invalid(ctx) storage.device.set_sd_salt_auth_key(new_auth_key) diff --git a/core/src/trezor/pin.py b/core/src/trezor/pin.py index a2888714f..d94374b90 100644 --- a/core/src/trezor/pin.py +++ b/core/src/trezor/pin.py @@ -4,10 +4,6 @@ if False: from typing import Any, Optional -def pin_to_int(pin: str) -> int: - return int("1" + pin) - - _previous_progress: Optional[int] = None _previous_seconds: Optional[int] = None keepalive_callback: Any = None diff --git a/core/src/trezor/ui/components/tt/pin.py b/core/src/trezor/ui/components/tt/pin.py index ab7389380..e90594f25 100644 --- a/core/src/trezor/ui/components/tt/pin.py +++ b/core/src/trezor/ui/components/tt/pin.py @@ -41,16 +41,40 @@ class PinInput(ui.Component): self.repaint = False def render_pin(self) -> None: - display.bar(0, 0, ui.WIDTH, 50, ui.BG) - count = len(self.pin) + MAX_LENGTH = const(14) # maximum length of displayed PIN + CONTD_MARK = "<" BOX_WIDTH = const(240) DOT_SIZE = const(10) - PADDING = const(14) + PADDING = const(4) RENDER_Y = const(20) - render_x = (BOX_WIDTH - count * PADDING) // 2 + TWITCH = const(3) + + display.bar(0, 0, ui.WIDTH, 50, ui.BG) + + if len(self.pin) > MAX_LENGTH: + contd_width = display.text_width(CONTD_MARK, ui.BOLD) + PADDING + twitch = TWITCH * (len(self.pin) % 2) + else: + contd_width = 0 + twitch = 0 + + count = min(len(self.pin), MAX_LENGTH) + render_x = (BOX_WIDTH - count * (DOT_SIZE + PADDING) - contd_width) // 2 + + if contd_width: + display.text( + render_x, RENDER_Y + DOT_SIZE, CONTD_MARK, ui.BOLD, ui.GREY, ui.BG + ) + for i in range(0, count): display.bar_radius( - render_x + i * PADDING, RENDER_Y, DOT_SIZE, DOT_SIZE, ui.GREY, ui.BG, 4 + render_x + contd_width + twitch + i * (DOT_SIZE + PADDING), + RENDER_Y, + DOT_SIZE, + DOT_SIZE, + ui.GREY, + ui.BG, + 4, ) def render_prompt(self) -> None: @@ -82,7 +106,7 @@ class PinDialog(ui.Layout): prompt: str, subprompt: Optional[str], allow_cancel: bool = True, - maxlength: int = 9, + maxlength: int = 50, ) -> None: self.maxlength = maxlength self.input = PinInput(prompt, subprompt, "") diff --git a/core/tests/test_storage.py b/core/tests/test_storage.py index 3103b922c..c0b4023e7 100644 --- a/core/tests/test_storage.py +++ b/core/tests/test_storage.py @@ -1,5 +1,4 @@ from common import * -from trezor.pin import pin_to_int from trezor import config from storage import device diff --git a/core/tests/test_trezor.config.py b/core/tests/test_trezor.config.py index a6879996d..14e95e03c 100644 --- a/core/tests/test_trezor.config.py +++ b/core/tests/test_trezor.config.py @@ -1,7 +1,6 @@ from common import * from trezor.crypto import random -from trezor.pin import pin_to_int from trezor import config @@ -27,7 +26,7 @@ class TestConfig(unittest.TestCase): def test_wipe(self): config.init() config.wipe() - self.assertEqual(config.unlock(pin_to_int(''), None), True) + self.assertEqual(config.unlock('', None), True) config.set(1, 1, b'hello') config.set(1, 2, b'world') v0 = config.get(1, 1) @@ -44,7 +43,7 @@ class TestConfig(unittest.TestCase): for _ in range(128): config.init() config.wipe() - self.assertEqual(config.unlock(pin_to_int(''), None), True) + self.assertEqual(config.unlock('', None), True) appid, key = random_entry() value = random.bytes(16) config.set(appid, key, value) @@ -58,7 +57,7 @@ class TestConfig(unittest.TestCase): def test_public(self): config.init() config.wipe() - self.assertEqual(config.unlock(pin_to_int(''), None), True) + self.assertEqual(config.unlock('', None), True) appid, key = random_entry() @@ -84,20 +83,46 @@ class TestConfig(unittest.TestCase): def test_change_pin(self): config.init() config.wipe() - self.assertEqual(config.unlock(pin_to_int(''), None), True) - with self.assertRaises(RuntimeError): - config.set(PINAPP, PINKEY, b'value') - self.assertEqual(config.change_pin(pin_to_int('000'), pin_to_int('666'), None, None), False) - self.assertEqual(config.change_pin(pin_to_int(''), pin_to_int('000'), None, None), True) - self.assertEqual(config.get(PINAPP, PINKEY), None) + self.assertTrue(config.unlock('', None)) config.set(1, 1, b'value') - config.init() - self.assertEqual(config.unlock(pin_to_int('000'), None), True) - config.change_pin(pin_to_int('000'), pin_to_int(''), None, None) - config.init() - self.assertEqual(config.unlock(pin_to_int('000'), None), False) - self.assertEqual(config.unlock(pin_to_int(''), None), True) - self.assertEqual(config.get(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(RuntimeError): + 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. + self.assertEqual(config.get(PINAPP, PINKEY), None) + + # 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_sd_salt(self): salt1 = b"0123456789abcdef0123456789abcdef" @@ -106,37 +131,37 @@ class TestConfig(unittest.TestCase): # Enable PIN and SD salt. config.init() config.wipe() - self.assertTrue(config.unlock(pin_to_int(''), None)) + self.assertTrue(config.unlock('', None)) config.set(1, 1, b'value') - self.assertFalse(config.change_pin(pin_to_int(''), pin_to_int(''), salt1, None)) - self.assertTrue(config.change_pin(pin_to_int(''), pin_to_int('000'), None, salt1)) + 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(pin_to_int('000'), None)) + self.assertFalse(config.unlock('000', None)) self.assertIsNone(config.get(1, 1)) - self.assertTrue(config.unlock(pin_to_int('000'), salt1)) - self.assertTrue(config.change_pin(pin_to_int('000'), pin_to_int(''), salt1, salt2)) + 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(pin_to_int('000'), salt2)) + self.assertFalse(config.unlock('000', salt2)) self.assertIsNone(config.get(1, 1)) - self.assertTrue(config.unlock(pin_to_int(''), salt2)) - self.assertTrue(config.change_pin(pin_to_int(''), pin_to_int(''), salt2, None)) + 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(pin_to_int(''), None)) + self.assertTrue(config.unlock('', None)) self.assertEqual(config.get(1, 1), b'value') def test_set_get(self): config.init() config.wipe() - self.assertEqual(config.unlock(pin_to_int(''), None), True) + self.assertEqual(config.unlock('', None), True) for _ in range(32): appid, key = random_entry() value = random.bytes(128) @@ -147,7 +172,7 @@ class TestConfig(unittest.TestCase): def test_compact(self): config.init() config.wipe() - self.assertEqual(config.unlock(pin_to_int(''), None), True) + self.assertEqual(config.unlock('', None), True) appid, key = 1, 1 for _ in range(259): value = random.bytes(259) @@ -158,7 +183,7 @@ class TestConfig(unittest.TestCase): def test_get_default(self): config.init() config.wipe() - self.assertEqual(config.unlock(pin_to_int(''), None), True) + self.assertEqual(config.unlock('', None), True) for _ in range(128): appid, key = random_entry() value = config.get(appid, key)