v0.5.2-pre.004
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
<!-- file: docs/NATIVE_FORMAT.md -->
|
||||
<!-- version: 3 -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Format natif `.kswallet` — version 1
|
||||
|
||||
## Statut
|
||||
|
||||
Ce document fixe le codec binaire de la version `1` introduit par `0.5.2-pre.003`.
|
||||
Ce document fixe le codec binaire de la version `1` introduit par `0.5.2-pre.003` et activé pour les wallets persistants protégés dans `0.5.2-pre.004`.
|
||||
|
||||
`pre.003` fixe le layout et implémente le décodage/validation strict ainsi que la lecture bornée nécessaires au scan, mais **ne chiffre encore aucune vraie keypair avec un mot de passe**. L'encodage de création, la publication atomique, le raccordement password/KDF/AEAD et l'ouverture signante appartiennent à `pre.004`.
|
||||
`pre.004` conserve exactement ce layout et implémente l'encodage, la publication atomique, la dérivation Argon2id, le chiffrement/déchiffrement XChaCha20-Poly1305, l'ouverture authentifiée et le changement de mot de passe.
|
||||
|
||||
Un fichier natif porte le nom :
|
||||
|
||||
@@ -29,34 +29,34 @@ Le conteneur doit :
|
||||
- permettre une publication atomique sans écrasement silencieux ;
|
||||
- rester incompatible par construction avec le JSON keypair Solana legacy.
|
||||
|
||||
Le caractère binaire du format ne constitue pas une protection cryptographique. La confidentialité du secret sera fournie par l'AEAD après dérivation d'une clé depuis le mot de passe en `pre.004`.
|
||||
Le caractère binaire du format ne constitue pas une protection cryptographique. La confidentialité du secret est fournie par l'AEAD après dérivation d'une clé depuis le mot de passe.
|
||||
|
||||
## Layout binaire v1
|
||||
|
||||
Le header fixe occupe **72 octets**.
|
||||
|
||||
| Offset | Taille | Champ | Contrat v1 |
|
||||
|--------------:|-----------:|----------------------|-------------------------------------------------|
|
||||
| `0` | `8` | magic | ASCII `KSWALLET` |
|
||||
| `8` | `2` | format version | `1` |
|
||||
| `10` | `2` | header length | `72 + alias_length` |
|
||||
| `12` | `2` | flags | `0` |
|
||||
| `14` | `1` | KDF id | `1` = Argon2id |
|
||||
| `15` | `1` | KDF version | `0x13` = Argon2 v19 |
|
||||
| `16` | `1` | AEAD id | `1` = XChaCha20-Poly1305 |
|
||||
| `17` | `1` | salt length | `16` |
|
||||
| `18` | `1` | nonce length | `24` |
|
||||
| `19` | `1` | alias length | `1..64` |
|
||||
| `20` | `4` | reserved | quatre octets `0` |
|
||||
| `24` | `4` | Argon2 memory KiB | borné, voir ci-dessous |
|
||||
| `28` | `4` | Argon2 passes | borné, voir ci-dessous |
|
||||
| `32` | `4` | Argon2 parallelism | borné, voir ci-dessous |
|
||||
| `36` | `4` | ciphertext length | `80` |
|
||||
| `40` | `32` | Solana pubkey | 32 octets bruts |
|
||||
| `72` | variable | alias | ASCII validé par `WalletAlias` |
|
||||
| après alias | `16` | salt | sel Argon2id |
|
||||
| après salt | `24` | nonce | nonce XChaCha20-Poly1305 |
|
||||
| après nonce | `80` | ciphertext | keypair 64 octets protégé + tag AEAD 16 octets |
|
||||
| Offset | Taille | Champ | Contrat v1 |
|
||||
|------------:|---------:|--------------------|------------------------------------------------|
|
||||
| `0` | `8` | magic | ASCII `KSWALLET` |
|
||||
| `8` | `2` | format version | `1` |
|
||||
| `10` | `2` | header length | `72 + alias_length` |
|
||||
| `12` | `2` | flags | `0` |
|
||||
| `14` | `1` | KDF id | `1` = Argon2id |
|
||||
| `15` | `1` | KDF version | `0x13` = Argon2 v19 |
|
||||
| `16` | `1` | AEAD id | `1` = XChaCha20-Poly1305 |
|
||||
| `17` | `1` | salt length | `16` |
|
||||
| `18` | `1` | nonce length | `24` |
|
||||
| `19` | `1` | alias length | `1..64` |
|
||||
| `20` | `4` | reserved | quatre octets `0` |
|
||||
| `24` | `4` | Argon2 memory KiB | borné, voir ci-dessous |
|
||||
| `28` | `4` | Argon2 passes | borné, voir ci-dessous |
|
||||
| `32` | `4` | Argon2 parallelism | borné, voir ci-dessous |
|
||||
| `36` | `4` | ciphertext length | `80` |
|
||||
| `40` | `32` | Solana pubkey | 32 octets bruts |
|
||||
| `72` | variable | alias | ASCII validé par `WalletAlias` |
|
||||
| après alias | `16` | salt | sel Argon2id |
|
||||
| après salt | `24` | nonce | nonce XChaCha20-Poly1305 |
|
||||
| après nonce | `80` | ciphertext | keypair 64 octets protégé + tag AEAD 16 octets |
|
||||
|
||||
Aucun octet supplémentaire n'est accepté après le ciphertext déclaré.
|
||||
|
||||
@@ -73,7 +73,7 @@ Le secret Ed25519 n'est jamais placé dans ce header. Le plaintext protégé de
|
||||
|
||||
Le nom `<alias>.kswallet` et l'alias encodé doivent correspondre exactement. Une divergence est une erreur et ne déclenche aucun fallback vers le legacy.
|
||||
|
||||
Avant déverrouillage, cette identité est **déclarative** : le codec peut vérifier sa structure, mais pas encore le tag AEAD. `pre.004` doit authentifier le conteneur puis vérifier que la pubkey dérivée du keypair déchiffré correspond exactement à cette pubkey déclarée avant de créer une capacité de signature.
|
||||
Avant déverrouillage, cette identité est **déclarative** : le codec peut vérifier sa structure, mais pas le tag AEAD sans mot de passe. `unlock()` et `unlock_file()` authentifient le conteneur puis vérifient que la pubkey dérivée du keypair déchiffré correspond exactement à cette pubkey déclarée avant de créer `UnlockedWallet`.
|
||||
|
||||
## KDF v1
|
||||
|
||||
@@ -104,7 +104,7 @@ La mémoire déclarée doit en plus respecter la relation Argon2 `m >= 8 * p` et
|
||||
Ces bornes servent deux objectifs distincts :
|
||||
|
||||
- refuser un conteneur v1 plus faible que le plancher retenu pour `ks-wallet` ;
|
||||
- empêcher qu'un fichier hostile impose des coûts KDF arbitrairement élevés lors de `pre.004`.
|
||||
- empêcher qu'un fichier hostile impose des coûts KDF arbitrairement élevés lors d'une ouverture authentifiée.
|
||||
|
||||
## AEAD v1
|
||||
|
||||
@@ -116,7 +116,7 @@ nonce = 24 bytes
|
||||
tag = 16 bytes
|
||||
```
|
||||
|
||||
`pre.004` devra générer un nonce neuf pour chaque nouvelle protection du payload. Une modification de mot de passe devra donc produire au minimum un nouveau sel et un nouveau nonce.
|
||||
Chaque nouvelle protection du payload génère un sel et un nonce neufs depuis la source aléatoire système. Une modification de mot de passe produit donc un nouveau sel et un nouveau nonce avant rechiffrement de la même keypair.
|
||||
|
||||
Ce choix appartient au format natif `ks-wallet`, pas à un format Solana externe. La documentation RustCrypto signale qu'il n'existe pas de spécification XChaCha20-Poly1305 autoritative unique, tout en documentant des implémentations interopérables et l'audit de la crate. Le layout v1 fixe donc explicitement l'algorithme et ses tailles au lieu de dépendre d'une convention implicite.
|
||||
|
||||
@@ -133,7 +133,7 @@ fixed header
|
||||
|
||||
Le champ `ciphertext_length` appartient donc lui aussi aux données authentifiées.
|
||||
|
||||
Ainsi, une altération de la version, des IDs crypto, des paramètres KDF, de la pubkey, de l'alias, du sel, du nonce ou de la longueur du ciphertext devra faire échouer l'ouverture authentifiée en `pre.004`.
|
||||
Ainsi, une altération de la version, des IDs crypto, des paramètres KDF, de la pubkey, de l'alias, du sel, du nonce ou de la longueur du ciphertext fait échouer l'ouverture authentifiée.
|
||||
|
||||
## Bornes du codec
|
||||
|
||||
@@ -159,7 +159,7 @@ wallet directory : 0700
|
||||
.kswallet file : 0600
|
||||
```
|
||||
|
||||
La publication que `pre.004` devra implémenter pour produire un conteneur v1 suit cette séquence :
|
||||
La publication implémentée pour produire un conteneur v1 suit cette séquence :
|
||||
|
||||
1. encoder entièrement le conteneur ;
|
||||
2. créer dans le même répertoire un fichier temporaire privé avec `create_new` ;
|
||||
@@ -190,19 +190,23 @@ Les structures internes contenant sel, nonce ou ciphertext :
|
||||
|
||||
Le chemin local reste conservé en interne pour permettre l'ouverture ultérieure du fichier sélectionné.
|
||||
|
||||
## Suite en `pre.004`
|
||||
## Implémentation `pre.004`
|
||||
|
||||
`pre.004` devra compléter ce codec sans modifier silencieusement son layout pour :
|
||||
`pre.004` complète le codec v1 sans modifier son layout :
|
||||
|
||||
- encoder le conteneur de création et publier le fichier atomiquement/no-clobber selon la séquence ci-dessus ;
|
||||
- dériver une clé avec Argon2id ;
|
||||
- chiffrer/déchiffrer les 64 octets du keypair avec XChaCha20-Poly1305 ;
|
||||
- vérifier que la pubkey dérivée du keypair déchiffré est exactement celle du header ;
|
||||
- créer une capacité de signature seulement après authentification réussie ;
|
||||
- changer le mot de passe en conservant la même keypair/pubkey ;
|
||||
- zéroïser les buffers plaintext et clés dérivées possédés par `ks-wallet`.
|
||||
- encodage et publication atomique/no-clobber des créations ;
|
||||
- dérivation Argon2id dans une tâche bloquante dédiée ;
|
||||
- chiffrement/déchiffrement des 64 octets du keypair avec XChaCha20-Poly1305 ;
|
||||
- authentification du préfixe complet comme AAD ;
|
||||
- vérification stricte de la pubkey après déchiffrement ;
|
||||
- création de `UnlockedWallet` seulement après authentification réussie ;
|
||||
- changement de mot de passe avec nouvelle protection du même keypair ;
|
||||
- zéroïsation des mots de passe, clés dérivées, plaintexts temporaires et buffers ciphertext possédés par `ks-wallet` lorsque leur durée de vie se termine ;
|
||||
- activation du feature `zeroize` du cipher pour effacer son état interne à la destruction.
|
||||
|
||||
Toute modification incompatible du layout défini ici doit utiliser une nouvelle version de format plutôt qu'une heuristique de décodage.
|
||||
La keypair runtime est possédée par `solana_keypair::Keypair`; sa dépendance `ed25519-dalek` active par défaut la zéroïsation de `SigningKey`.
|
||||
|
||||
La migration et les adaptateurs d'import/export ne font pas partie du format wire v1 et restent la tranche suivante. Toute modification incompatible du layout défini ici doit utiliser une nouvelle version de format plutôt qu'une heuristique de décodage.
|
||||
|
||||
## Références de conception v1
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/plans/V0_5_2_KS_WALLET_RESTRUCTURING_PLAN.md -->
|
||||
<!-- version: 6 -->
|
||||
<!-- version: 7 -->
|
||||
|
||||
# Plan `0.5.2` — restructuration de `ks-wallet`
|
||||
|
||||
@@ -142,9 +142,9 @@ Le format binaire devra au minimum permettre d'identifier sans ambiguïté :
|
||||
- ciphertext v1 : exactement 80 octets avec le tag AEAD ;
|
||||
- taille totale v1 : 193 à 256 octets selon la longueur de l'alias ;
|
||||
- paramètres KDF acceptés : mémoire 64 à 256 MiB, 3 à 10 passes, 1 à 8 lanes, avec les relations Argon2 de mémoire validées ;
|
||||
- données authentifiées réservées pour `pre.004` : header complet + alias + sel + nonce.
|
||||
- données authentifiées : header complet + alias + sel + nonce.
|
||||
|
||||
Le layout normatif exact est documenté dans `docs/NATIVE_FORMAT.md`. `pre.003` décode et valide strictement ces champs pour le scan mais n'encode encore aucun vrai wallet persistant et n'effectue aucune dérivation de clé ni aucun chiffrement réel avec un mot de passe ; la création/écriture reste strictement dans `pre.004`.
|
||||
Le layout normatif exact est documenté dans `docs/NATIVE_FORMAT.md`. `pre.003` a fermé la lecture/validation structurelle ; `pre.004` active l'encodage, la publication, la dérivation Argon2id, le chiffrement XChaCha20-Poly1305 et l'ouverture authentifiée sans modifier ce wire.
|
||||
|
||||
## 4. Caractérisation du legacy actuel
|
||||
|
||||
@@ -501,19 +501,25 @@ Le préambule d'identification introduit ici ne définit pas encore le payload p
|
||||
- [x] alias du header vérifié contre le nom `<alias>.kswallet` ;
|
||||
- [x] `WalletFileHandle` enrichi de la pubkey déclarée et de la version sans exposer le chemin ;
|
||||
- [x] validation des permissions privées à la lecture ;
|
||||
- [ ] encodage de création et publication atomique/no-clobber — `pre.004` ;
|
||||
- [x] encodage de création et publication atomique/no-clobber explicitement reportés à `pre.004` ;
|
||||
- [x] tests du wire v1, truncation, trailing bytes, KDF hors bornes, alias mismatch et réouverture bornée.
|
||||
|
||||
Aucune vraie keypair n'est encore chiffrée par cette tranche : le password, la dérivation Argon2id, l'appel AEAD et la capacité de signature ouverte restent `pre.004`.
|
||||
La tranche `pre.003` reste structurelle ; le cycle de vie protégé est activé par `pre.004` ci-dessous.
|
||||
|
||||
### `0.5.2-pre.004` — password et cycle de vie persistant
|
||||
|
||||
- création persistante avec password ;
|
||||
- ouverture/déverrouillage ;
|
||||
- fermeture/verrouillage selon le modèle runtime retenu ;
|
||||
- changement de mot de passe ;
|
||||
- signature après ouverture ;
|
||||
- non-divulgation et zéroïsation.
|
||||
- [x] `WalletPassword` possédé, non clonable, redacted et consommé par une opération ;
|
||||
- [x] création persistante avec password et publication atomique/no-clobber ;
|
||||
- [x] dérivation Argon2id et XChaCha20-Poly1305 effectifs hors du runtime async ;
|
||||
- [x] ouverture/déverrouillage par alias ;
|
||||
- [x] ouverture authentifiée d'un `WalletFileHandle` sélectionné hors store ;
|
||||
- [x] `UnlockedWallet` comme capacité de signature non clonable ;
|
||||
- [x] fermeture/verrouillage par consommation explicite ou fin de portée de `UnlockedWallet` ;
|
||||
- [x] changement de mot de passe après authentification de l'ancien, avec nouveau sel/nonce et même keypair/pubkey ;
|
||||
- [x] contrat explicite : la rotation du mot de passe reprotège le fichier mais ne révoque pas une `UnlockedWallet` déjà remise à un consommateur ;
|
||||
- [x] vérification de la pubkey déchiffrée contre le header authentifié avant exposition du signer ;
|
||||
- [x] zéroïsation des buffers secrets possédés et activation de la zéroïsation du cipher ;
|
||||
- [x] tests d'ancien/nouveau mot de passe, signature, ouverture externe, AAD altéré et canaris de non-divulgation.
|
||||
|
||||
### `0.5.2-pre.005` — migration, import et export
|
||||
|
||||
@@ -603,7 +609,7 @@ Le plan est désormais centré sur les décisions suivantes :
|
||||
14. le format keypair JSON des binaires Solana est obligatoire en import et en export dans `0.5.2` ;
|
||||
15. une matrice officielle des formats Phantom/Solflare/Backpack/Trust/Coinbase-Base et autres cibles pertinentes sera finalisée avant les adaptateurs ;
|
||||
16. un seul adaptateur wallet tiers est implémenté dans `0.5.2` comme exemple, les autres formats faisables étant reportés au TODO sans version déterminée ;
|
||||
17. le format v1 utilise Argon2id v19 et XChaCha20-Poly1305 selon `docs/NATIVE_FORMAT.md`, tandis que le modèle runtime d’un wallet ouvert reste à fermer en `pre.004` ;
|
||||
17. le format v1 utilise Argon2id v19 et XChaCha20-Poly1305 selon `docs/NATIVE_FORMAT.md`, et `pre.004` ferme le modèle runtime avec `WalletPassword` + `UnlockedWallet` ;
|
||||
18. le scan automatique reste limité au répertoire configuré, tandis qu'un consommateur peut fournir explicitement un autre chemin `.kswallet` à `ks-wallet` sans modifier le store ni enregistrer ce fichier automatiquement.
|
||||
|
||||
Ces décisions ont permis d'ouvrir `0.5.2-pre.002`.
|
||||
|
||||
Reference in New Issue
Block a user