blob: 05656e27c2f9eddedf4c78c9f7fbae68057414fe (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
// Author: cirakg & claude ai
// Summary: The app's shared state: options, the vault, the code cache, start-up errors and the crypto self-test.
//
// Created in getInitialView (never in glance mode, which has too little
// memory for the vault). Views reach the vault and options through here.
import Toybox.Application;
import Toybox.Lang;
import Toybox.Time;
module AppState {
const SELF_TEST_KEY = "st";
const SECONDS_PER_DAY = 86400;
var options as Options = new Options();
var vault as Vault? = null;
var codes as CodeCache? = null;
// A message that replaces the whole app (newer data, damaged metadata,
// failed self-test), or null.
var startError as String? = null;
// Codes are shown only after the self-test passed (README 7.10).
var cryptoReady as Boolean = false;
var selfTestSummary as String = "Not run yet";
// Load options and open the vault. Errors end up in startError.
function start() as Void {
options = new Options();
options.load();
if (TestMode.ON) {
Fixtures.resetStore();
}
try {
var v = new Vault(new Persist.AppStorageBackend(), new TimeSource());
v.eraseAfterFailures = options.eraseAfterFailures;
v.session.autoLockMs = TestMode.ON ? TestMode.AUTO_LOCK_MS : options.autoLockMs;
vault = v;
codes = new CodeCache(v);
if (TestMode.ON) {
Fixtures.load(v);
}
} catch (e instanceof VaultError) {
startError = e.code == VaultError.NEWER_SCHEMA
? "Data from a newer version of OTP Wallet. Update the app."
: "Stored data is damaged. Reinstall the app to start over.";
}
}
// The vault; only call after start() succeeded.
function theVault() as Vault {
return vault as Vault;
}
function theCodes() as CodeCache {
return codes as CodeCache;
}
// Run the known-answer self-test on the first start of each version and
// then once a day. Returns false (and sets startError) when it failed.
function runSelfTestIfDue() as Boolean {
var day = (Clock.nowUnix(0) / SECONDS_PER_DAY).toNumber();
var last = Application.Storage.getValue(SELF_TEST_KEY);
var due = true;
if (last instanceof Array && last.size() == 2 && last[0] instanceof String && last[1] instanceof Number) {
due = !(last[0] as String).equals(AppInfo.VERSION) || (last[1] as Number) != day;
}
if (!due) {
selfTestSummary = "Passed (today)";
cryptoReady = true;
return true;
}
var failed = SelfTest.run();
if (failed != null) {
selfTestSummary = "FAILED: " + failed;
startError = "Crypto self-test failed on this watch (" + failed + "). Codes are not shown.";
return false;
}
Application.Storage.setValue(SELF_TEST_KEY, [AppInfo.VERSION, day] as Array<Application.Storage.ValueType>);
selfTestSummary = "Passed";
cryptoReady = true;
return true;
}
// Note user activity (restarts the auto-lock timeout).
function activity() as Void {
var v = vault;
if (v != null) {
v.session.touch();
}
}
// Lock if the inactivity timeout passed. True when this call locked.
function checkAutoLock() as Boolean {
var v = vault;
if (v != null && v.session.checkExpiry()) {
theCodes().clear();
return true;
}
return false;
}
// Lock now and forget every computed code.
function lock() as Void {
var v = vault;
if (v != null) {
v.lock();
theCodes().clear();
}
}
function now() as Long {
return Clock.nowUnix(options.timeOffset);
}
}
|