Files
2026-08-19 11:29:05 +02:00

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.