3c1525e46f
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.
95 lines
4.3 KiB
Markdown
95 lines
4.3 KiB
Markdown
# 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.
|
|
|
|
## 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 <fichier> <mot-de-passe>
|
|
```
|
|
|
|
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.TestVector.pas` | le vecteur embarqué, pour le banc d'essai |
|
|
| `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.
|
|
|
|
## 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.
|