11 KiB
Delta 0.2.5-pre.003 — wire .kspwallet V1 strict + transcript/AAD + spécification interop
Base requise
livraison : 0.2.5-pre.002-fix.002
workspace.package.version = "0.2.5-pre.2.fix.2"
La base effective est l'archive Gitea opérateur 0.2.5-pre.002, complétée successivement par pre.002-fix.001 et pre.002-fix.002. Les validations opérateur de cette base sont toutes passées : cargo fmt --all, cargo check --workspace, cargo clippy --workspace --all-targets et cargo test -p ksp-wallet-lib.
Objectif
Figer la grammaire externe et les octets d'authentification contextuelle V1 avant toute implémentation KDF/AEAD réelle.
Cette tranche introduit :
KspWalletEnvelopeV1
JSON UTF-8 strict
Base64url canonique sans padding
limite fichier 1 MiB
key slots OWNER + VIEW optionnel
slot_id 16 octets
view_descriptor signé conceptuellement par OWNER
paramètres Argon2id structuraux sérialisés
compartiments owner_control / metadata / secret versionnés
state_signature Ed25519 wire
transcript OWNER déterministe TLV
AAD OWNER slot / VIEW slot
AAD owner-control / metadata / secret
fixture wire-only déterministe
spécification docs/formats/KSPWALLET_V1.md
Aucune dérivation de clé, aucun chiffrement/déchiffrement, aucune vérification Ed25519, aucune keypair Solana et aucune persistence ne sont exécutés dans pre.003.
Version Cargo
Conformément à VER-ID-009 :
0.2.5-pre.2.fix.2 -> 0.2.5-pre.3
Dépendances
serde et serde_json étaient déjà centralisés au workspace et sont maintenant consommés par Wallet pour le codec strict. La feature derive appartient au manifest membre, conformément à DEP-CARGO-003.
Nouvelle dépendance commune :
base64 = { version = "^0.23" }
Le manifest Wallet consomme :
base64.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
La génération actuelle réauditée lors de la tranche est base64 0.23.1. Le moteur URL_SAFE_NO_PAD impose l'alphabet URL-safe sans padding et rejette les trailing bits non canoniques ; Wallet effectue en plus decode -> re-encode == input.
Aucune dépendance Argon2, AEAD, CSPRNG, Ed25519 ou Solana supplémentaire n'est encore ajoutée.
Wire V1 figé
Enveloppe
Champs top-level exacts :
magic
format_version
owner_auth_public_key
view_descriptor
key_slots
owner_control
metadata
secret
state_signature
Tous les DTO serde V1 utilisent deny_unknown_fields.
Le parser effectue d'abord un probe magic + format_version afin qu'une version future soit rejetée explicitement comme format_version_unsupported avant d'essayer d'imposer la forme V1.
Binary encoding
Tous les champs binaires V1 sont Base64url sans padding. Longueurs fixes principales :
owner_auth_public_key 32 octets
slot_id 16 octets
XChaCha nonce 24 octets
Ed25519 state signature 64 octets
KDF salt 16..64 octets
Key slots
V1 accepte exactement :
1 OWNER
0 ou 1 VIEW
maximum 2 slots
slot_id uniques
view_descriptor est figé comme :
enabled=true + slot_id 16 octets correspondant au slot VIEW
enabled=false + slot_id=null + aucun slot VIEW
L'ordre JSON du tableau key_slots n'est pas sémantique. Le serializer KSP émet OWNER puis VIEW.
KDF structural bounds
Sans exécuter Argon2, le parser rejette avant crypto coûteuse :
algorithm = argon2id
version = 19
memory_kib = 1..1_048_576
iterations = 1..64
parallelism = 1..64
salt = 16..64 octets
Ces plafonds sont des limites de format/rejet hostile. Ils ne fixent pas les defaults de création, benchmarkés en pre.004.
Compartiments
Versions indépendantes initiales :
control_version = 1
metadata_version = 1
secret_version = 1
Tous utilisent le wire xchacha20-poly1305 avec nonce 24 octets. Limites ciphertext :
owner_control <= 4096 octets
metadata <= 65552 octets
secret <= 4096 octets
Transcript OWNER
La signature ne porte jamais sur les octets JSON bruts.
Codec binaire figé :
ASCII(domain) || 0x00
puis pour chaque champ :
tag u16 big-endian
length u64 big-endian
value length octets
u32 => 4 octets big-endian dans value
bool => 00 ou 01
Domain :
KSPWALLET-V1-STATE
Le transcript inclut :
magic/version
autorité publique OWNER
descripteur VIEW stable
slot OWNER complet
owner_control complet
metadata complète
secret complet
algorithme state signature
Il exclut volontairement :
KDF/salt VIEW
wrap nonce/ciphertext VIEW
state_signature.signature
Cela matérialise le contrat décidé : VIEW peut rewrapper son accès metadata pour changer son propre password sans posséder l'autorité OWNER, mais ne peut pas modifier l'état OWNER-controlled.
AAD
Domains distincts :
KSPWALLET-V1-AAD-OWNER-SLOT
KSPWALLET-V1-AAD-VIEW-SLOT
KSPWALLET-V1-AAD-OWNER-CONTROL
KSPWALLET-V1-AAD-METADATA
KSPWALLET-V1-AAD-SECRET
Les AAD utilisent le même TLV déterministe et lient au minimum magic/version, owner_auth_public_key, rôle/kind et paramètres publics pertinents.
Le nonce est fourni séparément à l'AEAD et n'est pas dupliqué dans l'AAD ; le ciphertext/tag est le résultat de l'opération et n'appartient pas à son propre AAD.
Fixture structurelle
Ajout :
crates/ksp-wallet-lib/tests/fixtures/kspwallet_v1_wire_only.json
Cette fixture :
est strictement test-only
n'utilise aucune clé réelle
contient des ciphertexts artificiels
contient une signature artificielle
n'est pas un wallet cryptographiquement valide
ne fixe pas les defaults Argon2 de production
Elle fixe en revanche les octets attendus du transcript et des cinq AAD.
API publique
La façade réexporte les contrats V1 sans exposer les modules internes :
KspWalletEnvelopeV1
WalletKeySlotV1
WalletKeySlotRoleV1
WalletKdfParametersV1
WalletKeyWrapV1
WalletViewDescriptorV1
WalletEncryptedCompartmentV1
WalletCompartmentKindV1
WalletStateSignatureV1
identifiants d'algorithmes V1
constantes de limites/domain separation
KspWalletEnvelopeV1 fournit :
parse_json(...)
to_json_bytes()
getters wire read-only
state_transcript()
owner_slot_aad()
view_slot_aad()
compartment_aad(...)
Aucun constructeur public ne permet de fabriquer arbitrairement un envelope non validé.
Les Debug des enveloppes/slots/compartiments n'affichent pas les ciphertexts, salts ou nonces complets ; ils n'exposent que les algorithmes, versions et tailles nécessaires au diagnostic.
Documentation de format
Nouveau contrat durable :
docs/formats/
docs/rules/FILE_CONTRACTS.md est mis à jour dans la même tranche, avant que cette famille devienne une convention répétée.
Ajouts :
docs/formats/000-README.md
docs/formats/KSPWALLET_V1.md
KSPWALLET_V1.md est indépendant du code Rust et décrit déjà de manière normative : grammaire, encodages, limites, strictness, key slots, tags TLV, ordre transcript et AAD. Les opérations KDF/AEAD/payloads et vecteurs crypto complets seront ajoutés dans les tranches qui les implémentent.
Frontières confirmées
Toujours interdit :
Wallet -> Config
Wallet -> Transport
Wallet -> ExecutionPolicy
Wallet -> Store
Wallet -> Tauri
Wallet -> tracing direct
Wallet -> solana-pubkey direct
Wallet -> environnement processus
Pubkey reste possédée/réexportée par ksp-core-lib; pre.003 n'introduit aucun type Pubkey Solana direct.
Le target comportemental reste TRACING_TARGET = "ksp-wallet-lib" dans src/constants.rs et le parsing wire émet uniquement un trace non secret via ksp-logging-lib après validation réussie.
Tests ajoutés/étendus
Unitaires wire
fixture V1 parse + semantic round-trip
unknown format version
unknown top-level field
Base64url paddé/non canonique
mismatch view_descriptor / VIEW slot
KDF zéro/pathologique
fichier > 1 MiB
Debug sans ciphertext complet
Unitaires transcript/AAD
state transcript exact
OWNER slot AAD exact
VIEW slot AAD exact
owner-control AAD exact
metadata AAD exact
secret AAD exact
domain separation
Intégration
Le canari public API vérifie que l'enveloppe, les rôles et codecs transcript/AAD sont réellement consommables depuis le crate-root.
Le canari de dépendances est étendu à base64, serde et serde_json tout en conservant le firewall existant.
Fichiers ajoutés
crates/ksp-wallet-lib/src/transcript.rs
crates/ksp-wallet-lib/src/wire.rs
crates/ksp-wallet-lib/tests/fixtures/kspwallet_v1_wire_only.json
crates/ksp-wallet-lib/unit_tests/transcript.rs
crates/ksp-wallet-lib/unit_tests/wire.rs
docs/formats/000-README.md
docs/formats/KSPWALLET_V1.md
deltas/0.2.5/pre.003.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/lib.rs
crates/ksp-wallet-lib/tests/dependency_boundary.rs
crates/ksp-wallet-lib/tests/public_api.rs
docs/000-README.md
docs/plans/000-README.md
docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md
docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md
docs/rules/FILE_CONTRACTS.md
Fichiers supprimés
Aucun.
Validations exécutées dans l'environnement de préparation
Cargo/Rust n'est pas installé dans l'environnement de préparation. Les contrôles statiques suivants ont été exécutés :
diff exact contre la base pre.002 + fix.001 + fix.002
scan production : aucun unwrap/expect/panic/?
scan boundaries : aucun solana_pubkey:: / tracing:: / std::env:: / Config / Transport / Tauri
scan RUST-API-004 : helpers pub(crate) transcript réexportés au crate-root
scan manifests : dépendances centralisées workspace
contrôle manuel des headers/version de fichiers
contrôle des longueurs/fixture et vecteurs transcript/AAD via implémentation indépendante Python du TLV
Validations opérateur requises
cargo fmt --all
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-wallet-lib
Si cette tranche constitue le checkpoint Rust de clôture intermédiaire retenu, exécuter également :
cargo test --workspace
Décisions
- geler le wire JSON V1 maintenant, avant la crypto réelle ;
- ne pas signer/canonicaliser les octets JSON ;
- utiliser un transcript sémantique TLV déterministe ;
- rendre l'ordre
key_slotsJSON non sémantique ; - fixer
slot_idà 16 octets ; - permettre VIEW self-service uniquement via exclusion contrôlée de son wrapping du transcript OWNER ;
- sérialiser les versions de payload indépendamment ;
- rejeter les paramètres KDF structurellement pathologiques avant toute KDF ;
- ne pas ajouter Argon2/AEAD/Ed25519 avant consommation effective ;
- créer
docs/formats/avec son contrat dansFILE_CONTRACTS.mddans la même tranche.
Questions ouvertes reportées à pre.004
defaults Argon2id mesurés sur machines cibles
API exacte de spawn_blocking pour KDF
crate CSPRNG exacte lors de l'implémentation
crate XChaCha20-Poly1305 exacte/feature set final
layout plaintext exact des wrapped capabilities
premiers vecteurs crypto publics valides
Ces questions ne doivent pas modifier silencieusement le wire/transcript/AAD figés par pre.003.
Commit attendu
v0.2.5-pre.003