diff options
Diffstat (limited to 'source/app')
| -rw-r--r-- | source/app/AppInfo.mc | 2 | ||||
| -rw-r--r-- | source/app/AppState.mc | 21 | ||||
| -rw-r--r-- | source/app/Fixtures.mc | 37 | ||||
| -rw-r--r-- | source/app/OtpWalletApp.mc | 9 |
4 files changed, 67 insertions, 2 deletions
diff --git a/source/app/AppInfo.mc b/source/app/AppInfo.mc index ab5ceb9..1f40fee 100644 --- a/source/app/AppInfo.mc +++ b/source/app/AppInfo.mc @@ -12,5 +12,5 @@ import Toybox.Lang; module AppInfo { // Semantic version of the app: MAJOR.MINOR.PATCH. - const VERSION = "0.4.0"; + const VERSION = "0.5.0"; } diff --git a/source/app/AppState.mc b/source/app/AppState.mc index 05656e2..b900943 100644 --- a/source/app/AppState.mc +++ b/source/app/AppState.mc @@ -16,6 +16,7 @@ module AppState { var options as Options = new Options(); var vault as Vault? = null; var codes as CodeCache? = null; + var processor as SettingsProcessor? = null; // A message that replaces the whole app (newer data, damaged metadata, // failed self-test), or null. var startError as String? = null; @@ -36,6 +37,7 @@ module AppState { v.session.autoLockMs = TestMode.ON ? TestMode.AUTO_LOCK_MS : options.autoLockMs; vault = v; codes = new CodeCache(v); + processor = new SettingsProcessor(); if (TestMode.ON) { Fixtures.load(v); } @@ -81,6 +83,25 @@ module AppState { return true; } + // The phone changed the settings: reload the options and apply forms. + function settingsChanged() as Void { + options.load(); + var v = vault; + if (v != null) { + v.eraseAfterFailures = options.eraseAfterFailures; + v.session.autoLockMs = TestMode.ON ? TestMode.AUTO_LOCK_MS : options.autoLockMs; + processSettings(); + } + } + + // Apply any settings form that asks for it (after start-up and on change). + function processSettings() as Void { + var p = processor; + if (p != null && cryptoReady) { + p.start(); + } + } + // Note user activity (restarts the auto-lock timeout). function activity() as Void { var v = vault; diff --git a/source/app/Fixtures.mc b/source/app/Fixtures.mc index 0b641ee..8ed35c9 100644 --- a/source/app/Fixtures.mc +++ b/source/app/Fixtures.mc @@ -1,5 +1,5 @@ // Author: cirakg & claude ai -// Summary: Debug-only fake entries for the simulator and screenshots; release builds get empty stubs. +// Summary: Debug-only fake entries (and, in UI tests, a fake import via the settings form); release builds get empty stubs. // // All data is fabricated: the secrets are the RFC 4226/6238 test keys and the // names are made up. The UI-test build (source/util/TestMode.mc) wipes the @@ -52,6 +52,41 @@ module Fixtures { card.payload = Bytes.fromUtf8("0000-1111-2222"); vault.add(card); vault.lock(); + if (TestMode.ON) { + startImport(); + } + } + + // UI-test build: an Aegis import of 100 entries through the settings + // form, the same path as the phone: 97 new, one duplicate of GitHub, one + // error and one skipped type. The results screen opens when it's done. + function startImport() as Void { + var rfcKey = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"; // lint: allow-literal (RFC 4226 test key, = GitHub's) + var proton = "KRSXG5BAKNSWG4TFOQQEWZLZEBEGK4TF"; // lint: allow-literal (fake: Base32 of "Test Secret Key Here") + var json = "{\"version\": 1, \"header\": {\"slots\": null, \"params\": null}, \"db\": {\"version\": 3, \"entries\": [" + + aegisEntry("totp", "Proton", "zoe@example.com", proton, 6) + "," + + aegisEntry("totp", "GitHub copy", "alice@example.com", rfcKey, 6) + "," + + aegisEntry("totp", "Broken", "x", proton, 9) + "," + + aegisEntry("motp", "Legacy", "y", rfcKey, 6); + // 96 more, so the flow imports 100 entries through the paced job. + // Fake 16-character Base32 secrets built from alphabet letters (cheap: + // Base32-encoding 96 secrets in this one event would trip the watchdog + // on the Forerunner 255S). + var abc = Base32.ALPHABET; + for (var i = 0; i < 96; i++) { + var secret = "MFRGGZDF" + abc.substring(i / 32, i / 32 + 1) + abc.substring(i % 32, i % 32 + 1) + "MZTWQ2LK"; + json += "," + aegisEntry("totp", "Site " + i, "user" + i + "@example.com", secret, 6); + } + json += "], \"groups\": []}}"; + Application.Properties.setValue("impText1", json); + Application.Properties.setValue("impMode", 0); + Application.Properties.setValue("impLocked", false); + Application.Properties.setValue("impApply", true); + } + + function aegisEntry(type as String, issuer as String, name as String, secret as String, digits as Number) as String { + return "{\"type\": \"" + type + "\", \"name\": \"" + name + "\", \"issuer\": \"" + issuer + + "\", \"info\": {\"secret\": \"" + secret + "\", \"algo\": \"SHA1\", \"digits\": " + digits + ", \"period\": 30}}"; } function otp(name as String, account as String, group as String, type as Number, algorithm as Number, diff --git a/source/app/OtpWalletApp.mc b/source/app/OtpWalletApp.mc index 7419433..596ccc2 100644 --- a/source/app/OtpWalletApp.mc +++ b/source/app/OtpWalletApp.mc @@ -54,6 +54,15 @@ class OtpWalletApp extends Application.AppBase { return [list, new ListDelegate(list)]; } + // The phone sent new settings (only while the full app runs). + (:typecheck(disableGlanceCheck)) + function onSettingsChanged() as Void { + if (_started) { + AppState.settingsChanged(); + WatchUi.requestUpdate(); + } + } + (:glance) function getGlanceView() as [WatchUi.GlanceView] or [WatchUi.GlanceView, WatchUi.GlanceViewDelegate] or Null { return [new OtpGlanceView()]; |
