440 lines
11 KiB
Markdown
440 lines
11 KiB
Markdown
<!-- file: deltas/0.2.5/pre.003.md -->
|
|
<!-- version: 1 -->
|
|
|
|
# Delta `0.2.5-pre.003` — wire `.kspwallet` V1 strict + transcript/AAD + spécification interop
|
|
|
|
## Base requise
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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` :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```toml
|
|
base64 = { version = "^0.23" }
|
|
```
|
|
|
|
Le manifest Wallet consomme :
|
|
|
|
```toml
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
1 OWNER
|
|
0 ou 1 VIEW
|
|
maximum 2 slots
|
|
slot_id uniques
|
|
```
|
|
|
|
`view_descriptor` est figé comme :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
control_version = 1
|
|
metadata_version = 1
|
|
secret_version = 1
|
|
```
|
|
|
|
Tous utilisent le wire `xchacha20-poly1305` avec nonce 24 octets. Limites ciphertext :
|
|
|
|
```text
|
|
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é :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
KSPWALLET-V1-STATE
|
|
```
|
|
|
|
Le transcript inclut :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
crates/ksp-wallet-lib/tests/fixtures/kspwallet_v1_wire_only.json
|
|
```
|
|
|
|
Cette fixture :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
KspWalletEnvelopeV1
|
|
WalletKeySlotV1
|
|
WalletKeySlotRoleV1
|
|
WalletKdfParametersV1
|
|
WalletKeyWrapV1
|
|
WalletViewDescriptorV1
|
|
WalletEncryptedCompartmentV1
|
|
WalletCompartmentKindV1
|
|
WalletStateSignatureV1
|
|
identifiants d'algorithmes V1
|
|
constantes de limites/domain separation
|
|
```
|
|
|
|
`KspWalletEnvelopeV1` fournit :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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
|
|
|
|
```text
|
|
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
|
|
|
|
```text
|
|
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
|
|
|
|
```text
|
|
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
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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_slots` JSON 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 dans `FILE_CONTRACTS.md` dans la même tranche.
|
|
|
|
## Questions ouvertes reportées à `pre.004`
|
|
|
|
```text
|
|
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
|
|
|
|
```text
|
|
v0.2.5-pre.003
|
|
```
|