A living document on how to juggle these damned things.
What’s a KEM?
A KEM is a Key Encapsulation Mechanism, a cryptographic primitive that has a KeyGen()
algorithm, an Encaps()
algorithm, and a Decaps()
algorithm. KeyGen
produces a
(decaps_key, encaps_key)
keypair. Encaps(encaps_key)
uses an encapsulation key (similar to
an asymmetric public key) to compute and return a shared secret and the encrypted ciphertext
encapsulation of that shared secret (often called ct
or enc
). Decaps(ct, decaps_key)
takes
the encapsulated shared secret ciphertext and the decapsulation key (similar to an asymmetric
secret key) to re-construct the shared secret.
KEM diagram from FIPS 203
In general, cryptographic primitives are designed to fulfill a particular security property
when instantiated correctly, and that property is defined as being able to beat an adversary
modeled with certain abilities within a security game. For digital signatures, there is usually
an unforgeability property under chosen message attack (EUF-CMA
); for KEMs, the property is
indistinguishability under chosen ciphertext attack (IND-CCA
). Informally this means that the
computed shared secret is indistinguishable from random against an adversary who can decapsulate
maliciously constructed ciphertexts, but who doesn’t have direct access to the decapsulation
key.
IND-CCA experiment for KEMs, from “Keeping Up with the KEMs”
KEMs have become ‘all the rage’ recently (not because they are sexy or special, they just seem to be the best we’ve got in a post-quantum world). In the shadow of a cryptographically-relevant quantum computer that can break our Diffie-Hellman’s and elliptic curve-based constructions in ~polynomial time, including stuff that’s been hoovered up and stored for a later day when those quantum computers come online (Store and Decrypt Later), there’s been movement to migrate cryptography protocols off of Diffie-Hellman onto primitives like KEMs, because they enable things like key establishment, but base their security on quantum-resistant problems such as Module Learning-with-Errors.
However. KEMs are not DH, and protocols that were originally designed based on the properties
(formal and informal) of Diffie-Hellman are now trying to swap them out for KEMs or hybridize
them with KEMs. KEMs generally are designed to fulfill IND-CCA
and IND-CCA
only, and some
designs have been changed to simplify and make clearer IND-CCA
security proofs and to
improve performance, but that also resulted in the removal of some of the other security properties
that make them trickier to just use without caveats.
Re-encapsulation attacks
As coined by Cremers et al and explored by the Cryspen
team, re-encapsulation attacks exploit the fact that some KEMs allow the
decapsulation of a ciphertext to an output key k and then can produce a second ciphertext for a
different public key that decapsulates to the same k. Re-encapsulation attacks in protocols,
often known as an unknown-key-share attack, result in two parties computing the same key despite
disagreeing on their respective partners. This doesn’t sound great, but is completely allowed
under the IND-CCA
security definition! To avoid these sorts of attacks, we need to know more
about a KEM than just that it’s an IND-CCA
-secure KEM: we need to know how it binds its shared
secrets to encapsulation keys and ciphertexts. This is a thing we didn’t need to think about
with Diffie-Hellman, as it was only in weird cases (small-order points, ‘non-contributory
behavior’, etc) that Diffie-Hellman public keys were not tightly
bound to the shared secret resulting from the computation, and there was no ciphertext to juggle
either.
Binding properties
Cremers, Dax, and Medinger formalized these notions of ‘binding’ properties for KEMs and explored their relations to similar notions like ‘robustness’ and ‘contributivity’ in the literature, including describing a spectrum of adversaries with different capabilities, how these notions relate (or imply) each other, and checked them via symbolic analysis with Tamarin. For a value to ‘bind’ another value, such as a shared secret K binding an encapsulation key PK, is to uniquely determine it; more formally, “P binds Q” if, for fixed instances of P, there are no collisions in the instances of Q.
The generic binding property instantiations X-BIND-P-Q before choosing X ∈ {HON , LEAK, MAL} from “Keeping Up with the KEMs”
There are different security models of adversaries for these properties: honest (HON), leak
(LEAK), and malicious (MAL). Honest variants involve everyone behaving themselves, such that the
involved key pairs are output by KeyGen()
and not accessible by the adversary, but the
adversary has access to a Decaps()
oracle. Leakage variants have honestly-generated key pairs
leaked to the adversary. Finally, in malicious variants, the adversary can create the key pairs
any way they like in addition to the key generation, so they can do…a lot. Ideally, our KEMs
would be resistant to such adversaries, so these MAL-BIND-P-Q
properties are attractive.
General hierarchy of binding properties for KEMs from “Keeping Up with the KEMs” (NB: implicit rejection KEMs have a restricted hierarchy)
Some of the big takeaways here are that we like MAL-BIND-K-CT
and MAL-BIND-K-PK
properties
in our KEMs:
-
A
MAL-BIND-K-PK
-secure KEM means that the KEM’s shared secret (K) binds to the encapsulation key (PK) used to compute it, even against a malicious adversary that has access to leaked key material and is capable of manufacturing maliciously-generated keypairs. This strong binding to the encapsulation key protects broadly against re-encapsulation attacks because different parties (as identified by their public key) cannot be coerced into deriving the same key. -
A
MAL-BIND-K-CT
-secure KEM means that the KEM’s shared secret (K) binds to the encapsulated shared secret ciphertext (CT), even against a malicious adversary that has access to leaked key material and is capable of manufacturing maliciously-generated keypairs. This strong binding to the ciphertext protects broadly against re-encapsulation attacks so that two different ciphertexts cannot decapsulate to the same shared secret K.
Implicit rejection
Digging even deeper, a lot of popular KEMs are implicitly rejecting, as a result of their
particular flavor of applying the Fujisaki-Okamoto (FO) transform. The FO transform takes a
public key encryption (PKE) scheme secure under chosen-plaintext attack (IND-CPA) and turns it
into an IND-CCA KEM scheme. Implicitly rejecting KEMs always return a value, even if the PKE
decryption step inside Decaps()
fails or returns an unexpected value. This design means that
Decaps()
always returns a pseudorandom value, even if decryption fails. This is hopefully
safer to use in higher-level protocols that may or may not check the decapsulation
value. Because of this implicit rejection design, there are fewer binding
properties available to such KEMs, period, and this generally eliminates their ciphertexts alone
binding to public keys or shared secrets: any ciphertext will be accepted, and these will almost
certainly decapsulate to different shared secrets. This means that implicitly rejecting KEMS
cannot be X-BIND-CT-K
-secure or X-BIND-CT-PK
-secure for any of the binding adversary
variants, and we are only possibly left with the subset of binding properties below.
Restricted hierarchy of binding properties for implicitly-rejecting KEMs from “Keeping Up with the KEMs”
ML-KEM
Updated September 2024
Per the analysis of the final standard FIPS 203 in Sophie Schmieg’s “Unbindable Kemmy
Schmidt”, a strictly compliant instantiation of ML-KEM is LEAK-BIND-K-PK
-secure
andLEAK-BIND-K-CT
-secure, but not MAL-BIND-K-PK
-secure nor MAL-BIND-K-CT
-secure
(although keep reading for caveats). This means that the computed shared secret binds to the
encapsulation key used to compute it against a malicious adversary that has access to leaked,
honestly-generated key material but is not capable of manufacturing maliciously generated
keypairs. This binding to the encapsulation key broadly protects against re-encapsulation attacks
but not completely.
This step down from MAL
security and vulnerability to the Kemmy Schmidt attacks
is due to the ‘expanded’ key format of ML-KEM as specified in FIPS 203: the K-PKE PK
hash is stored in the decapsulation key format instead of being re-computed from the K-PKE secret
key. A MAL
attacker can replace this K-PKE PK hash to trick the decapsulator into calculating the
same K for a different CT, therefore violating the MAL-BIND-K-CT
and MAL-BIND-K-PK
properties. A
MAL
attacker can also mess with the z
FO-transform rejection value that is cached in the
expanded decapsulation key format, which can be the same as the z
in another key pair, and when
the rejection value of K is returned, it will match another K for different keypair, violating the
MAL-BIND-K-PK
property.
Luckily, before the standard was finalized, NIST listened to feedback about some of these attacks
and officially supported storing ML-KEM keys as two seeds, the K-PKE seed d
, and the FO-transform
rejection value z
:
FIPS 203 ML-KEM seed format for keys
This is functionally one 64-byte value, but according to the standard, this is not a single 64-byte
sampled value, but two 32-byte sampled values that together make up the decapsulation key
seed. Unfortunately, implementing this format is not required, merely optional; therefore, one may
have a compliant instantiation of ML-KEM according to FIPS 203, which would be only
LEAK-BIND-K-CT
-secure, vs another compliant instantiation of ML-KEM, that only uses the (d, z)
decapsulation key seed format, which is MAL-BIND-K-CT
-secure. 🙃
Using ML-KEM keys in this manner in practice protects against the first Kemmy Schmidt attack above,
but it does not protect against the second. It would be nice to have a single 32-byte
seed that could be expanded in a compliant way into both d
and z
, but alas NIST said no. If that were allowed, it would have
mitigated the MAL-BIND-K-CT
and MAL-BIND-K-PK
Kemmy Schmidt attacks completely.
The (d, z)
seed format is a fully FIPS-compliant way to store and use ML-KEM, and I recommend
using it in practice and plan to do so myself. To get close to MAL-BIND-K-CT security with this
format, the keypair _MUST_be validated as well-formed via the ‘key pair check’ in section 7.1 - this
regenerates the expanded decapsulation key format and encapsulation key from the seed format and
checks that they match. This does have performance implications.
ML-KEM is also LEAK-BIND-K,PK-CT
-secure (ciphertext collision-resistant, CCR). This
means that the computed shared secret and the encapsulation key together bind the
ciphertext. Since ML-KEM is MAL-BIND-K-PK
-secure, given a shared secret K, we know it uniquely
binds to a PK, and because together K and PK uniquely bind to a CT, that means we get
LEAK-BIND-K-CT
for ML-KEM, but not MAL-BIND-K-CT
. This is formalized by corollary A.8 in
keeping-up-with-the-kems.
Once upon a time, there was Kyber before it was changed to not hash in the ciphertext anymore. Kyber,
as defined in version 3.02 of the NIST PQC submission, hashed in the ciphertext into
the derivation of the shared secret and thus was MAL-BIND-K-CT
as well as MAL-BIND-K-PK
. This
was removed as Kyber transformed into ML-KEM, ostensibly to get the speedup by avoiding hashing the
large ciphertext while also making the IND-CCA proofs of security simpler because that was the
security goal at the time. Alas.
ML-KEM (and Kyber and some others) are implicitly rejecting KEMs. In ML-KEM, the implicit rejection
value is derived from randomness (part of the decapsulation key) and the ciphertext (which is
ostensibly attacker-controlled). Yes, I am aware of the irony that the ciphertext is bound by the
implicit rejection ‘key’ but not the real shared secret. 💀 This means that ML-KEM cannot be
X-BIND-CT-K
-secure or X-BIND-CT-PK
-secure for any of the binding adversary variants, and we are
restricted to the smaller set of binding properties for implicitly rejecting KEMs.
ML-KEM.Decaps_internal from FIPS 203, demonstrating implicit rejection
So to sum up, straight-up ML-KEM is LEAK-BIND-K-PK
, LEAK-BIND-K,CT-PK
, LEAK-BIND-K,PK-CT
, and
LEAK-BIND-K-CT
-secure, but not any flavor of X-BIND-CT-K
or X-BIND-CT-PK
-secure. Using the
(d, z)
format of the decapsulation keys and validating keypairs mitigates attacks such that
MAL-BIND-K-CT
can be achieved…if you don’t fuck it up. 🫠
How do I hold it?
If I’m using ML-KEM in a protocol, in general, because it lacks MAL-BIND-K-CT
and MAL-BIND-K-PK
security, I would make sure to commit to the encapsulated shared secret ciphertext (CT) and
encapsulation key (PK) along with the decapsulated shared secret, either in the final protocol KDF
or something similar. TLS 1.3 commits to basically everything in the handshake
transcript as part of its key schedule, so they are already doing this ‘for free.’ Apple’s
PQ3 upgrade to iMessage includes the ML-KEM ciphertext and encapsulation key in the
label field of the HKDF-Expand()
call in its KDFRKCK
KDF function, along with a lot of other
transcript stuff. In general, if you can afford it, I would hash in / commit to all public data in
the protocol transcript, including the encapsulation keys - It can really save your ass.
Unless you write a security proof showing otherwise, the ML-KEM shared secret does not bind to the
ciphertext nor against adversaries that maliciously twiddle key material, so I would recommend
hashing it into your KDF or equivalent along with the shared secret. Otherwise, there is a burden of
proof to demonstrate security against MAL adversaries.
To get close to MAL-BIND-K-CT security with the compliant seed format, the keypair MUST_be validated as well-formed - I recommend it. You may be able to skip including the CT in your external KDF if you do. Since MAL-BIND-K-PK is not achievable in a compliant way, I _strongly recommend including the PK (encapsulation key) in your KDF.
Classic McEliece
Classic McEliece is another PQ-secure KEM that is built on code-based cryptography (instead of lattices, like ML-KEM or Kyber). It is a fourth-round candidate in NIST’s PQC KEM competition, a proposed ISO standard, and the general McEliece public-key encryption design has a long and robust history behind it.
Classic McEliece, as specified in the NIST submission, is
MAL-BIND-K-CT
-secure and MAL-BIND-PK,K-CT
-secure. This means you can use the shared secret
output without also committing to the encapsulated shared secret ciphertext. But that’s it. Classic
McEliece lacks any binding to the encapsulation key (PK) in any of the adversary security models,
as the underlying public key encryption scheme allows a ciphertext CT to decrypt to the plaintext
message under any decryption key, not just the decryption key associated
with the public encryption key used to generate the ciphertext. Since Classic McEliece derives the
KEM shared secret only from the ciphertext CT and the randomness (message), and the underlying PKE
allows us to generate the same shared secret K from multiple ciphertexts for any key pair, Classic
McEliece is not HON-BIND-K-PK or any other variant thereof.
Since Classic McEliece is also an implicitly rejecting KEM, it lacks the X-BIND-CT-PK and X-BIND-CT-K properties too.
Classic McEliece from Anonymous, Robust Post-Quantum Public Key Encryption
Therefore Classic McEliece is MAL-BIND-K-CT
-secure and MAL-BIND-PK,K-CT
-secure, but not any
flavor of X-BIND-CT-K
, X-BIND-CT-PK
, X-BIND-K-PK
, or X-BIND-K,CT-PK
-secure.
How do I hold it?
You MUST commit to the Classic McEliece public encapsulation key (PK) as well as the derived shared secret K in your KDF or equivalent. This may be quite annoying as Classic McEliece encapsulation keys are much larger than, say, ML-KEM encapsulation keys, and hashing them or including them as inputs to your KDF may be computationally expensive.
FrodoKEM
FrodoKEM is another implicitly rejecting PQ KEM based on lattices, but it relies on the learning-with-errors problem instead of the module-LWE problem (like ML-KEM and Kyber), and it uses a different flavor of FO-transform to produce a KEM from its underlying FrodoPKE public key encryption scheme. It was a NIST PQC candidate KEM and is a proposed ISO standard.
FrodoKEM hashes in its encapsulation key (PK) and its ciphertext (CT) into the final internal
hashing step that produces the final shared secret (K). Even without looking at the underlying PKE
scheme, we can see it has MAL-BIND-K-PK
, MAL-BIND-K,CT-PK
, MAL-BIND-K-CT
, and
MAL-BIND-PK,K-CT
security. Since it’s an implicitly rejecting KEM, it does not have any
X-BIND-CT-K
or X-BIND-CT-PK
binding properties. Easy-peasy.
FrodoKEM FO-transform IND-CCA construction
How do I hold it?
In terms of binding properties, FrodoKEM is pretty great. In general, I still would commit to all the public transcript data in my KDF, along with the shared secret produced by FrodoKEM, but not because the shared secret is not bound to the FrodoKEM encapsulation key or ciphertext but for other protocol security properties.
Updated Sep 18 2024 for FIPS 203 final and updated analysis of ML-KEM.