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

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).

These examples really run — not a simulation
The PQC code on this page (and across this guide) is genuinely runnable: liboqs is a real C cryptographic library, and ML-KEM, ML-DSA and the rest are real implementations of the NIST standards. Running them on your local CPU is the real post-quantum computation — not a simulation of one. Unlike quantum-computing hardware, PQC is ordinary software cryptography, so local execution is the genuine article. You just need liboqs plus the matching language wrapper installed, with the algorithm enabled in that build (see “Enumerate & probe algorithms” below).

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 wrappersliboqs-python, liboqs-go, liboqs-rust, liboqs-cpp, and liboqs-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 bytes are immutable and cannot be reliably zeroized, so keep them short-lived — and rely on the with block to free the C-side secret key promptly.
  • Compare in constant time. Use hmac.compare_digest for 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.
Note
liboqs is a research and aggregation library: it bundles algorithms at very different maturity levels and is not itself FIPS-validated. For the standardized FIPS algorithms in production, prefer a validated module or OpenSSL 3.5 native support; reach for liboqs when you need an experimental scheme, a uniform benchmarking harness, or rapid prototyping.

Related

Standards & references

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 月)。

这些代码是真实可运行的 并非模拟
本页(以及本指南全站)的 PQC 代码是真实可运行的——liboqs 是真实的 C 密码库 ML-KEM ML-DSA 等都是 NIST 标准算法的真实实现 在本地 CPU 上运行即为真实的后量子密码运算 而非对其的模拟。与量子计算硬件不同 PQC 属于普通软件密码学 本地运行就是它正式的运行方式 只需装好 liboqs 及对应语言封装 并确认该构建已启用所需算法(见下方 枚举与探查算法)。

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-pythonliboqs-goliboqs-rustliboqs-cppliboqs-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 是研究聚合型库 它打包了成熟度差异很大的算法 本身并未通过 FIPS 验证 生产环境要用标准化 FIPS 算法 应优先选择已验证模块或 OpenSSL 3.5 原生支持 需要实验性算法 统一基准测试或快速原型时再用 liboqs。

相关

标准与参考

⚑ Report an error⚑ 纠错与校正