# Validation `0.2.5` — Wallet security / interoperability / compliance ## 1. Objet Cette matrice constitue la validation durable de clôture de la release stable `0.2.5`. `pre.009` a fermé le premier gate technique security/interoperability/compliance, `pre.010` le gate documentaire, puis `pre.010-fix.001`–`fix.003` ont fermé la mise à niveau `ed25519-dalek 3.0.0`, la normalisation Rust workspace-wide et la compatibilité entre rustfmt et l’audit Python avant publication. Elle ne remplace ni la spécification [`../formats/KSPWALLET_V1.md`](../formats/KSPWALLET_V1.md), ni le threat model du plan [`../plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md`](../plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md), ni les deltas. Le verdict recherché porte sur quatre axes : ```text security/adversarial interoperability indépendante frontières KSP Cargo/dependency compliance ``` ## 2. État fonctionnel audité Au gate `pre.009`, Wallet possède : ```text format autonome .kspwallet V1 VIEW / OWNER indépendants Argon2id v19 XChaCha20-Poly1305 CSPRNG OS content keys séparées OWNER / metadata / secret Ed25519 distinct pour state_signature OWNER keypair Solana immuable et encapsulée create/open VIEW/OWNER persistence no-clobber replacement administratif capability-bound signature Solana OWNER alias/notes + rotations OWNER/VIEW strong disable/recreate VIEW import/export Solana CLI JSON + Base58 keypair 64 octets ``` Aucune dépendance Wallet vers Config, Transport, ExecutionPolicy, Store ou Tauri n'est autorisée. ## 3. Matrice adversariale | Cas | Résultat attendu | Preuve | |-----------------------------------------------------|---------------------------------------------------------|-----------------------------------------------------------------------------------------------| | mutation canonique KDF/wrap OWNER | rejet | `security_tests::owner_signed_regions_reject_canonical_tampering_before_unlock` | | mutation owner-control | rejet | même canari | | mutation metadata | rejet | même canari | | mutation secret | rejet | même canari | | mutation `state_signature` | rejet | même canari | | état OWNER-signed invalide + password vide | `wallet.authentication_failed` avant Argon2 | `security_tests::owner_signature_verification_precedes_owner_and_view_password_kdf` | | mutation des credentials rotatables VIEW uniquement | état OWNER toujours valide, OWNER ouvrable, VIEW rejeté | `security_tests::view_credential_tampering_does_not_forge_owner_state_and_cannot_unlock_view` | | wrong VIEW | `wallet.view_unlock_failed` générique | tests Wallet + security | | wrong OWNER | `wallet.owner_unlock_failed` générique | tests Wallet + security | | password canary dans Display/Debug | absent | `security_tests::unlock_failures_do_not_echo_password_material` | | metadata plaintext dans wallet verrouillé | absent | `wallet::tests::locked_full_vector_contains_no_authorized_identity_or_metadata_plaintext` | | stale OWNER handle | `wallet.state_conflict` | administration tests | | wallet A handle vers wallet B | `wallet.state_conflict` | administration tests | | écriture interrompue avant publish | ancien/destination absent préservé | persistence fault tests | | concurrence create no-clobber | exactement un gagnant | persistence concurrency test | | export secret depuis VIEW | surface publique absente | dependency/public-surface canary | ## 4. Ordre de vérification et anti-oracle Pour VIEW et OWNER : ```text parse structurel borné -> state_signature OWNER -> seulement ensuite Argon2 du slot demandé -> unwrap AEAD -> déchiffrement compartment -> parsing plaintext protégé ``` Le canari `state_signature invalide + password vide` distingue explicitement cet ordre : si Argon2 était évalué avant l'authentification d'état, l'entrée vide produirait une erreur de paramètres crypto ; le résultat attendu reste `wallet.authentication_failed`. Les erreurs de password ne distinguent pas KDF, wrapping AEAD ou contenu protégé et ne reflètent pas le password fourni. ## 5. Interopérabilité indépendante Les fixtures publiques test-only restent : ```text kspwallet_v1_wire_only.json kspwallet_v1_crypto_vectors.json kspwallet_v1_full_vector.json kspwallet_v1_full_vector_meta.json ``` Une sonde indépendante hors Rust/KSP exécutée pendant `pre.009` reproduit : ```text Argon2id OWNER derived key = exact Argon2id VIEW derived key = exact pre.004 XChaCha wrapped key = exact pre.004 unwrap content key = exact Ed25519 state signature = vérifiée sur le transcript exact Solana keypair Base58 = reproduite depuis 64 octets Solana Pubkey Base58 = reproduite depuis les 32 octets publics ``` La sonde utilise Python avec Argon2 et Ed25519/ChaCha20-Poly1305 disponibles localement ; XChaCha20 est reproduit par la construction HChaCha20 + ChaCha20-Poly1305 IETF. Cette sonde n'est ni versionnée ni requise par KSP : elle sert uniquement de seconde implémentation de vérification. Références cryptographiques normatives/primaires réauditées : - RFC 9106 — Argon2 ; le profil Argon2id `64 MiB / t=3` est la seconde recommandation pour environnements contraints ; - RFC 8032 — Ed25519/EdDSA et signatures 64 octets ; - documentation RustCrypto `chacha20poly1305` — XChaCha20-Poly1305 et nonce étendu 192 bits ; - documentation `solana-keypair 3.1.2` — keypair Ed25519 64 octets, validation secret/public et codecs Base58/JSON ; - documentation `tempfile 3.27` — publication/replacement et limites d'atomicité selon OS/filesystem. ## 6. Audit Cargo final observé après `pre.009` Commande opérateur : ```bash cargo tree -p ksp-wallet-lib cargo tree -p ksp-wallet-lib -d cargo tree -i solana-keypair@3.1.2 ``` Dépendances directes de Wallet observées : ```text argon2 0.5.3 base64 0.23.1 chacha20poly1305 0.11.0 ed25519-dalek 2.2.0 getrandom 0.4.3 ksp-core-lib ksp-logging-lib serde 1.0.229 serde_json 1.0.151 solana-keypair 3.1.2 tempfile 3.27.0 tokio 1.53.1 zeroize 1.9.0 ``` Points de convergence recherchés : ```text ed25519-dalek 2.2.0 : une seule génération, partagée KSP + solana-keypair solana-address 2.7.0: une seule génération, partagée solana-pubkey + solana-keypair solana-keypair 3.1.2: unique parent KSP direct = ksp-wallet-lib ``` Doublons transitifs observés et acceptés au gate : ```text block-buffer 0.10 / 0.12 cpufeatures 0.2 / 0.3 crypto-common 0.1 / 0.2 digest 0.10 / 0.11 getrandom 0.3 / 0.4 rand 0.9 / 0.10 rand_core 0.6 / 0.9 / 0.10 sha2 0.10 / 0.11 syn 2 / 3 ``` Ces doublons proviennent des générations transitives actuelles de RustCrypto, `solana-keypair`/`solana-ed25519` et Logging. Ils ne correspondent pas à deux versions directes déclarées par Wallet pour une même fonction. Les éliminer exigerait de forcer des upstream incompatibles ou de régresser les versions retenues ; aucun contournement KSP n'est introduit. ## 7. Frontières KSP Canaries durables : ```text Wallet -> Core + Logging + primitives explicites Wallet -X-> Config Wallet -X-> Transport Wallet -X-> ExecutionPolicy Wallet -X-> Store Wallet -X-> Tauri Wallet -X-> tracing direct Wallet -X-> std::env Wallet -X-> solana-pubkey direct Wallet -X-> solana-signer direct Wallet -X-> solana-signature direct Wallet -X-> bs58 direct ``` `ksp-core-lib::Pubkey` reste le type public transversal. `solana-keypair` reste un détail secret/signing propre à Wallet ; aucune `Keypair`, `Signer` ou `Signature` Solana externe n'est réexportée par la surface publique Wallet. `WalletTransferFormat` est `#[non_exhaustive]` : la liste built-in reste additive et les consumers ne peuvent pas supposer qu'elle est fermée. `pre.009` ne crée pas de trait/plugin externe de codec secret : un tel trait devrait exposer les 64 octets de keypair à une implémentation tierce et constituerait une nouvelle surface secrète. Cette extension éventuelle exige un contrat dédié futur plutôt qu'une abstraction V1 prématurée. ## 8. Limites et non-garanties V1 Le verdict security est positif **dans le threat model documenté**, avec les limites explicites suivantes : 1. un remplacement total par un autre wallet autonome valide n'est pas détectable à partir du nouveau fichier seul ; 2. un rollback total vers une ancienne copie valide n'est pas détecté sans état/ancre externe ; 3. le garde-fou `wallet.state_conflict` réduit les erreurs de cible/stale handle mais n'est pas un CAS filesystem portable linéarisable ; 4. l'atomicité et la crash-durability du filesystem dépendent de l'OS/filesystem ; 5. les ACL/permissions sont une hygiène externe, pas une garantie cryptographique ; 6. `zeroize` s'applique aux buffers possédés explicitement mais ne prouve pas l'effacement physique de toutes les copies potentielles ; 7. aucune protection hardware, second facteur, keychain, remote signer ou anti-rollback externe n'est incluse en V1. `pre.010` les conserve explicitement dans README/USAGE/spec et ne les transforme pas en garanties. ## 9. Gate opérateur `pre.009` À exécuter après application du delta : ```bash cargo fmt --all cargo check --workspace cargo clippy --workspace --all-targets cargo test -p ksp-wallet-lib cargo test --workspace cargo tree -p ksp-wallet-lib cargo tree -p ksp-wallet-lib -d cargo tree -i ed25519-dalek@2.2.0 cargo tree -i solana-keypair@3.1.2 cargo tree -i solana-address@2.7.0 ``` Ce gate est considéré vert uniquement si les canaris adversariaux passent sans warning et si le tree ne révèle aucune nouvelle dépendance directe ou génération Dalek/Solana-address inattendue ; le résultat effectivement observé est enregistré en section 10. ## 10. Résultat opérateur `pre.009` Checkpoint communiqué le 2026-08-19 après application de `0.2.5-pre.009` : ```text cargo fmt --all OK cargo check --workspace OK, aucun warning cargo clippy --workspace --all-targets OK, aucun warning cargo test -p ksp-wallet-lib OK unit 58 passed / 1 ignored dependency_boundary 3 passed public_api 9 passed doctests 2 passed cargo test --workspace OK ``` Le tree opérateur confirme : ```text ed25519-dalek 2.2.0 <- ksp-wallet-lib <- solana-keypair 3.1.2 <- ksp-wallet-lib solana-keypair 3.1.2 <- ksp-wallet-lib uniquement comme parent KSP direct solana-address 2.7.0 <- solana-keypair 3.1.2 <- solana-pubkey 4.3.0 via ksp-core-lib ``` Les doublons transitifs déjà inventoriés restent inchangés et aucun nouveau bypass de frontière n'est observé. ## 11. Gate documentaire `pre.010` La candidate finale doit vérifier : ```text crates/ksp-wallet-lib/README.md existe et décrit les responsabilités/frontières crates/ksp-wallet-lib/USAGE.md couvre create/open/sign/admin/rotation/import/export KSPWALLET_V1.md ne laisse aucune opération V1 planifiée comme restant à implémenter le graphe Wallet documente Pubkey via Core et keypair encapsulée dans Wallet le plan 0.2.5 pointe désormais vers rel.001 le prompt 0.2.6 Wallet Desk existe et commence par audit/sizing les liens Markdown locaux modifiés sont valides aucun fichier Rust ni manifest Cargo n'est modifié par pre.010 workspace.package.version reste 0.2.5-pre.9 car pre.010 est doc-only ``` ## 12. Correctifs de clôture `pre.010-fix.001` à `fix.003` La clôture technique apporte : ```text ed25519-dalek direct KSP : ^2.2 -> ^3.0 normalisation Rust workspace-wide scripts/audit_rust_workspace_rules.py obligatoire après cargo fmt corrections des chemins crate::/super:: et des réexports crate-root espacement structurel normalisé audit Python rendu compatible avec l'ordre canonique produit par rustfmt prompt 0.2.6 renforcé + TODO 0.2.11 prix offchain dans Wallet Desk ``` Le wire `.kspwallet` V1, les paramètres Argon2/XChaCha, la sémantique Ed25519, les capabilities VIEW/OWNER et les API Wallet fonctionnelles ne changent pas pendant ces fixes. Le tree final attendu et observé contient deux générations Dalek intentionnelles : ```text ed25519-dalek 3.0.0 <- ksp-wallet-lib direct ed25519-dalek 2.2.0 <- solana-keypair 3.1.2 <- ksp-wallet-lib solana-address 2.7.0 <- solana-keypair 3.1.2 <- solana-pubkey 4.3.0 via ksp-core-lib ``` La génération `2.2.0` n'est pas un doublon direct KSP à corriger artificiellement : elle reste imposée par `solana-keypair 3.1.2`. ## 13. Checkpoint opérateur final `pre.010-fix.003` Validation communiquée le **20 août 2026** : ```text cargo fmt --all OK python3 scripts/audit_rust_workspace_rules.py General Rust rule audit clean Rust export completeness audit 0 candidate(s) KSP workspace Rust rule audit clean cargo check --workspace OK cargo clippy --workspace --all-targets OK cargo test -p ksp-wallet-lib OK unit 58 passed / 1 ignored dependency_boundary 3 passed public_api 9 passed doctests 2 passed cargo test --workspace OK ``` Le checkpoint ne rapporte aucun warning Clippy ou compilation. Les tests Config Desk, Config, Core, Logging, Transport et Wallet passent dans le workspace complet ; les smokes réseau opt-in restent volontairement `ignored` dans la suite déterministe. ## 14. Audit Cargo final Le dernier audit de dépendances avant release confirme notamment : ```text ed25519-dalek 3.0.0 <- ksp-wallet-lib ed25519-dalek 2.2.0 <- solana-keypair 3.1.2 <- ksp-wallet-lib solana-keypair 3.1.2 <- ksp-wallet-lib uniquement comme parent KSP direct solana-address 2.7.0 <- solana-keypair 3.1.2 <- solana-pubkey 4.3.0 via ksp-core-lib ``` Les doublons transitifs RustCrypto/Solana déjà documentés restent acceptés et n'introduisent aucun second owner KSP du secret Solana. ## 15. Verdict final `0.2.5` **Verdict positif.** Tous les gates déterministes et structurels requis avant publication sont verts sur `0.2.5-pre.010-fix.003`. `0.2.5-rel.001` est donc strictement publicationnelle : ```text workspace.package.version -> 0.2.5 aucun changement Rust fonctionnel aucun changement du wire .kspwallet V1 aucune nouvelle dépendance/feature CHANGELOG/ROADMAP/plans/validation synchronisés commit attendu : v0.2.5-rel.001 tag stable attendu : v0.2.5 ``` Après validation du delta `rel.001`, la session suivante s'ouvre avec `prompts/011-V0_2_6_START_PROMPT.md` et commence par `0.2.6-pre.001` audit/sizing.