14 KiB
Delta 0.2.5-pre.005 — payloads V1 + create/open VIEW/OWNER + autorité OWNER Ed25519
Base requise
livraison : 0.2.5-pre.004-fix.002
workspace.package.version = "0.2.5-pre.4.fix.2"
Les validations opérateur de cette base sont propres :
cargo fmt --all OK
cargo check --workspace OK
cargo clippy --workspace --all-targets OK
cargo test -p ksp-wallet-lib OK : 22 passés, 1 benchmark ignoré
+ 2 dependency-boundary
+ 5 public API
+ 2 doc-tests compile_fail
cargo test -p ksp-logging-lib --test ownership OK : 2/2
cargo test --workspace OK
Le benchmark Argon2 opérateur acquis avant cette tranche donne :
64 MiB / 3 passes / 1 lane -> 1742 ms
128 MiB / 3 passes / 1 lane -> 3459 ms
256 MiB / 3 passes / 1 lane -> 6925 ms
Objectif
Matérialiser les compartiments protégés et les capabilities in-memory complètes de lecture sans ouvrir encore la persistence filesystem ni l'administration :
profil de création KSP V1 issu du benchmark
payload owner_control exact
payload metadata exact
payload secret Solana exact
autorité de format Ed25519 distincte de la keypair Solana
state_signature OWNER vérifiée avant KDF
create V1 in-memory
open VIEW indépendant
open OWNER indépendant
inspection locked authentifiée
vecteur .kspwallet complet interopérable
Restent explicitement hors tranche :
filesystem / persistence atomique / no-clobber
signature Solana publique
export du secret
modification alias/notes
rotation password VIEW par VIEW
rotation password VIEW/OWNER par OWNER
disable/recreate VIEW
révocation VIEW forte
import/export
Ces opérations restent prévues dans pre.006+ sans déplacer de responsabilité vers Config ou Transport.
Version Cargo
Conformément au séquencement des prereleases :
0.2.5-pre.4.fix.2 -> 0.2.5-pre.5
workspace.package.version reste le signal technique de la tranche.
Profil Argon2 de création KSP
À partir des mesures opérateur, les créations KSP V1 utilisent initialement :
algorithm Argon2id
version 19
memory_kib 65_536
iterations 3
parallelism 1
salt 32 octets CSPRNG indépendants par slot
output KEK 32 octets
Le choix vise un coût d'environ 1,7 s sur la machine de calibration plutôt que les profils 128/256 MiB mesurés à environ 3,5/6,9 s.
Ce profil est un default de création KSP, jamais une condition de lecture du format : chaque slot sérialise toujours ses paramètres KDF complets. Un ancien wallet reste donc lisible après un futur durcissement des defaults.
Dépendances introduites
Nouvelles dépendances communes sous [workspace.dependencies] :
ed25519-dalek = { version = "^2.2", default-features = false }
solana-keypair = { version = "^3.1", default-features = false }
Le membre Wallet consomme :
ed25519-dalek = { workspace = true, features = ["signature", "zeroize"] }
solana-keypair.workspace = true
tokio = { workspace = true, features = ["rt"] }
tokio était déjà une dépendance commune du workspace ; seule la feature locale nécessaire à spawn_blocking est activée.
Le choix direct ed25519-dalek ^2.2 est volontaire : solana-keypair 3.1.2 dépend lui-même de ed25519-dalek ^2.1.1. Rester dans la génération 2.x doit permettre à Cargo d'unifier Dalek au lieu d'ajouter une branche 3.x concurrente. La feature zeroize est activée explicitement.
Aucune dépendance directe solana-pubkey, solana-signer ou solana-signature n'est ajoutée. La Pubkey du domaine Wallet reste exclusivement ksp_core_lib::Pubkey.
Un cargo tree opérateur est obligatoire pour confirmer la résolution réelle après application.
Content keys et autorité OWNER
La création génère indépendamment :
K_owner_root 32 octets
K_metadata 32 octets
K_secret 32 octets
admin seed 32 octets Ed25519
Solana secret 32 octets Ed25519 distincts
La clé d'administration du format reste distincte de la keypair Solana.
Le slot OWNER wrappe uniquement K_owner_root.
Le slot VIEW optionnel wrappe uniquement K_metadata.
OWNER ne dépend donc jamais de VIEW et VIEW ne reçoit jamais K_owner_root ni K_secret.
Payload owner_control
Le plaintext V1 est fixé à exactement 96 octets :
offset 0..32 admin Ed25519 secret seed
offset 32..64 K_metadata
offset 64..96 K_secret
Il est chiffré sous K_owner_root avec l'AAD compartment figé en pre.003.
À l'ouverture OWNER, la seed admin redérive obligatoirement la owner_auth_public_key de l'enveloppe. Un mismatch est rejeté.
Payload metadata
Le plaintext metadata est un JSON UTF-8 strict et protégé :
{
"pubkey": "<canonical Solana Pubkey Base58>",
"alias": null,
"notes": [
{ "id": "<16 bytes Base64url-no-pad>", "text": "..." }
]
}
Invariants :
Pubkey type ksp_core_lib::Pubkey après parsing
alias optionnel, <= 256 octets UTF-8
notes maximum 64
note.id exactement 16 octets, unique dans le payload
note.text <= 8192 octets UTF-8
payload metadata <= 65 536 octets
Les IDs de note sont générés au CSPRNG. Une collision pendant la création est traitée comme une défaillance de génération plutôt que de produire des IDs ambigus. Un payload décodé avec IDs dupliqués est rejeté.
Payload secret Solana
Le plaintext secret est exactement le keypair Ed25519 Solana 64 octets :
offset 0..32 secret Ed25519
offset 32..64 public Ed25519
À l'ouverture OWNER :
1. le payload doit faire exactement 64 octets ;
2. solana-keypair valide la cohérence secret/public ;
3. les 32 octets publics construisent un ksp_core_lib::Pubkey ;
4. cette Pubkey doit correspondre exactement à la Pubkey metadata.
Un mismatch n'est jamais accepté comme changement d'identité implicite.
state_signature OWNER
La signature d'état V1 est désormais effective :
algorithm Ed25519
public key owner_auth_public_key dans l'enveloppe
message state_transcript TLV figé en pre.003
signature 64 octets
La vérification stricte de cette signature intervient avant toute dérivation Argon2 lors de inspect, open VIEW et open OWNER.
Le transcript continue d'exclure le matériau self-service rotatable du slot VIEW (KDF/salt/wrap) et d'authentifier son descripteur stable OWNER-controlled. Cette frontière préserve la future rotation du propre password VIEW sans donner à VIEW la capacité de modifier metadata/secret/OWNER-state.
API in-memory
La crate expose désormais :
create_wallet_v1(owner_password, optional_view_password, metadata).await
open_wallet_view_v1(source, view_password).await
open_wallet_owner_v1(source, owner_password).await
inspect_locked_wallet_v1(source)
create_wallet_v1 crée une nouvelle identité Solana et une autorité admin séparée, mais n'effectue aucun I/O filesystem.
WalletOwner::to_json_bytes() et WalletView::to_json_bytes() permettent seulement de projeter l'enveloppe verrouillée complète en mémoire en attendant pre.006.
VIEW
L'ouverture VIEW :
parse strict
verify state_signature OWNER
Argon2 du seul slot VIEW via spawn_blocking
unwrap K_metadata
decrypt metadata
retour WalletView
Elle ne déchiffre jamais owner_control ou secret, ne construit jamais la keypair Solana et ne matérialise jamais K_secret.
OWNER
L'ouverture OWNER :
parse strict
verify state_signature OWNER
Argon2 du slot OWNER via spawn_blocking
unwrap K_owner_root
decrypt owner_control
valider admin secret -> owner_auth_public_key
récupérer K_metadata / K_secret
decrypt metadata
decrypt et valider keypair Solana 64 octets
vérifier Pubkey keypair == Pubkey metadata
retour WalletOwner
OWNER reste fonctionnel même si VIEW est absent, inconnu ou inutilisable.
Secret memory
Les invariants précédents sont conservés :
password wrappers non-Clone et zeroized au Drop
content keys non-Copy/non-Clone et zeroized au Drop
buffers plaintext owner-control/metadata/secret zeroized après consommation raisonnable
admin SigningKey avec feature zeroize
solana-keypair conservé uniquement dans l'état OWNER opaque
aucun Debug/Display public de secret
aucun secret/password/alias/note dans les logs
Les limites habituelles de zeroization Rust restent documentées : aucune garantie absolue n'est revendiquée contre toutes les copies temporaires possibles du compilateur/runtime.
Logging
Les nouveaux flux utilisent exclusivement ksp-logging-lib avec :
target = crate::TRACING_TARGET = "ksp-wallet-lib"
Les événements ne contiennent que l'opération/capability/version non secrète et l'issue catégorisée. Aucun path, password, secret, metadata ou payload arbitraire n'est journalisé.
Vecteur complet d'interopérabilité
Nouveaux fixtures publics TEST ONLY :
crates/ksp-wallet-lib/tests/fixtures/kspwallet_v1_full_vector.json
crates/ksp-wallet-lib/tests/fixtures/kspwallet_v1_full_vector_meta.json
Ils fixent de manière auto-contenue :
password OWNER connu
password VIEW connu
salts / slot IDs / nonces
content keys test-only
admin key test-only
keypair Solana test-only
Pubkey / alias / notes attendus
ciphertexts owner-control / metadata / secret
wrapped keys OWNER / VIEW
state transcript
state signature Ed25519
Le vecteur utilise volontairement un KDF faible/test-fast (32 KiB / 2 / 1) et ne constitue jamais un wallet ni un profil de production recommandé.
Une implémentation indépendante du code Rust a revérifié :
state transcript exact : 1182 octets
signature Ed25519 valide
dérivations OWNER/VIEW exactes
unwrap K_owner_root / K_metadata exact
déchiffrement owner_control exact
déchiffrement metadata exact
déchiffrement secret exact : 64 octets
admin seed -> owner_auth_public_key exacte
secret Solana -> public half cohérente
Tests
Les tests Wallet ajoutent notamment :
vecteur complet ouvre VIEW et OWNER indépendamment
password VIEW ne déverrouille pas OWNER
password OWNER ne déverrouille pas VIEW
mutation OWNER-authentifiée metadata rejetée
mutation du seul wrapping VIEW ne casse pas OWNER mais invalide VIEW
fichier verrouillé ne contient pas Pubkey/alias/notes en clair
salts / slot IDs / nonces OWNER/VIEW indépendants
create utilise le profil KSP benchmarké
projection locked reste privée
duplicate note IDs rejetés
Les tests d'architecture continuent d'imposer :
Wallet -X-> Config
Wallet -X-> Transport
Wallet -X-> ExecutionPolicy
Wallet -X-> Store
Wallet -X-> Tauri
Wallet -X-> tracing direct
Wallet -X-> solana-pubkey direct
Wallet -X-> accès environnement direct
Documentation
Mises à jour :
ROADMAP.md
docs/000-README.md
docs/formats/000-README.md
docs/formats/KSPWALLET_V1.md
docs/plans/000-README.md
docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md
docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md
KSPWALLET_V1.md devient suffisamment précis pour les payloads et procédures pre.005; la persistence et les opérations d'administration restent explicitement futures.
CHANGELOG.md reste inchangé : la policy KSP réserve les entrées de release stable à la clôture/publication, les prereleases étant décrites par plans/deltas/roadmap.
Fichiers ajoutés
crates/ksp-wallet-lib/src/payload.rs
crates/ksp-wallet-lib/src/wallet.rs
crates/ksp-wallet-lib/unit_tests/payload.rs
crates/ksp-wallet-lib/unit_tests/wallet.rs
crates/ksp-wallet-lib/tests/fixtures/kspwallet_v1_full_vector.json
crates/ksp-wallet-lib/tests/fixtures/kspwallet_v1_full_vector_meta.json
deltas/0.2.5/pre.005.md
Fichiers modifiés
Cargo.toml
ROADMAP.md
crates/ksp-wallet-lib/Cargo.toml
crates/ksp-wallet-lib/src/constants.rs
crates/ksp-wallet-lib/src/crypto.rs
crates/ksp-wallet-lib/src/error.rs
crates/ksp-wallet-lib/src/lib.rs
crates/ksp-wallet-lib/src/metadata.rs
crates/ksp-wallet-lib/src/owner.rs
crates/ksp-wallet-lib/src/password.rs
crates/ksp-wallet-lib/src/view.rs
crates/ksp-wallet-lib/src/wire.rs
crates/ksp-wallet-lib/tests/dependency_boundary.rs
crates/ksp-wallet-lib/tests/public_api.rs
docs/000-README.md
docs/formats/000-README.md
docs/formats/KSPWALLET_V1.md
docs/plans/000-README.md
docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md
docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md
Aucun fichier hors de cette différence avec pre.004-fix.002 ne doit être embarqué.
Fichiers supprimés
Aucun.
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 obligatoire :
cargo tree -p ksp-wallet-lib
cargo tree -p ksp-wallet-lib -d
cargo tree -e features -p ksp-wallet-lib
cargo tree -i ed25519-dalek@2.2.0
cargo tree -i solana-keypair@3.1.2
cargo tree -i solana-address@2.7.0
Points à confirmer :
une seule génération ed25519-dalek dans le sous-arbre Wallet
pas de duplication crypto injustifiée introduite par Wallet
solana-address résolu sans second type Pubkey KSP
aucune dépendance Config/Transport/Tauri depuis Wallet
zeroize effectivement activé sur ed25519-dalek
Décisions
- retenir
65 536 KiB / 3 / 1comme profil initial de création KSP V1 à partir du benchmark opérateur ; - conserver les paramètres KDF sérialisés comme autorité de lecture du wallet ;
- utiliser une autorité Ed25519 de format distincte de la keypair Solana ;
- vérifier la state signature avant tout KDF ;
- garder
ed25519-daleken génération2.xpour s'aligner sursolana-keypair 3.1.2; - ne jamais dépendre directement de
solana-pubkeydans Wallet ; - ouvrir VIEW sans matérialiser le secret Solana ;
- garder OWNER indépendant de VIEW ;
- ne pas introduire persistence/signature/export/admin avant leurs tranches dédiées.
Questions ouvertes
Aucune question de format V1 nouvelle n'est introduite par cette tranche.
pre.006 doit choisir et prouver la stratégie filesystem portable pour :
create destination nouvelle/no-clobber
atomic replace des futures mutations OWNER/VIEW
fsync/durability raisonnable selon plateforme
fault injection
concurrence
récupération après interruption
Commit attendu
v0.2.5-pre.005