liboqs & Open Quantum Safe
liboqs is the C library at the heart of the Open Quantum Safe (OQS) project — a single, uniform API over post-quantum KEMs and signatures, with language wrappers for Python, Go, Rust, C++, and Java. OQS is now a project of the Linux Foundation's Post-Quantum Cryptography Alliance (PQCA), and liboqs is the fastest way to experiment with every NIST candidate (current release: 0.15.0, Nov 2025).
What OQS gives you
- liboqs — the core C library exposing KEMs (ML-KEM, Classic McEliece, HQC, BIKE, FrodoKEM, NTRU, NTRU-Prime) and signatures (ML-DSA, SLH-DSA, Falcon, plus the NIST additional-signature on-ramp candidates CROSS, MAYO, SNOVA, UOV) behind two simple object types. Stateful hash signatures (LMS, XMSS) are also provided.
- Language wrappers —
liboqs-python,liboqs-go,liboqs-rust,liboqs-cpp, andliboqs-java. - oqs-provider — an OpenSSL 3 provider that plugs liboqs algorithms into the OpenSSL TLS/X.509 machinery (see OpenSSL).
- OQS forks — patched builds of OpenSSL and other tools for end-to-end demos.
Build liboqs from source
liboqs uses CMake. On a Debian/Ubuntu box:
sudo apt update
sudo apt install -y cmake gcc ninja-build libssl-dev python3-pip git
git clone --depth 1 https://github.com/open-quantum-safe/liboqs.git
cd liboqs
mkdir build && cd build
cmake -GNinja -DCMAKE_INSTALL_PREFIX=/usr/local ..
ninja
sudo ninja install
sudo ldconfig
Install the Python wrapper
The Python binding builds against (or downloads) liboqs automatically:
pip install liboqs-python
Then verify the install and list what your build enabled:
import oqs
print("liboqs version:", oqs.oqs_version())
print("enabled KEMs:", oqs.get_enabled_kem_mechanisms())
print("enabled sigs:", oqs.get_enabled_sig_mechanisms())
KEM example: ML-KEM-768
A full encapsulate/decapsulate round trip with the oqs.KeyEncapsulation object:
import oqs
kem_name = "ML-KEM-768"
with oqs.KeyEncapsulation(kem_name) as server:
public_key = server.generate_keypair()
# The client encapsulates against the server's public key.
with oqs.KeyEncapsulation(kem_name) as client:
ciphertext, shared_secret_client = client.encap_secret(public_key)
# The server recovers the identical shared secret.
shared_secret_server = server.decap_secret(ciphertext)
assert shared_secret_client == shared_secret_server
print("agreed on", len(shared_secret_client), "bytes") # 32
Teaching demo only — production code needs exception handling and secure handling of the secret key material. See Production hardening below.
Signature example: ML-DSA-65
Signing and verification use the oqs.Signature object:
import oqs
sig_name = "ML-DSA-65"
message = b"post-quantum signatures in production"
with oqs.Signature(sig_name) as signer:
public_key = signer.generate_keypair()
signature = signer.sign(message)
# Verification needs only the public key, so use a fresh object.
with oqs.Signature(sig_name) as verifier:
is_valid = verifier.verify(message, signature, public_key)
print("signature valid:", is_valid)
Enumerating and probing mechanisms
Because a given build may not enable every algorithm, gate your code on what is actually available:
import oqs
wanted = "Classic-McEliece-348864"
if wanted in oqs.get_enabled_kem_mechanisms():
with oqs.KeyEncapsulation(wanted) as kem:
details = kem.details
print(details["name"], "ek/ct bytes:",
details["length_public_key"],
details["length_ciphertext"])
else:
print(wanted, "is not enabled in this liboqs build")
The details dictionary exposes claimed NIST security level and the byte lengths of keys, ciphertexts, and signatures — handy for budgeting bandwidth before you commit to a scheme.
Production hardening
The round-trip examples above are deliberately minimal — they show the API, not a deployable pattern. Before shipping liboqs-backed code, add explicit error handling, treat the shared secret as live key material, and compare secrets in constant time:
import hmac
import oqs
kem_name = "ML-KEM-768"
try:
# Each `with` block frees the underlying C key material — including the
# secret key — deterministically when the object goes out of scope.
with oqs.KeyEncapsulation(kem_name) as server:
public_key = server.generate_keypair()
with oqs.KeyEncapsulation(kem_name) as client:
ciphertext, ss_client = client.encap_secret(public_key)
ss_server = server.decap_secret(ciphertext)
except oqs.MechanismNotSupportedError:
raise SystemExit(f"{kem_name} is not enabled in this liboqs build")
# Compare shared secrets in constant time — never with `==`.
if not hmac.compare_digest(ss_client, ss_server):
raise ValueError("KEM shared-secret mismatch")
# Use the shared secret only through a KDF bound to the protocol transcript,
# e.g. HKDF-SHA256 over ss_client together with the public key and ciphertext.
- Handle errors explicitly. An unavailable mechanism raises
oqs.MechanismNotSupportedError; catch it rather than failing deep inside a handshake. Derive your session key from the KEM output through a KDF over the full transcript, so a tampered ciphertext yields a different key instead of a silent success. - Treat the shared secret as key material. Feed it straight into an HKDF; never log, print, or persist it. Python
bytesare immutable and cannot be reliably zeroized, so keep them short-lived — and rely on thewithblock to free the C-side secret key promptly. - Compare in constant time. Use
hmac.compare_digestfor any secret or MAC comparison; a plain==can leak information through timing. - Mind side channels. Prefer constant-time implementations and validate the ones your build ships. Falcon / FN-DSA floating-point Gaussian sampling and naive Classic McEliece decoders are classic leak sources — avoid secret-dependent branches and memory access, and never roll your own sampler. See Side-channel resistance.
- Use a validated module in production. liboqs is not FIPS-validated. For the standardized algorithms prefer a CMVP/CAVP-validated module or OpenSSL 3.5 native support, and pin the standardized parameter sets and object identifiers so encodings stay interoperable.
- Seed the RNG properly. liboqs uses the operating-system CSPRNG by default; make sure that entropy source is present and well-seeded in your deployment — containers, early boot, and minimal images are common failure points.
Related
Library matrix →
Compare liboqs against the mainstream crypto stacks.
OpenSSL →
Wire liboqs into TLS via oqs-provider.
ML-KEM →
The KEM used in the example above.
ML-DSA →
The signature scheme used above.
Standards & references
- Open Quantum Safe — the OQS project that develops liboqs and its wrappers.
- liboqs (GitHub) — source, build instructions, and issue tracker.
- PQClean — reference implementations that liboqs draws from.
- Resources — full standards register
liboqs 与 Open Quantum Safe
liboqs 是 Open Quantum Safe OQS 项目的核心 C 库,用一套统一 API 封装后量子 KEM 与签名,并提供 Python、Go、Rust、C++ 与 Java 封装。OQS 现为 Linux 基金会后量子密码联盟 PQCA 旗下项目,liboqs 是体验各类 NIST 候选算法最快的途径。当前版本 0.15.0(2025 年 11 月)。
OQS 提供了什么
- liboqs——核心 C 库,通过两个简单的对象类型暴露 KEM(ML-KEM、Classic McEliece、HQC、BIKE、FrodoKEM、NTRU、NTRU-Prime)与签名(ML-DSA、SLH-DSA、Falcon,以及 NIST 追加签名补充征集候选 CROSS、MAYO、SNOVA、UOV),另提供有状态哈希签名 LMS、XMSS。
- 语言封装——
liboqs-python、liboqs-go、liboqs-rust、liboqs-cpp与liboqs-java。 - oqs-provider——把 liboqs 算法接入 OpenSSL 3 的 TLS 与 X.509 体系,参见 OpenSSL。
- OQS 分支——对 OpenSSL 等工具打补丁的构建,用于端到端演示。
从源码构建 liboqs
liboqs 使用 CMake,以下为 Debian/Ubuntu 上的步骤。
sudo apt update
sudo apt install -y cmake gcc ninja-build libssl-dev python3-pip git
git clone --depth 1 https://github.com/open-quantum-safe/liboqs.git
cd liboqs
mkdir build && cd build
cmake -GNinja -DCMAKE_INSTALL_PREFIX=/usr/local ..
ninja
sudo ninja install
sudo ldconfig
安装 Python 封装
Python 绑定会自动针对 liboqs 构建或下载。
pip install liboqs-python
随后验证安装,并列出当前构建启用的算法。
import oqs
print("liboqs version:", oqs.oqs_version())
print("enabled KEMs:", oqs.get_enabled_kem_mechanisms())
print("enabled sigs:", oqs.get_enabled_sig_mechanisms())
KEM 示例 ML-KEM-768
使用 oqs.KeyEncapsulation 对象完成完整的封装解封装往返。
import oqs
kem_name = "ML-KEM-768"
with oqs.KeyEncapsulation(kem_name) as server:
public_key = server.generate_keypair()
# 客户端针对服务端公钥进行封装
with oqs.KeyEncapsulation(kem_name) as client:
ciphertext, shared_secret_client = client.encap_secret(public_key)
# 服务端恢复出相同的共享密钥
shared_secret_server = server.decap_secret(ciphertext)
assert shared_secret_client == shared_secret_server
print("agreed on", len(shared_secret_client), "bytes") # 32
仅教学演示 生产环境需补充异常捕获与私钥内存安全清理逻辑 详见下方 生产环境加固。
签名示例 ML-DSA-65
签名与验证使用 oqs.Signature 对象。
import oqs
sig_name = "ML-DSA-65"
message = b"post-quantum signatures in production"
with oqs.Signature(sig_name) as signer:
public_key = signer.generate_keypair()
signature = signer.sign(message)
# 验证只需公钥 因此用新对象
with oqs.Signature(sig_name) as verifier:
is_valid = verifier.verify(message, signature, public_key)
print("signature valid:", is_valid)
枚举与探查算法
由于某次构建未必启用全部算法,代码应先判断算法是否真正可用。
import oqs
wanted = "Classic-McEliece-348864"
if wanted in oqs.get_enabled_kem_mechanisms():
with oqs.KeyEncapsulation(wanted) as kem:
details = kem.details
print(details["name"], "ek/ct bytes:",
details["length_public_key"],
details["length_ciphertext"])
else:
print(wanted, "is not enabled in this liboqs build")
details 字典给出算法声称的 NIST 安全级别 以及密钥 密文与签名的字节长度 便于在选定方案前预估带宽开销。
生产环境加固
上面的往返示例刻意做到最简,只为展示 API,并非可直接上线的写法。正式部署 liboqs 代码前,需补上显式错误处理,把共享密钥当作真正的密钥材料对待,并以常量时间比较密钥。
import hmac
import oqs
kem_name = "ML-KEM-768"
try:
# 每个 with 块在对象离开作用域时 确定性释放底层 C 端密钥材料 含私钥
with oqs.KeyEncapsulation(kem_name) as server:
public_key = server.generate_keypair()
with oqs.KeyEncapsulation(kem_name) as client:
ciphertext, ss_client = client.encap_secret(public_key)
ss_server = server.decap_secret(ciphertext)
except oqs.MechanismNotSupportedError:
raise SystemExit(f"{kem_name} 未在当前 liboqs 构建中启用")
# 以常量时间比较共享密钥 切勿用 ==
if not hmac.compare_digest(ss_client, ss_server):
raise ValueError("KEM 共享密钥不一致")
# 共享密钥只经 KDF 使用 并绑定协议握手记录
# 例如对 ss_client 连同公钥与密文做 HKDF-SHA256
- 显式处理错误——算法不可用会抛出
oqs.MechanismNotSupportedError,应捕获,而不是在握手深处失败。会话密钥应由 KEM 输出经 KDF 对完整握手记录派生,这样被篡改的密文只会得到不同的密钥,而不会悄无声息地成功。 - 把共享密钥当密钥材料——直接喂给 HKDF,切勿打印、记录或落盘。Python
bytes不可变,无法可靠清零,因此要尽量缩短其生命周期,并依赖 with 块及时释放 C 端私钥。 - 常量时间比较——任何密钥或 MAC 比较都用
hmac.compare_digest,普通==可能通过时序泄露信息。 - 警惕侧信道——优先使用常量时间实现,并核验构建所带的实现。Falcon / FN-DSA 的浮点高斯采样以及朴素的 Classic McEliece 解码器都是经典泄露源,应避免依赖私钥的分支与内存访问,切勿自行实现采样器,参见 侧信道防护。
- 生产环境用已验证模块——liboqs 未通过 FIPS 验证,标准化算法在生产中应优先选择 CMVP/CAVP 已验证模块或 OpenSSL 3.5 原生支持,并固定标准化参数集与对象标识符,以保证编码可互操作。
- 正确播种随机数——liboqs 默认使用操作系统 CSPRNG,要确保部署环境中该熵源可用且充分播种,容器、早期启动与精简镜像都是常见隐患。
相关
开源库对照表 →
liboqs 与主流密码栈的比较
OpenSSL →
通过 oqs-provider 把 liboqs 接入 TLS
ML-KEM →
示例中使用的 KEM
ML-DSA →
示例中使用的签名方案
标准与参考
- Open Quantum Safe — 开发 liboqs 及其各语言封装的 OQS 项目
- liboqs (GitHub) — 源码 构建说明与问题追踪
- PQClean — liboqs 所引用的参考实现
- 资源链接 — 完整标准登记册