diff options
| author | cirakg <cirakg@cirakg.xyz> | 2026-10-20 06:56:58 +0200 |
|---|---|---|
| committer | cirakg <cirakg@cirakg.xyz> | 2026-10-20 06:56:58 +0200 |
| commit | 2df98d1ba129f95a10c6b81665671d3b12560c79 (patch) | |
| tree | bc40d8bea0679cde90f0b2e84306116ee6aaba2f /source/storage | |
| parent | 3548312586aeafbfca0d0b87db90f9bdd972c831 (diff) | |
feat(ui): show QR codes, encoded once with progress and cached sealed per entry
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'source/storage')
| -rw-r--r-- | source/storage/Vault.mc | 34 |
1 files changed, 34 insertions, 0 deletions
diff --git a/source/storage/Vault.mc b/source/storage/Vault.mc index e77bbf2..46c8de2 100644 --- a/source/storage/Vault.mc +++ b/source/storage/Vault.mc @@ -153,6 +153,38 @@ class Vault { session.keys(); // throws LOCKED } write("e." + entry.id, seal(entry)); + _store.deleteValue("x." + entry.id); // the cached matrix may be stale + } + + // ---- Encoded barcode cache (README 12.2) ----------------------------- + + // Store an encoded matrix for a code entry, sealed with the entry's own + // key (device key, or the master key for locked entries) and bound to the + // ID. A full store just means no cache. Throws NOT_FOUND or LOCKED. + function saveCodeCache(id as String, data as ByteArray) as Void { + var keys = keysFor(isLockedRecord(id)); + try { + _store.setValue("x." + id, SealedBox.seal(keys, cacheAad(id), data)); + } catch (e instanceof Lang.Exception) { + // Storage full: the code is simply encoded again next time. + } + } + + // The cached matrix, or null when there is none or it doesn't verify. + // Throws NOT_FOUND or LOCKED. + function loadCodeCache(id as String) as ByteArray? { + var keys = keysFor(isLockedRecord(id)); + var stored = _store.getValue("x." + id); + return stored instanceof ByteArray ? SealedBox.open(keys, cacheAad(id), stored) : null; + } + + private function isLockedRecord(id as String) as Boolean { + var record = recordFor(id); + return record != null && Record.keyRef(record) == Record.KEY_MASTER; + } + + private function cacheAad(id as String) as ByteArray { + return Bytes.fromUtf8("OTPW-X-1" + id); } // Set a HOTP counter (re-seals the record, since the MAC covers it). @@ -178,6 +210,7 @@ class Vault { _ids.remove(id); saveMeta(); _store.deleteValue("e." + id); + _store.deleteValue("x." + id); } // Move an entry to position `index` (clamped to the list). Throws NOT_FOUND. @@ -316,6 +349,7 @@ class Vault { saveMeta(); for (var i = 0; i < erased.size(); i++) { _store.deleteValue("e." + erased[i]); + _store.deleteValue("x." + erased[i]); } for (var i = 0; i < pending.size(); i++) { _store.deleteValue("p." + pending[i]); |
