v0.2.5-pre.007
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/formats/KSPWALLET_V1.md -->
|
||||
<!-- version: 4 -->
|
||||
<!-- version: 5 -->
|
||||
|
||||
# `.kspwallet` V1 — spécification du format natif Wallet KSP
|
||||
|
||||
@@ -20,7 +20,7 @@ AAD des compartiments owner-control / metadata / secret
|
||||
règles unknown-field / unknown-version
|
||||
```
|
||||
|
||||
`0.2.5-pre.004` ajoute les primitives KDF/AEAD normatives et un premier vecteur cryptographique public. `0.2.5-pre.005` fixe maintenant les payloads plaintext V1, l'autorité Ed25519 OWNER, les procédures de création et d'ouverture VIEW/OWNER, le profil de création KSP issu du benchmark opérateur et un vecteur `.kspwallet` complet généré indépendamment du code Rust. La persistence filesystem, les mutations administratives, la signature Solana publique et les adapters import/export restent dans les tranches suivantes. Toute évolution qui modifie un élément déjà déclaré **figé** par cette spécification exige une évolution explicitement tracée avant la release stable ; après publication de V1, une incompatibilité de wire exige un nouveau `format_version`.
|
||||
`0.2.5-pre.004` ajoute les primitives KDF/AEAD normatives et un premier vecteur cryptographique public. `0.2.5-pre.005` fixe les payloads plaintext V1, l'autorité Ed25519 OWNER, les procédures de création et d'ouverture VIEW/OWNER, le profil de création KSP issu du benchmark opérateur et un vecteur `.kspwallet` complet généré indépendamment du code Rust. `0.2.5-pre.006` matérialise la persistence filesystem bornée et la création no-clobber. `0.2.5-pre.007` matérialise la signature Solana OWNER, l'administration des metadata, les rotations OWNER/VIEW, la révocation forte VIEW et leur remplacement filesystem capability-bound. Les adapters import/export restent dans les tranches suivantes. Toute évolution qui modifie un élément déjà déclaré **figé** par cette spécification exige une évolution explicitement tracée avant la release stable ; après publication de V1, une incompatibilité de wire exige un nouveau `format_version`.
|
||||
|
||||
Le but final est qu'une implémentation indépendante en Rust, Python, Go, C/C++, Java ou autre puisse créer, parser, vérifier et ouvrir un `.kspwallet` sans lire le code source de `ksp-wallet-lib`.
|
||||
|
||||
@@ -798,6 +798,87 @@ Une ouverture VIEW ne déchiffre **jamais** `owner_control` ni `secret` et ne ma
|
||||
|
||||
Aucun getter public de secret n'est introduit par cette procédure.
|
||||
|
||||
### 21.4 Signature Solana OWNER
|
||||
|
||||
La signature de message est une capacité OWNER. L'implémentation reconstruit/utilise la keypair Solana déjà authentifiée lors de l'ouverture OWNER et produit une signature Ed25519 de **64 octets** sur les octets exacts du message fourni :
|
||||
|
||||
```text
|
||||
OWNER + message bytes -> Ed25519(Solana keypair) -> signature[64]
|
||||
```
|
||||
|
||||
VIEW ne possède aucune primitive de signature. La surface publique n'expose ni getter de seed, ni getter de keypair brute, ni type Solana secret supplémentaire. Une rotation de password ou une mutation metadata ne change pas la keypair Solana ; signer le même message avec le même wallet produit donc la même signature Ed25519 déterministe.
|
||||
|
||||
### 21.5 Administration OWNER des metadata
|
||||
|
||||
OWNER peut modifier l'alias et les notes protégées. La Pubkey metadata reste **immutable** en V1. Une mutation metadata suit conceptuellement :
|
||||
|
||||
```text
|
||||
metadata authentifiées courantes
|
||||
-> mutation alias/note validée
|
||||
-> nouveau nonce metadata
|
||||
-> rechiffrement avec le même K_metadata
|
||||
-> reconstruction du transcript OWNER
|
||||
-> nouvelle state_signature OWNER
|
||||
```
|
||||
|
||||
Les notes conservent des identifiants 16 octets uniques. Une mise à jour ou suppression visant un identifiant absent retourne `wallet.note_not_found`. VIEW peut lire le nouvel état après réouverture mais ne dispose d'aucune API pour produire cette mutation.
|
||||
|
||||
### 21.6 Rotation simple du password VIEW
|
||||
|
||||
VIEW peut changer **uniquement son propre password VIEW**. OWNER peut effectuer la même rotation sans connaître l'ancien password VIEW. La rotation simple conserve :
|
||||
|
||||
```text
|
||||
slot_id VIEW
|
||||
K_metadata
|
||||
metadata ciphertext
|
||||
owner_control
|
||||
secret
|
||||
keypair Solana
|
||||
state_signature OWNER
|
||||
```
|
||||
|
||||
Elle génère de nouveaux paramètres de credential autorisés pour le slot VIEW : au minimum salt, KDF de création courant, wrap nonce et wrapped key. Le wrapping réencapsule le **même `K_metadata`** sous le nouveau password. La `state_signature` reste inchangée parce que les champs self-service rotatables du slot VIEW sont explicitement exclus du transcript OWNER V1, alors que son descripteur stable reste OWNER-signed.
|
||||
|
||||
Après publication réussie, l'ancien password n'ouvre plus le slot VIEW **courant**. Cette opération est une rotation de credential et non une révocation forte : un détenteur ayant conservé une ancienne copie du fichier et/ou `K_metadata` peut toujours lire l'état correspondant à cette ancienne copie.
|
||||
|
||||
### 21.7 Rotation du password OWNER
|
||||
|
||||
OWNER peut changer son password sans changer l'identité Solana. La rotation conserve `slot_id OWNER` et `K_owner_root`, génère de nouveaux KDF/salt/wrap nonce, rewrappe `K_owner_root`, reconstruit le transcript et produit une nouvelle `state_signature` avec la même autorité administrative.
|
||||
|
||||
Elle ne modifie ni `K_metadata`, ni `K_secret`, ni la keypair Solana, ni la Pubkey. Après publication réussie, l'ancien password OWNER n'ouvre plus le slot OWNER courant.
|
||||
|
||||
### 21.8 Disable/recreate VIEW et révocation forte
|
||||
|
||||
`disable_view` et `recreate_view` sont OWNER-only et constituent la primitive de révocation forte V1 pour les **futures metadata sous l'état courant**. OWNER génère un nouveau `K_metadata`, rechiffre les metadata, met à jour `owner_control`, puis :
|
||||
|
||||
```text
|
||||
disable_view -> aucun slot VIEW + view_descriptor disabled
|
||||
recreate_view -> nouveau slot_id VIEW + nouveau password VIEW + view_descriptor enabled
|
||||
```
|
||||
|
||||
Le nouvel état est resigné par OWNER. L'ancien password VIEW ne peut pas ouvrir le nouvel état, même s'il connaissait le wrapping précédent. Cette garantie n'efface pas les anciennes copies déjà possédées : V1 ne fournit ni anti-rollback externe ni ancre de confiance externe.
|
||||
|
||||
### 21.9 Remplacement persistant capability-bound
|
||||
|
||||
Les mutations `pre.007` ne publient aucun `replace(path, bytes)` générique. Elles partent d'un handle VIEW/OWNER déjà authentifié, construisent le nouvel état en mémoire, puis demandent à la persistence de remplacer **le chemin explicitement fourni par le caller**.
|
||||
|
||||
Avant publication, la persistence relit le fichier destination, le parse strictement, vérifie sa `state_signature` OWNER et compare son enveloppe sémantique à l'état authentifié attendu par le handle. Un mauvais chemin ou un handle devenu stale retourne `wallet.state_conflict` au lieu d'écraser silencieusement un autre état. Le contrôle est répété juste avant la publication du temp file.
|
||||
|
||||
```text
|
||||
expected authenticated envelope
|
||||
-> bounded read current destination
|
||||
-> strict parse + OWNER state signature verify
|
||||
-> semantic equality check
|
||||
-> temp same-directory + write + sync_all
|
||||
-> second expected-state check
|
||||
-> atomic/best-effort platform replace
|
||||
-> sync published file
|
||||
-> parent sync best-effort on Unix
|
||||
-> commit du nouvel état dans le handle seulement après succès
|
||||
```
|
||||
|
||||
Cette vérification protège les erreurs de cible et les mises à jour stale dans l'API KSP, mais **ne constitue pas un compare-and-swap filesystem portable et linéarisable** : il subsiste une fenêtre TOCTOU entre la dernière vérification et le remplacement sur les plateformes/filesystems sans primitive transactionnelle correspondante. Les ACL, verrous interprocess externes, anti-rollback et remplacement total hostile du fichier restent hors du contrat V1.
|
||||
|
||||
## 22. Vecteur complet interopérable `pre.005`
|
||||
|
||||
Le dépôt publie :
|
||||
@@ -846,6 +927,6 @@ caller path explicite
|
||||
|
||||
`create_wallet_file_v1` n'écrase jamais une destination existante. Une collision ou une course concurrente retourne `wallet.destination_exists`; il n'existe aucun mode overwrite pour create/import. Les ouvertures fichier sont bornées à `KSPWALLET_MAX_FILE_BYTES` avant parser/KDF et délèguent ensuite exactement aux procédures VIEW/OWNER des sections 21.2/21.3.
|
||||
|
||||
La couche filesystem est async-first, mais les appels OS bloquants sont regroupés derrière `tokio::task::spawn_blocking`. KSP ne lit ni Config ni environnement pour choisir le chemin et ne journalise pas les chemins par défaut.
|
||||
La couche filesystem est async-first, mais les appels OS bloquants sont regroupés derrière `tokio::task::spawn_blocking`. KSP ne lit ni Config ni environnement pour choisir le chemin et ne journalise pas les chemins par défaut. Les mutations `pre.007` réutilisent la même stratégie temp/sync pour un remplacement **capability-bound** avec contrôle d'état attendu décrit en section 21.9 ; ce remplacement n'est jamais exposé comme primitive publique générique et ne modifie pas la règle create/import = no-clobber.
|
||||
|
||||
La durabilité est décrite sans surpromesse : `persist_noclobber` ne garantit pas une atomicité universelle sur tous les filesystems et un crash brutal peut laisser un artefact ou hard-link temporaire complet. KSP garantit le no-clobber et l'absence de publication partielle dans le flux normal et dans les fault tests avant publication ; il ne garantit pas la suppression des temporaires après kill/power-loss ni une durabilité identique de l'entrée de répertoire sur tous les OS. Ces limites concernent la persistence et ne changent pas les garanties cryptographiques du format.
|
||||
|
||||
Reference in New Issue
Block a user