QianHeng乾珩 PQC Docs Hub量子文档 ✦ Ask AI✦ 问问文档 ⚐ Scan⚐ 扫一扫

SP 800-227 — KEM Usage Guidance

NIST Special Publication 800-227, "Recommendations for Key-Encapsulation Mechanisms" (final, Sept 2025), is guidance — not a new algorithm. It tells implementers how to use KEMs such as ML-KEM correctly: key derivation, combiners, and the pitfalls to avoid.

Why a usage document is needed

FIPS 203 specifies what ML-KEM computes; it does not fully specify how to wire a KEM into a real protocol. Most cryptographic failures in practice come not from broken algorithms but from misuse — feeding a shared secret directly into a cipher, mishandling decapsulation failures, or combining secrets insecurely. SP 800-227 closes that gap with normative guidance for the KEM ecosystem.

The KEM interface

A KEM exposes three operations:

OperationInputOutput
KeyGen(public key, private key)
Encapspublic key(ciphertext, shared secret)
Decapsprivate key, ciphertextshared secret

Correct KEM use: the rules

  1. Always run the shared secret through a KDF. The raw KEM shared secret (32 bytes for ML-KEM) is not a finished key. Derive your actual keys with an approved KDF (e.g., HKDF, KMAC) that also binds in context: the protocol identifier, the public key/ciphertext, and a transcript hash.
  2. Bind the public values into key derivation. Include the encapsulation key and ciphertext in the KDF input so the derived key is tied to this exact exchange (helps prevent reflection and key-reuse attacks).
  3. Respect implicit rejection. ML-KEM never signals decapsulation failure; on a bad ciphertext it returns a pseudorandom secret. Do not add your own failure branch that leaks whether decapsulation "succeeded" — that reintroduces a timing/oracle vulnerability the FO transform was designed to remove.
  4. Treat shared secrets as ephemeral. Do not log, cache, or reuse them; derive session keys and discard.

Combiners and hybrids

During the transition, most deployments run a hybrid: a classical KEM/DH (e.g., X25519) alongside ML-KEM. A KEM combiner merges the two shared secrets into one so that the result is secure if either component remains unbroken. SP 800-227 gives guidance on doing this safely:

  • Feed both shared secrets plus both ciphertexts/public keys into a single KDF call — do not XOR raw secrets naively.
  • Use a combiner with a security proof (e.g., concatenation-then-KDF with proper labeling).
  • Ensure the combiner is robust: a malicious or malformed component must not undermine the honest one.
# Teaching example: hybrid KEM combiner (bind context, then KDF)
import hashlib, hmac

def hkdf_extract(salt, ikm):
    return hmac.new(salt, ikm, hashlib.sha256).digest()

def hkdf_expand(prk, info, length=32):
    okm = b""
    t = b""
    counter = 1
    while len(okm) < length:
        t = hmac.new(prk, t + info + bytes([counter]), hashlib.sha256).digest()
        okm += t
        counter += 1
    return okm[:length]

def combine(ss_classical, ss_pqc, ct_classical, ct_pqc, context):
    ikm = ss_classical + ss_pqc
    salt = hashlib.sha256(ct_classical + ct_pqc + context).digest()
    prk = hkdf_extract(salt, ikm)
    return hkdf_expand(prk, b"hybrid-kem-v1 session-key", 32)
Note
This is a teaching example, not a substitute for the protocol spec. A real protocol should use the combiner and KDF structure that protocol already prescribes; this only illustrates the idea of "bind the context, then KDF."
Warning
Do not roll your own KEM construction or combiner. Subtle mistakes — skipping the KDF, reusing ephemeral keys, leaking decapsulation status, or XOR-combining secrets without binding the ciphertexts — can void the security proof entirely. Use a vetted library and follow SP 800-227 to the letter.

Standards & references

Note
SP 800-227 is now final (Sept 2025), but encodings and combiner choices still vary across protocols. Treat KDF labels, combiner constructions, and context binding as configurable so you can align with evolving IETF profiles without protocol surgery. Build crypto-agility in from the start.

