Skip to content

crypto: support derandomized ML-KEM encapsulation#64207

Open
dotCooCoo wants to merge 1 commit into
nodejs:mainfrom
dotCooCoo:crypto-mlkem-encapsulate-entropy
Open

crypto: support derandomized ML-KEM encapsulation#64207
dotCooCoo wants to merge 1 commit into
nodejs:mainfrom
dotCooCoo:crypto-mlkem-encapsulate-entropy

Conversation

@dotCooCoo

Copy link
Copy Markdown

Summary

Adds an entropy option to crypto.encapsulate() for derandomized (deterministic)
ML-KEM encapsulation, mapping to OpenSSL 3.5's OSSL_KEM_PARAM_IKME (FIPS 203 §6.2
Encaps_internal). The same entropy + public key + algorithm deterministically produce
the same ciphertext and sharedKey — needed for known-answer testing and for hybrid
KEMs such as X-Wing (draft-connolly-cfrg-xwing-kem), which are defined in terms of
ML-KEM's internal encapsulation.

The existing randomized encapsulate(key) / encapsulate(key, callback) forms are
unchanged; the new shape is encapsulate(key, { entropy }). The 32-byte buffer is threaded
through KEMEncapsulateJobncrypto::KEM::Encapsulate and set on the EVP_PKEY_CTX
before EVP_PKEY_encapsulate, gated on OpenSSL >= 3.5 (OPENSSL_WITH_KEM_IKME). The binding
enforces an exactly-32-byte length and is ML-KEM-only; the randomized path is byte-identical
to today (an absent/undefined entropy takes the existing CSPRNG path).

Docs and tests are included (determinism, decapsulate round-trip, both length bounds,
empty/undefined handling, and type rejection). Built and tested locally on Windows with
OpenSSL 3.5.7; test/parallel/test-crypto-encap-decap.js passes.

Fixes: #64206

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/crypto
  • @nodejs/security-wg

@nodejs-github-bot nodejs-github-bot added lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Jun 30, 2026
Add an `entropy` option to `crypto.encapsulate()` that injects the 32-byte
message m as OSSL_KEM_PARAM_IKME, selecting FIPS 203 (6.2) Encaps_internal
derandomized encapsulation. The same entropy, public key, and algorithm
then deterministically produce the same ciphertext and shared key, which
is required for known-answer testing and for protocols such as X-Wing.

The existing randomized `encapsulate(key)` and `encapsulate(key, callback)`
forms are unchanged; the new shape is `encapsulate(key, { entropy })`. The
buffer is threaded through KEMEncapsulateJob into ncrypto::KEM::Encapsulate
and set on the EVP_PKEY_CTX before EVP_PKEY_encapsulate. It is gated on
OpenSSL >= 3.5 (OPENSSL_WITH_KEM_IKME) and is not supported for RSA, EC,
X25519, or X448 keys. The binding rejects an entropy buffer that is not
exactly 32 bytes.

Fixes: nodejs#64206
Signed-off-by: dotCooCoo <robertleelw@gmail.com>
@dotCooCoo dotCooCoo force-pushed the crypto-mlkem-encapsulate-entropy branch from 00a3ccd to 69e5174 Compare June 30, 2026 03:36

@panva panva left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://docs.openssl.org/3.6/man7/EVP_KEM-ML-KEM/#ml-kem-kem-parameters
This parameter should not be used for purposes other than testing.

https://www.ietf.org/archive/id/draft-connolly-cfrg-xwing-kem-10.html#name-derandomized
For testing, it is convenient to have a deterministic version of encapsulation. An X-Wing implementation MAY provide the following derandomized function.

This is distinctly only for test vector validation, as such I don't think it's worth the churn in node:crypto. Hybrid KEMs do not make use of it other than vector testing either.

@dotCooCoo

Copy link
Copy Markdown
Author

That's a fair point @panva. I wasn't thinking of this as a production feature so much as exposing a testing primitive that OpenSSL already provides.

The main use case I had in mind was deterministic vector validation and interoperability/regression testing. Today that requires dropping down to OpenSSL or another implementation, even though the underlying capability already exists.

@panva

panva commented Jul 1, 2026

Copy link
Copy Markdown
Member

I don't think we should be adding an option which we'd need to document effectively as "this is a footgun, don't use it".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

crypto: support derandomized ML-KEM encapsulation in crypto.encapsulate()

3 participants