From 2df98d1ba129f95a10c6b81665671d3b12560c79 Mon Sep 17 00:00:00 2001 From: cirakg Date: Tue, 20 Oct 2026 06:56:58 +0200 Subject: feat(ui): show QR codes, encoded once with progress and cached sealed per entry Co-Authored-By: Claude Opus 5.5 --- source/storage/Vault.mc | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'source/storage') 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]); -- cgit v1.2.3