v0.2.5-pre.005

This commit is contained in:
2026-08-19 13:09:16 +02:00
parent 3c069347b7
commit 36b98e0abc
27 changed files with 2305 additions and 113 deletions

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/012-V0_2_5_WALLET_FOUNDATION_PLAN.md -->
<!-- version: 7 -->
<!-- version: 8 -->
# Plan `0.2.5` — Wallet foundation
@@ -363,7 +363,7 @@ Il faut distinguer :
**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.
## 8. Primitives et dépendances réauditées le 2026-08-18
## 8. Primitives et dépendances réauditées jusquau 2026-08-19
Aucune dépendance ci-dessous n'est ajoutée dans `pre.001`. Ce sont les candidates pour les tranches qui les consommeront réellement.
@@ -378,9 +378,9 @@ Aucune dépendance ci-dessous n'est ajoutée dans `pre.001`. Ce sont les candida
`solana-keypair` fournit le keypair Ed25519, la conversion stricte depuis 64 octets, la signature, le format JSON de 64 entiers et une représentation Base58 complète. Aucun client RPC Solana n'est nécessaire.
La génération SDK récente définit `Pubkey` comme alias du type `Address`, mais l'écosystème a déjà connu des incompatibilités lorsque plusieurs générations de `solana-address` coexistent. `pre.002` verrouille donc la surface publique Wallet sur **`ksp_core_lib::Pubkey` exclusivement** et n'ajoute aucune dépendance directe `solana-pubkey`. Lorsque `solana-keypair` sera réellement introduit, la tranche concernée devra prouver par compilation/cargo-tree que son `Address/Pubkey` est compatible avec la génération possédée par Core avant toute conversion interne. Aucun second type d'adresse KSP n'est introduit pour contourner un mismatch.
La génération SDK récente définit `Pubkey` comme alias du type `Address`, mais l'écosystème a déjà connu des incompatibilités lorsque plusieurs générations de `solana-address` coexistent. `pre.002` verrouille donc la surface publique Wallet sur **`ksp_core_lib::Pubkey` exclusivement** et n'ajoute aucune dépendance directe `solana-pubkey`. `pre.005` introduit réellement `solana-keypair 3.1.2` uniquement pour posséder/valider la keypair Ed25519 ; la Pubkey Wallet reste construite via `ksp_core_lib::Pubkey` depuis les 32 octets publics du keypair, sans exposer un second type d'adresse KSP.
Un `cargo tree` est obligatoire lors de l'introduction réelle afin de contrôler les versions `ed25519-dalek`, `solana-signature`, `rand/getrandom` et éviter des duplications évitables.
Un `cargo tree` est obligatoire avec `pre.005` afin de confirmer l'unification de `solana-address`, `ed25519-dalek`, `rand/getrandom` et d'éviter des duplications crypto injustifiées.
Audit source notable : `solana-keypair 3.1.2` contient un bloc `unsafe` interne dans sa conversion Base58 vers `String`. Cela ne modifie pas la règle `#![forbid(unsafe_code)]` du code KSP, mais doit rester visible dans l'audit des transitifs ; `pre.008` réévaluera le chemin Base58 réellement appelé et les alternatives avant de figer l'adapter.
@@ -392,9 +392,9 @@ Audit source notable : `solana-keypair 3.1.2` contient un bloc `unsafe` interne
| scrypt | `0.12.0` | alternative maintenue, non ajoutée |
| PBKDF2 | `0.13.0` | compatibilité/legacy seulement, non ajouté |
Les paramètres Argon2 de création sont benchmarkés sur les machines cibles. `pre.004` livre trois candidats explicites (64 MiB / 128 MiB / 256 MiB, trois passes, une lane) et un test opérateur ignoré par défaut ; **aucun candidat n'est déclaré default avant retour du benchmark opérateur**. Ils ne sont pas copiés de bot3, d'un RFC ou d'un default de crate. Le fichier sérialise tous les paramètres nécessaires afin qu'un ancien wallet conserve son profil historique.
Les paramètres Argon2 de création ont été mesurés avec le benchmark opérateur de `pre.004` : `64 MiB / 3 / 1 = 1742 ms`, `128 MiB / 3 / 1 = 3459 ms`, `256 MiB / 3 / 1 = 6925 ms` sur la machine/profil testés le 2026-08-19. `pre.005` retient donc **64 MiB / 3 passes / 1 lane** comme profil initial de création KSP, avec un salt CSPRNG indépendant de 32 octets par slot. Ce choix n'est copié ni de bot3, ni d'un RFC, ni d'un default de crate. Le fichier sérialise tous les paramètres nécessaires afin qu'un ancien wallet conserve son profil historique même lorsque les defaults KSP seront durcis.
Le parseur impose des **bornes maximales** avant de lancer le KDF, afin qu'un fichier hostile ne puisse demander arbitrairement mémoire/CPU. `pre.004` ajoute également l'invariant Argon2 `memory_kib >= 8 * parallelism` avant tout calcul coûteux. Les bornes structurelles restent indépendantes du default de création ; celui-ci est figé seulement après le benchmark opérateur de `pre.004`.
Le parseur impose des **bornes maximales** avant de lancer le KDF, afin qu'un fichier hostile ne puisse demander arbitrairement mémoire/CPU. `pre.004` ajoute également l'invariant Argon2 `memory_kib >= 8 * parallelism` avant tout calcul coûteux. Les bornes structurelles restent indépendantes du profil de création KSP.
### 8.3 AEAD
@@ -409,13 +409,13 @@ AES-GCM-SIV apporte une meilleure tolérance à la réutilisation accidentelle d
### 8.4 CSPRNG
`getrandom 0.4.3` est retenu comme candidate low-level pour les octets aléatoires propres au format. Les API de génération de keypair Solana restent libres d'utiliser leur CSPRNG interne maintenu.
`getrandom 0.4.3` est retenu depuis `pre.004` comme primitive low-level pour les octets aléatoires propres au format. `pre.005` l'utilise également pour les seeds/slot IDs/salts/nonces de création. Les API de génération de keypair Solana ne deviennent pas pour autant propriétaires du CSPRNG du format.
### 8.5 Secret memory
`zeroize 1.9.0` est retenu et devient la seule nouvelle dépendance tierce de `pre.002`, car les wrappers `ViewPassword` / `OwnerPassword` lutilisent immédiatement pour leur nettoyage au `Drop`. `secrecy` n'est pas ajouté tant qu'un besoin ergonomique concret n'est pas démontré ; des types KSP simples imposent eux-mêmes redaction/non-Clone et utilisent `zeroize`.
Pour la clé admin Ed25519 distincte, `ed25519-dalek 3.0.0` est une candidate standard, mais son ajout direct est **conditionné au cargo-tree** de la tranche qui implémente l'authentification afin d'éviter une génération concurrente inutile avec celle déjà tirée par `solana-keypair`.
`pre.005` introduit directement **`ed25519-dalek ^2.2`** avec `default-features = false` et les features `signature` + `zeroize`. La génération `3.0.0`, bien que plus récente, n'est volontairement pas ajoutée : `solana-keypair 3.1.2` dépend de `ed25519-dalek ^2.1.1`, donc la branche directe `2.2` permet à Cargo d'unifier une seule génération Dalek et d'activer `zeroize` sur la `SigningKey` utilisée à la fois par l'autorité de format KSP et par le wrapper Solana. Une duplication `2.x + 3.x` n'apporterait aucune capacité nécessaire à V1.
### 8.6 État des audits de sécurité publics
@@ -432,14 +432,14 @@ L'absence d'une ligne « audited » dans ce document ne signifie donc pas « sû
### 8.7 Encodage et persistence
Candidates :
État au terme de `pre.005` :
```text
base64 0.23.1 pour Base64url sans padding des champs binaires JSON
tempfile 3.27.0 pour temp files same-directory + persist/persist_noclobber
base64 0.23.1 acquis depuis pre.003 pour Base64url sans padding des champs binaires JSON
tempfile 3.27.0 candidate pre.006 pour temp files same-directory + persist/persist_noclobber
```
Elles ne sont ajoutées que lorsqu'elles sont réellement consommées.
`tempfile` ne sera ajouté que si `pre.006` confirme sa sémantique portable et son besoin réel pour l'atomicité/no-clobber.
## 9. Format natif `.kspwallet` V1
@@ -516,7 +516,7 @@ Structure V1 figée par le codec `pre.003` (les longueurs/encodages exacts sont
}
```
Les `0` ci-dessus signifient « valeur déterminée par le benchmark futur », pas des paramètres valides.
Les paramètres de création KSP V1 sont désormais `memory_kib = 65536`, `iterations = 3`, `parallelism = 1`, avec un salt CSPRNG indépendant de 32 octets par slot. Le wire continue toutefois d'accepter tout profil V1 valide dans les bornes documentées, car les paramètres sont sérialisés par wallet.
### 9.3 Informations visibles wallet verrouillé
@@ -1031,6 +1031,26 @@ L'injection de random déterministe reste privée aux tests/codec fixtures ; l'A
- aucun `unsafe` KSP ;
- cargo-tree sans duplication crypto injustifiée.
### 19.7 État acquis après `pre.005`
`pre.005` matérialise désormais les invariants crypto centraux sans filesystem :
```text
K_owner_root, K_metadata, K_secret indépendants
OWNER slot -> K_owner_root
VIEW slot -> K_metadata
owner_control = admin Ed25519 secret || K_metadata || K_secret (96 octets)
metadata = JSON protégé Pubkey/alias/notes avec note IDs 16 octets uniques
secret = keypair Solana 64 octets secret||public
state_signature Ed25519 vérifiée avant tout KDF
open VIEW ne matérialise jamais owner-control/secret
open OWNER vérifie autorité admin + cohérence keypair + Pubkey metadata
create/open KDF via spawn_blocking
vecteur complet test-only généré et revérifié indépendamment du Rust
```
La persistence, la signature Solana publique, les mutations metadata et les rotations restent volontairement absentes de cette tranche.
## 20. Sizing
| Domaine | Taille | Risque principal |
@@ -1070,31 +1090,32 @@ rel.001 publication strictement publicationnelle
Une `fix` ou tranche supplémentaire est préférable à la suppression d'une garantie sécurité si un des gates révèle une incompatibilité.
## 22. Dépendances candidates par tranche
## 22. Dépendances par tranche
`pre.001` najoutait aucune dépendance. `pre.002` ajoute uniquement :
État réellement acquis au terme de `pre.005` :
```text
zeroize ^1.9
pre.002 zeroize ^1.9
pre.003 base64 ^0.23
pre.004 argon2 ^0.5
chacha20poly1305 ^0.11
getrandom ^0.4
pre.005 ed25519-dalek ^2.2
solana-keypair ^3.1
tokio déjà workspace, feature locale rt pour spawn_blocking
```
La dépendance est centralisée sous `[workspace.dependencies]` puis consommée avec `zeroize.workspace = true`. Elle est utilisée immédiatement par les wrappers de password ; aucune crate crypto/KDF/Solana supplémentaire nest ajoutée par anticipation. La génération courante `zeroize 1.9.0` a été réauditée avant insertion.
Toutes les dépendances tierces communes restent centralisées sous `[workspace.dependencies]`; le membre Wallet active uniquement les features nécessaires. `ed25519-dalek ^2.2` est volontairement aligné avec la contrainte `^2.1.1` de `solana-keypair 3.1.2` afin de permettre une seule génération Dalek et d'activer `zeroize` sur la `SigningKey` partagée par résolution Cargo.
Liste de travail restante, à réauditer juste avant insertion sous `[workspace.dependencies]` :
Candidates restantes, à réauditer juste avant insertion :
```text
argon2 ^0.5
base64 ^0.23
chacha20poly1305 ^0.11
ed25519-dalek ^3.0 # seulement si cargo-tree justifie le direct
getrandom ^0.4
solana-keypair ^3.1
solana-signer ^3.0 # si réellement nécessaire directement
tempfile ^3.27 # pre.006 si la sémantique atomic/no-clobber est confirmée
solana-signer ^3.0 # seulement si un contrat public/impl l'exige réellement
solana-signature ^3.5 # seulement si le type public l'exige
tempfile ^3.27
```
Déjà présents et réutilisables :
Déjà présents et réutilisés :
```text
serde
@@ -1122,7 +1143,7 @@ Le fait que `bs58` ne soit pas requis sera révalidé lorsque les adapters sont
## 23. Sources externes réauditées
Sources primaires consultées le 2026-08-18 :
Sources primaires consultées initialement le 2026-08-18 puis réauditées le 2026-08-19 pour les dépendances effectivement introduites par `pre.005` :
```text
https://docs.rs/solana-keypair/3.1.2/
@@ -1136,12 +1157,12 @@ https://docs.rs/chacha20poly1305/0.11.0/
https://docs.rs/aes-gcm-siv/0.12.0/
https://docs.rs/getrandom/0.4.3/
https://docs.rs/zeroize/1.9.0/
https://docs.rs/ed25519-dalek/3.0.0/
https://docs.rs/ed25519-dalek/2.2.0/
https://docs.rs/base64/0.23.1/
https://docs.rs/tempfile/3.27.0/
```
Les versions sont des constats d'audit du gate, pas des dépendances ajoutées par ce delta. Chaque tranche réaudite sa candidate au moment où elle devient réellement consommée.
Les versions sont des constats d'audit successifs : elles ne signifient pas que toutes les crates listées ont été ajoutées dans une même tranche. Chaque dépendance sensible est réauditée au moment où elle devient réellement consommée.
## 24. Critères de sortie `0.2.5`
@@ -1197,4 +1218,4 @@ Une future `format_version >= 2` pourra réétudier des facteurs/ancrages extern
## 26. Suite immédiate
`0.2.5-pre.003` fige le codec JSON strict, les limites structurelles, `slot_id` 16 octets, le descripteur VIEW, les DTOs denveloppe/key slots, les TLV transcript/AAD et la première spécification `docs/formats/KSPWALLET_V1.md`. `0.2.5-pre.004` ajoute les primitives effectives Argon2id v19, XChaCha20-Poly1305, CSPRNG OS, wrapping de content keys, zeroization des clés possédées et un vecteur crypto public déterministe. La seule partie de `pre.004` qui reste volontairement à confirmer par lopérateur est le choix du **default de création Argon2**, via le benchmark candidat livré et ignoré par défaut. `pre.005` ne doit pas figer ce default sans ce résultat.
`0.2.5-pre.003` fige le codec JSON strict, les limites structurelles, `slot_id` 16 octets, le descripteur VIEW, les DTOs denveloppe/key slots, les TLV transcript/AAD et la première spécification `docs/formats/KSPWALLET_V1.md`. `pre.004` ajoute Argon2id/XChaCha20-Poly1305/CSPRNG OS et le wrapping de content keys. Le benchmark opérateur a ensuite permis à `pre.005` de retenir le profil initial `64 MiB / 3 / 1`, de figer les payloads `owner_control`/metadata/secret, d'introduire l'autorité Ed25519 OWNER distincte de la keypair Solana, de créer/ouvrir réellement VIEW et OWNER en mémoire et de publier un vecteur `.kspwallet` complet interopérable. **La suite immédiate est `pre.006` : persistence async/atomique/no-clobber**, sans déplacer de logique filesystem dans Config.