Files
Password-Manager/android-client/testvector.md
T
r-zakarya 02d89c58d2 feat(android): container crypto unit plus a known-answer test vector
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.
2026-08-14 18:04:39 +01:00

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.