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>
This commit is contained in:
r-zakarya
2026-08-15 01:19:47 +01:00
parent 4effc3611d
commit bf7aac478b
4 changed files with 1026 additions and 324 deletions
+41 -2
View File
@@ -53,6 +53,41 @@ réception paraît naturel et c'est un piège : après un échec, le prochain ap
répond 304 et l'application se retrouve sans données tout en se croyant à jour.
L'ETag appartient au **clair détenu**, pas au téléchargement.
## L'interface
Trois écrans dans une seule forme, construits en code (`mainApk.pas`) :
déverrouillage, liste, détail. Une forme par écran aurait coûté trois `.fmx`
pour des dispositions triviales.
L'écran de déverrouillage s'adapte à ce que l'appareil détient : coffre local
présent → *Ouvrir* (biométrie si la clé l'exige) ; absent → seul le
téléchargement peut en produire un. La liste filtre à la frappe sur le nom, le
site, l'identifiant et les étiquettes — **pas** sur le mot de passe : personne
ne cherche par mot de passe, et le comparer allongerait son séjour en mémoire.
Deux règles tenues dans l'interface, pas ailleurs :
- **Le clair est jeté quand l'application passe en arrière-plan.** L'écran est
masqué sur `WillBecomeInactive`, qui précède la capture de l'aperçu des
applications récentes — sans quoi la liste des comptes y resterait visible —
et le coffre est réellement effacé sur `EnteredBackground`.
- **Le presse-papiers se vide après 45 s**, et seulement si son contenu est
toujours celui qu'on y a mis : l'utilisateur a pu copier autre chose
entre-temps.
L'écriture n'existe pas. Le poste Windows reste la source ; modifier depuis le
téléphone demanderait le `PUT` côté serveur et une fusion côté client.
### Tester sans téléphone
Tout sauf le Keystore et la biométrie tourne en **Win64** — `PMA.Store`
retourne « pas de coffre » hors Android, donc chaque lancement retélécharge et
redérive. Suffisant pour éprouver la liste, la recherche et la copie :
```bash
"C:\Program Files (x86)\Embarcadero\Studio\23.0\bin\dcc64.exe" -B "-UC:\Program Files (x86)\Embarcadero\Studio\23.0\lib\Win64\release" pmserverApk.dpr
```
## Diagnostiquer un échec de déchiffrement
`try-open.js` ouvre un conteneur avec Node, donc avec les primitives du
@@ -76,11 +111,15 @@ Node ouvre le fichier et pas le client, le défaut est dans le client.
| | |
|---|---|
| `PMA.Crypto.pas` | PBKDF2 (Pascal + natif JNI), AES-GCM, ouverture du conteneur |
| `PMA.TestVector.pas` | le vecteur embarqué, pour le banc d'essai |
| `PMA.SyncClient.pas` | `GET` authentifié, ETag, statuts distingués |
| `PMA.Store.pas` | coffre local chiffré sous une clé du Keystore |
| `PMA.Biometric.pas` | authentification via `FMX.BiometricAuth` |
| `PMA.Vault.pas` | l'instantané en mémoire : lecture et recherche |
| `PMA.TestVector.pas` | le vecteur embarqué, plus référencé par l'interface |
| `make-testvector.js` | régénère le vecteur avec WebCrypto |
| `testvector.md` | valeurs attendues, étage par étage |
Le banc d'essai est `mainApk.pas`, à la racine du dépôt.
L'interface est `mainApk.pas`, à la racine du dépôt.
## Piège de compilation rencontré