488 lines
14 KiB
Markdown
488 lines
14 KiB
Markdown
<!-- file: deltas/0.2.5/pre.005.md -->
|
|
<!-- version: 1 -->
|
|
|
|
# Delta `0.2.5-pre.005` — payloads V1 + create/open VIEW/OWNER + autorité OWNER Ed25519
|
|
|
|
## Base requise
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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]` :
|
|
|
|
```toml
|
|
ed25519-dalek = { version = "^2.2", default-features = false }
|
|
solana-keypair = { version = "^3.1", default-features = false }
|
|
```
|
|
|
|
Le membre Wallet consomme :
|
|
|
|
```toml
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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é :
|
|
|
|
```json
|
|
{
|
|
"pubkey": "<canonical Solana Pubkey Base58>",
|
|
"alias": null,
|
|
"notes": [
|
|
{ "id": "<16 bytes Base64url-no-pad>", "text": "..." }
|
|
]
|
|
}
|
|
```
|
|
|
|
Invariants :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
offset 0..32 secret Ed25519
|
|
offset 32..64 public Ed25519
|
|
```
|
|
|
|
À l'ouverture OWNER :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```rust
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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é :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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
|
|
|
|
```text
|
|
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
|
|
|
|
```text
|
|
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
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```text
|
|
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 / 1` comme 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-dalek` en génération `2.x` pour s'aligner sur `solana-keypair 3.1.2` ;
|
|
- ne jamais dépendre directement de `solana-pubkey` dans 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 :
|
|
|
|
```text
|
|
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
|
|
|
|
```text
|
|
v0.2.5-pre.005
|
|
```
|