aboutsummaryrefslogtreecommitdiff
path: root/source/storage
diff options
context:
space:
mode:
authorcirakg <cirakg@cirakg.xyz>2026-10-20 18:36:54 +0200
committercirakg <cirakg@cirakg.xyz>2026-10-20 18:36:54 +0200
commit151bfda5d4c567ff909a61dc9c90bf8eece1db37 (patch)
treef8487b91cf6a9ce59dba02e95e67250fc84d8e80 /source/storage
parent25910f3a7a7fb28c4a58367b6e56dd2468b60933 (diff)
feat(settings): add the Add OTP, Add code and Manage entry forms with a PIN queue for locked entries
The full phone settings form (README 10.2) and its processing (10.3) as one resumable job: Import, Add OTP, Add code, Manage, then the About fields. Forms are applied only when their switch is on, cleared afterwards (secret first; only the secret after a problem), deduplicated by keyed hash, and report into their Result field and the results screen. - Add OTP validates per README 10.4; Add code runs the real encoder (CodeCheck, a job for 2D codes) and caches 2D codes on the way in. - Manage entry: edit, move, require/drop PIN, replace, counter, delete. Changes to locked entries are sealed under "q" and applied only after the PIN is entered under "Phone changes" in the menu. - About: catalog (800 characters max), version, storage use. - "Encrypt names" now applies to new and existing entries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'source/storage')
-rw-r--r--source/storage/KeyManager.mc24
-rw-r--r--source/storage/Vault.mc19
2 files changed, 43 insertions, 0 deletions
diff --git a/source/storage/KeyManager.mc b/source/storage/KeyManager.mc
index 567288f..47dbd59 100644
--- a/source/storage/KeyManager.mc
+++ b/source/storage/KeyManager.mc
@@ -84,6 +84,30 @@ class KeyManager {
return out;
}
+ // A keyed hash (16 hex characters) of a settings form's content, for
+ // the applied-forms log (README 10.3). Keyed like the fingerprints, with
+ // its own label, because the content includes secrets. The parts are
+ // fed to the native HMAC one by one, each preceded by `separator`, so a
+ // large import form costs no byte loop here (the watchdog).
+ function formHash(parts as Array<ByteArray>, separator as Number) as String {
+ deviceKeys();
+ var mac = new Cryptography.HashBasedMessageAuthenticationCode({
+ :algorithm => Cryptography.HASH_SHA256,
+ :key => _fingerprintKey as ByteArray
+ });
+ mac.update(Bytes.fromUtf8("OTPW-FH-1"));
+ for (var i = 0; i < parts.size(); i++) {
+ mac.update([separator]b);
+ if (parts[i].size() > 0) {
+ mac.update(parts[i]);
+ }
+ }
+ var digest = mac.digest();
+ var out = Bytes.toHex(digest.slice(0, FINGERPRINT_BYTES));
+ Bytes.wipe(digest);
+ return out;
+ }
+
// True when a PIN has been set.
function hasPin() as Boolean {
return _store.getValue(WRAP_KEY) != null;
diff --git a/source/storage/Vault.mc b/source/storage/Vault.mc
index 46c8de2..88054e0 100644
--- a/source/storage/Vault.mc
+++ b/source/storage/Vault.mc
@@ -31,6 +31,9 @@ class Vault {
var lockout as Lockout;
// The "Erase locked entries after 10 wrong PINs" setting (off by default).
var eraseAfterFailures as Boolean = false;
+ // The "Encrypt names" setting: new entries keep their names in the
+ // encrypted part (existing ones are converted by AboutScan).
+ var encryptNames as Boolean = false;
private var _store as Persist.Backend;
private var _meta as Dictionary<Application.Storage.KeyType, Application.Storage.ValueType>;
@@ -126,6 +129,7 @@ class Vault {
throw new VaultError(VaultError.LIMIT, "at most " + MAX_ENTRIES + " entries");
}
entry.id = newId();
+ entry.namesEncrypted = encryptNames;
entry.fingerprint = keyManager.fingerprint(entry);
if (entry.created == 0) {
entry.created = Clock.nowUnix(0);
@@ -156,6 +160,21 @@ class Vault {
_store.deleteValue("x." + entry.id); // the cached matrix may be stale
}
+ // Re-seal an entry with or without encrypted names ("Encrypt names").
+ // Nothing else changes, so the barcode cache stays. Throws NOT_FOUND,
+ // LOCKED, DAMAGED or FULL.
+ function setNamesEncrypted(id as String, encrypted as Boolean) as Void {
+ var entry = open(id);
+ entry.namesEncrypted = encrypted;
+ try {
+ write("e." + id, seal(entry));
+ } catch (e instanceof VaultError) {
+ entry.wipeSecrets();
+ throw e;
+ }
+ entry.wipeSecrets();
+ }
+
// ---- Encoded barcode cache (README 12.2) -----------------------------
// Store an encoded matrix for a code entry, sealed with the entry's own