Commit Graph

10 Commits

Author SHA1 Message Date
r-zakarya 6d31bf83d2 docs(android): the res tree comes from any IDE build, not the 32-bit one 2026-08-15 11:54:29 +01:00
r-zakarya 4805c9ac31 docs(android): building and installing the APK without the IDE 2026-08-15 11:52:44 +01:00
r-zakarya 9a4170c44f feat(android): folder filter, icons, sort, template, updated_at, FLAG_SECURE
Works through the plan's §2 and §3 items that need no hardware and no
platform decision.

- allowBackup="false". The private directory holds vault.bin; a cloud
  backup has no business in a password manager's path, even though what
  would leave is encrypted by a key that stays in the chip.
- FLAG_SECURE, Release builds only - in debug it also blocks development
  screenshots. Complementary to hiding on WillBecomeInactive rather than
  redundant: that one covers the recents preview, the flag also covers
  manual captures, screen recording and casting to an external display.
- List sorted by name (snapshot order is DB order, arbitrary to the eye),
  entry icons decoded once per vault rather than on every keystroke, and a
  folder dropdown whose selection survives a background refresh. No tag
  filter: text search already matches tags.
- template and updated_at read and shown in the detail. updated_at is also
  step 1 of the write path - nothing is arbitrable without it.

Android code is now type-checked without deploying: dccaarm64 reaches the
link step and fails only on the NDK's ldandroid.exe. A JNI declaration that
does not compile no longer waits for an APK to say so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 11:23:29 +01:00
r-zakarya 3bb6f6f903 feat(android): show first, sync after
The local vault now opens by itself at launch and the GET runs behind it,
silently. A 304 shows nothing; a network failure shows nothing either -
offline is a normal state, and an alert on every launch only teaches people
to dismiss alerts. Only a new snapshot manifests, by refreshing the list.

That silence is what puts the sync password inside the Keystore-encrypted
file next to the vault: without it a new snapshot cannot be opened, so every
edit made on the desktop would raise a password prompt at launch. The file
already holds every password in plaintext; this one adds almost nothing to
its exposure. A typed password still wins over the stored one, which is how
you fix one that no longer works.

Adopting a snapshot while the app is already open rebuilds the list in place
instead of navigating - mandatory even from the detail screen, since list
rows carry indices into the old vault and a tap would open the wrong entry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 11:14:44 +01:00
r-zakarya ba0d3c1d3c docs(android): plan of the remaining work 2026-08-15 10:56:49 +01:00
r-zakarya 20ba2438df feat(android): TOTP codes and custom fields in the detail screen
The detail screen showed site, username and password and silently dropped
everything else - custom fields, notes, 2FA. It is now built per entry
rather than from a fixed template: every row is a caption, a value, and
whether it is secret, so reveal and copy exist once instead of once per
kind of field.

Two rows stay special on purpose. A note's body is stored in the password
column, so it gets its own caption and no mask. The 2FA code is the only
live row - refreshed every second, placed first, since it is what you pull
a phone out for.

PMA.Totp takes bare base32 or an otpauth:// URI; both travel in snapshots
depending on how the entry was created. Checked against all six RFC 6238
vectors, including T=20000000000, which overflows 32 bits.

The rows hold plaintext, so clearing them is part of locking, not of
navigating away.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 10:35:09 +01:00
r-zakarya 0ddfe5c5fd feat(crypto): wire AES-GCM and PBKDF2 to Windows CNG
The viewer compiled for Win64 but died on first use: AesGcmDecrypt existed
only as JNI, so the desktop build downloaded 27 KB and then refused it.
Windows has the same primitive in bcrypt.dll - no DLL to ship, no OpenSSL,
and BCryptDeriveKeyPBKDF2 takes bytes, so none of the char[] encoding doubt
that keeps the Pascal loop around on Android.

This exists so the client can be exercised on the desktop; deploying an APK
to watch a list redraw is a slow way to build a UI.

selftest.dpr replaces the crypto-vector button the viewer dropped: same
known-answer vector, console, exit code = failures. Measured here - Pascal
1670 ms, CNG 528 ms, identical keys on the non-ASCII password.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 05:59:36 +01:00
r-zakarya bf7aac478b feat(android): read-only viewer - unlock, search, detail
Replaces the crypto test bench with the three screens the client actually
needs. PMA.Vault holds the decrypted snapshot and answers the search;
mainApk builds unlock/list/detail in code rather than three .fmx files.

Two safety rules live in the UI because nowhere else can hold them: the
plaintext is dropped when the app backgrounds (screen hidden on
WillBecomeInactive, which precedes the recents-preview capture), and the
clipboard is cleared after 45 s only if it still holds what we put there.

The search deliberately ignores the password field.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 01:19:47 +01:00
r-zakarya 3c1525e46f feat(android): end-to-end read path validated on device, add the container diagnostic
GET in 80 ms, decryption in 3893 ms, entries and trash counted, second call answers 304. try-open.js opens a container with Node so a failure can be attributed: a GCM tag that does not verify means wrong password OR altered file, and the app cannot tell which.
2026-08-15 00:31:01 +01:00
r-zakarya c684d9073f feat(android): adopt the platform PBKDF2, record the timing and what follows from it
Measured on device: Android derives the same key as WebCrypto on a non-ASCII password, so PBEKeySpec does encode as UTF-8 - the doubt that justified the Pascal implementation was unfounded. It is barely faster though, 3869 ms against 4139 ms: PBKDF2 gets no hardware acceleration.

So 4 seconds per container is incompressible, and the iteration count belongs to the desktop format. The answer is not to speed up the derivation but to stop repeating it: derive once per snapshot, then keep the vault locally under an Android Keystore key. Written up in android-client/README.md.
2026-08-14 23:30:15 +01:00