# Client Android — état et décisions Lecture seule de l'instantané chiffré du coffre. La crypto est **validée sur appareil réel** contre un vecteur produit par WebCrypto. ## Ce qui est mesuré, pas supposé | Question | Réponse | Mesure | |---|---|---| | Le conteneur s'ouvre-t-il en Delphi ? | oui | clair de 572 octets, empreinte identique | | AES-GCM par JNI fonctionne-t-il ? | oui | `javax.crypto`, tag concaténé, rien à découper | | Android encode-t-il le `char[]` en UTF-8 ? | **oui** | clé identique sur un mot de passe non-ASCII | | Le PBKDF2 natif est-il plus rapide ? | **à peine** | 3869 ms contre 4139 ms | Le natif est adopté — c'est du code de plateforme plutôt que le nôtre — avec le Pascal en repli, seul disponible hors Android. ## La conséquence architecturale **4 secondes pour ouvrir un instantané, et c'est incompressible.** PBKDF2 ne bénéficie d'aucune accélération matérielle, et on ne peut pas baisser les 600 000 itérations : elles sont inscrites dans le conteneur produit par le poste Windows. Le piège serait de payer ces 4 secondes à **chaque ouverture de l'application**. Ce n'est pas nécessaire, parce que le coût est lié au **conteneur**, pas à la session — et chaque export génère un **nouveau sel**, donc une mise en cache par mot de passe ne servirait à rien. L'architecture qui en découle : 1. **À la synchronisation** — télécharger, dériver (4 s, une fois), déchiffrer. 2. **Puis stocker le coffre localement** chiffré sous une clé du **Android Keystore**, adossée à la biométrie. AES est accéléré matériellement : la relecture est instantanée. 3. **À l'ouverture** — biométrie, puis lecture locale. Aucune dérivation. Les 4 secondes ne se paient donc qu'à l'arrivée d'un instantané **nouveau**, ce qui est acceptable et compréhensible pour l'utilisateur. > Le coffre déchiffré contient **tous les mots de passe en clair**. Il ne doit > jamais toucher le disque autrement que chiffré sous la clé du Keystore, et la > sauvegarde automatique Android doit être désactivée pour l'application. ## Chaîne validée de bout en bout Sur appareil : `GET` authentifié (80 ms) → déchiffrement (3893 ms) → lecture du JSON, entrées vivantes et corbeille comptées séparément. Second appel : **304**, rien de retéléchargé, rien de re-déchiffré. **L'ETag n'est retenu que si le déchiffrement a réussi.** Le conserver dès la réception paraît naturel et c'est un piège : après un échec, le prochain appel 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 navigateur : ```bash node try-open.js ``` Il sépare deux causes que l'application ne peut pas distinguer — un tag GCM qui ne vérifie pas signifie *mot de passe faux OU fichier altéré*, sans plus. Si Node ouvre le fichier et pas le client, le défaut est dans le client. > Le mot de passe de sync n'est **pas** le mot de passe maître. Il n'est affiché > nulle part : `prefs.bin` le garde chiffré par DPAPI, et l'application sait > seulement le définir. En cas d'oubli, le redéfinir puis relancer un sync pour > rechiffrer l'instantané distant. ## Fichiers | | | |---|---| | `PMA.Crypto.pas` | PBKDF2 (Pascal + natif JNI), AES-GCM, ouverture du conteneur | | `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 | L'interface est `mainApk.pas`, à la racine du dépôt. ## Piège de compilation rencontré Cette installation de Delphi ne produisait **aucun** APK FireMonkey fonctionnel : `fmx.dex.jar` avait été renommé sur disque **et** retiré de la configuration de l'EDI. Symptôme : l'application s'installe puis se ferme aussitôt, avec `ClassNotFoundException: FMXNativeActivity` dans `adb logcat`. Le projet référence désormais explicitement `fmx.dex.jar` dans `EnabledSysJars`. Après un tel changement, **supprimer les `classes*.dex` intermédiaires** : la compilation les réutilise sans les régénérer, et le correctif semble sans effet.