Cryptographie expliquée
← Retour à l'accueil · Le contrat · Qui êtes-vous ? · Réseau P2P
En résumé : Vos messages sont protégés par plusieurs couches de cryptographie.
Seul le destinataire peut les lire (chiffrement), et il peut vérifier que c'est bien vous qui les avez envoyés (signature).
Tout cela sans que personne ne connaisse vos clés secrètes.
Comment ça marche en simple ?
Imaginez que vous voulez envoyer une lettre secrète à un ami. Dans le monde réel, vous pourriez :
- Mettre la lettre dans une enveloppe fermée (chiffrement) — seul celui qui a la clé peut l'ouvrir
- Signer l'enveloppe (signature) — pour prouver que c'est bien vous qui l'avez envoyée
- Donner la clé au destinataire (échange de clé) — mais sans que personne d'autre ne puisse l'intercepter
C'est exactement ce que fait la cryptographie numérique, mais de façon mathématiquement impossible à falsifier.
Les algorithmes utilisés
Voici les « outils » cryptographiques du système. Chacun a un rôle précis :
| Algorithme |
Rôle |
Analogie |
SHA-256 |
Empreinte unique du message |
Comme une empreinte digitale : unique et impossible à inverser |
ECDH secp256k1 |
Échange de clé sécurisé |
Comme mélanger deux couleurs en public pour créer un secret commun |
HKDF-SHA256 |
Dérivation de clé |
Transformer le secret partagé en une clé utilisable |
AES-256-GCM |
Chiffrement du message |
Un coffre-fort numérique ultra-sécurisé |
Schnorr secp256k1 |
Signature numérique |
Votre signature manuscrite, mais impossible à falsifier |
Détail de chaque algorithme
SHA-256 — L'empreinte digitale
SHA-256 crée une « empreinte » unique de 64 caractères hexadécimaux (256 bits) pour n'importe quel message.
Deux messages différents auront toujours des empreintes différentes.
- Entrée : n'importe quel texte ou données
- Sortie : 64 caractères hexadécimaux (ex: a7f3c9...)
- Propriété clé : impossible de retrouver le message à partir de l'empreinte
Usage : identifier chaque message de façon unique sur le relais.
ECDH secp256k1 — Le secret partagé
ECDH (Elliptic Curve Diffie-Hellman) permet à deux personnes de créer un secret commun
sans jamais l'échanger directement. C'est comme de la magie mathématique !
L'analogie des couleurs : Imaginez qu'Alice et Bob veulent créer une couleur secrète.
- Alice mélange une couleur publique avec sa couleur secrète → obtient « couleur A »
- Bob mélange la même couleur publique avec sa couleur secrète → obtient « couleur B »
- Ils échangent « couleur A » et « couleur B » (publiquement, tout le monde peut voir)
- Alice mélange « couleur B » + sa couleur secrète → obtient la couleur finale
- Bob mélange « couleur A » + sa couleur secrète → obtient la même couleur finale
Résultat : Alice et Bob ont la même couleur secrète, sans l'avoir jamais échangée !
Un espion qui a vu « couleur A » et « couleur B » ne peut pas retrouver la couleur finale.
- Courbe : secp256k1 (la même que Bitcoin)
- Entrée : votre clé privée + la clé publique du destinataire
- Sortie : un secret partagé (32 octets)
Usage : créer un secret pour chiffrer les messages entre deux personnes.
HKDF-SHA256 — Le raffineur de clé
HKDF (HMAC-based Key Derivation Function) transforme le secret partagé ECDH en une clé
de chiffrement de haute qualité.
- Entrée : le secret partagé ECDH (32 octets)
- Sortie : une clé AES-256 (32 octets) de qualité cryptographique
- Pourquoi : s'assurer que la clé est uniformément aléatoire
AES-256-GCM — Le coffre-fort
AES-256-GCM est l'algorithme de chiffrement qui protège le contenu du message.
Il offre à la fois la confidentialité (personne ne peut lire) et
l'intégrité (personne ne peut modifier sans qu'on le sache).
- Taille de clé : 256 bits (très sécurisé)
- Mode : GCM (Galois/Counter Mode) avec authentification
- IV : vecteur d'initialisation de 12 octets (différent à chaque chiffrement)
- Tag : code d'authentification de 16 octets (détecte les modifications)
Usage : chiffrer le message pour que seul le destinataire puisse le lire.
Schnorr secp256k1 — La signature
La signature Schnorr prouve que vous êtes bien l'auteur du message, sans révéler votre clé privée.
Elle est plus efficace et plus simple que les signatures ECDSA traditionnelles.
- Entrée : le hash du message + votre clé privée
- Sortie : une signature de 64 octets (128 caractères hex)
- Vérification : n'importe qui peut vérifier avec votre clé publique
Important : La signature prouve que vous avez signé, mais elle ne révèle
jamais votre clé privée. Même en voyant 1000 de vos signatures, personne ne peut
retrouver votre clé secrète.
Dérivation de clés et clés multiples par pair
Chaque pair (appareil local ou distant) peut être associé à plusieurs clés publiques :
une clé principale et, optionnellement, une liste de clés supplémentaires. Cela permet d’utiliser
plusieurs clés pour un même pair sans multiplier les secrets à gérer.
Dérivation déterministe à partir d’une clé privée
À partir d’une seule clé privée (celle de l’identité), le système peut calculer d’autres paires
clé privée / clé publique de façon déterministe : pour un index entier (0, 1, 2, …),
le calcul produit toujours la même paire. Aucune donnée aléatoire n’est utilisée pour cette dérivation.
- Entrée : la clé privée parente (64 caractères hexadécimaux) et un index (entier ≥ 0).
- Procédé : une fonction de dérivation (HMAC-SHA256 avec un domaine fixe et l’index)
produit 32 octets, puis une réduction modulo l’ordre de la courbe secp256k1 donne un nombre
valide comme clé privée sur la courbe ; la clé publique enfant est obtenue par multiplication
du point de base de la courbe par ce nombre (format compressé, 66 caractères hex).
- Sortie : une paire (clé privée enfant, clé publique enfant). La clé « principale »
est celle dérivée directement de la clé privée de l’identité (sans index enfant).
Ainsi, une même identité peut exposer plusieurs clés publiques (la principale et les dérivées 0, 1, 2, …)
tout en ne stockant qu’une seule clé privée ; les autres sont recalculées à la demande.
Vérification rapide : une clé publique appartient-elle à mon identité ?
Pour savoir si une clé publique donnée correspond à votre clé privée (identité), le système fait :
- Calculer la clé publique à partir de votre clé privée (une opération sur la courbe).
- Comparer le résultat à la clé publique donnée. Si elles sont égales, la clé appartient à votre identité.
- Si on autorise les clés dérivées : calculer les clés publiques dérivées pour les indices 0, 1, 2, …
jusqu’à une borne fixée, et comparer chacune à la clé donnée. Dès qu’une égalité est trouvée, la réponse est oui.
Le coût est donc une seule opération pour la clé principale, puis au plus N opérations si on teste
N clés dérivées. En limitant N (par exemple 100), la vérification reste rapide.
Pour un pair distant (autre appareil), les clés sont celles enregistrées pour ce pair (clé principale
et liste optionnelle) : la vérification consiste à tester si la clé donnée est dans cet ensemble,
sans dérivation côté local.
Le workflow complet
Voici ce qui se passe quand vous envoyez un message sécurisé, étape par étape :
Phase 1 : Préparation
1
Création du message
Votre message est structuré avec : les données, un horodatage, un identifiant unique (UUID), et les règles de validation.
2
Calcul de l'empreinte (hash)
Message → SHA-256 → Hash (64 caractères)
Cette empreinte unique identifie le message sur le réseau.
3
Génération du nonce
Un nombre aléatoire unique (nonce) est créé pour éviter qu'un même message soit rejoué.
Phase 2 : Chiffrement
4
Échange de clé ECDH
Votre clé privée + Clé publique du destinataire → ECDH → Secret partagé
Un secret commun est calculé mathématiquement, sans jamais être transmis.
5
Dérivation de la clé AES
Secret partagé → HKDF-SHA256 → Clé AES-256
Le secret est transformé en une clé de chiffrement de qualité.
6
Chiffrement du message
Clé AES + IV aléatoire + Message → AES-256-GCM → Message chiffré + Tag
Le message est enfermé dans un « coffre-fort » numérique.
Phase 3 : Signature
7
Signature Schnorr
Hash + Nonce + Votre clé privée → Schnorr → Signature
Vous signez l'empreinte du message avec votre clé privée.
Phase 4 : Publication sur le relais
8
Envoi au relais
Trois éléments sont envoyés séparément :
- MsgChiffre : le message chiffré + son hash
- MsgCle : l'IV + votre clé publique (pour que le destinataire puisse déchiffrer)
- MsgSignature : votre signature + clé publique
Phase 5 : Collecte des signatures (multi-signature)
9
Attente des co-signataires
Si le contrat exige plusieurs signatures (ex: 2 appareils sur 3), le système attend que les autres signent :
- Interrogation du relais toutes les 2 secondes
- Timeout après 5 minutes si signatures manquantes
- Progression affichée (ex: "2/3 signatures")
10
Validation
Le message est validé quand :
- Toutes les signatures requises sont présentes (cardinalité)
- Les dépendances sont respectées (ex: "A doit signer avant B")
- Les clés publiques correspondent aux signataires autorisés
Phase 6 : Réception et déchiffrement
11
Scan des clés
Le destinataire interroge le relais pour récupérer les MsgCle récentes.
12
Récupération du message
Avec le hash trouvé dans la MsgCle, il récupère le message chiffré.
13
Déchiffrement ECDH inverse
Sa clé privée + Clé publique de l'émetteur (df) → ECDH → Même secret partagé
Il recalcule le même secret, puis déchiffre avec AES-GCM.
14
Vérification des signatures
Chaque signature est vérifiée avec la clé publique du signataire.
Schéma récapitulatif
┌─────────────────────────────────────────────────────────────────┐
│ ÉMETTEUR │
├─────────────────────────────────────────────────────────────────┤
│ Message → SHA-256 → Hash │
│ │
│ Clé privée émetteur ─┐ │
│ ├──→ ECDH → Secret partagé │
│ Clé publique dest. ──┘ │
│ │
│ Secret partagé → HKDF-SHA256 → Clé AES-256 │
│ │
│ Clé AES + IV + Message → AES-256-GCM → Message chiffré │
│ │
│ Hash + Nonce + Clé privée → Schnorr → Signature │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ RELAIS │
├─────────────────────────────────────────────────────────────────┤
│ POST /messages ← MsgChiffre (hash, message_chiffre) │
│ POST /keys ← MsgCle (hash, iv, df_ecdh = clé pub émett.) │
│ POST /signatures← MsgSignature (hash, signature, clé publique) │
│ │
│ ... attente des co-signatures (GET /signatures/:hash) ... │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ DESTINATAIRE │
├─────────────────────────────────────────────────────────────────┤
│ GET /keys?start=&end= → Liste des MsgCle récentes │
│ GET /messages/:hash → MsgChiffre │
│ GET /signatures/:hash → MsgSignature[] │
│ │
│ Clé privée dest. ────┐ │
│ ├──→ ECDH → Même secret partagé │
│ Clé publique émett. ─┘ (df_ecdh_scannable) │
│ │
│ Secret partagé → HKDF-SHA256 → Clé AES-256 │
│ │
│ Clé AES + IV + Ciphertext → AES-256-GCM → Message original │
│ │
│ Vérification : Signature + Clé publique → Schnorr verify → OK │
└─────────────────────────────────────────────────────────────────┘
Pourquoi c'est sécurisé ?
Confidentialité
- Seul le destinataire peut lire : le secret partagé ECDH ne peut être calculé que par l'émetteur et le destinataire
- AES-256 : considéré incassable avec les technologies actuelles (2²⁵⁶ combinaisons possibles)
- IV unique : même si vous envoyez le même message deux fois, le résultat chiffré sera différent
Intégrité
- GCM : le mode Galois/Counter détecte toute modification du message chiffré
- Hash : l'empreinte SHA-256 garantit que le message n'a pas été altéré
Authenticité
- Signature Schnorr : prouve mathématiquement que c'est bien vous qui avez signé
- Multi-signature : plusieurs appareils peuvent être requis pour valider
- Anti-rejeu : le nonce empêche de réutiliser une ancienne signature
Séparation des données
- Message, clé et signature séparés : même si quelqu'un intercepte un élément, il ne peut rien faire sans les autres
- Clé privée jamais transmise : seules les clés publiques et les signatures circulent
Détails techniques pour les développeurs
Paramètres cryptographiques
| Paramètre | Valeur |
| Courbe elliptique | secp256k1 (256 bits) |
| Taille clé AES | 256 bits |
| Taille IV (AES-GCM) | 96 bits (12 octets) |
| Taille Tag (AES-GCM) | 128 bits (16 octets) |
| Hash | SHA-256 (256 bits) |
| KDF | HKDF-SHA256 |
| Signature | Schnorr secp256k1 (64 octets) |
Bibliothèques utilisées
@noble/secp256k1 : ECDH, Schnorr, manipulation de clés
@noble/hashes : SHA-256, HKDF
Web Crypto API : AES-256-GCM (navigateur)
Format des clés
- Clé privée : 32 octets (64 caractères hex)
- Clé publique (compressée) : 33 octets (66 caractères hex, préfixe 02 ou 03)
- Signature Schnorr : 64 octets (128 caractères hex)
Fichiers source
userwallet/src/utils/encryption.ts : encryptWithECDH, decryptWithECDH
userwallet/src/utils/crypto.ts : signMessage, verifySignature, generateChallenge, deriveChildKeyPair, getDerivedPublicKeys, publicKeyBelongsToIdentity
userwallet/src/utils/pairing.ts : getPairPublicKeys, pairContainsPublicKey, addPairPublicKey
userwallet/src/utils/relay.ts : postMessageChiffre, postSignature, postKey
userwallet/src/utils/collectSignatures.ts : runCollectLoop, fetchSignaturesForHash
userwallet/src/utils/loginValidation.ts : hasEnoughSignatures, checkDependenciesSatisfied
service-login-verify/ : verifyLoginProof (côté parent)
Dérivation de clés et clés multiples par pair : docs/USERWALLET_KEY_DERIVATION.md.
Collecte des signatures
| Constante | Valeur | Description |
| COLLECT_POLL_MS | 2 000 ms | Intervalle entre chaque interrogation |
| COLLECT_TIMEOUT_MS | 300 000 ms | Timeout global (5 minutes) |
| COLLECT_FETCH_TIMEOUT_MS | 15 000 ms | Timeout par requête |
En résumé
Le système combine plusieurs couches de protection :
- ECDH pour créer un secret sans jamais l'échanger
- AES-256-GCM pour chiffrer le message
- Schnorr pour prouver votre identité
- SHA-256 pour identifier chaque message de façon unique
- Multi-signature pour exiger plusieurs validations
Résultat : vos messages sont
confidentiels,
authentiques et
infalsifiables.
← Retour à l'accueil · Le contrat · Qui êtes-vous ? · Réseau P2P