325 lines
12 KiB
Markdown
325 lines
12 KiB
Markdown
<!-- 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)
|