v0.5.2-pre.007

This commit is contained in:
2026-08-11 15:15:03 +02:00
parent 56572cec40
commit 279fd67cc0
27 changed files with 1151 additions and 143 deletions

324
docs/guides/WALLETS.md Normal file
View File

@@ -0,0 +1,324 @@
<!-- file: docs/guides/WALLETS.md -->
<!-- version: 2 -->
# Wallets, sécurité et opérations
## 1. Objet
Ce guide décrit le contrat opérationnel de `ks-wallet` à partir de `0.5.2`.
`ks-wallet` possède :
- le stockage local des wallets Solana persistants ;
- la protection par mot de passe ;
- l'ouverture authentifiée ;
- la capacité de signature ;
- les imports/exports de formats secrets explicitement supportés.
Il ne possède pas :
- la politique d'autorisation d'une transaction ;
- les plafonds de dépense ;
- le routage RPC ;
- la classification on-chain d'une pubkey ;
- les DTO frontend d'une application Tauri.
## 2. Modèle de données
### 2.1 Identité publique
Une identité publique contient uniquement :
- alias ;
- pubkey ;
- persistance temporaire ou persistante.
Une identité publique ne contient jamais le secret, le mot de passe, le ciphertext ou un chemin local.
### 2.2 Wallet temporaire
`TemporaryWallet::generate()` crée un keypair en mémoire. Il ne nécessite ni mot de passe ni fichier.
Cette surface reste adaptée aux tests synthétiques et signers jetables. Les fixtures/keypairs persistées uniquement pour les campagnes historiques restent sous `wallets/temporary/**`; elles ne doivent pas être confondues avec les `.kswallet` persistants de `wallets/`.
### 2.3 Wallet persistant
Le format natif est :
```text
<alias>.kswallet
```
Le layout exact est défini dans [`../NATIVE_FORMAT.md`](../NATIVE_FORMAT.md).
Le store automatique de `WalletManager` :
- scanne uniquement son répertoire configuré ;
- ne descend pas récursivement ;
- ignore les extensions étrangères ;
- refuse symlinks et fichiers non réguliers ;
- exige des permissions privées sous Unix ;
- valide le conteneur complet avant de retourner un handle.
## 3. Protection cryptographique
Le format v1 utilise :
| Élément | Contrat v1 |
| ----------------- | ---------------------------------- |
| KDF | Argon2id v19 |
| Profil d'écriture | 64 MiB, 3 passes, 4 lanes |
| Sel | 16 octets |
| Clé dérivée | 32 octets |
| AEAD | XChaCha20-Poly1305 |
| Nonce | 24 octets |
| Plaintext | keypair Solana exacte de 64 octets |
| Ciphertext + tag | 80 octets |
| AAD | header + alias + sel + nonce |
Les paramètres lus sont bornés avant l'exécution du KDF.
La pubkey présente dans le header n'est pas considérée comme authentifiée avant l'unlock. Après déchiffrement, `ks-wallet` reconstruit le keypair et vérifie que sa pubkey correspond exactement au header avant de produire une capacité de signature.
## 4. Mot de passe et cycle de vie
`WalletPassword` :
- prend possession de sa chaîne ;
- n'est pas clonable ;
- n'est pas sérialisable ;
- possède un `Debug` redacted ;
- zéroïse la valeur qu'il possède à la destruction.
`WalletManager::create()` crée un wallet natif et retourne `UnlockedWallet`.
`WalletManager::unlock()` ouvre un wallet du store par alias.
`WalletManager::unlock_file()` ouvre un `WalletFileHandle` obtenu par inspection explicite d'un fichier externe.
`UnlockedWallet` :
- possède le keypair authentifié ;
- n'est pas clonable ;
- expose `Signer` / `Signer + Sync` ;
- n'expose pas les 64 octets privés ;
- est verrouillé par consommation explicite avec `lock()` ou par fin de portée.
Un changement de mot de passe ne change pas la keypair. Il produit un nouveau sel et un nouveau nonce, puis reprotège exactement la même identité Solana.
Une `UnlockedWallet` déjà remise à un consommateur n'est pas révoquée rétroactivement par un changement de mot de passe. Son propriétaire doit la détruire ou appeler `lock()`.
## 5. Écriture et permissions
Les nouveaux `.kswallet` sont publiés sans écrasement silencieux.
Sous Unix :
- répertoire wallet : `0700` ;
- fichier `.kswallet` : `0600` ;
- exports secrets : `0600`.
Les écritures natives utilisent un fichier temporaire privé dans le même répertoire, `sync_all`, puis une publication atomique/no-clobber. Le changement de mot de passe remplace le fichier après authentification de l'ancien secret.
Les collisions concurrentes de création d'un même alias doivent produire exactement un gagnant et conserver une destination native valide.
## 6. Configuration
`wallet.config.json` contient une racine globale `wallets_directory` et des profils.
Un profil peut sélectionner :
```json
{
"wallet_alias": "operator"
}
```
ou laisser :
```json
{
"wallet_alias": null
}
```
L'alias est une sélection non sensible. Aucun mot de passe n'est stocké dans `ks-config`.
Lorsqu'un alias persistant est configuré, un consommateur ne doit pas retomber silencieusement sur un wallet temporaire : il doit acquérir explicitement le mot de passe et appeler l'API d'unlock.
## 7. Migration legacy
Le legacy historique est :
```text
<alias>.json
```
avec le tableau JSON des 64 octets du keypair Solana.
`WalletManager::migrate_legacy()` :
1. lit la source legacy privée ;
2. valide le keypair ;
3. crée `<alias>.kswallet` ;
4. vérifie que la pubkey est identique ;
5. conserve la source JSON intacte.
La suppression du legacy reste une décision explicite de l'opérateur après validation.
`TemporaryWalletStore` reste disponible pour les scénarios historiques. Ses erreurs, logs et `Debug` ne doivent pas projeter les chemins locaux, même si ses getters explicites `directory()` et `wallet_path()` restent nécessaires aux outils de fixtures.
## 8. Import et export
Les formats secrets actuellement supportés sont :
| Format | Import | Export | Mot de passe requis à l'export |
| ---------------------------------------------- | :----: | :----: | :----------------------------: |
| `WalletTransferFormat::SolanaCliJson` | oui | oui | oui |
| `WalletTransferFormat::SolanaPrivateKeyBase58` | oui | oui | oui |
La matrice détaillée est dans [`../WALLET_FORMAT_COMPATIBILITY.md`](../WALLET_FORMAT_COMPATIBILITY.md).
L'import refuse :
- source non régulière ou symlink ;
- permissions trop ouvertes sous Unix ;
- format invalide ;
- keypair de taille incorrecte ;
- alias déjà occupé ;
- pubkey déjà gérée sous un autre alias natif.
L'export authentifie d'abord le `.kswallet`, puis seulement après crée la destination. Il n'écrase jamais silencieusement un fichier existant.
## 9. Keypair, wallet, mint et authority
Une keypair Solana ne contient pas son rôle métier.
Le même type de secret Ed25519 peut servir comme :
- payer/opérateur ;
- mint authority ;
- freeze authority ;
- clé utilisée lors de la création d'un mint ;
- recipient possédant lui-même un signer ;
- autre authority ou signer de fixture.
Il est donc incorrect de décider « ce fichier est un mint et non un wallet » uniquement à partir du tableau JSON de 64 octets.
La future inspection de `wallets/temporary/**` prévue avec `0.5.4` doit produire un inventaire local factuel :
```text
format détecté
keypair valide / invalide / format non supporté
pubkey si valide
```
Un consommateur peut ensuite interroger un réseau donné avec cette pubkey et classifier l'état actuel de l'adresse, par exemple :
- compte absent ;
- compte système ;
- mint SPL Token ;
- mint Token-2022 ;
- compte token ;
- programme ou autre compte.
Cette classification reste réseau-dépendante et ne prouve pas tous les rôles historiques du signer.
`ks-wallet` ne doit pas dépendre de `ks-onchain-transport` pour réaliser cette classification.
## 10. Desktop de démonstration
`kb-app-demo-desktop` possède ses propres DTO Tauri et ne sérialise pas les types secrets de `ks-wallet`.
La fenêtre Wallets permet :
- inventaire des `.kswallet` de `wallets/` ;
- création d'un wallet natif avec un secret backend-only ;
- inspection d'un `.kswallet` externe ;
- inspection/import d'un keypair Solana CLI JSON ou Base58 sans exposer son secret au frontend ;
- export secret authentifié vers `data/wallets/` avec nom de fichier borné et sans chemin arbitraire côté frontend ;
- sélection explicite d'un profil RPC pour les lectures publiques ;
- consultation du solde SOL, des comptes SPL Token/Token-2022, des signatures récentes et du détail `getTransaction` ;
- sélection session-only du `.kswallet` utilisé par les démos Devnet.
Le mot de passe de démonstration est lu uniquement côté backend depuis :
```text
KB_SECRET_DEMO_WALLET_PASSWORD
```
La sélection runtime du wallet d'exécution est authentifiée avant activation et ne réécrit pas `wallet.config.json`. L'ordre de résolution est :
```text
override session demo_wallet
-> wallet_alias du profil
-> wallet temporaire uniquement si aucun alias persistant n'est sélectionné
```
Lorsqu'un alias persistant est effectif, les adaptateurs Devnet System/SPL/Metadata utilisent une capacité `UnlockedWallet` et ne retombent pas silencieusement sur le JSON temporaire. La validation opérateur `0.5.2` a observé `persistence="persistent"` avec la pubkey sélectionnée.
Le profil RPC choisi dans la fenêtre Wallets ne modifie ni le profil actif global ni le store wallet actif ; il borne uniquement les lectures on-chain de cette fenêtre.
### 10.1 Répertoires opérateur
La convention du desktop est :
```text
wallets/
<alias>.kswallet
temporary/<profil>/...
data/wallets/
<exports secrets explicites>
```
`wallets/` est la racine persistante native. `wallets/temporary/**` appartient aux fixtures/keypairs historiques. `data/wallets/` contient les exports explicites destinés à des outils externes et ne doit pas être rescanné automatiquement comme store natif.
### 10.2 Validation du changement de mot de passe
Le changement de mot de passe n'est volontairement pas exposé dans le frontend. La crate `ks-wallet-demo-scenarios` valide ce cycle comme consommateur externe de l'API publique :
```text
A -> B
A rejeté
B ouvre et signe
B -> A
A ouvre et signe
```
Le test utilise un `.kswallet` synthétique dans un répertoire temporaire privé et ne modifie aucun wallet réel de l'opérateur.
## 11. Non-divulgation
Ne jamais placer un secret wallet dans :
- `wallet.config.json` ;
- `KS_PUBLIC_*` ou `KB_PUBLIC_*` ;
- un payload Tauri ;
- un DTO TS-RS généraliste ;
- un log ;
- un message d'erreur normal ;
- un `Debug` automatique d'un type sensible.
Les chemins locaux ne sont pas des secrets cryptographiques, mais ils restent des informations internes et ne doivent pas apparaître inutilement dans les logs, erreurs et DTO fonctionnels.
## 12. Modèle de menace borné
`0.5.2` protège le secret persistant contre la lecture directe du fichier sans mot de passe et impose des permissions privées ainsi qu'un chiffrement authentifié.
Cette version ne prétend pas protéger contre :
- une machine déjà compromise pendant qu'un wallet est déverrouillé ;
- un processus disposant des mêmes droits utilisateur et capable de lire la mémoire ;
- la saisie volontaire du mot de passe dans un programme malveillant ;
- la copie volontaire d'un `.kswallet` et de son mot de passe vers une autre installation compatible ;
- hardware wallet, Ledger, KMS ou HSM.
## 13. Références
- [`../../ks-wallet/README.md`](../../ks-wallet/README.md)
- [`../../ks-wallet/USAGE.md`](../../ks-wallet/USAGE.md)
- [`../../ks-wallet-demo-scenarios/README.md`](../../ks-wallet-demo-scenarios/README.md)
- [`../NATIVE_FORMAT.md`](../NATIVE_FORMAT.md)
- [`../WALLET_FORMAT_COMPATIBILITY.md`](../WALLET_FORMAT_COMPATIBILITY.md)
- [`../validation/V0_5_2_WALLET_VALIDATION_REPORT.md`](../validation/V0_5_2_WALLET_VALIDATION_REPORT.md)