aboutsummaryrefslogtreecommitdiff
path: root/source/app
diff options
context:
space:
mode:
Diffstat (limited to 'source/app')
-rw-r--r--source/app/AppInfo.mc2
-rw-r--r--source/app/AppState.mc21
-rw-r--r--source/app/Fixtures.mc37
-rw-r--r--source/app/OtpWalletApp.mc9
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()];