v0.2.5-pre.001-fix.001

This commit is contained in:
2026-08-19 09:37:07 +02:00
parent 58134c81d1
commit c2ccef3849
4 changed files with 349 additions and 77 deletions

View File

@@ -1,5 +1,5 @@
<!-- file: ROADMAP.md -->
<!-- version: 45 -->
<!-- version: 46 -->
# Roadmap KSP
@@ -49,7 +49,7 @@ Le roadmap décrit les objectifs à atteindre et les grandes étapes prévues. U
- [X] `0.2.2` — HTTP Accounts + Tokens + Cluster : 22 wrappers typés (5 Accounts + 5 Tokens + 12 Cluster), canaries de complétude 52+14, smoke Devnet Transport pur et smoke historique Config -> Transport validés, documentation durable et prompt `0.2.3` publiés stables.
- [X] `0.2.3` — HTTP Transactions stable : 11/11 wrappers typés publiés, classification `8 Read / 2 WriteSubmission / 1 Simulation`, no-resend ambigu prouvé pour les write submissions, `KSP-TRANSPORT-007` réaudité conforme sur les 37 wrappers HTTP courants, graphes Cargo et deux smokes Devnet validés ; `0.2.4` reprend les 15 Blocks/Economics restants.
- [X] `0.2.4` — HTTP Blocks + Economics stable : 15/15 wrappers `V0_2_4` publiés, surface typed complète à 52/52 méthodes courantes, 14/14 historiques conservées, réaudit SIMD/inventaire final et `KSP-TRANSPORT-007` global validés ; deux smokes Devnet passés avant publication.
- [/] `0.2.5` — Wallet foundation ouverte par `pre.001` : threat model offline et héritage bot2/bot3 réaudités, `.kspwallet` V1 entièrement autonome sans facteur externe, design content-keys/key-slots VIEW/OWNER indépendant, read-only VIEW niveau B via autorité Ed25519 de format distincte couvrant tout état mutable, keypair V1 immuable, spec multi-langages séparée + vecteurs publics exigés, paramètres KDF à benchmarker avant freeze, persistence/signature/rotations/import-export répartis jusquà `pre.010` ; `WalletPolicy` reste exclu.
- [/] `0.2.5` — Wallet foundation ouverte par `pre.001` : threat model offline et héritage bot2/bot3 réaudités, `.kspwallet` V1 entièrement autonome sans facteur externe, design content-keys/key-slots VIEW/OWNER indépendant, metadata VIEW read-only niveau B via autorité Ed25519 de format distincte, slot VIEW auto-rotatable uniquement pour son propre password, OWNER capable de rotation OWNER/VIEW et dadministration complète, keypair V1 immuable, spec multi-langages séparée + vecteurs publics exigés, paramètres KDF à benchmarker avant freeze, persistence/signature/rotations/import-export répartis jusquà `pre.010` ; `WalletPolicy` reste exclu.
- [ ] `0.2.6` — Introduire `ksp-app-wallet-desk` utilisant Config composite + Wallet + transport HTTP, notamment pour afficher l'identité et le solde d'un wallet.
- [ ] `0.2.7` — Étendre `ksp-onchain-transport-lib` au WebSocket Solana standard complet ; permettre plusieurs sessions sur une même URL sans imposer encore un pool automatique complexe.
- [ ] `0.2.8` — Ajouter Helius LaserStream WebSocket comme extension du moteur WebSocket standard, sans duplication de client.

View File

