Cryptographie post-quantique en Delphi

· Composants
Cryptographie post-quantique en Delphi

Il y a une question qui mérite d'être posée sur les données que votre application traite aujourd'hui : combien de temps restent-elles sensibles ? Un dossier médical, un contrat, un fichier de paie, un ensemble d'identifiants. Si la réponse dépasse quelques années, alors la menace intéressante n'est pas un attaquant qui casse votre chiffrement maintenant. C'est un attaquant qui enregistre le trafic aujourd'hui, le stocke, et le déchiffre plus tard, quand les outils auront rattrapé leur retard. Cette pratique a un nom, récolter maintenant, déchiffrer plus tard, et elle ne demande à personne de construire un ordinateur quantique aujourd'hui. Il suffit qu'ils croient que quelqu'un le fera.

C'est pourquoi les normes ont bougé les premières. Le NIST a publié les algorithmes en 2024, les navigateurs et les CDN ont activé l'échange de clés hybride au cours de 2025, et les échéances pour retirer RSA et l'échange de clés à courbe elliptique sont désormais inscrites dans les recommandations publiques. sgcWebSockets 2026.10 apporte l'ensemble à Delphi, écrit en Object Pascal dans le pack sgcCrypto, sans aucune bibliothèque externe à installer ou à déployer.

Trois algorithmes, un seul pack

Les trois normes du NIST remplissent des rôles différents, et vous utiliserez normalement les deux premières :

Établir une clé avec ML-KEM

Un mécanisme d'encapsulation de clé est plus simple à utiliser que Diffie-Hellman. L'expéditeur prend la clé publique du destinataire et produit deux choses : un nouveau secret partagé, et un texte chiffré qui le transporte. Le destinataire retransforme ce texte chiffré en ce même secret.

uses
  sgcCrypto_MLKEM;

var
  oPublicKey, oPrivateKey, oCiphertext, oSecret, oSame: TBytes;
begin
  sgcMLKEM_GenerateKeyPair(mlkem768, oPublicKey, oPrivateKey);

  // the sender, who only ever sees the public key
  sgcMLKEM_Encapsulate(mlkem768, oPublicKey, oCiphertext, oSecret);

  // the recipient, who holds the private key, gets the same 32 bytes
  oSame := sgcMLKEM_Decapsulate(mlkem768, oPrivateKey, oCiphertext);
end;

Les jeux de paramètres sont mlkem512, mlkem768 et mlkem1024. Les contrôles d'entrée exigés par FIPS 203 s'exécutent avant tout le reste, de sorte qu'une clé publique malformée lève une exception au lieu de produire silencieusement un secret, et qu'un texte chiffré qui ne se déchiffre pas produit un secret pseudo-aléatoire plutôt qu'une erreur, ce qui correspond au rejet implicite exigé par la norme.

X-Wing, quand vous ne voulez pas choisir

Adopter un nouvel algorithme signifie lui faire confiance. Les constructions hybrides suppriment cette décision : elles combinent un algorithme classique avec un algorithme post-quantique, de sorte que le résultat reste sûr tant que l'une des deux moitiés tient. X-Wing associe X25519 à ML-KEM-768 et expose une seule paire de clés.

uses
  sgcCrypto_MLKEM_Hybrid;

var
  oPublicKey, oPrivateKey, oCiphertext, oSecret: TBytes;
begin
  sgcXWing_GenerateKeyPair(oPublicKey, oPrivateKey);
  sgcXWing_Encapsulate(oPublicKey, oCiphertext, oSecret);
  oSecret := sgcXWing_Decapsulate(oPrivateKey, oCiphertext);
end;

Signer avec ML-DSA

La signature prend le jeu de paramètres, la clé privée, le message et une chaîne de contexte, normalement vide :

uses
  sgcCrypto_MLDSA;

var
  oSeed, oPublicKey, oPrivateKey, oSignature: TBytes;
begin
  sgcMLDSA_GenerateKeyPairAndSeed(mldsa65, oSeed, oPublicKey, oPrivateKey);

  oSignature := sgcMLDSA_Sign(mldsa65, oPrivateKey, aMessage, nil);

  if sgcMLDSA_Verify(mldsa65, oPublicKey, aMessage, oSignature, nil) then
    ShowMessage('signature is valid');
end;

