Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/krux/krux_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@

DEFAULT_LOCALE = "en-US"

TAMPER_CHECK_INPUT_SCAN_QR = "Scan QR"
TAMPER_CHECK_INPUT_MANUAL = "Manual"
TAMPER_CHECK_INPUT_ASK_EVERY_TIME = "Ask every time"

DEFAULT_TX_PIN = (
board.config["board_info"]["CONNEXT_A"]
if "CONNEXT_A" in board.config["board_info"]
Expand Down Expand Up @@ -475,13 +479,23 @@ class SecuritySettings(SettingsNamespace):
auto_shutdown = NumberSetting(int, "auto_shutdown", 10, [0, 60])
hide_mnemonic = CategorySetting("hide_mnemonic", False, [False, True])
boot_flash_hash = CategorySetting("boot_flash_hash", False, [False, True])
tamper_check_code_input_mode = CategorySetting(
"tamper_check_code_input_mode",
TAMPER_CHECK_INPUT_ASK_EVERY_TIME,
[
TAMPER_CHECK_INPUT_SCAN_QR,
TAMPER_CHECK_INPUT_MANUAL,
TAMPER_CHECK_INPUT_ASK_EVERY_TIME,
],
)

def label(self, attr):
"""Returns a label for UI when given a setting name or namespace"""
return {
"auto_shutdown": t("Shutdown Time"),
"hide_mnemonic": t("Hide Mnemonics"),
"boot_flash_hash": t("TC Flash Hash at Boot"),
"tamper_check_code_input_mode": t("TC Input Mode"),
}[attr]


Expand Down
43 changes: 30 additions & 13 deletions src/krux/pages/settings_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,15 +260,36 @@ def handler():
)
for ns in namespace_list
]
items.extend(
[
(
settings_namespace.label(setting.attr),
self.setting(settings_namespace, setting),
)
for setting in setting_list
]
)
if settings_namespace.namespace == "settings.security":
regular_settings = []
tc_input_item = None
for setting in setting_list:
if setting.attr == "tamper_check_code_input_mode":
tc_input_item = (
settings_namespace.label(setting.attr),
self.setting(settings_namespace, setting),
)
else:
regular_settings.append(
(
settings_namespace.label(setting.attr),
self.setting(settings_namespace, setting),
)
)
items.extend(regular_settings)
items.append((t("Tamper Check Code"), self.enter_modify_tc_code))
if tc_input_item:
items.append(tc_input_item)
else:
items.extend(
[
(
settings_namespace.label(setting.attr),
self.setting(settings_namespace, setting),
)
for setting in setting_list
]
)

# If there is only one item in the namespace, don't show a submenu
# and instead jump straight to the item's menu
Expand All @@ -281,10 +302,6 @@ def handler():
items.append((t("Factory Settings"), self.restore_settings))
back_status = self._settings_exit_check

# Case for security settings
if settings_namespace.namespace == "settings.security":
items.append((t("Tamper Check Code"), self.enter_modify_tc_code))

submenu = Menu(self.ctx, items, back_status=back_status)
index, status = submenu.run_loop()
if index == submenu.back_index:
Expand Down
95 changes: 85 additions & 10 deletions src/krux/pages/tc_code_verification.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,17 @@
UPPERCASE_LETTERS,
NUM_SPECIAL_1,
NUM_SPECIAL_2,
Menu,
)
from ..krux_settings import (
t,
TC_CODE_PATH,
TC_CODE_PBKDF2_ITERATIONS,
Settings,
TAMPER_CHECK_INPUT_SCAN_QR,
TAMPER_CHECK_INPUT_MANUAL,
TAMPER_CHECK_INPUT_ASK_EVERY_TIME,
)
from ..krux_settings import t, TC_CODE_PATH, TC_CODE_PBKDF2_ITERATIONS


class TCCodeVerification(Page):
Expand All @@ -43,15 +52,16 @@ def capture(self, changing_tc_code=False, return_hash=False):
import uhashlib_hw
from machine import unique_id

label = (
t("Current Tamper Check Code")
if changing_tc_code
else t("Tamper Check Code")
)
tc_code = self.capture_from_keypad(
label, [NUM_SPECIAL_1, LETTERS, UPPERCASE_LETTERS, NUM_SPECIAL_2]
)
if tc_code == ESC_KEY:
method = self._select_input_method(changing_tc_code)
if method is None:
return False

if method == "qr":
tc_code = self._capture_from_qr()
else:
tc_code = self._capture_from_keypad(changing_tc_code)

if not tc_code:
return False
# Hashes the tamper check code
tc_code_bytes = tc_code.encode()
Expand All @@ -76,3 +86,68 @@ def capture(self, changing_tc_code=False, return_hash=False):

self.flash_error(t("Invalid Tamper Check Code"))
return False

def _select_input_method(self, changing_tc_code):
if changing_tc_code:
return "manual"

mode = Settings().security.tamper_check_code_input_mode
if mode == TAMPER_CHECK_INPUT_ASK_EVERY_TIME:
return self._prompt_input_method()
if mode == TAMPER_CHECK_INPUT_SCAN_QR:
return "qr"
return "manual"

def _prompt_input_method(self):
menu = Menu(
self.ctx,
[
(t("Scan QR"), lambda: "qr"),
(t("Manual"), lambda: "manual"),
],
back_label=None,
)
_, status = menu.run_loop()
if status == ESC_KEY:
return None
return status

def _capture_from_keypad(self, changing_tc_code):
label = (
t("Current Tamper Check Code")
if changing_tc_code
else t("Tamper Check Code")
)
tc_code = self.capture_from_keypad(
label, [NUM_SPECIAL_1, LETTERS, UPPERCASE_LETTERS, NUM_SPECIAL_2]
)
if tc_code == ESC_KEY:
return False
return tc_code

def _capture_from_qr(self):
from .qr_capture import QRCodeCapture

self.flash_text(t("Point camera at TC Code QR"))
qr_capture = QRCodeCapture(self.ctx)
data, _ = qr_capture.qr_capture_loop()
tc_code = self._sanitize_tc_code(data)
if tc_code:
return tc_code
return False

def _sanitize_tc_code(self, tc_code):
if tc_code is None:
return None
if isinstance(tc_code, bytes):
try:
tc_code = tc_code.decode()
except:
return None
if not isinstance(tc_code, str):
return None

cleaned = "".join(tc_code.splitlines()).strip()
if cleaned == "":
return None
return cleaned