@@ -0,0 +1,238 @@
<!-- file: deltas/0.2.5/pre.001-fix.001.md -->
<!-- version: 1 -->
# Delta `0.2.5-pre.001-fix.001` — correction des capacités VIEW/OWNER et du modèle d'authentification
## Base requise
Ce fix s'applique après :
```text
0.2.5-pre.001
workspace.package.version = "0.2.5-pre.1"
```
Le delta historique `deltas/0.2.5/pre.001.md` reste inchangé.
## Type de livraison
```text
ksp-general-0.2.5-pre.001-fix.001.zip
```
La livraison reste purement documentaire du point de vue Cargo, mais elle modifie `ROADMAP.md` à la racine ; conformément à `VER-ARCHIVE-001`, le paquet est donc de type `general` et non `doc`.
## Motif du fix
Deux corrections sont nécessaires avant `pre.002`.
### 1. Identifiant de livraison
La correction postérieure à `pre.001` ne doit pas réémettre silencieusement `ksp-general-0.2.5-pre.001.zip`. Conformément à `VER-ID-003`, `VER-DELTA-003` et `VER-DELTA-007`, elle devient une livraison distincte :
```text
0.2.5-pre.001-fix.001
```
### 2. Matrice exacte des capacités
Le plan précédent formulait trop largement VIEW comme incapable de toute mutation persistante. Le contrat fonctionnel attendu est plus précis :
```text
VIEW
lire Pubkey / alias / notes
changer uniquement son propre password VIEW
VIEW -X-> signer
VIEW -X-> exporter le secret
VIEW -X-> modifier Pubkey / alias / notes
VIEW -X-> changer le password OWNER
VIEW -X-> activer/désactiver/recréer VIEW
VIEW -X-> modifier le slot OWNER ou l'état OWNER-controlled
OWNER
tout ce que VIEW peut lire
signer
exporter via adapter explicitement OWNER-only
modifier alias / notes
changer son propre password OWNER
changer le password VIEW sans connaître l'ancien password VIEW
désactiver/recréer VIEW
effectuer une révocation VIEW forte par rekey metadata
```
La keypair Solana reste immuable dans un `.kspwallet` V1 après création/import.
## Correction du modèle cryptographique VIEW
La correction de capability impose une correction réelle du modèle d'authentification : si VIEW doit pouvoir changer son propre password sans OWNER, les champs de protection de son slot ne peuvent pas tous être couverts par une signature qui exige la clé privée d'administration OWNER à chaque réécriture.
Le plan sépare donc désormais :
### État OWNER-controlled
Authentifié par `state_signature` Ed25519 OWNER :
```text
magic / format_version / autorité de format
slot OWNER et sa protection
owner-control
metadata chiffrées
secret chiffré
descripteur stable VIEW
activation/désactivation VIEW
identité/rôle du slot VIEW
versions/algorithmes OWNER-controlled
```
Sans OWNER, cet état ne peut pas être modifié sous l'autorité courante du wallet.
### Champs self-service du slot VIEW
Le slot VIEW possède un descripteur stable OWNER-signed. Seuls ses champs de protection courants sont rotatables par VIEW :
```text
paramètres Argon2id VIEW autorisés par V1
salt VIEW
nonce/ciphertext du wrapping AEAD VIEW
```
Ils rewrappent **la même `K_metadata`** et sont liés par AAD au wallet, au rôle VIEW et au descripteur de slot OWNER-signed.
Un VIEW authentifié peut donc :
```text
ancien password VIEW
-> déverrouiller K_metadata
-> choisir nouveau password VIEW
-> nouveaux KDF/salt/KEK
-> rewrap de la même K_metadata
-> même descripteur VIEW
```
Il ne peut pas transformer cette opération en écriture de metadata, changement de OWNER, désactivation de VIEW ou remplacement de keypair.
## Rotation et révocation
La distinction est désormais explicite.
### Rotation VIEW par VIEW
- ne requiert pas OWNER ;
- ne modifie que le credential du slot VIEW courant ;
- conserve `K_metadata`, Pubkey, alias, notes et keypair ;
- l'ancien password ne déverrouille plus le **slot courant** ;
- ce n'est pas une révocation forte d'un ancien détenteur ayant conservé une ancienne copie ou déjà extrait `K_metadata`.
### Rotation VIEW par OWNER
- ne requiert pas l'ancien password VIEW ;
- OWNER récupère `K_metadata` via owner-control ;
- crée un nouveau wrapping du slot VIEW avec le nouveau password ;
- conserve l'identité/keypair et les metadata.
### Révocation VIEW forte par OWNER
- génère une nouvelle `K_metadata` ;
- rechiffre les metadata ;
- met à jour owner-control ;
- recrée/supprime le slot VIEW selon l'opération ;
- retire à une ancienne `K_metadata` l'accès aux futurs états metadata, sous réserve de la limite déjà documentée des anciennes copies/rollbacks.
### Rotation OWNER
- OWNER peut changer son propre password ;
- rewrap `K_owner_root` avec de nouveaux KDF/salt/KEK ;
- renouvelle l'authentification OWNER correspondante ;
- ne change jamais la keypair Solana.
## Invariants de sécurité conservés
La correction ne réduit aucune des garanties centrales de `0.2.5` :
```text
.kspwallet V1 reste entièrement autonome
aucun facteur externe requis
Pubkey/alias/notes cachés wallet verrouillé
VIEW ne matérialise jamais le secret Solana
VIEW ne signe jamais
VIEW n'exporte jamais le secret
VIEW ne modifie jamais Pubkey/alias/notes
VIEW ne change jamais OWNER
OWNER reste indépendant de VIEW
OWNER peut gérer VIEW sans ancien password VIEW
import => nouveau .kspwallet no-clobber
keypair V1 immuable après création/import
spec multi-langages + vecteurs publics restent obligatoires
```
Les permissions/ACL OS restent hors du threat model Wallet.
## Version Cargo
Ce fix est documentaire et ne modifie aucun fichier consommé par le build/runtime. Conformément à `VER-ID-008` :
```text
workspace.package.version = "0.2.5-pre.1"
```
`Cargo.toml` reste inchangé.
## Fichiers modifiés
```text
ROADMAP.md
docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md
docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md
```
Versions documentaires :
```text
ROADMAP.md 45 -> 46
002-FUNCTIONAL_RELEASE_SEQUENCE.md 45 -> 46
012-V0_2_5_WALLET_FOUNDATION_PLAN.md 3 -> 4
```
## Fichier ajouté
```text
deltas/0.2.5/pre.001-fix.001.md
```
## Fichiers volontairement inchangés
```text
Cargo.toml
CHANGELOG.md
deltas/0.2.5/pre.001.md
docs/000-README.md
docs/plans/000-README.md
crates/**
```
## Validations exécutées
- relecture de `VERSION_WORKFLOW.md`, notamment `VER-ID-003`, `VER-ID-008`, `VER-DELTA-003`, `VER-DELTA-007` et `VER-ARCHIVE-001..004` ;
- vérification que le delta historique `pre.001.md` n'est pas modifié ;
- vérification que `Cargo.toml` reste identique à `pre.001` ;
- vérification de la matrice VIEW/OWNER dans le plan, la séquence fonctionnelle et le roadmap ;
- vérification que le transcript OWNER exclut seulement les paramètres Argon2id VIEW autorisés, le salt et le nonce/ciphertext de wrapping du slot VIEW et conserve un descripteur VIEW OWNER-signed ;
- vérification que VIEW ne reçoit aucune API de mutation metadata, signature, export secret ou administration OWNER ;
- vérification que OWNER peut changer les passwords OWNER et VIEW ;
- vérification que l'import reste une création no-clobber d'un nouveau `.kspwallet` ;
- vérification que les ACL/permissions OS restent hors des garanties Wallet.
## Validations Cargo
Aucune source Rust, dépendance, configuration runtime ou version Cargo n'est modifiée par ce fix. Les validations Cargo ne sont donc pas revendiquées comme nouvelles validations de cette livraison.
## Commit attendu
```text
v0.2.5-pre.001-fix.001
```
## Suite
Après application/validation de ce fix, `0.2.5-pre.002` peut créer la foundation de `ksp-wallet-lib` en matérialisant dès l'API les capacités corrigées : VIEW lecture metadata + rotation de son seul password, OWNER administration complète + rotations OWNER/VIEW, sans codec/chiffrement lourd avant les tranches prévues.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/002-FUNCTIONAL_RELEASE_SEQUENCE.md -->
<!-- version: 44 -->
<!-- version: 46 -->
# Séquence des releases fonctionnelles KSP
@@ -402,9 +402,9 @@ Mission : créer `ksp-wallet-lib` et le format natif interopérable `.kspwallet`
`0.2.5-pre.001` ouvre la release par un gate documentaire : l'ancien JSON temporaire et `.kswallet` ne sont pas migrés, mais leurs invariants utiles (keypair strict, signature sans getter secret, zeroization, `spawn_blocking`, no-clobber/atomic persistence, adapters CLI JSON/Base58) servent de canaris conceptuels. Le nouveau format cache Pubkey/alias/notes lorsqu'il est verrouillé et sépare VIEW/OWNER par content keys et key slots indépendants.
Le design retient un read-only VIEW de niveau B : une clé Ed25519 d'administration du format, distincte de la keypair Solana, authentifie un transcript déterministe de tout l'état mutable/de sécurité. Sans OWNER, un détenteur VIEW ne peut donc pas fabriquer sous l'autorité OWNER originale une modification de metadata, key slot/password, compartment secret ou keypair qui soit acceptée. La cryptographie détecte/rejette les mutations ; elle ne prétend pas empêcher un attaquant filesystem d'écrire physiquement des octets ni distinguer une substitution intégrale par un autre wallet auto-cohérent.
Le design retient un **metadata read-only VIEW de niveau B** : une clé Ed25519 d'administration du format, distincte de la keypair Solana, authentifie l'état OWNER-controlled, notamment metadata, identité Solana, secret, slot OWNER et descripteur stable du slot VIEW. Les paramètres Argon2id VIEW autorisés, le salt et le nonce/ciphertext de wrapping du slot VIEW restent volontairement auto-rotatables par VIEW afin qu'il puisse changer **son seul password VIEW** sans OWNER ; ils restent liés au même wallet/slot par AEAD/AAD et ne donnent aucun droit de modifier alias/notes, de signer, d'exporter le secret, de changer OWNER ou de désactiver/recréer VIEW. OWNER peut changer son propre password ainsi que le password VIEW et reste seul capable des mutations administratives. Les ACL/permissions OS sont hors du modèle Wallet ; remplacer intégralement le fichier par un autre `.kspwallet` valide revient seulement à substituer un autre wallet et ne révèle ni ne rend utilisable l'ancienne keypair.
Le format V1 est cadré comme JSON UTF-8 strict avec binary Base64url sans padding, `format_version`, paramètres Argon2id sérialisés, key slots génériques, owner-control/metadata/secret séparés et XChaCha20-Poly1305. **V1 est entièrement autonome** : aucun salt externe, pepper, OTP, secret KSP, service distant ou ancre externe n'est requis. Les paramètres Argon2 de création ne sont gelés qu'après benchmark. OWNER reste indépendant de VIEW, peut rekey les metadata pour une révocation VIEW forte sans changer la keypair Solana, et la keypair V1 reste immuable après création/import.
Le format V1 est cadré comme JSON UTF-8 strict avec binary Base64url sans padding, `format_version`, paramètres Argon2id sérialisés, key slots génériques, owner-control/metadata/secret séparés et XChaCha20-Poly1305. **V1 est entièrement autonome** : aucun salt externe, pepper, OTP, secret KSP, service distant ou ancre externe n'est requis. Les paramètres Argon2 de création ne sont gelés qu'après benchmark. VIEW peut rewrapper le même accès metadata sous un nouveau password VIEW, ce qui remplace son credential courant sans constituer une révocation cryptographique forte d'un ancien détenteur ayant déjà extrait le matériau VIEW. OWNER peut également changer le password VIEW sans connaître l'ancien, changer son propre password, ou rekey les metadata pour une révocation VIEW forte, sans jamais changer la keypair Solana. La keypair V1 reste immuable après création/import. Tout import crée un nouveau `.kspwallet` avec sémantique no-clobber ; il ne remplace jamais un `.kspwallet` existant et ne sert jamais à muter sa keypair.
La release doit fournir `docs/formats/KSPWALLET_V1.md` comme spécification séparée et indépendante de Rust, accompagnée de vecteurs publics auto-contenus permettant une réimplémentation dans un autre langage. La prévision est étendue jusqu'à `pre.010` afin de séparer codec, crypto, capabilities, persistence, administration, import/export, security audit et documentation interopérable. Le plan actif est `docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md`.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md -->
<!-- version: 2 -->
<!-- version: 4 -->
# Plan `0.2.5` — Wallet foundation
@@ -72,14 +72,18 @@ Clarifications confirmées pendant la revue du gate :
23. **`.kspwallet` V1 est entièrement autonome** : le fichier et le password correspondant sont les seuls éléments requis pour parser, vérifier l'intégrité sous son autorité OWNER embarquée, déverrouiller et récupérer les capacités prévues. V1 n'utilise et ne requiert aucun salt externe, pepper KSP, OTP, secret serveur, keychain, état machine, réseau ou ancre de confiance externe ;
24. la spécification finale est un document séparé `docs/formats/KSPWALLET_V1.md`, indépendant de l'implémentation Rust et suffisamment précis pour réimplémenter le format dans un autre langage sans lire `ksp-wallet-lib` ;
25. toute mutation persistante acceptée sous l'autorité d'un wallet V1 est **OWNER-authorized** : key slots, paramètres KDF, compartments, metadata et état de protection sont couverts par l'authentification OWNER ; VIEW reste incapable de forger un nouvel état valide sous cette même autorité ;
26. la keypair Solana est **immuable dans un wallet V1 après création/import**. V1 ne fournit pas d'opération de remplacement de keypair ; une autre keypair crée un autre wallet.
25. l'état **OWNER-controlled** d'un wallet V1 est authentifié par OWNER : identité Solana, metadata, secret, slot OWNER, activation/désactivation de VIEW, descripteur stable du slot VIEW et paramètres de format concernés ne peuvent pas être modifiés sous l'autorité courante sans OWNER ;
26. **VIEW peut modifier une seule chose : son propre password VIEW**. Cette opération remplace uniquement les paramètres KDF VIEW autorisés par V1, le salt et le nonce/ciphertext de wrapping de son slot en rewrappant le même accès metadata ; elle ne donne aucun droit d'écriture sur Pubkey/alias/notes, aucun droit de signature/export, aucun droit de changer OWNER et aucun droit de désactiver/recréer VIEW ;
27. **OWNER peut changer son propre password OWNER et le password VIEW**, sans connaître l'ancien password VIEW ; OWNER reste seul capable de l'administration metadata et des key slots au-delà du self-service de rotation VIEW ;
28. la keypair Solana est **immuable dans un wallet V1 après création/import**. V1 ne fournit pas d'opération de remplacement de keypair ; une autre keypair crée un autre wallet ;
29. les permissions/ACL du système de fichiers ne constituent **ni une garantie cryptographique ni une responsabilité de sécurité de `ksp-wallet-lib`**. Le contrat Wallet porte sur les capacités : sans OWNER, aucune signature Solana, aucun export secret, aucune écriture acceptée de Pubkey/alias/notes et aucune mutation OWNER-controlled ; la seule mutation volontairement accordée à VIEW est la rotation de son propre password ;
30. tout import produit un **nouveau `.kspwallet`** selon la sémantique no-clobber de `create`. Un import ne remplace, n'écrase et ne transforme jamais un `.kspwallet` existant.
## 4. Réaudit de l'héritage bot2/bot3
### 4.1 bot2 — `TemporaryWalletStore`
L'archive historique contient un store de laboratoire basé sur le keypair JSON Solana 64 octets. Les propriétés utiles sont : validation stricte du keypair, no-clobber, permissions Unix `0700/0600`, effacement des buffers sérialisés et frontière `Signer` sans exposition publique des octets privés.
L'archive historique contient un store de laboratoire basé sur le keypair JSON Solana 64 octets. Les propriétés utiles sont : validation stricte du keypair, no-clobber, effacement des buffers sérialisés et frontière `Signer` sans exposition publique des octets privés. Les permissions Unix `0700/0600` observées historiquement ne sont pas reprises comme garantie de sécurité Wallet : les ACL/permissions OS restent hors du threat model de `.kspwallet` V1.
Le format lui-même est **abandonné** pour KSP natif : il n'offre aucun chiffrement password et le prompt `0.2.5` exclut explicitement sa migration.
@@ -178,7 +182,7 @@ Solana secret/keypair
format-admin signing secret
```
L'API KSP VIEW ne conserve pas la metadata content key après déchiffrement ; elle expose une projection read-only et ne possède aucune méthode de signature ou d'administration.
L'API KSP VIEW expose une projection metadata read-only et ne possède aucune méthode de signature, d'export secret ni d'administration metadata/OWNER. Elle peut toutefois conserver, pour la durée de vie explicitement contrôlée de la capability déverrouillée, le **minimum de matériau VIEW** nécessaire à la lecture et au rewrap de son propre slot ; ce matériau est privé, zeroized au drop lorsque raisonnablement possible et ne contient jamais le secret Solana ni une clé OWNER.
### 5.4 OWNER compromis
@@ -188,52 +192,53 @@ Une future opération de rekey cryptographique complet pourrait changer l'owner
### 5.5 Modification partielle du fichier
Les ciphertexts sont authentifiés par AEAD et l'état global accepté est couvert par l'authentification OWNER définie plus bas. Toute modification partielle d'un champ couvert doit provoquer un rejet déterministe sans exposer une cause crypto trop fine.
Les ciphertexts sont authentifiés par AEAD. L'état OWNER-controlled est couvert par l'authentification OWNER définie plus bas ; les champs self-service du slot VIEW sont couverts par leur wrapping AEAD et leur binding AAD. Toute modification partielle d'un champ hors contrat doit provoquer un rejet déterministe sans exposer une cause crypto trop fine.
### 5.6 Remplacement total / rollback
Un attaquant capable de remplacer le fichier entier peut substituer :
Un remplacement intégral par un autre `.kspwallet` valide ne révèle **aucun** matériau secret de l'ancien wallet : il revient seulement à substituer un autre wallet, avec sa propre autorité OWNER, ses propres key slots et sa propre keypair Solana. Si l'attaquant ne connaît pas VIEW de l'ancien wallet, cette substitution ne lui donne pas davantage accès à la Pubkey/alias/notes de l'ancien fichier.
```text
owner auth public key
key slots
ciphertexts
state signature
```
Un format V1 entièrement autonome ne peut pas distinguer, à partir du seul nouveau fichier, qu'une autre autorité OWNER a remplacé l'ancienne. La même limite vaut pour un rollback complet vers une ancienne copie valide. Ce point n'est **pas** une garantie recherchée par `ksp-wallet-lib` V1 et n'autorise aucune dépendance externe : aucun salt externe, pepper, OTP, secret KSP, service distant, keychain ou ancre de confiance extérieure n'est requis.
par ceux d'un autre wallet valide. Un format autonome ne peut pas détecter cette substitution à partir d'une information de confiance stockée uniquement dans le fichier substitué.
La même limite vaut pour un rollback complet vers une ancienne copie valide. Cette limite n'autorise **aucune dépendance externe en V1** : le format V1 reste volontairement auto-contenu et interdit comme prérequis tout salt externe, pepper, OTP, secret KSP, service distant, keychain ou ancre de confiance extérieure. Une éventuelle version de format future pourrait étudier un facteur/ancrage externe, mais ce mécanisme n'appartient ni au format V1 ni à `0.2.5`.
Les permissions/ACL et le contrôle de qui peut écrire le chemin relèvent du système d'exploitation et sont hors du threat model Wallet. Le contrat V1 porte sur les capacités : **sans OWNER**, aucun détenteur ne peut signer avec la keypair Solana, exporter son secret, modifier Pubkey/alias/notes ni muter l'état OWNER-controlled. Un détenteur VIEW authentifié conserve uniquement l'exception explicitement accordée de rotation de son propre password VIEW.
### 5.7 Matrice des garanties
| Garantie | Cryptographie fichier | Types/capabilities KSP | Filesystem | Ancre externe éventuelle, hors V1 |
|-------------------------------------------------------------------|----------------------:|-----------------------:|----------------------:|----------------------------------------------------------:|
| confidentialité metadata verrouillées | oui | oui | complément | non |
| confidentialité secret Solana face à VIEW | oui | oui | complément | non |
| indépendance VIEW/OWNER | oui | oui | non | non |
| VIEW ne signe pas | séparation de clés | oui | non | non |
| VIEW ne modifie pas via API | indirect | oui | non | non |
| metadata modifiées par VIEW détectées sous la même autorité OWNER | oui, state signature | oui | non | non |
| détection corruption/tampering partiel | oui | parsing strict | complément | non |
| no-clobber / atomic replace | non | API | oui | non |
| protection autres users OS | non | non | best-effort | non |
| détection remplacement total valide | non | non | permissions seulement | théoriquement oui, mais **interdite comme dépendance V1** |
| détection rollback total valide | non | non | non | théoriquement oui, mais **interdite comme dépendance V1** |
| Garantie | Cryptographie du format | Types/capabilities KSP |
|------------------------------------------------------------------------|-------------------------------------------------------:|-------------------------:|
| confidentialité metadata verrouillées | oui | oui |
| confidentialité secret Solana face à VIEW | oui | oui |
| indépendance VIEW/OWNER | oui | oui |
| VIEW ne signe pas | séparation de clés | oui |
| VIEW ne modifie pas Pubkey/alias/notes via API | authentification OWNER des metadata | oui |
| VIEW change son propre password | slot VIEW rewrappable sous la même capability metadata | oui, opération dédiée |
| VIEW ne change pas password OWNER / activation VIEW / autres key slots | authentification OWNER de l'état de contrôle | oui |
| metadata modifiées par VIEW détectées sous la même autorité OWNER | oui, `state_signature` | oui |
| tection corruption/tampering partiel | oui | parsing strict |
| signature Solana sans OWNER | impossible sous les primitives retenues | API absente hors OWNER |
| export secret sans OWNER | secret non déverrouillable | API absente hors OWNER |
| mutation Pubkey/alias/notes/OWNER-state sans OWNER | état non authentifiable | API absente hors OWNER |
| no-clobber / atomic replace | non | propriété de persistence |
| détection remplacement total par un autre wallet valide | hors garantie V1 | hors garantie V1 |
| détection rollback total vers une copie valide | hors garantie V1 | hors garantie V1 |
## 6. Niveau B — VIEW read-only renforcé cryptographiquement
Les ACL/permissions OS ne figurent volontairement pas dans cette matrice : elles ne participent pas au modèle de sécurité de `.kspwallet` V1.
`pre.001` retient **B**.
## 6. Niveau B — metadata VIEW read-only + self-service credential
`pre.001` retient **B** pour les données et capacités OWNER-controlled, tout en accordant explicitement à VIEW une mutation étroite : la rotation de **son propre password VIEW**.
Le wallet possède une **clé d'administration Ed25519 propre au format**, distincte de la keypair Solana. Sa clé publique de vérification est dans l'enveloppe minimale ; sa clé privée est accessible seulement par OWNER.
Chaque état persistant valide porte une `state_signature` OWNER calculée sur un transcript déterministe de **tout l'état mutable et de sécurité du fichier**, à l'exception de la signature elle-même : magic/version, autorité publique, key slots et leurs paramètres KDF/wrapping, versions de compartments, algorithmes, nonces et ciphertexts. Toute opération qui change le fichier doit donc être réalisée depuis une capacité OWNER capable de récupérer la clé privée d'administration et de produire une nouvelle signature valide.
Chaque état persistant valide porte une `state_signature` OWNER calculée sur un transcript déterministe de l'**état OWNER-controlled** : magic/version, autorité publique, slot OWNER complet, owner-control, metadata, secret, versions/algorithmes couverts, ainsi qu'un **descripteur VIEW stable signé** indiquant notamment si VIEW est activé et l'identité/rôle du slot attendu.
Conséquence : un détenteur VIEW qui aurait conservé la metadata content key peut techniquement produire un nouveau ciphertext metadata, mais il ne peut pas produire, sous la même autorité OWNER, une nouvelle `state_signature` acceptée. Il ne peut pas davantage modifier un key slot, changer un password, remplacer le compartment secret ou substituer la keypair tout en conservant un état accepté sous l'autorité OWNER originale.
Les seuls champs mutables exclus de cette autorisation OWNER sont les **champs de protection du slot VIEW courant** nécessaires à son self-service de password : paramètres Argon2id VIEW autorisés par V1, salt VIEW, nonce et ciphertext du wrapping AEAD de la même capability metadata. Les identifiants dalgorithmes restent imposés par V1 et ne deviennent pas librement mutables par VIEW. Ces champs sont eux-mêmes authentifiés par le wrapping AEAD et liés par AAD au wallet, au rôle VIEW et au descripteur de slot signé. VIEW peut donc rewrapper le même accès metadata sous un nouveau password sans posséder la clé privée d'administration OWNER.
La cryptographie ne peut pas empêcher un attaquant disposant d'un accès écriture filesystem de modifier physiquement les octets ; elle garantit que ces octets modifiés sont **rejetés** faute d'authentification OWNER valide. Les permissions filesystem complètent cette propriété en empêchant l'écriture lorsque l'OS peut effectivement l'interdire.
Conséquence : un détenteur VIEW peut changer son password VIEW, mais ne peut pas produire, sous la même autorité OWNER, une modification acceptée de Pubkey/alias/notes, du slot OWNER, de l'owner-control, du compartiment secret, de l'activation/désactivation de VIEW, du descripteur de slot VIEW ou de la keypair Solana. Modifier ou supprimer le slot VIEW d'une manière incompatible avec son descripteur OWNER-signed est rejeté ; seule la réécriture de ses champs de protection selon le contrat de rotation est admise.
Limite incompressible : une substitution du **fichier entier** par un autre wallet auto-cohérent, incluant une nouvelle autorité publique OWNER et une nouvelle signature valide, est indétectable par le seul fichier substitué. Cela reste vrai en particulier si l'attaquant connaît le password VIEW et crée le faux wallet avec ce même password. Détecter cette substitution ou un rollback intégral exigerait de comparer à un état de confiance conservé ailleurs ; V1 n'en dépend pas et ne prétend donc pas fournir cette garantie.
`ksp-wallet-lib` ne contrôle pas qui peut écrire des octets sur le chemin : les ACL/permissions OS sont hors périmètre. Sa garantie porte sur les capacités acceptées par le format et l'API.
Un remplacement du **fichier entier** par un autre wallet auto-cohérent crée seulement l'équivalent d'un autre wallet ; il ne révèle pas l'ancien secret et ne donne aucun moyen de signer avec l'ancienne keypair. V1 ne cherche pas à distinguer cette substitution d'un fichier autonome valide ni un rollback intégral vers une ancienne copie valide.
La keypair Solana **n'est pas** réutilisée comme clé d'administration du format.
@@ -243,15 +248,26 @@ Sans password OWNER ni clé privée d'administration exfiltrée, un attaquant
```text
modifie Pubkey / alias / notes
modifie un paramètre KDF, salt, nonce ou wrapped key
remplace / supprime / ajoute un key slot
change le password VIEW ou OWNER
modifie le slot OWNER ou son password
remplace / supprime / ajoute un key slot hors rotation du slot VIEW existant
active / désactive / recrée VIEW
change le descripteur stable du slot VIEW
remplace le compartment secret
swap la keypair Solana
change les versions/algorithmes couverts
change les versions/algorithmes OWNER-controlled
```
Le parseur applique d'abord les bornes structurelles/anti-DoS, puis vérifie la `state_signature` avant tout déverrouillage coûteux ou projection autorisée. Une mutation non signée est un fichier invalide, pas un nouvel état Wallet.
La seule exception volontaire est :
```text
VIEW authentifié
-> nouveau password VIEW
-> nouveaux paramètres/salt/KEK VIEW valides
-> rewrap de la même capability metadata
-> même descripteur VIEW OWNER-signed
```
Le parseur applique d'abord les bornes structurelles/anti-DoS. Il vérifie l'état OWNER-signed et, lors d'un open VIEW, l'authenticité/binding du slot VIEW fourni. Une mutation de metadata ou d'état OWNER-controlled sans signature OWNER valide est un fichier invalide, pas un nouvel état Wallet.
Cette garantie est volontairement formulée « sous l'autorité OWNER originale » afin de ne pas confondre **forgery/tampering** avec la substitution totale d'un fichier auto-signé par une autre autorité.
@@ -291,6 +307,8 @@ exactement 1 slot OWNER
0 ou 1 slot VIEW
```
Le slot OWNER et l'état de contrôle sont OWNER-signed. Pour VIEW, l'état OWNER-signed contient un **descripteur stable** (`enabled`, rôle et identifiant de slot ou équivalent wire à figer en `pre.003`). Lorsque VIEW est activé, le fichier doit contenir exactement un slot VIEW correspondant à ce descripteur. Ses paramètres Argon2id autorisés, son salt et son nonce/ciphertext de wrapping restent rotatables sans OWNER, mais les algorithmes V1, son rôle, son identité et son activation ne le sont pas. Le wrapping VIEW utilise un AAD qui lie au minimum format/version, autorité wallet, rôle VIEW et identifiant signé du slot.
Le wire utilise néanmoins un tableau générique `key_slots` afin qu'un futur `format_version` puisse introduire d'autres rôles sans remodeler toute l'enveloppe.
### 7.3 Compartiment OWNER control
@@ -334,11 +352,13 @@ Seul OWNER reçoit `K_secret` via le control compartment.
Il faut distinguer :
**rotation de password VIEW** : nouveau salt/KDF/KEK et nouveau wrapping du même `K_metadata`. L'ancien password ne déverrouille plus le fichier courant, mais cette opération ne révoque pas une `K_metadata` déjà exfiltrée par un VIEW malveillant.
**rotation de password VIEW par VIEW** : VIEW authentifié choisit un nouveau password, de nouveaux paramètres/salt/KEK VIEW et rewrappe le même `K_metadata` dans le **même slot VIEW/descripteur signé**. Il ne modifie aucune metadata et n'a pas besoin d'OWNER. L'ancien password ne déverrouille plus le slot courant. Cette opération est une rotation de credential, **pas une révocation cryptographique forte** : un ancien détenteur ayant conservé une ancienne copie/slot ou déjà extrait `K_metadata` peut conserver la capacité de lecture correspondante.
**révocation/recréation forte de VIEW** : OWNER génère une nouvelle `K_metadata`, rechiffre les metadata, met à jour le control compartment puis crée ou retire le slot VIEW. Une ancienne `K_metadata` ne lit alors plus les **futurs** états metadata. Les anciennes copies du wallet restent naturellement lisibles par qui en possédait déjà les secrets.
**rotation de password VIEW par OWNER** : OWNER récupère `K_metadata` via owner-control et peut produire directement un nouveau wrapping VIEW avec un nouveau password, sans connaître l'ancien password VIEW. Le descripteur reste inchangé pour une simple rotation.
**rotation du password OWNER** : nouveau salt/KDF/KEK et nouveau wrapping de `K_owner_root`, sans changement de keypair Solana ni dépendance à VIEW.
**révocation/recréation forte de VIEW** : OWNER génère une nouvelle `K_metadata`, rechiffre les metadata, met à jour le control compartment puis recrée ou retire le slot/descripteur VIEW. Une ancienne `K_metadata` ne lit alors plus les **futurs** états metadata. Les anciennes copies du wallet restent naturellement lisibles par qui en possédait déjà les secrets.
**rotation du password OWNER** : OWNER génère un nouveau salt/KDF/KEK et un nouveau wrapping de `K_owner_root`, puis renouvelle la `state_signature` OWNER ; aucun changement de keypair Solana ni dépendance à VIEW.
**keypair Solana** : immuable dans V1 après création/import. Aucun `replace_keypair` n'est exposé. Toute tentative de modifier `secret` ou la Pubkey metadata sans une réécriture OWNER valide échoue sur la `state_signature`; même avec OWNER, l'import d'une autre keypair crée un nouveau `.kspwallet` au lieu de muter l'identité cryptographique existante.
@@ -447,6 +467,10 @@ Structure de travail à figer par le codec `pre.003` :
"magic": "KSPWALLET",
"format_version": 1,
"owner_auth_public_key": "<base64url>",
"view_descriptor": {
"enabled": true,
"slot_id": "<base64url>"
},
"key_slots": [
{
"role": "owner|view",
@@ -519,7 +543,7 @@ content keys
password-derived keys
```
La clé publique d'administration révèle un fingerprint aléatoire du **format wallet**, pas l'identité blockchain Solana. Elle constitue la racine de vérification **embarquée** de l'état sous la même autorité OWNER ; sa présence ne crée aucune dépendance à un facteur externe.
La clé publique d'administration révèle un fingerprint aléatoire du **format wallet**, pas l'identité blockchain Solana. Elle constitue la racine de vérification **embarquée** de l'état sous la même autorité OWNER ; sa présence ne crée aucune dépendance à un facteur externe. Le placement et l'encodage exacts de `view_descriptor` restent à figer en `pre.003` ; son invariant est qu'il est OWNER-signed alors que seuls les champs de protection du slot VIEW correspondant sont self-rotatables.
### 9.4 Pubkey et secret payload
@@ -598,7 +622,7 @@ Aucune migration automatique mutante n'est exécutée lors d'un simple `open`. T
### 9.9 Checksum
Pas de checksum séparé V1 : AEAD protège chaque ciphertext et la `state_signature` protège l'état sémantique global accepté sous l'autorité OWNER. Un checksum supplémentaire ne fournit pas de garantie de sécurité utile.
Pas de checksum séparé V1 : AEAD protège chaque ciphertext/wrapping, la `state_signature` protège l'état OWNER-controlled, et le slot VIEW self-service est authentifié par son wrapping AEAD lié au descripteur OWNER-signed. Un checksum supplémentaire ne fournit pas de garantie de sécurité utile.
## 10. Authentification, transcript et AAD
@@ -613,7 +637,9 @@ longueurs explicites
entiers en big-endian
octets binaires décodés, pas leurs représentations Base64 texte
rôle/algorithme/version inclus
chaque KDF param/salt/nonce/ciphertext inclus
chaque champ OWNER-controlled, KDF/slot OWNER et ciphertext OWNER-controlled inclus
descripteur stable VIEW inclus
paramètres Argon2id VIEW autorisés + salt + nonce/ciphertext de wrapping du slot VIEW explicitement exclus
state_signature elle-même exclue
```
@@ -627,6 +653,7 @@ Chaque opération de wrapping/chiffrement utilise un AAD domain-separated inclua
magic/format_version
owner_auth_public_key
kind de compartiment ou rôle de slot
identifiant/descripteur signé du slot lorsqu'il existe
identifiants algo/version pertinents
paramètres publics nécessaires à lier le ciphertext à son contexte
```
@@ -639,7 +666,7 @@ Contrat :
```text
ViewPassword / OwnerPassword non Copy, non Clone par défaut, Debug redacted
WalletView aucune key de secret et aucune metadata key retenue
WalletView aucun secret Solana/OWNER ; seulement matériau VIEW minimal privé pour lecture + rotation de son password
WalletOwner secret/root/admin keys privés, sans getter raw public
secret/key buffers temporaires Zeroizing/Zeroize lorsque possédés
message signé jamais loggé
@@ -696,9 +723,10 @@ pubkey()
alias()
notes()
info()
rotate_view_password(new_password, ...)
```
Aucune méthode de mutation/signature/export secret/key-slot.
`rotate_view_password` est la **seule mutation** disponible à VIEW : elle ne modifie que les champs de protection de son slot courant et rewrappe la même capability metadata. Aucune méthode de mutation Pubkey/alias/notes, signature, export secret, changement OWNER, disable/recreate VIEW ou autre administration n'est exposée.
`WalletOwner` :
@@ -716,6 +744,8 @@ rotate_owner_password(...)
export_with(...)
```
OWNER peut donc changer son propre password et le password VIEW. Une simple rotation du password VIEW par OWNER n'exige pas de connaître l'ancien password VIEW ; OWNER récupère le matériau metadata depuis owner-control.
Les mutations persistées sont async et atomiques. `sign()` est locale/CPU et reste naturellement sync.
## 13. Async / sync
@@ -752,24 +782,21 @@ Pour créer/remplacer un `.kspwallet` :
`create` ne fait jamais d'overwrite implicite. En concurrence, un seul créateur peut publier la destination ; les autres reçoivent `destination_exists`.
### 14.3 Unix
### 14.3 Import vers format natif
Durcissement visé :
Tout import d'un format externe est une **création de wallet natif** :
```text
wallet file créé par KSP 0600
répertoire dédié créé par KSP 0700
source externe valide
-> conversion en nouvelle identité/metadata native
-> create no-clobber d'un nouveau fichier .kspwallet
```
KSP ne change pas arbitrairement les permissions d'un parent fourni par l'appelant.
L'import ne fournit aucun mode overwrite/replace d'un `.kspwallet` existant et ne sert jamais à remplacer la keypair d'un wallet déjà créé. Une destination existante retourne `destination_exists`.
Les sources secrètes d'import doivent être des fichiers réguliers et les symlinks sont refusés par défaut pour réduire les ambiguïtés/TOCTOU usuelles. La stratégie exacte est testée dans la tranche persistence/import.
### 14.4 Écriture interrompue
### 14.4 Autres plateformes
Aucune promesse POSIX `0600/0700` sur Windows. La documentation distingue garanties portables, ACL/permissions système et durcissement Unix.
Un crash peut laisser un temp artifact complet à nettoyer ; l'invariant prioritaire est de ne pas transformer l'ancien wallet valide en fichier partiellement remplacé.
Un crash peut laisser un temp artifact complet à nettoyer ; l'invariant prioritaire est de ne pas transformer l'ancien wallet valide en fichier partiellement remplacé. Les ACL/permissions du système d'exploitation ne font pas partie des garanties de `ksp-wallet-lib`.
## 15. Erreurs et non-oracle
@@ -940,23 +967,28 @@ L'injection de random déterministe reste privée aux tests/codec fixtures ; l'A
### 19.2 VIEW/OWNER
- VIEW lit seulement metadata autorisées ;
- aucune signature/mutation/admin/export via type VIEW ;
- VIEW peut changer son propre password VIEW ;
- VIEW ne peut modifier ni Pubkey/alias/notes, ni OWNER, ni activation/descripteur VIEW ;
- aucune signature/export secret/admin OWNER via type VIEW ;
- OWNER fonctionne sans VIEW ;
- OWNER signe et administre ;
- mauvais rôle/password échoue sans fuite ;
- salts/KDF material indépendants ;
- VIEW ne dérive pas OWNER/secret ;
- modification metadata par détenteur VIEW sans admin signing key rejetée ;
- modification d'un key slot/KDF/wrapped key par détenteur VIEW rejetée ;
- tentative de changer password VIEW/OWNER sans OWNER rejetée ;
- modification du slot OWNER ou du descripteur/activation VIEW par détenteur VIEW rejetée ;
- rotation valide des seuls paramètres Argon2id VIEW autorisés, salt et nonce/ciphertext de wrapping du slot VIEW courant par VIEW acceptée ;
- tentative de changer password OWNER via VIEW rejetée ;
- substitution du ciphertext secret/keypair sans OWNER rejetée ;
- `state_signature` invalide/manquante rejetée avant projection ;
- substitution totale explicitement non promise.
### 19.3 Rotation/revocation
- rotation VIEW conserve keypair/Pubkey ;
- old password VIEW n'ouvre plus le slot courant ;
- rotation VIEW par VIEW conserve keypair/Pubkey/metadata et ne change que son credential ;
- rotation VIEW par OWNER fonctionne sans ancien password VIEW ;
- old password VIEW n'ouvre plus le slot courant après rotation normale ;
- limites de révocation d'un ancien détenteur ayant conservé `K_metadata`/une ancienne copie explicitement testées/documentées ;
- rotation OWNER conserve keypair/Pubkey ;
- old password OWNER n'ouvre plus le slot courant ;
- disable/recreate VIEW ne nécessite pas ancien password VIEW ;
@@ -979,8 +1011,8 @@ L'injection de random déterministe reste privée aux tests/codec fixtures ; l'A
- concurrence no-clobber ;
- replace atomique ;
- fault injection avant publication préserve l'ancien wallet ;
- permissions Unix ;
- symlink/source handling ;
- import vers destination nouvelle/no-clobber ;
- import vers destination existante rejeté sans écrasement ;
- éventuels temp remnants documentés sans corruption du wallet publié.
### 19.6 Architecture
@@ -1017,11 +1049,11 @@ Conclusion : **pas de rescoping fonctionnel**, mais split supplémentaire avant
```text
pre.001 audit bot2/bot3 + deps actuelles + threat model + VIEW/OWNER + format/API + sizing
pre.002 crate foundation + capability/password/info types + erreurs + logging contract + canaries architecture
pre.002 crate foundation + capability/password/info types (VIEW self-rotation only, OWNER admin) + erreurs + logging contract + canaries architecture
pre.003 codec JSON strict + limites + key-slot/envelope DTOs + transcript/AAD codec + contrat docs/formats + spec V1 initiale
pre.004 benchmark KDF + Argon2id/XChaCha wrapping + content keys + deterministic crypto vectors in-memory
pre.005 owner-control + metadata/secret compartments + create/open VIEW/OWNER + state signature niveau B
pre.006 persistence async/atomic/no-clobber + Unix hardening + fault/concurrency tests
pre.006 persistence async/atomic/no-clobber + interrupted-write + fault/concurrency tests
pre.007 signature Solana + alias/notes + rotations passwords + disable/recreate VIEW avec rekey metadata
pre.008 import/export adapters + Solana CLI JSON + generic keypair Base58 + inspect
pre.009 audit security/interoperability/compliance + adversarial vectors/tests + cargo trees
@@ -1108,12 +1140,14 @@ KSPWALLET_V1.md permet une implémentation indépendante sans consulter le code
.kspwallet V1 est spécifié hors code et couvert par vecteurs publics auto-contenus
VIEW/OWNER sont indépendants cryptographiquement et dans l'API
Pubkey/alias/notes restent cachés verrouillé
VIEW ne peut pas signer/admin/exporter via l'API
niveau B rejette toute mutation de l'état couvert sous la même autorité OWNER sans signature OWNER valide
VIEW connu ne permet ni changement de password/key slot ni swap de keypair sous l'autorité OWNER originale
VIEW ne peut pas signer/exporter ni modifier Pubkey/alias/notes/OWNER via l'API
VIEW peut changer uniquement son propre password en rewrappant la même capability metadata dans son slot courant
niveau B rejette toute mutation de l'état OWNER-controlled sous la même autorité OWNER sans signature OWNER valide
VIEW connu ne permet ni changement du password OWNER/descripteur VIEW/slot OWNER ni swap de keypair sous l'autorité OWNER originale
OWNER peut changer son propre password et le password VIEW sans dépendre de VIEW
limite substitution/rollback complet est documentée sans surpromesse
keypair V1 est immuable après création/import
rotation password ne change pas keypair
rotation de password VIEW ou OWNER ne change pas keypair
OWNER gère VIEW sans ancien password VIEW
révocation forte VIEW rekey les metadata futures
sign() n'exige pas d'extraction publique du secret