perf(android): the derived key is kept in the cache

Opening from the cache derives nothing, so the key learned last session was
gone and the FIRST save of every session paid the 4.5 seconds again. It now
travels in the sealed blob, next to the plaintext vault and to the password
that opens the container - that neighbourhood is what makes it acceptable:
it exposes nothing the file does not already hold.

The three parts go together or not at all. A key without its salt does not
say what it fits, which makes it useless, or worse, used where it does not
belong. A cache written by an earlier version carries none: LoadVault
returns nil, one derivation happens, and the next write stores it.

A freshly registered vault still derives once - EncryptContainer does not
hand back the key it just derived.

The plan records what the desktop confirmed: the two entries created on the
phone imported with nothing to correct.
This commit is contained in:
r-zakarya
2026-08-21 14:54:05 +01:00
parent 642dfdb077
commit 9a32fe8881
3 changed files with 103 additions and 12 deletions
+35
View File
@@ -665,6 +665,41 @@ téléphone qu'on voudra ensuite ouvrir sur le poste demandera un import.
ajoute un dépôt. C'est là que la fusion revient, et elle a été évitée
jusqu'ici pour de bonnes raisons.
### Vérifié : le poste reprend ce que le téléphone crée — 2026-08-21
Deux entrées créées sur le téléphone, puis synchronisées depuis le poste :
importées sans rien à corriger. C'est ce qui valide `AddEntry` — la forme de
l'entrée, l'uuid, les horodatages — autrement que par relecture du code.
### Le coût de la dérivation, et où il est parti
Ajouter une entrée prenait cinq à sept secondes. Le journal a tranché :
`key derived in 4395 ms`. Chaque enregistrement redérivait le mot de passe
maître pour retrouver la clé qui venait de servir.
Deux temps :
1. **La clé vit le temps de la session.** Réutilisée seulement si le conteneur
porte le même sel et le même nombre d'itérations — le poste peut avoir
rechiffré le sien avec un sel neuf, et une clé qui ne convient pas échoue
sur la marque de GCM en accusant un mot de passe qui serait bon.
2. **Elle est rangée dans le cache**, à côté du coffre en clair et du mot de
passe qui ouvre le conteneur. C'est ce voisinage qui rend l'ajout
acceptable : elle n'expose rien que ce fichier ne détienne déjà. Sans elle,
le *premier* enregistrement de chaque session repayait les 4,5 s, puisque
ouvrir depuis le cache ne dérive rien.
Un cache écrit par une version antérieure ne porte pas de clé : `LoadVault`
rend `nil`, on redérive une fois, et l'écriture suivante la range.
Reste dérivant une fois : un coffre **fraîchement inscrit** — `EncryptContainer`
ne rend pas la clé qu'il vient de dériver.
**Pas mesuré sur l'appareil** : la ligne `opened with the key already in hand`.
Une lecture répond 304 et ne déchiffre rien ; un verrouillage jette la clé.
Seul un enregistrement réel la montre, et le coffre de test publie vers un
vrai dépôt.
### Ce qui devient facultatif
Sans dépôt configuré, *Sync* n'a rien à faire : les réglages du serveur