SP 800-227 KEM 使用指引

NIST 特别出版物 800-227《密钥封装机制建议》(2025 年 9 月正式发布)是一份使用指引,而非新算法。它告诉实现者如何正确使用 ML-KEM 等 KEM,涵盖密钥派生、组合器以及应当规避的各类陷阱。

为何需要一份使用文档

FIPS 203 规定了 ML-KEM 算什么,却没有完整规定如何把 KEM 接入真实协议。实践中,多数密码故障并非源于算法被攻破,而是源于误用:把共享密钥直接喂给密码算法、错误处理解封装失败,或以不安全的方式组合密钥。SP 800-227 正是以面向 KEM 生态的规范性指引填补了这一空缺。

KEM 接口

KEM 暴露三项操作

操作输入输出
KeyGen公钥私钥
Encaps公钥密文共享密钥
Decaps私钥密文共享密钥

正确使用 KEM 的规则

  1. 始终让共享密钥经过 KDF。原始 KEM 共享密钥(ML-KEM 为 32 字节)并非成品密钥。请用经批准的 KDF(如 HKDF、KMAC)派生实际密钥,并绑定上下文——协议标识符、公钥/密文以及握手记录哈希。
  2. 把公开值绑入密钥派生。将封装密钥与密文一并纳入 KDF 输入,使派生密钥与本次交换严格绑定,有助于防范反射攻击与密钥重用攻击。
  3. 尊重隐式拒绝。ML-KEM 从不报告解封装失败,遇到坏密文时返回一个伪随机密钥。切勿自行添加泄露解封装成败的失败分支,那会重新引入 FO 变换本欲消除的时序或预言机漏洞。
  4. 把共享密钥视为临时值。不要记录、缓存或重用;派生出会话密钥后即应丢弃。

组合器与混合

过渡期内,多数部署采用混合模式,即让经典 KEM/DH(如 X25519)与 ML-KEM 并行。KEM 组合器把两个共享密钥合并为一个,使得只要任一组件未被攻破,结果就仍然安全。SP 800-227 给出了安全做法的指引:

  • 两个共享密钥连同两份密文/公钥一并喂入单次 KDF 调用,切勿朴素地把原始密钥异或了事。
  • 使用带安全证明的组合器,例如“先连接后 KDF”,并正确加标签。
  • 确保组合器具备稳健性:恶意或畸形的组件不得削弱诚实组件的安全。
# 教学示例 混合 KEM 组合器 先绑定上下文 再 KDF
import hashlib, hmac

def hkdf_extract(salt, ikm):
    return hmac.new(salt, ikm, hashlib.sha256).digest()

def hkdf_expand(prk, info, length=32):
    okm = b""
    t = b""
    counter = 1
    while len(okm) < length:
        t = hmac.new(prk, t + info + bytes([counter]), hashlib.sha256).digest()
        okm += t
        counter += 1
    return okm[:length]

def combine(ss_classical, ss_pqc, ct_classical, ct_pqc, context):
    ikm = ss_classical + ss_pqc
    salt = hashlib.sha256(ct_classical + ct_pqc + context).digest()
    prk = hkdf_extract(salt, ikm)
    return hkdf_expand(prk, b"hybrid-kem-v1 session-key", 32)
注意
本例仅为教学示例,并不能替代协议规范。真实协议应使用该协议已规定的组合器与 KDF 结构;本例仅展示“绑定上下文后 KDF”的思想。
警告
切勿自行设计 KEM 构造或组合器。哪怕是细微的错误——跳过 KDF、重用临时密钥、泄露解封装状态,或在不绑定密文的情况下异或密钥——都可能彻底使安全证明失效。请使用经审查的库,并严格遵循 SP 800-227。

标准与参考

注意
SP 800-227 虽已于 2025 年 9 月正式发布,但具体的编码与组合器选择在各协议之间仍有差异。请把 KDF 标签、组合器构造与上下文绑定设计为可配置项,以便无需改动协议即可对齐持续演进的 IETF 配置。务必从一开始就内建密码敏捷性。
⚑ Report an error⚑ 纠错与校正