blob: 891b71395a8313164e4b0c37d4b0bc63c426bbd7 (
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
|
// Author: cirakg & claude ai
// Summary: The unlocked session: holds the master key in RAM and locks after inactivity or on request.
//
// README 7.5: unlocking puts the locked master key (and its derived keys) in
// RAM; locking zeroes and drops them. The UI calls touch() on every user
// action and checkExpiry() from its 1 Hz timer; the app calls lock() in
// onStop. An auto-lock time of 0 means "Immediately": the UI locks as soon as
// you leave a locked entry, and checkExpiry() locks on its next call.
import Toybox.Lang;
class Session {
static const DEFAULT_AUTO_LOCK_MS = 60000;
// Inactivity time before locking, in ms (0 = immediately).
var autoLockMs as Number = DEFAULT_AUTO_LOCK_MS;
private var _time as TimeSource;
private var _masterKey as ByteArray? = null;
private var _keys as SealedBox.Keys? = null;
private var _lastActivity as Number = 0;
function initialize(time as TimeSource) {
_time = time;
}
// Take ownership of the master key (the caller must not wipe or keep it).
function unlock(masterKey as ByteArray) as Void {
lock();
_masterKey = masterKey;
_keys = new SealedBox.Keys(masterKey);
touch();
}
function isUnlocked() as Boolean {
return _keys != null;
}
// The master key's derived keys. Throws VaultError LOCKED when locked.
function keys() as SealedBox.Keys {
var keys = _keys;
if (keys == null) {
throw new VaultError(VaultError.LOCKED, "locked");
}
return keys;
}
// The master key itself (only to re-wrap it under a new PIN). Throws
// VaultError LOCKED when locked.
function masterKey() as ByteArray {
var key = _masterKey;
if (key == null) {
throw new VaultError(VaultError.LOCKED, "locked");
}
return key;
}
// Note user activity; restarts the inactivity timeout.
function touch() as Void {
_lastActivity = _time.uptimeMs();
}
// Lock if the inactivity timeout has passed. Returns true when this call
// locked the session, so the UI can close open locked entries.
function checkExpiry() as Boolean {
if (!isUnlocked()) {
return false;
}
var now = _time.uptimeMs();
// A timer that went backwards (watch restart, wrap) counts as expired.
if (now < _lastActivity || now - _lastActivity >= autoLockMs) {
lock();
return true;
}
return false;
}
// Zero and drop every key. Safe to call when already locked.
function lock() as Void {
var keys = _keys;
if (keys != null) {
keys.wipe();
}
Bytes.wipe(_masterKey);
_keys = null;
_masterKey = null;
}
}
|