Dans le capstone TLS, le serveur prouvait son identité en signant — pour que le navigateur sache qu’il parle au bon interlocuteur, et pas à un imposteur. Cette signature, c’était Ed25519. C’est l’autre moitié de l’identité TLS : AES chiffre les données, Ed25519 prouve qui parle.

Ce chapitre clôt l’arc crypto. On regarde la signature à l’intérieur — émise, comme le reste, en code machine verbose — et on s’arrête sur ce qui la rend remarquable : elle ne tire aucun nombre aléatoire.


Signer, vérifier : une asymétrie

Une signature numérique repose sur une paire de clés. La privée (que seul le serveur connaît) sert à signer. La publique (que tout le monde peut voir, dans le certificat) sert à vérifier.

  SIGNER     (le serveur, avec sa clé privée)
     message  +  clé privée   ──►   signature (64 octets)

  VÉRIFIER   (le navigateur, avec la clé publique)
     message  +  signature  +  clé publique   ──►   vrai / faux

Personne ne peut produire une signature valide sans la clé privée, mais n’importe qui peut la vérifier avec la clé publique. C’est ce qui permet au navigateur de faire confiance au serveur sans jamais connaître son secret.


Le cœur : multiplier un point sur une courbe

Toute la sécurité d’Ed25519 tient dans une opération : la multiplication scalaire [n]P — additionner un point P de la courbe à lui-même n fois. C’est facile à calculer dans un sens, et pratiquement impossible à inverser (retrouver n à partir du résultat). C’est là qu’est cachée la clé.

La courbe est une courbe d’Edwards tordue, sur le même corps GF(2²⁵⁵−¹⁹) que le X25519 du handshake — l’arithmétique de corps est partagée. Et comme pour le ladder X25519, la multiplication scalaire est l’une des rares boucles : 256 étapes, écrites en récursion, avec une preuve de terminaison (article #3) :

rule ed_scalarmult
  logic:
    ...
    out = if s.j == 0 then <les coordonnées du point résultat>
          else ed_scalarmult(EdScalarState { ..., j: s.j - 1, ... })
  proofs:
    termination:
      decreasing : j

(L’état réel porte une quarantaine de champs — le point en coordonnées étendues (X, Y, Z, T), sur 10 limbes chacune — élidés ici.) Le point qui compte : decreasing : j est vérifié à la compilation. La marche j descend de 256 à 0, donc le compilateur prouve que la multiplication s’arrête. L’addition de points sous-jacente (ed_add) et le passage en coordonnées affines (ed_affine, via un inverse de Fermat) sont, eux, déroulés.


Le truc qui claque : zéro aléa

Voici ce qui distingue Ed25519, et pourquoi il a remplacé les anciens schémas. La plupart des signatures sur courbe (ECDSA) ont besoin, à chaque signature, d’un nombre aléatoire secret — un nonce. Et si ce nonce fuit, ou pire, se répète ne serait-ce qu’une fois, la clé privée entière est exposée.

Ce n’est pas théorique : c’est exactement comme ça que la clé de signature de la PlayStation 3 a été extraite — Sony réutilisait le même nonce.

  ECDSA classique :  signature = f(clé, message, ALÉA)

                            si l'aléa fuit ou se répète → clé privée exposée

  Ed25519 :          signature = f(clé, message)

                            le « hasard » est dérivé du message via SHA-512
                            déterministe : il n'y a aucun aléa à fuir

Ed25519 dérive son nonce du message lui-même (haché avec SHA-512) et d’une partie de la clé privée. Même message, même clé → même signature, toujours. Aucun générateur aléatoire n’est sollicité au moment de signer. Toute une classe de catastrophes disparaît par construction.

Pour verbose, ça a une conséquence concrète : SHA-512 fait partie de la signature, et il est lui aussi émis en natif. Le os.urandom de l’hôte, qui servait à l’échange de clés, n’intervient pas ici. Signer est entièrement déterministe — donc entièrement reproductible, donc entièrement vérifiable.


Comment on sait que c’est juste

Le test : les vecteurs de la RFC 8032 §7.1. Le premier prend une clé privée connue et exige une clé publique précise :

clé privée :  9d61b19deffd5a60ba844af492ec2cc4...
clé publique: d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a

Le binaire verbose dérive public_key à partir de la privée et tombe exactement sur cette valeur — octet pour octet. Idem pour sign sur les trois vecteurs §7.1. Pas « ça ressemble » : la valeur publiée par la RFC, au bit près.

Toute la cryptographie — la multiplication scalaire, l’addition de points, SHA-512, la réduction mod-L (sc_reduce, sc_muladd) — est du code machine verbose. L’hôte n’assemble que les octets et fait l’I/O.


L’arc est bouclé

On a commencé par un hash de 12 Ko. On a vu le chiffrement qui protège les données, et l’assemblage complet où un vrai navigateur affiche une page HTTPS. Voici la dernière brique : la signature qui prouve l’identité.

C’est Ed25519 qui signait le certificat dans le capstone — jusqu’à ce qu’un vrai navigateur réclame du P-256 à la place. Mais le principe est le même partout, et c’est toute la thèse de la série : on ne fait pas confiance à la cryptographie parce qu’un outil l’a produite. On la lit, et on la confronte — aux vecteurs des RFC, à OpenSSL, à un navigateur. Une signature assez petite et assez claire pour être auditée ligne à ligne, dont la sortie matche la référence officielle, et qui — élégance finale — n’a même pas besoin de hasard pour être sûre.

L’arc cryptographique est complet. Du bit à la page chiffrée, chaque transformation est du code machine qu’on peut ouvrir, lire, et vérifier.