Files
khadhroony-bot3/docs/NATIVE_FORMAT.md
2026-08-10 16:23:18 +02:00

10 KiB

Format natif .kswallet — version 1

Statut

Ce document fixe le codec binaire de la version 1 introduit par 0.5.2-pre.003.

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.

Un fichier natif porte le nom :

<alias>.kswallet

Tous les entiers multi-octets sont encodés en little-endian.

Objectifs du format v1

Le conteneur doit :

  • être identifiable sans heuristique ;
  • distinguer clairement sa version et ses algorithmes ;
  • exposer uniquement l'identité publique nécessaire avant unlock ;
  • borner toutes les longueurs et paramètres avant allocation ou KDF ;
  • authentifier avec l'AEAD toutes les métadonnées influençant identité et déchiffrement ;
  • 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.

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

Aucun octet supplémentaire n'est accepté après le ciphertext déclaré.

Identité publique et payload secret

L'alias et la pubkey sont disponibles avant unlock afin de permettre :

  • le scan et le lookup ;
  • l'affichage d'une identité non sensible ;
  • la détection des collisions ;
  • le choix du wallet par un consommateur.

Le secret Ed25519 n'est jamais placé dans ce header. Le plaintext protégé de la version 1 est exactement le keypair Solana brut de 64 octets déjà caractérisé par le format CLI legacy. XChaCha20-Poly1305 ajoute un tag de 16 octets : le ciphertext v1 est donc exactement de 80 octets. Toute évolution de la structure secrète nécessite une nouvelle version du format.

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.

KDF v1

Le KDF sélectionné est Argon2id version 19.

Le profil d'écriture par défaut retenu est :

memory      = 65536 KiB (64 MiB)
passes      = 3
parallelism = 4 lanes
salt        = 16 bytes
output key  = 32 bytes

Ce profil correspond au second profil recommandé par RFC 9106 pour les environnements à mémoire contrainte et produit la clé de 256 bits requise par l'AEAD choisi.

Le codec accepte uniquement des paramètres dans les bornes suivantes :

memory      = 65536..262144 KiB
passes      = 3..10
parallelism = 1..8

La mémoire déclarée doit en plus respecter la relation Argon2 m >= 8 * p et être un multiple de 4 * p afin que la valeur encodée corresponde sans ambiguïté au nombre de blocs réellement retenu.

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.

AEAD v1

L'AEAD sélectionné est XChaCha20-Poly1305 :

key   = 32 bytes
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.

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.

Données authentifiées

Le contrat v1 réserve comme AEAD associated data l'intégralité des octets précédant le ciphertext :

fixed header
+ alias
+ salt
+ nonce

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.

Bornes du codec

Le codec v1 impose :

alias                 <= 64 bytes
plaintext keypair      = 64 bytes
ciphertext + tag       = 80 bytes
native file total      = 193..256 bytes

La taille du fichier est vérifiée à partir des métadonnées du fichier ouvert avant l'allocation du buffer de lecture. Après lecture des octets attendus, le lecteur vérifie également qu'aucun octet supplémentaire n'est apparu pendant l'opération. La somme exacte des longueurs déclarées doit être égale à la taille réelle ; truncation, croissance concurrente et trailing bytes sont refusés.

Les flags inconnus, IDs KDF/AEAD inconnus, version Argon2 inconnue et octets reserved non nuls sont refusés.

Permissions et publication

Sous Unix :

wallet directory : 0700
.kswallet file   : 0600

La publication que pre.004 devra implémenter 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 ;
  3. écrire le contenu complet ;
  4. sync_all du fichier temporaire ;
  5. publier sans remplacement par hard-link vers <alias>.kswallet ;
  6. synchroniser le répertoire parent sous Unix ;
  7. supprimer le nom temporaire ;
  8. synchroniser à nouveau le répertoire parent sous Unix.

L'utilisation d'un hard-link de même répertoire garantit que la destination existante n'est jamais remplacée silencieusement. Si le filesystem ne permet pas cette publication, l'opération échoue au lieu de revenir à une écriture destructive.

Non-divulgation

Les structures internes contenant sel, nonce ou ciphertext :

  • ne sont pas une API crate-root publique ;
  • n'implémentent pas Debug automatiquement ;
  • ne sont pas sérialisées vers Tauri ;
  • ne doivent pas apparaître dans les erreurs ou logs.

WalletFileHandle ne projette que :

  • alias ;
  • pubkey déclarée ;
  • version du format ;
  • identité persistante non sensible.

Le chemin local reste conservé en interne pour permettre l'ouverture ultérieure du fichier sélectionné.

Suite en pre.004

pre.004 devra compléter ce codec sans modifier silencieusement son layout pour :

  • 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.

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

  • RFC 9106 — Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications, notamment le second profil recommandé Argon2id t=3, p=4, m=64 MiB, sel 128 bits et sortie 256 bits.
  • Documentation RustCrypto argon2 0.5 — dérivation de clé Argon2id et paramètres explicites.
  • Documentation RustCrypto chacha20poly1305 0.11 — contrat XChaCha20Poly1305, clé 32 octets, nonce 24 octets et tag 16 octets, ainsi que la note de statut de spécification XChaCha.