Remarquez la graine (seed). Une clé privée ML-DSA peut être stockée sous la forme des 32 octets à partir desquels elle a été générée, sous la forme de la clé étendue, ou sous les deux formes à la fois, et la bibliothèque lit et écrit ces trois formes. La graine est la forme à privilégier dans un fichier de configuration : elle est petite, et la clé étendue en est dérivée de manière déterministe.

Des clés qui voyagent

Un algorithme avec lequel personne ne peut échanger de clés n'a guère d'utilité, aussi les encodages suivent-ils les profils publiés : SubjectPublicKeyInfo et PKCS#8, en DER ou en PEM, selon la RFC 9935 pour ML-KEM, la RFC 9881 pour ML-DSA et la RFC 9909 pour SLH-DSA.

var
  vPublicPEM, vPrivatePEM: string;
begin
  vPublicPEM := sgcMLDSA_ExportPublicKeyPEM(mldsa65, oPublicKey);
  vPrivatePEM := sgcMLDSA_ExportPrivateKeyPEM(mldsa65, oSeed, nil);
end;

Ce sont des blocs BEGIN PUBLIC KEY et BEGIN PRIVATE KEY ordinaires, que d'autres implémentations savent lire. Une clé privée est vérifiée au moment de son import : la moitié publique est de nouveau dérivée puis comparée, de sorte qu'une clé qui n'appartient pas à son certificat est refusée sur-le-champ plutôt que de produire des signatures que personne ne peut vérifier.

Certificats et autorité de certification post-quantique

Les certificats et les demandes de certificat peuvent porter des clés post-quantiques, et une autorité de certification peut elle aussi en détenir une, de sorte qu'une chaîne entière peut être post-quantique :

var
  vKey: TsgcX509SignKey;
  vOptions: TsgcX509Options;
  oCertDER: TBytes;
begin
  vKey := sgcX509_MLDSAKey(mldsa65, oPrivateKey, oPublicKey);
  vOptions.Subject.CommonName := 'My Post-Quantum CA';
  vOptions.KeyUsage := [kuKeyCertSign, kuCRLSign];
  oCertDER := sgcX509_CreateSelfSignedEx(vKey, vOptions);
end;

Les règles d'usage de clé des profils sont vérifiées avant toute signature, de sorte qu'une clé post-quantique à qui l'on demande le chiffrement de clé lève une exception plutôt que de produire un certificat qui viole son propre profil. La validation de chaîne applique désormais aussi la longueur de chemin et les contraintes de nom déclarées par les certificats émetteurs.

Où c'est déjà branché

Deux endroits de la bibliothèque utilisent déjà tout cela pour vous, sans aucune cryptographie à écrire :

Comment nous savons que c'est correct

L'implémentation d'une norme n'est qu'une affirmation tant que rien ne la vérifie. Les trois algorithmes sont validés par rapport aux vecteurs de test connus du NIST, et ces vecteurs sont fournis avec la bibliothèque sous la forme d'un jeu de tests exécutable, et non d'une simple phrase dans une fiche technique. Le signataire ML-DSA est la variante déterministe, de sorte que la même clé et le même message donnent toujours la même signature, ce qui rend les exemples publiés dans les RFC reproductibles octet pour octet.

Lequel choisir

Pour l'échange de clés, utilisez un hybride : X-Wing seul, ou les groupes TLS hybrides si le trafic est du TLS. Vous ne sacrifiez presque rien et vous êtes couvert quelle que soit l'hypothèse qui se révélera fausse. Pour les signatures, ML-DSA-65 est le choix par défaut raisonnable, avec ML-DSA-44 quand la taille compte et ML-DSA-87 quand elle ne compte pas. Tournez-vous vers SLH-DSA quand la signature sera vérifiée dans de nombreuses années par quelque chose que vous ne pouvez pas mettre à jour, comme un firmware, et que la taille de signature reste acceptable.

Mise à niveau

Tout cela vit dans le pack sgcCrypto et n'ajoute aucune dépendance : pas d'OpenSSL, pas de DLL, rien à installer sur la machine qui exécute votre application. Le code existant reste inchangé tant que vous ne l'appelez pas.

À lire aussi

À regarder

Il y a une courte vidéo à ce sujet sur la chaîne eSeGeCe.

Des questions, des retours ou besoin d'aide pour migrer ? Contactez-nous — vous recevrez une réponse de la part des personnes qui ont écrit le code.