fix(android): report the biometric failure instead of destroying the cache

The launch-time auto-open received boUnavailable and wiped the cache, seconds after the write path had proved the device could authenticate. The diagnosis was wrong and the reaction irreversible. An automatic action now never clears the vault: it logs the cause and leaves the user on the unlock screen. Only an explicit tap on Open, followed by a clean refusal from the system, may clear it. PMA.Biometric keeps the reason in LastBiometricError, and a Java-layer exception now maps to boFailed rather than boUnavailable - an exception says nothing about the device. The log memo also wraps: it was cutting off exactly the end of the message, which is where the cause is named.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-08-15 13:42:07 +01:00
parent 5187da3070
commit 09048410e1
4 changed files with 164 additions and 10 deletions
+37
View File
@@ -57,6 +57,43 @@ retéléchargé, pas rester illisible. C'est l'autre branche de la même garde.
---
## 1 bis. La décision qui bloque le cache (constatée le 2026-08-15)
Le cache ne s'écrit presque jamais. Journal sur appareil :
```
dechiffre en 4446 ms
cache non ecrit : EJNIException - android.security.key…
```
**Cause.** Une clé Keystore créée avec `setUserAuthenticationRequired` +
`setUserAuthenticationValidityDurationSeconds` exige une authentification
récente **pour chiffrer autant que pour déchiffrer**. Or l'écriture suit un
téléchargement silencieux, sans invite : passé la fenêtre de 30 s depuis le
dernier déverrouillage, `Cipher.init(ENCRYPT_MODE)` lève. La toute première
écriture avait réussi seulement parce que le téléphone venait d'être
déverrouillé — d'où un test qui passe puis ne passe plus.
Conséquence : l'application fonctionne, mais redérive 4,4 s à chaque
lancement. Le cache, qui est toute la raison d'être de `PMA.Store`, ne sert
jamais.
**Trois issues, par ordre de justesse :**
1. **Paire de clés asymétrique** — c'est le motif documenté par Android pour
« écrire à tout moment, lire sous authentification ». La partie publique
chiffre sans invite, la partie privée (protégée) déchiffre. Comme RSA ne
chiffre pas 16 Ko, il faut un chiffrement hybride : une clé AES aléatoire
par écriture, enveloppée par la clé publique. Plus de fenêtre de validité,
plus de course contre le temps. ~80 lignes de JNI.
2. **Authentifier avant d'écrire** — une invite à chaque synchronisation de
fond, ce qui détruit précisément le silence recherché.
3. **Allonger la fenêtre** — atténue sans corriger, et affaiblit la garantie
proportionnellement.
Retenir la 1. Les deux autres échangent une propriété de sécurité contre du
confort, ce qui est le mauvais sens pour un gestionnaire de mots de passe.
## 2. Durcissement — petit, à faire avant tout usage réel
Par ordre de gravité, pas de taille.