15 KiB
Validation 0.2.5 — Wallet security / interoperability / compliance
1. Objet
Cette matrice constitue la validation durable de clôture de 0.2.5. pre.009 a fermé le premier gate technique security/interoperability/compliance et pre.010 a ajouté le gate documentaire. pre.010-fix.001 rouvre volontairement le gate technique pour la mise à niveau ed25519-dalek 3.0.0 et la normalisation Rust workspace-wide avant toute publication stable. Elle ne remplace ni la spécification ../formats/KSPWALLET_V1.md, ni le threat model du plan ../plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md, ni les deltas.
Le verdict recherché porte sur quatre axes :
security/adversarial
interoperability indépendante
frontières KSP
Cargo/dependency compliance
2. État fonctionnel audité
Au gate pre.009, Wallet possède :
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 :
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 :
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 :
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=3est 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 :
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 :
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 :
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 :
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 :
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 :
- un remplacement total par un autre wallet autonome valide n'est pas détectable à partir du nouveau fichier seul ;
- un rollback total vers une ancienne copie valide n'est pas détecté sans état/ancre externe ;
- le garde-fou
wallet.state_conflictréduit les erreurs de cible/stale handle mais n'est pas un CAS filesystem portable linéarisable ; - l'atomicité et la crash-durability du filesystem dépendent de l'OS/filesystem ;
- les ACL/permissions sont une hygiène externe, pas une garantie cryptographique ;
zeroizes'applique aux buffers possédés explicitement mais ne prouve pas l'effacement physique de toutes les copies potentielles ;- 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 :
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 :
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 :
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 :
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. Correctif technique pre.010-fix.001
Le correctif de clôture apporte deux changements qui exigent une nouvelle validation opérateur :
ed25519-dalek direct KSP : ^2.2 -> ^3.0
normalisation Rust workspace + scripts/audit_rust_workspace_rules.py obligatoire
Le résultat pre.009 des sections 6 et 10 reste une preuve historique valide pour l'état 2.2.0; il ne doit pas être présenté comme le tree final après le fix. solana-keypair 3.1.2 conserve actuellement sa branche Dalek 2.x, donc le tree attendu après le fix est :
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
Gate opérateur post-fix :
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
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@3.0.0
cargo tree -i ed25519-dalek@2.2.0
cargo tree -i solana-keypair@3.1.2
cargo tree -i solana-address@2.7.0
Le fix ne modifie ni le wire .kspwallet V1, ni les paramètres Argon2/XChaCha, ni la sémantique Ed25519, ni les capabilities VIEW/OWNER. Le verdict final reste en attente de ce checkpoint opérateur.
13. Verdict final candidate 0.2.5
Le verdict pre.009 était positif pour l'état alors audité, mais la candidate stable n'est plus considérée fermée tant que pre.010-fix.001 n'a pas passé son gate opérateur. Le changement de génération Dalek et la normalisation Rust sont intentionnels et ne changent pas le wire V1, mais ils constituent un changement technique réel qui doit être compilé, linté et testé dans l'environnement opérateur.
Après validation complète de pre.010-fix.001, le verdict peut redevenir positif et seulement alors 0.2.5-rel.001 redevient la prochaine étape publicationnelle.