If you have followed post-quantum cryptography at all over the past several years, you have probably encountered the name liboqs. It is the C library at the heart of the Open Quantum Safe project, and it has become the reference implementation that many other tools and libraries pull from. liboqs is not a finished product in itself. It is a research and prototyping library: a collection of post-quantum algorithm implementations packaged in a unified API so that researchers, library authors, and integrators can experiment with PQC without writing every algorithm from scratch. Over the past decade, liboqs has provided implementations of every major PQC candidate that NIST has considered, including the eventual finalists ML-KEM, ML-DSA, FN-DSA (Falcon), and SLH-DSA. This post is a developer-oriented tour of liboqs: what it is, how it is structured, what algorithms it includes, and how to think about it when integrating PQC into a real product.
What liboqs Is and Is Not
liboqs is a C library that exposes a unified API for post-quantum key encapsulation mechanisms (KEMs) and digital signature algorithms (DSAs). It bundles in a single source tree the upstream reference implementations or vetted derivatives of every major NIST PQC candidate. The build system (CMake-based) compiles the algorithms into a single shared library or static library that consumers can link against.
The "is not" part is important. liboqs is explicitly described by its maintainers as a research library, not a production-grade cryptographic library suitable for high-assurance deployments. The README and documentation make this clear: liboqs is intended as a testing ground and a baseline, with production users expected to either fork specific algorithms into hardened libraries or use upstream-vetted production libraries that depend on liboqs as one input among many.
The reason for this caveat is that liboqs prioritizes coverage over hardening. The library contains implementations from many different upstream sources, with varying levels of constant-time guarantees, side-channel resistance, and code quality. Some implementations are heavily optimized assembly. Others are clean reference C code. Some are from academic researchers. Others are from vendor submissions. The library tries to integrate them all into a single API, which is useful for testing but not a substitute for careful per-algorithm hardening.
Repository Structure
The liboqs repository (https://github.com/open-quantum-safe/liboqs) is organized into several top-level directories. The src/ directory contains the algorithm implementations, organized by KEM or signature category. Inside src/kem/ you find subdirectories like ml_kem/, kyber/, hqc/, ntru/, and others. Inside src/sig/ you find ml_dsa/, dilithium/, falcon/, sphincs/, and so on. Each algorithm directory contains the upstream sources, typically with light modifications to fit the liboqs API.
The include/ directory contains the public header files that consumers use. The main header oqs/oqs.h defines the OQS_KEM and OQS_SIG types, the OQS_KEM_keypair, OQS_KEM_encaps, and OQS_KEM_decaps functions, and the corresponding signature functions. The API is uniform across algorithms, which is one of the main values liboqs provides.
The tests/ directory contains correctness tests, KAT (Known Answer Test) verification, and basic performance benchmarks. Running the tests after a build provides a baseline check that the algorithms produce expected outputs against the NIST KAT vectors.
The docs/ directory contains build instructions, algorithm documentation, and integration notes.
API Walkthrough
The liboqs KEM API looks like this in C:
```c OQS_KEM kem = OQS_KEM_new(OQS_KEM_alg_ml_kem_768); uint8_t public_key = malloc(kem->length_public_key); uint8_t *secret_key = malloc(kem->length_secret_key); OQS_KEM_keypair(kem, public_key, secret_key);
uint8_t ciphertext = malloc(kem->length_ciphertext); uint8_t shared_secret = malloc(kem->length_shared_secret); OQS_KEM_encaps(kem, ciphertext, shared_secret, public_key);
uint8_t *recovered_secret = malloc(kem->length_shared_secret); OQS_KEM_decaps(kem, recovered_secret, ciphertext, secret_key);
OQS_KEM_free(kem); ```
This is straightforward. The unified API hides the details of which algorithm you are using behind the OQS_KEM struct, which provides the relevant key, ciphertext, and shared-secret lengths along with function pointers to the algorithm-specific implementations.
The signature API mirrors this structure with OQS_SIG and the corresponding sign/verify functions.
The algorithm identifier strings (OQS_KEM_alg_ml_kem_768 etc.) are macros that map to the specific implementation. liboqs provides identifiers for ML-KEM-512, ML-KEM-768, and ML-KEM-1024 (the FIPS 203 standard variants), as well as the older Kyber-512/768/1024 round-3 variants for backward compatibility.
Algorithm Coverage
As of 2026, liboqs includes implementations of the following PQC algorithms:
- ML-KEM-512, ML-KEM-768, ML-KEM-1024 (FIPS 203 standardized). See ML-KEM Explained for the algorithm.
- Kyber-512, Kyber-768, Kyber-1024 (NIST round-3 variants, kept for backward compatibility).
- HQC-128, HQC-192, HQC-256 (NIST round-4 candidate, code-based).
- ML-DSA-44, ML-DSA-65, ML-DSA-87 (FIPS 204 standardized). See ML-DSA vs SLH-DSA.
- Dilithium-2, Dilithium-3, Dilithium-5 (round-3 variants).
- Falcon-512, Falcon-1024 (FN-DSA, FIPS 206 standardized).
- SPHINCS+ / SLH-DSA in multiple parameter sets (FIPS 205 standardized).
The library also includes some experimental algorithms that did not make NIST's standardization list, such as FrodoKEM and various code-based and isogeny-based candidates. These are useful for research but should not be deployed in production.
How liboqs Compares to Production Libraries
For production-grade PQC, several options exist that depend on or learn from liboqs but are themselves more hardened:
- OpenSSL: The 3.4 release added native ML-KEM support. OpenSSL 3.5 (planned) extends to ML-DSA. These are built directly in OpenSSL with attention to constant-time operation, side-channel resistance, and FIPS 140-3 validation.
- wolfSSL: Embedded TLS library with PQC support. See wolfSSL Post-Quantum for the current state.
- Botan: General-purpose cryptography library with ML-KEM and ML-DSA in production-quality form.
- rustcrypto: The Rust ecosystem has separate crates (ml-kem, ml-dsa) that are pure-Rust implementations with constant-time guarantees suitable for production.
QNSQY uses Rust-native PQC implementations from the rustcrypto and pqcrypto-* ecosystems, not liboqs directly. Rust-native crates avoid the FFI overhead of calling into a C library and provide stronger memory-safety guarantees by virtue of the Rust language.
Build and Integration
Building liboqs is straightforward on modern Linux:
`` git clone https://github.com/open-quantum-safe/liboqs cd liboqs mkdir build && cd build cmake -GNinja .. ninja ninja run_tests sudo ninja install ``
The CMake build supports several options to control which algorithms are included, whether to use AVX2 or other optimized variants, and whether to enable specific debugging flags. For embedded targets, cross-compilation is supported with the standard CMake toolchain mechanism.
Integration into a C/C++ application is then a matter of including oqs/oqs.h, linking against -loqs, and using the OQS_KEM and OQS_SIG APIs. For applications in higher-level languages, FFI bindings exist for Python (liboqs-python), Java (liboqs-java), Go (liboqs-go), and others.
OQS-OpenSSL and OQS-BoringSSL
The Open Quantum Safe project also maintains forks of OpenSSL and BoringSSL with PQC support. These were the primary way that researchers experimented with PQC in TLS handshakes before mainline OpenSSL added native support. The OQS-OpenSSL fork provides a TLS 1.3 implementation that can negotiate hybrid PQC+ECDH key exchanges and PQC certificate signatures.
These forks are still useful for research and for bridging to environments where the latest mainline OpenSSL is not available. They are not recommended for production use, since the fork is necessarily behind mainline OpenSSL on security patches and other improvements.
Performance Characteristics
liboqs ships both reference implementations (clean C, portable, slower) and optimized implementations (often AVX2 or AVX-512 assembly) for x86-64 platforms. Performance varies significantly by algorithm and parameter set.
For ML-KEM-768 on a modern x86-64 CPU with AVX2:
- Key generation: ~30 microseconds
- Encapsulation: ~40 microseconds
- Decapsulation: ~50 microseconds
For ML-DSA-65 on the same CPU:
- Key generation: ~150 microseconds
- Signing: ~500 microseconds
- Verification: ~150 microseconds
These are substantially faster than RSA-3072 (which takes milliseconds for signing and key generation) and competitive with classical ECDH (X25519) and ECDSA. The myth that PQC is slow is largely outdated; modern lattice-based PQC is fast on commodity hardware.
For embedded platforms without AVX2 (ARM Cortex-M, RISC-V), the reference C implementations are slower but still feasible for typical uses. liboqs has accepted contributions of NEON-optimized implementations for ARM, which improve performance significantly on phones and embedded hardware.
The Hardening Question
A fair criticism of liboqs is that the implementations vary in how carefully they handle side channels. Some algorithm implementations have undergone external audit (notably the AVX2 ML-KEM and ML-DSA derived from official NIST submissions). Others have less external review.
For high-assurance use, the recommendation is to use a production-grade library that has invested in hardening for the specific algorithms you need. OpenSSL, wolfSSL, Botan, and the rustcrypto crates have all done significant hardening work for the FIPS-standardized algorithms (ML-KEM, ML-DSA, FN-DSA, SLH-DSA). For algorithms still in research (HQC, the round-4 candidates), liboqs is often the best you have.
How QNSQY Relates to liboqs
QNSQY does not link liboqs directly. The Rust-native PQC crates (ml-kem, ml-dsa, pqcrypto-falcon, pqcrypto-hqc) are independent implementations that share design lineage with liboqs but are written in Rust with the language's memory-safety guarantees. The crates have their own constant-time implementations and are tested against NIST KAT vectors.
The hybrid construction in QNSQY combines these post-quantum primitives with classical X25519 and Ed25519 from the curve25519-dalek and ed25519-dalek crates. See Hybrid Encryption for the layered design that ensures even a flaw in one primitive does not break the whole system.
For the file format and AEAD encryption, QNSQY uses the AES-256-GCM implementation from RustCrypto's aes-gcm crate. See AES-256-GCM Explained for the algorithm.
When to Use liboqs
liboqs is the right tool when you are:
- Researching PQC algorithms that are not in mainstream libraries yet.
- Prototyping a PQC integration before committing to a production library.
- Implementing a custom protocol where you need maximum algorithm flexibility.
- Writing test harnesses and KAT verifiers.
- Comparing performance across multiple PQC candidates.
- Building research demonstrations or academic papers.
liboqs is not the right tool when you are:
- Shipping a production cryptographic product to end users.
- Integrating PQC into an existing TLS stack that has its own PQC support.
- Working in a regulated environment that requires FIPS validation (use a FIPS-validated library instead).
- Building memory-safe code in a managed-memory language (use language-native bindings or pure-language alternatives).
How the Test Vectors Work
A useful aspect of liboqs is its consistent KAT (Known Answer Test) infrastructure. Each algorithm directory contains test vectors derived from the original NIST submission package (or, for FIPS-standardized algorithms, from the corresponding FIPS publication's reference implementation). The tests/ directory has a unified harness that loads the KAT files and verifies that the algorithm implementations produce expected outputs.
This is more important than it sounds. Cryptographic algorithms are notoriously prone to subtle implementation bugs that pass naive testing but break under specific edge cases. KAT vectors codify the expected behavior at the byte level, so any deviation flags a bug. liboqs's continuous-integration system runs the KAT tests on every commit, against multiple compilers and architectures.
For consumers integrating PQC into their own products, the liboqs KAT vectors are a useful resource even if you are not using liboqs directly. You can extract the vectors and use them to test your own implementation, ensuring byte-for-byte interoperability with the broader ecosystem.
FAQ
Is liboqs FIPS validated? No. liboqs is a research library and is not on the FIPS 140-3 validated module list. For FIPS-validated PQC, use a library that has gone through CMVP validation (which currently includes some commercial offerings; OpenSSL FIPS validation for PQC is in progress).
Can I use liboqs in commercial software? The liboqs license permits commercial use (it is MIT-licensed), but as discussed above, you should evaluate whether liboqs is the right choice for your specific assurance requirements. Many commercial products that started with liboqs eventually migrated to production-hardened alternatives.
How does liboqs handle FIPS 203/204/205 transitions? liboqs has updated its ML-KEM, ML-DSA, and SLH-DSA implementations to match the final FIPS standards (FIPS 203, 204, 205, all August 2024). The older Kyber, Dilithium, and SPHINCS+ identifiers remain available for backward compatibility, with documentation indicating they correspond to round-3 variants rather than the standardized algorithms.
Does liboqs include FN-DSA (Falcon)? Yes. Falcon-512 and Falcon-1024 are included. The implementations are the official NIST submission code, with the floating-point requirements that come with Falcon. NIST standardized FN-DSA in FIPS 206 with deterministic-only signing to avoid floating-point issues.
Should I use liboqs or rustcrypto for a Rust project? For Rust projects, the rustcrypto crates (ml-kem, ml-dsa) are typically the better choice because they avoid FFI and provide native Rust memory-safety guarantees. liboqs has Rust bindings (liboqs-rust) that work but add C-level dependencies and possible memory-safety issues at the FFI boundary.
Sources
- Open Quantum Safe project. liboqs source repository. https://github.com/open-quantum-safe/liboqs
- Stebila, D., and Mosca, M. "Post-quantum key exchange for the Internet and the Open Quantum Safe project." Selected Areas in Cryptography 2016.
- National Institute of Standards and Technology. "Module-Lattice-Based Key-Encapsulation Mechanism Standard." FIPS 203, August 2024. https://csrc.nist.gov/pubs/fips/203/final
- National Institute of Standards and Technology. "Module-Lattice-Based Digital Signature Standard." FIPS 204, August 2024. https://csrc.nist.gov/pubs/fips/204/final
- National Institute of Standards and Technology. "Stateless Hash-Based Digital Signature Standard." FIPS 205, August 2024. https://csrc.nist.gov/pubs/fips/205/final
- Open Quantum Safe project. "OQS-OpenSSL Provider documentation." https://github.com/open-quantum-safe/oqs-provider
Related Articles
- What Is Post-Quantum Cryptography
- ML-KEM Explained
- ML-DSA vs SLH-DSA
- Hybrid Encryption
- AES-256-GCM Explained
Protect Your Data Before Q-Day Arrives
QNSQY's NIST-standardized post-quantum encryption protects files against both current and quantum-era threats.