PBKDF2 is implemented in Pascal rather than through javax.crypto SecretKeyFactory: PBEKeySpec takes a char[] and the provider decides how those chars become bytes, so a non-ASCII sync password would derive a different key than the browser and fail as 'wrong password'. AES-GCM does go through JNI - one call, hardware-backed, and WebCrypto's tag-appended layout is exactly what Cipher expects. The vector is generated with WebCrypto itself and uses a non-ASCII password on purpose; an ASCII-only vector would have hidden that bug. It records the derived key separately from the plaintext so a failure points at one stage instead of the whole chain.
2.4 KiB
Vecteur de test — conteneur pm-encrypted-export-v1
testvector.json est produit par make-testvector.js avec WebCrypto, la
primitive exacte du navigateur. Sel et IV sont figés, donc le fichier est
reproductible : une différence signale un vrai changement de comportement, pas
du bruit.
Le fichier de test réutilise un couple sel/IV fixe. C'est acceptable pour un vecteur, jamais dans l'application : réutiliser un IV avec la même clé détruit la sécurité de GCM.
Paramètres
| Mot de passe | sync-test-password-éàü |
| KDF | PBKDF2-SHA256, 600 000 itérations, sel de 32 octets |
| Chiffrement | AES-256-GCM, IV de 12 octets, tag de 16 octets en fin de ciphertext |
Le mot de passe est volontairement non-ASCII. C'est le seul cas qui
distingue une implémentation correcte d'une implémentation approximative : Java
PBEKeySpec prend un char[] et laisse le fournisseur choisir l'encodage, ce
qui donne une clé différente pour é, à, ü. Un vecteur en pur ASCII aurait
laissé passer le bug.
Valeurs attendues
Vérifier dans cet ordre — chaque étage isole une cause :
1. Clé dérivée (DeriveKeyPBKDF2, avant tout déchiffrement)
6bd7683ea78e163062f02681b03f2906fcf5a3a25025005af4e71982e6e2b286
Si elle diffère, le problème est dans PBKDF2 ou dans l'encodage du mot de passe
— surtout pas dans AES. Contrôler d'abord que le mot de passe est bien encodé
en UTF-8 (sync-test-password-éàü fait 25 octets, pas 22 caractères).
2. Texte clair (AesGcmDecrypt puis DecryptContainer)
longueur UTF-8 : 572 octets
SHA-256 : ddaee44f8ac8374f1ece6d9ee2abd104d59cd283556f1c4c07afa95ae1dbd2a7
début : {"version":1,"exported_at":"2026-08-14T12:00:00.000Z","username":"test
Si la clé est bonne et que le déchiffrement échoue, chercher du côté de la
longueur du tag (128 bits) ou d'un tag qui aurait été séparé du corps —
WebCrypto le concatène, javax.crypto l'attend concaténé, il n'y a rien à
découper.
Contenu
2 entrées, dont une en corbeille (deleted: 1 avec son deleted_at), et
un dossier avec sa couleur. De quoi vérifier que le client mobile affiche la
corbeille séparément au lieu de la mélanger à la liste.
Régénérer
node make-testvector.js > testvector.json
En PowerShell, la redirection ajoute un BOM qui rend le JSON illisible.
Utiliser cmd, ou écrire le fichier sans BOM.