Files
Password-Manager/android-client
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
..

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.

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.