11 KiB
Delta 0.2.5-pre.004 — Argon2id + XChaCha20-Poly1305 + CSPRNG + vecteur crypto
Base requise
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 :
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 :
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 :
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 :
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] :
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 :
argon2 = { workspace = true, features = ["zeroize"] }
chacha20poly1305 = { workspace = true, features = ["alloc", "zeroize"] }
getrandom.workspace = true
Audit actuel de la tranche :
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<u8>, 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 :
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 :
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 :
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 :
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 :
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 :
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 :
wallet/randomness_failed
Aucun secret, seed ou état RNG KSP n'est introduit.
Le type interne de clé 32 octets :
n'est ni Copy ni Clone
Debug => SecretKeyV1(<redacted>)
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 :
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 :
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 :
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 :
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 :
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 :
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 :
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 :
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 :
cargo test -p ksp-wallet-lib benchmark_argon2_creation_candidates -- --ignored --nocapture --test-threads=1
Fichiers ajoutés
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
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.