From dd5d0f68726fe56ee4d199867d414aea81905cc4 Mon Sep 17 00:00:00 2001 From: cirakg Date: Mon, 19 Oct 2026 15:27:37 +0200 Subject: feat(storage): add the encrypted vault, PIN keys, lockout, session and migrations Records are one ByteArray each: a readable TLV header plus an encrypt-then-MAC box whose aad binds the entry ID and the header. The device key seals unlocked entries and pending items; a random master key, wrapped under a PBKDF2 PIN key, seals locked ones. Wrong PINs are counted before the check, with delays on the trusted clock and an uptime fallback. Bytes.toUtf8 validates UTF-8 first, because the native conversion crashes on invalid input. Co-Authored-By: Claude Opus 5.5 --- source/storage/Backend.mc | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 source/storage/Backend.mc (limited to 'source/storage/Backend.mc') diff --git a/source/storage/Backend.mc b/source/storage/Backend.mc new file mode 100644 index 0000000..6596d9c --- /dev/null +++ b/source/storage/Backend.mc @@ -0,0 +1,54 @@ +// Author: cirakg & claude ai +// Summary: Key-value persistence behind an interface: Application.Storage on the watch, a dictionary in tests. +// +// Everything the vault persists goes through a Backend, so unit tests can run +// against MemoryBackend (test/storage/) and simulate a full store, while the +// app uses AppStorageBackend. Values are the types Application.Storage +// accepts: numbers, strings, booleans, ByteArrays, arrays and dictionaries. +// Records are single ByteArrays, so their stored size is known exactly. + +import Toybox.Application; +import Toybox.Lang; + +module Persist { + + // Base class; subclasses override all three methods. + class Backend { + + function initialize() { + } + + // The stored value, or null when the key is absent. + function getValue(key as String) as Application.Storage.ValueType { + return null; + } + + // Store a value. Throws when the store is full (the vault turns that + // into VaultError FULL). + function setValue(key as String, value as Application.Storage.ValueType) as Void { + } + + function deleteValue(key as String) as Void { + } + } + + // The real store: Application.Storage, private to this app. + class AppStorageBackend extends Backend { + + function initialize() { + Backend.initialize(); + } + + function getValue(key as String) as Application.Storage.ValueType { + return Application.Storage.getValue(key); + } + + function setValue(key as String, value as Application.Storage.ValueType) as Void { + Application.Storage.setValue(key, value); + } + + function deleteValue(key as String) as Void { + Application.Storage.deleteValue(key); + } + } +} -- cgit v1.2.3