nqenc(3)
NAME
nqenc, nqencd,
nqdec, nqkkg,
nqkkgd, nqksize,
nqkname, nqkfind —
ML-KEM and HQC key encapsulation
LIBRARY
libnoqtis, -lnoqtis
SYNOPSIS
#include
<noqtis.h>
int
nqkkg(nqkalg alg,
uint8_t *pk, size_t pklen,
uint8_t *sk, size_t sklen);
int
nqkkgd(nqkalg alg,
const uint8_t *seed, size_t
seedlen, uint8_t *pk, size_t
pklen, uint8_t *sk, size_t
sklen);
int
nqenc(nqkalg alg,
const uint8_t *pk, size_t pklen,
uint8_t *ct, size_t ctlen,
uint8_t *ss, size_t sslen);
int
nqencd(nqkalg alg,
const uint8_t *pk, size_t pklen,
const uint8_t *seed, size_t
seedlen, uint8_t *ct, size_t
ctlen, uint8_t *ss, size_t
sslen);
int
nqdec(nqkalg alg,
const uint8_t *sk, size_t sklen,
const uint8_t *ct, size_t ctlen,
uint8_t *ss, size_t sslen);
int
nqksize(nqkalg
alg, nqksz
*sz);
const char *
nqkname(nqkalg
alg);
int
nqkfind(const
char *name, nqkalg
*alg);
DESCRIPTION
A key encapsulation mechanism lets anyone with a public key make a ciphertext and a 32-byte shared secret, which only the holder of the secret key can recover from the ciphertext. noqtis provides ML-KEM (FIPS 203) and HQC (version 5.0.0 of 2025-08-22). HQC is experimental: NIST selected it in March 2025 but its standard is not final, and the final one may differ in its encoding; keys, ciphertexts and secrets made now may then not interoperate. Use ML-KEM for anything that must last.
nqkalg names the parameter set:
| constant | name | pk | sk | ciphertext | ss |
NQMLKEM512 |
ML-KEM-512 | 800 | 1632 | 768 | 32 |
NQMLKEM768 |
ML-KEM-768 | 1184 | 2400 | 1088 | 32 |
NQMLKEM1024 |
ML-KEM-1024 | 1568 | 3168 | 1568 | 32 |
NQHQC128 |
HQC-128 | 2241 | 2321 | 4433 | 32 |
NQHQC192 |
HQC-192 | 4514 | 4602 | 8978 | 32 |
NQHQC256 |
HQC-256 | 7237 | 7333 | 14421 | 32 |
nqksize()
stores the sizes in *sz, an
nqksz with the members pk,
sk, ct and
ss.
nqkname()
returns the name, or NULL for an unknown value;
nqkfind()
finds the algorithm whose name is exactly name.
nqkkg()
makes a key pair from the operating system's random source.
nqkkgd() makes it from the standard's seed: 64 bytes
(d || z) for ML-KEM, 32 bytes for HQC.
nqenc()
encapsulates to the public key pk: it writes the
ciphertext to ct and the shared secret to
ss.
nqencd()
takes the encapsulation randomness from seed instead,
for test vectors: the 32-byte m for ML-KEM, and m || salt for HQC (32, 40 or
48 bytes for HQC-128, -192, -256). For ML-KEM the public key is checked as
FIPS 203 section 7.2 requires.
nqdec()
recovers the shared secret from ct with the secret key
sk; for ML-KEM the key is first checked as section 7.3
requires. A ciphertext of the right length that was not made for this key,
or was changed, gives a pseudorandom secret unrelated to the sender's and
returns NQOK (implicit rejection, as both standards
require). It fails only on malformed sizes, on a secret key whose embedded
hash does not match (NQEDEC), and, as ML-KEM
decapsulation is masked with fresh randomness, when the operating system's
random source fails (NQERNG). Protocols must
therefore confirm the secret, for example by using it in an authenticated
cipher.
Output buffers may be larger than the algorithm's
sizes; only the defined number of bytes is written. Keys and ciphertexts
given as input must have exactly the algorithm's sizes. The seed of
nqkkgd() may
overlap pk or sk; on any error
the outputs are cleared.
RETURN VALUES
Every function except nqkname() returns
NQOK (0) or a negative error code:
- [
NQEARG] - alg is not an nqkalg, a
pointer is
NULL, or a seed does not have the standard's length. - [
NQEBUF] - An output buffer is smaller than the algorithm's size.
- [
NQEDEC] - An input key or ciphertext does not have the algorithm's length, or a key fails the standard's input check.
- [
NQERNG] - The operating system's random source failed
(
nqkkg(),nqenc(), andnqdec() for ML-KEM, whose masked decapsulation draws fresh mask shares). - [
NQENOSUP] nqkfind(): no algorithm has that name.
On any failure, the output buffers are zeroed.
EXAMPLES
The program docs/examples/kem.c of the source tree, with the sizes taken at run time:
nqksz z; uint8_t *pk, *sk, *ct, a[32], b[32]; int rc = nqksize(NQMLKEM768, &z), same = 0; if (rc != NQOK || z.ss != sizeof a) return 2; pk = malloc(z.pk); sk = malloc(z.sk); ct = malloc(z.ct); if (pk == NULL || sk == NULL || ct == NULL) return 2; rc = nqkkg(NQMLKEM768, pk, z.pk, sk, z.sk); if (rc == NQOK) rc = nqenc(NQMLKEM768, pk, z.pk, ct, z.ct, a, sizeof a); if (rc == NQOK) rc = nqdec(NQMLKEM768, sk, z.sk, ct, z.ct, b, sizeof b); if (rc == NQOK) same = nqcteq(a, b, sizeof a); nqwipe(sk, z.sk); nqwipe(a, sizeof a); nqwipe(b, sizeof b);
SEE ALSO
noqtis-dec(1), noqtis-enc(1), noqtis(3), nqcteq(3), nqwipe(3)
STANDARDS
FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard, NIST, August 2024. Hamming Quasi-Cyclic (HQC), version 5.0.0, August 2025.
SECURITY CONSIDERATIONS
Key generation, encapsulation and decapsulation run in constant time with respect to the seeds, the randomness, the secret key and the shared secret; ML-KEM decapsulation is also first-order masked. In HQC, a secret-derived bit becomes public in the three places its specification allows, and nowhere else.