diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/gen_fixture_wrap.py | 16 | ||||
| -rw-r--r-- | tools/lint_project.py | 2 | ||||
| -rw-r--r-- | tools/tests/test_gen_fixture_wrap.py | 8 | ||||
| -rw-r--r-- | tools/ui_flow.py | 15 |
4 files changed, 28 insertions, 13 deletions
diff --git a/tools/gen_fixture_wrap.py b/tools/gen_fixture_wrap.py index c389fea..6eb207a 100644 --- a/tools/gen_fixture_wrap.py +++ b/tools/gen_fixture_wrap.py @@ -1,16 +1,19 @@ #!/usr/bin/env python3 # Author: cirakg & claude ai -# Summary: Compute the UI-test fixture's PIN wrap (fake PIN 123456) so the watch needn't run PBKDF2 at start-up. +# Summary: Compute the UI-test fixture's PIN wrap (fake PIN 1234) so the watch needn't run PBKDF2 at start-up. """Print the fake PIN wrap that source/app/Fixtures.mc stores in the UI-test build. The wrap follows docs/SECURITY.md ("PIN wrap, version 1") with fixed, fake -inputs: PIN 123456, salt 10..1f, IV 20..2f, master key 40..5f, 2000 +inputs: PIN 1234, salt 10..1f, IV 20..2f, master key 40..5f, 2000 PBKDF2 iterations. Deriving the PIN key on the watch in one event trips the watchdog, and the UI tests need the store ready before the first screen, so the fixture stores this precomputed value instead. The app's own unlock code then checks it like any other wrap, which also cross-checks the format. +The fixture also stores the 4-digit PIN marker (KeyManager.PIN_LENGTH_KEY = +PIN_DIGITS), so the PIN isn't treated as a legacy 4-to-10-digit one. -Usage: gen_fixture_wrap.py (prints FIXTURE_WRAP and FIXTURE_MASTER_KEY in hex) +Usage: gen_fixture_wrap.py (prints FIXTURE_PIN, FIXTURE_WRAP and FIXTURE_MASTER_KEY in hex, + and the marker the fixture stores) """ from __future__ import annotations @@ -20,7 +23,9 @@ import hmac import struct import subprocess -PIN = b"123456" +PIN = b"1234" +PIN_DIGITS = 4 # source/storage/KeyManager.mc: every new PIN, and the marker value +PIN_LENGTH_KEY = "wl" # source/storage/KeyManager.mc ITERATIONS = 2000 SALT = bytes(range(0x10, 0x20)) IV = bytes(range(0x20, 0x30)) @@ -65,8 +70,11 @@ def fixture_wrap() -> bytes: def main() -> None: + assert len(PIN) == PIN_DIGITS + print("FIXTURE_PIN =", PIN.decode()) print("FIXTURE_WRAP =", fixture_wrap().hex()) print("FIXTURE_MASTER_KEY =", MASTER_KEY.hex()) + print(f"marker: {PIN_LENGTH_KEY} = {PIN_DIGITS}") if __name__ == "__main__": diff --git a/tools/lint_project.py b/tools/lint_project.py index e02215b..04d525f 100644 --- a/tools/lint_project.py +++ b/tools/lint_project.py @@ -42,7 +42,7 @@ AUTHOR = "cirakg & claude ai" HEADER_LINES = 15 DIAG_FILE = Path("source/util/Diag.mc") # Folders that hold local tool state or generated output, never project files. -SKIP_DIRS = {".git", ".cache", "build", "node_modules", "__pycache__", ".pytest_cache", ".mypy_cache", ".ruff_cache"} +SKIP_DIRS = {".git", ".cache", ".claude", "build", "node_modules", "__pycache__", ".pytest_cache", ".mypy_cache", ".ruff_cache"} # Written by a tool that would drop a hand-added header on the next run. GENERATED = {Path("docker/docs/package-lock.json")} diff --git a/tools/tests/test_gen_fixture_wrap.py b/tools/tests/test_gen_fixture_wrap.py index 1c72e66..c7b11d5 100644 --- a/tools/tests/test_gen_fixture_wrap.py +++ b/tools/tests/test_gen_fixture_wrap.py @@ -31,3 +31,11 @@ def test_hkdf_matches_rfc5869_case_3() -> None: def test_generated_wrap_matches_the_fixture() -> None: assert gen_fixture_wrap.fixture_wrap().hex() == fixture_constant("FIXTURE_WRAP") assert gen_fixture_wrap.MASTER_KEY.hex() == fixture_constant("FIXTURE_MASTER_KEY") + + +def test_fixture_pin_has_the_new_pin_length() -> None: + assert gen_fixture_wrap.PIN.decode() == fixture_constant("FIXTURE_PIN") + assert len(gen_fixture_wrap.PIN) == gen_fixture_wrap.PIN_DIGITS + key_manager = (ROOT / "source" / "storage" / "KeyManager.mc").read_text(encoding="utf-8") + assert f"static const PIN_DIGITS = {gen_fixture_wrap.PIN_DIGITS};" in key_manager + assert f'static const PIN_LENGTH_KEY = "{gen_fixture_wrap.PIN_LENGTH_KEY}";' in key_manager diff --git a/tools/ui_flow.py b/tools/ui_flow.py index a17d435..8c08caf 100644 --- a/tools/ui_flow.py +++ b/tools/ui_flow.py @@ -14,7 +14,7 @@ the screen. Watches without a button for an action use touch instead (Venu Sq 2 has no UP/DOWN and no select button). The app (UI-test build, source/util/TestMode.mc) starts with fake entries, -the PIN 123456 and a frozen clock, and prints "DIAG shown <screen>" after +the PIN 1234 and a frozen clock, and prints "DIAG shown <screen>" after drawing each screen, so every step waits for the screen it expects instead of sleeping. Menus are native Menu2 views that print nothing; for those the script waits a fixed time. @@ -65,10 +65,10 @@ MENU_SETTLE_S = 1.5 # Menu2 has no "shown" line SETTLE_S = 0.4 # after a "shown" line, before the screenshot CLICK_PAUSE_S = 0.25 HOLD_S = 1.6 # long enough for a hold (menu) on every device -FIXTURE_PIN = "123456" # source/app/Fixtures.mc -WRONG_PIN = "000000" +FIXTURE_PIN = "1234" # source/app/Fixtures.mc +WRONG_PIN = "0000" FAILURES_UNTIL_WAIT = 5 # source/storage/Lockout.mc: the 5th failure starts a delay -SELECTOR_OK = 10 # source/ui/PinView.mc: selector positions 0-9, then OK +SELECTOR_DIGITS = 10 # source/ui/PinView.mc: selector positions 0-9 (no OK; the 4th digit submits) GYM_CARD = "5901234123457" # source/app/Fixtures.mc: EAN-13 with its check digit MEMBER_QR = ( # source/app/Fixtures.mc: a version 6 QR "https://example.com/member/fake-0042?name=Alice%20Example&card=0000111122223333&valid=2027-12-31&level=gold" @@ -298,12 +298,11 @@ class Flow: value, xy = item.split("=") x, y = xy.split(",") centres[int(value)] = (int(x), int(y)) - for digit in pin: + for digit in pin: # the 4th digit submits self.sim.tap(*centres[int(digit)]) - self.sim.tap(*centres[-2]) # OK return - for position in [int(d) for d in pin] + [SELECTOR_OK]: - steps = (position - self.selector) % (SELECTOR_OK + 1) + for position in [int(d) for d in pin]: + steps = (position - self.selector) % SELECTOR_DIGITS for _ in range(steps): self.sim.press("down") self.selector = position |
