# Delta `0.2.5-pre.004` — Argon2id + XChaCha20-Poly1305 + CSPRNG + vecteur crypto ## Base requise ```text livraison : 0.2.5-pre.003-fix.001 workspace.package.version = "0.2.5-pre.3.fix.1" ``` La base effective est `pre.003` complétée par `pre.003-fix.001` et par les deux réorganisations opérateur conservées dans `constants.rs` et `lib.rs` : bloc unique de `pub const` avant `pub(crate) const`, puis groupe unique de `pub use` avant le groupe `pub(crate) use`. Les validations opérateur fournies pour cette base sont propres : ```text cargo fmt --all -> terminé cargo check --workspace -> terminé sans warning cargo clippy --workspace --all-targets -> terminé sans warning cargo test -p ksp-wallet-lib -> 18 unit tests OK 2 dependency-boundary tests OK 5 public API tests OK 2 doc-tests compile_fail OK 0 échec cargo tree -p ksp-wallet-lib -d -> aucune duplication dans le sous-arbre Wallet ``` Le workspace possède par ailleurs plusieurs générations transitives de `base64` (`0.21.7`, `0.22.1`, `0.23.1`), mais le sous-arbre `ksp-wallet-lib` validé en `pre.003-fix.001` ne contient aucune duplication. Les audits inverses doivent donc qualifier la version (`base64@0.23.1`) lorsque nécessaire. ## Objectif Matérialiser la première couche cryptographique **in-memory** du format V1 sans encore créer/ouvrir un wallet complet : ```text Argon2id v19 password KDF KEK 32 octets XChaCha20-Poly1305 content keys 32 octets wrapping / unwrapping authentifié CSPRNG OS via getrandom zeroization des clés/buffers secrets possédés premier vecteur public KDF + wrapping benchmark opérateur des candidats Argon2 de création ``` Restent explicitement hors tranche : ```text payload owner-control réel payload metadata réel payload secret réel state signature Ed25519 effective create/open VIEW/OWNER filesystem/persistence signature Solana rotations de passwords import/export ``` ## Version Cargo Conformément à `VER-ID-009` : ```text 0.2.5-pre.3.fix.1 -> 0.2.5-pre.4 ``` Le compteur documentaire du `Cargo.toml` racine passe de `146` à `148` : `pre.003-fix.001` avait modifié ce fichier sans consommer le numéro `147`; cette livraison restaure l'historique `GEN-FILE-004` avant sa propre modification. Le même principe est appliqué à `wire.rs` (`1 -> 3`, avec `2` consommé conceptuellement par `pre.003-fix.001`) et à `lib.rs` (`2 -> 4`, avec `3` correspondant à la réorganisation opérateur post-`pre.003`). ## Dépendances Nouvelles dépendances communes sous `[workspace.dependencies]` : ```toml argon2 = { version = "^0.5", default-features = false } chacha20poly1305 = { version = "^0.11", default-features = false } getrandom = { version = "^0.4", default-features = false } ``` Le manifest Wallet active uniquement les features consommées : ```toml argon2 = { workspace = true, features = ["zeroize"] } chacha20poly1305 = { workspace = true, features = ["alloc", "zeroize"] } getrandom.workspace = true ``` Audit actuel de la tranche : ```text argon2 génération stable 0.5.x, publiée 0.5.3 ; 0.6 reste release-candidate chacha20poly1305 génération stable 0.11.x, publiée 0.11.0 getrandom génération stable 0.4.x, publiée 0.4.3 zeroize déjà présent au workspace ``` `argon2` n'active ni API PHC/password-hash, ni rand, ni alloc par commodité ; Wallet utilise directement `hash_password_into`. `chacha20poly1305` active `alloc` parce que l'API AEAD utilisée retourne les ciphertexts/plaintexts dans des `Vec`, et `zeroize` afin de durcir les états internes de la primitive. Le CSPRNG est possédé explicitement par Wallet via `getrandom` plutôt que par une feature de génération aléatoire de l'AEAD. Aucun client RPC, aucune crate Solana supplémentaire, Config, Transport, Store, Tauri ou `tracing` direct n'est ajouté. ## Argon2id V1 La dérivation utilise exactement : ```text algorithm Argon2id version 19 / 0x13 password octets UTF-8 exacts fournis par le caller salt octets sérialisés dans le key slot m_cost memory_kib sérialisé t_cost iterations sérialisé p_cost parallelism sérialisé output 32 octets pepper aucun secret ext. aucun ``` Les bornes V1 déjà figées sont réappliquées avant le KDF. La tranche ajoute explicitement l'invariant Argon2 : ```text memory_kib >= 8 * parallelism ``` Un password vide ou supérieur à `1024` octets est rejeté par la primitive interne. Le KDF reste sync/in-memory à ce niveau bas. `pre.005+` doit l'appeler depuis les futurs flux async create/open via la frontière blocking prévue par le plan ; aucune API async fictive n'est ajoutée ici. ## Default Argon2 non figé Cette livraison **ne choisit pas arbitrairement** le profil de création. Le test opérateur ignoré par défaut compare : ```text 64 MiB / 3 passes / 1 lane 128 MiB / 3 passes / 1 lane 256 MiB / 3 passes / 1 lane ``` Commande dédiée après les validations normales : ```bash cargo test -p ksp-wallet-lib benchmark_argon2_creation_candidates -- --ignored --nocapture --test-threads=1 ``` Le résultat doit être conservé dans le retour opérateur. `pre.005` ne figera le default de création qu'après examen de ces mesures. Les wallets existants resteront indépendants de ce default grâce aux paramètres sérialisés dans chaque slot. ## XChaCha20-Poly1305 La primitive V1 utilise : ```text key 32 octets nonce 24 octets AAD bytes TLV domain-separated figés en pre.003 ciphertext = encrypted bytes || tag Poly1305 16 octets ``` Le nonce est toujours fourni séparément à l'AEAD. La tranche ne construit aucune canonicalisation JSON crypto : les AAD restent ceux du codec sémantique TLV de `pre.003`. Toute erreur de tag/déchiffrement est ramenée au code générique : ```text wallet/authentication_failed ``` Le code ne produit donc pas d'oracle public distinguant key, nonce, AAD ou ciphertext incorrect. ## CSPRNG et secrets en mémoire `getrandom::fill` fournit directement les octets provenant de la source cryptographique de l'OS. Une défaillance devient : ```text wallet/randomness_failed ``` Aucun secret, seed ou état RNG KSP n'est introduit. Le type interne de clé 32 octets : ```text n'est ni Copy ni Clone Debug => SecretKeyV1() zeroize ses octets au Drop ``` L'unwrap zeroize également son buffer plaintext intermédiaire après transfert dans le type secret possédé. Les primitives restent volontairement privées en `pre.004`. Un `allow(dead_code)` **local au seul module `crypto.rs` et documenté** existe parce que cette tranche matérialise ces primitives une prerelease avant leurs callers production `create/open` de `pre.005`; il ne modifie aucun lint workspace et doit disparaître lorsque ces callers sont branchés. ## Vecteur cryptographique public Nouveau fixture : ```text crates/ksp-wallet-lib/tests/fixtures/kspwallet_v1_crypto_vectors.json ``` Il contient exclusivement des valeurs publiques **TEST ONLY** et fixe de manière reproductible : ```text password UTF-8 salt m/t/p Argon2id KEK dérivée attendue content key 32 octets nonce XChaCha 24 octets AAD wrapped key attendu ``` Les paramètres du vecteur (`32 KiB / 2 passes / 1 lane`) sont volontairement faibles pour conserver un test déterministe rapide. Ils ne constituent jamais le default de production ni une recommandation de sécurité. Le vecteur a été recalculé indépendamment du code Rust avec une implémentation Argon2id et une construction XChaCha20-Poly1305 `HChaCha20 + ChaCha20-Poly1305 IETF`; l'implémentation indépendante a d'abord été vérifiée contre le vecteur HChaCha20 publié par la spécification XChaCha. Les tests vérifient : ```text KEK attendue ciphertext/tag attendu unwrap exact de la content key tampering -> authentication_failed Debug secret redacted CSPRNG key/nonce callable ``` ## Wire et spécification `wire.rs` rejette maintenant avant crypto un tuple Argon2 dont la mémoire ne satisfait pas les huit blocs de 1 KiB par lane. `docs/formats/KSPWALLET_V1.md` documente désormais indépendamment du code Rust : ```text Argon2id v19 exact password UTF-8 sans normalisation implicite output KEK 32 octets absence de pepper/secret externe XChaCha20-Poly1305 key/nonce/tag CSPRNG OS content keys 32 octets wrapping OWNER / VIEW emplacement du vecteur crypto public statut explicitement non fixé du default Argon2 de création ``` Le format V1 reste autonome : tous les salts, paramètres et nonces nécessaires sont dans le `.kspwallet`; aucun facteur externe n'est introduit. ## Canaries de dépendances `dependency_boundary.rs` vérifie les trois nouvelles dépendances Wallet et leurs features, tout en conservant les interdictions : ```text Wallet -X-> Config Wallet -X-> Transport Wallet -X-> ExecutionPolicy Wallet -X-> Store Wallet -X-> Tauri Wallet -X-> tracing direct Wallet -X-> solana-pubkey direct ``` `Pubkey` reste possédée/réexportée par `ksp-core-lib`; cette tranche n'ajoute aucune dépendance Solana Wallet. ## Validation attendue Après application : ```bash cargo fmt --all cargo check --workspace cargo clippy --workspace --all-targets cargo test -p ksp-wallet-lib cargo test --workspace ``` Audit de dépendances/features de la tranche : ```bash cargo tree -p ksp-wallet-lib cargo tree -p ksp-wallet-lib -d cargo tree -e features -p ksp-wallet-lib cargo tree -i argon2@0.5.3 cargo tree -i chacha20poly1305@0.11.0 cargo tree -i getrandom@0.4.3 cargo tree -i base64@0.23.1 cargo tree -i zeroize@1.9.0 ``` L'audit doit confirmer au minimum : ```text pas de duplication dans le sous-arbre Wallet sans justification pas de default features Argon2/ChaCha réactivées accidentellement pas de Config/Transport/Tauri/RPC Solana transitif propre à Wallet base64 0.23.1 reste la génération consommée directement par Wallet ``` Puis exécuter séparément le benchmark opérateur : ```bash cargo test -p ksp-wallet-lib benchmark_argon2_creation_candidates -- --ignored --nocapture --test-threads=1 ``` ## Fichiers ajoutés ```text crates/ksp-wallet-lib/src/crypto.rs crates/ksp-wallet-lib/unit_tests/crypto.rs crates/ksp-wallet-lib/tests/fixtures/kspwallet_v1_crypto_vectors.json deltas/0.2.5/pre.004.md ``` ## Fichiers modifiés ```text Cargo.toml ROADMAP.md crates/ksp-wallet-lib/Cargo.toml crates/ksp-wallet-lib/src/error.rs crates/ksp-wallet-lib/src/lib.rs crates/ksp-wallet-lib/src/wire.rs crates/ksp-wallet-lib/tests/dependency_boundary.rs crates/ksp-wallet-lib/tests/public_api.rs crates/ksp-wallet-lib/unit_tests/wire.rs docs/formats/000-README.md docs/formats/KSPWALLET_V1.md docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md ``` ## Suite `0.2.5-pre.005` consommera ces primitives pour les compartiments `owner_control` / metadata / secret, `create/open VIEW/OWNER` et l'authentification d'état OWNER niveau B. Le default Argon2 de création sera figé uniquement après réception du benchmark opérateur de cette tranche.