Color scheme: Riviera
You are here: start » en » Documentation » Post-Quantum Cryptography » WKL.System.Security.Cryptography overview » How-to guides » Agree on a hybrid key

Agree on a hybrid key

Two sides of your own protocol need to agree on a shared key, and that key must stay secure as long as either ECDH or ML-KEM is secure.

Solution overview

ElementPurpose
ECDiffieHellman.Create(ECCurve.NamedCurves.nistP384)the recipient's classical key pair, from .NET
MLKemKey.Generate(MLKemParameterSet.MLKem768)the recipient's ML-KEM key pair
HybridKeyAgreement.Encapsulatesender: derives the key and returns what the recipient needs to derive it too
HybridKeyAgreement.Decapsulaterecipient: derives the same key
HybridEncapsulationthe sender's result: SharedKey, EphemeralPublicKey, MLKemCiphertext

By default, both methods combine the secrets with KeyCombine as defined in NIST SP 800-227, as BSI TR-02102-1 recommends; see Post-quantum algorithm policy.

Implementation

The recipient generates its keys and passes the public parts to the sender:

using var recipientEcdh = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP384);
using var recipientKem = MLKemKey.Generate(MLKemParameterSet.MLKem768);
 
ECDiffieHellmanPublicKey ecdhPublic = recipientEcdh.PublicKey;
byte[] kemPublic = recipientKem.ExportEncapsulationKey();

The sender derives the key and sends EphemeralPublicKey and MLKemCiphertext to the recipient:

byte[] info = Encoding.UTF8.GetBytes("example.com file transfer v1");
 
using var kemForSender = MLKemKey.ImportEncapsulationKey(MLKemParameterSet.MLKem768, kemPublic);
var sent = HybridKeyAgreement.Encapsulate(ecdhPublic, kemForSender, info);
 
byte[] message = SymmetricEncryptionExtensions.EncryptAesGcm(
    Encoding.UTF8.GetBytes("Hello"), sent.SharedKey);

The recipient derives the same key and decrypts:

byte[] key = HybridKeyAgreement.Decapsulate(
    recipientEcdh, recipientKem, sent.EphemeralPublicKey, sent.MLKemCiphertext, info);
 
Console.WriteLine(Encoding.UTF8.GetString(SymmetricEncryptionExtensions.DecryptAesGcm(message, key)));

The samples need using System.Security.Cryptography;, using System.Text;, using WKL.System.Security.Cryptography;, using WKL.System.Security.Cryptography.KeyDerivation;, and using WKL.System.Security.Cryptography.Encryption;.

Remarks

  • Same values on both sides. info, keyLength, and combiner must match on both sides. A different value doesn't cause an exception; it produces a different key. Decryption then fails the AES-GCM authentication check.
  • Tampered data. A modified ciphertext or someone else's ephemeral key also produce a different key. That's by design: ML-KEM doesn't reject invalid ciphertexts with an error, and the protocol on top detects the tampering.
  • Parameter sets. ML-KEM-768 and ML-KEM-1024 are allowed without an exemption. With ML-KEM-512, Encapsulate throws a CryptographicException as long as PostQuantumExemptions.ReducedParameterSets isn't set.
  • Peers on an earlier version. If the peer runs a version of the library with the first combiner, pass combiner: HybridKeyCombiner.Version1 to both methods. The recipient doesn't need an exemption for that and raises the PolicyDeviation event. The sender needs PostQuantumExemptions.NonRecommendedCombiners.
  • Your own protocols with other algorithms. For a pairing other than ECDH and ML-KEM, KeyCombiner.Combine combines two secrets with their ciphertexts and public keys according to KeyCombine.
en/wiki/pqcrypt/lib/how-to/agree-hybrid-keys.txt · Last modified: by Wolfgang van der Stille