Commit Graph

4 Commits

Author SHA1 Message Date
r-zakarya 9649dac7b2 feat(android): protect the local vault with biometric or device credential
The Keystore key now requires authentication, valid for 30 s afterwards - short enough that an unattended phone is not exploitable, long enough to decrypt and display. Time-bound rather than a per-use CryptoObject: BiometricPrompt's callback would need a Java class compiled into a jar, while FMX already bridges it.

Two aliases, because the auth constraint is fixed when the key is created and cannot be added later. The file header records which key wrote it, so a read knows whether to authenticate first.

A device with neither fingerprint nor lock screen cannot hold such a key, so SaveVault falls back to the plain key rather than giving up the cache. And a failed authentication no longer wipes the cache: the vault is intact, it is the user who cancelled - erasing it would cost a download and 4 s of derivation for a dismissed prompt.
2026-08-15 00:42:50 +01:00
r-zakarya 5fdb7f914e feat(android): keep the decrypted vault locally under an Android Keystore key
Opening a container costs ~4 s and the iteration count belongs to the desktop format, so the fix is to stop repeating it: derive once per snapshot, then read a local AES file which the hardware accelerates. The key is generated inside the Keystore and never leaves it - the file alone is useless, even rooted.

ETag and vault are encrypted together on purpose: an ETag outliving an unreadable vault would make the server answer 304 with nothing to show. A failed read wipes the cache rather than guessing.

Also normalises every Delphi source to pure ASCII: Delphi reads a BOM-less file as ANSI, so accented comments came back as mojibake, and each PowerShell rewrite added another layer.
2026-08-15 00:37:59 +01:00
r-zakarya 8796bb341d feat(android): HTTP client for the sync endpoint, with ETag reuse
Fetches the opaque container and the ETag; a 304 skips both the download and the 4-second derivation, which is the whole point of keeping the tag between calls. Redirects are not followed - one to another host would send the bearer token there.

Local testing goes through adb reverse tcp:8080 tcp:80, which makes the phone reach the PC as 127.0.0.1 and satisfies the endpoint's loopback exemption. That needs usesCleartextTraffic in the manifest, marked for removal before shipping.
2026-08-14 23:37:06 +01:00
r-zakarya 386bf74a2a feat(android): crypto self-test harness on the new FMX project
Two stages tested separately, because a global failure says nothing about where: PBKDF2 against the known derived key - which also runs on Win64, so that stage validates in seconds without deploying to a phone - then AES-GCM against the plaintext hash, Android only.

The vector is embedded as a unit rather than read from a file: no network, no deployment, so a first-run failure can only come from the decryption itself. Work runs on a thread; 600k HMAC rounds on the UI thread would have Android kill the app for not responding.
2026-08-14 18:29:12 +01:00