ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
mlkem.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4#ifndef PROTOCORE_MLKEM_H
5#define PROTOCORE_MLKEM_H
6
7#include "protocore_config.h" // the entry point: protocore_types.h for the widths
8
10
11/**
12 * @file mlkem.h
13 * @brief ML-KEM-768 (FIPS 203): KeyGen, Encaps (responder) and Decaps (initiator).
14 *
15 * The post-quantum half of the mlkem768x25519-sha256 (SSH) and X25519MLKEM768 (TLS 1.3) hybrid key
16 * exchanges. Both KEM roles are present:
17 * - responder (server terminating an inbound handshake): Encaps takes the peer's encapsulation key
18 * and produces (ciphertext, shared secret);
19 * - initiator (the device dialling out as an SSH/TLS *client*): KeyGen produces (ek, dk), the peer
20 * Encaps against ek, and Decaps recovers the shared secret from the returned ciphertext.
21 *
22 * Decaps carries the full constant-time Fujisaki-Okamoto transform (re-encrypt m' under the embedded
23 * ek and select the real key vs the implicit-reject key J(z || ct) under a constant-time ciphertext
24 * compare), so a malformed or tampered ciphertext yields a pseudorandom secret rather than leaking a
25 * decryption failure - FIPS 203 ยง6.3.
26 *
27 * KeyGen, Encaps and Decaps are the FIPS 203 "internal" (derandomized) forms: the caller supplies the
28 * randomness (KeyGen's (d, z), Encaps's message m), drawn from the platform RNG in production and
29 * fixed in known-answer tests. Deterministic given their inputs, which is exactly what the ACVP
30 * keyGen / encapDecap vectors pin.
31 *
32 * Arithmetic is a software NTT over q=3329 with Montgomery reduction (the twiddle factors are fixed
33 * constants premultiplied into Montgomery form, so each butterfly is two int16 multiplies and a
34 * shift - no division, and the hardware MPI, which targets RSA/DH-sized operands, would only add
35 * marshaling overhead). Zero heap; peak stack ~9 KB (Decaps, which re-encrypts).
36 *
37 * @ref MlKemNs::encaps runs the FIPS 203 modulus check on the peer key first: on a key whose decoded
38 * coefficients are not all < q it writes nothing and leaves @ref MlKemNs::ok false.
39 *
40 * @ref MlKemNs::decaps has no failure of its own: a malformed or tampered ciphertext selects
41 * J(z || ct) in constant time and the call still reports true.
42 *
43 * @c work is PROTOCORE_MLKEM_BORROW secure bytes the CALLER took, at an address it knows. It is not held past the call,
44 * so nothing here aliases it. The caller releases it, and the pool wipes on release; this module neither takes it,
45 * holds it, releases it, nor wipes it. That is what keeps the seeds, the noise and the decrypted message from outliving
46 * the caller.
47 *
48 * @author Douglas Quigg (dstroy0)
49 * @date 2026
50 */
51
52#define MLKEM768_EK_BYTES 1184 ///< encapsulation key (public key): 384*k + 32
53#define MLKEM768_DK_BYTES 2400 ///< decapsulation key (private): 768*k + 96
54#define MLKEM768_CT_BYTES 1088 ///< ciphertext: 32*(du*k + dv) = 32*(30+4)
55#define MLKEM768_SS_BYTES 32 ///< shared secret
56#define MLKEM768_MSG_BYTES 32 ///< the random message m fed to Encaps
57#define MLKEM768_D_BYTES 32 ///< KeyGen seed d (K-PKE key material)
58#define MLKEM768_Z_BYTES 32 ///< KeyGen seed z (implicit-reject value)
59
60/** @brief Dispatch table. Addressed by offset, so the layout is asserted below. */
61typedef struct
62{
63 proto_bool (*keygen)(uint8_t *, const uint8_t *, const uint8_t *, uint8_t *, uint8_t *);
64 proto_bool (*encaps)(uint8_t *, const uint8_t *, const uint8_t *, uint8_t *, uint8_t *);
65 proto_bool (*decaps)(uint8_t *, const uint8_t *, const uint8_t *, uint8_t *);
66} MlKemNs;
67PROTOCORE_NS_LAYOUT(MlKemNs, keygen, encaps, decaps);
68
69/**
70 * @brief (ek, dk) from the two seeds, deterministic given them.
71 * @param work PROTOCORE_ML_KEM_BORROW bytes the caller took. Not held past the call.
72 * @param d MLKEM768_D_BYTES key-material seed
73 * @param z MLKEM768_Z_BYTES implicit-reject seed
74 * @param ek MLKEM768_EK_BYTES encapsulation key
75 * @param dk MLKEM768_DK_BYTES decapsulation key, embedding ek, H(ek) and z
76 * @return PROTO_TRUE on success.
77 */
78proto_bool protocore_ml_kem_keygen(uint8_t *work, const uint8_t *d, const uint8_t *z, uint8_t *ek, uint8_t *dk);
79/**
80 * @brief (ct, ss) from a peer key and a message.
81 * @param work PROTOCORE_ML_KEM_BORROW bytes the caller took. Not held past the call.
82 * @param ek MLKEM768_EK_BYTES peer encapsulation key
83 * @param m MLKEM768_MSG_BYTES encapsulation randomness
84 * @param ct MLKEM768_CT_BYTES ciphertext
85 * @param ss MLKEM768_SS_BYTES shared secret
86 * @return PROTO_TRUE on success.
87 */
88proto_bool protocore_ml_kem_encaps(uint8_t *work, const uint8_t *ek, const uint8_t *m, uint8_t *ct, uint8_t *ss);
89/**
90 * @brief The shared secret from a ciphertext, through the FO transform.
91 * @param work PROTOCORE_ML_KEM_BORROW bytes the caller took. Not held past the call.
92 * @param dk MLKEM768_DK_BYTES decapsulation key from a KeyGen
93 * @param ct MLKEM768_CT_BYTES ciphertext from the peer's Encaps
94 * @param ss MLKEM768_SS_BYTES shared secret
95 * @return PROTO_TRUE on success.
96 */
97proto_bool protocore_ml_kem_decaps(uint8_t *work, const uint8_t *dk, const uint8_t *ct, uint8_t *ss);
98
99/** @brief Module namespace. */
102
104
105#endif // PROTOCORE_MLKEM_H
proto_bool protocore_ml_kem_keygen(uint8_t *work, const uint8_t *d, const uint8_t *z, uint8_t *ek, uint8_t *dk)
(ek, dk) from the two seeds, deterministic given them.
proto_bool protocore_ml_kem_encaps(uint8_t *work, const uint8_t *ek, const uint8_t *m, uint8_t *ct, uint8_t *ss)
(ct, ss) from a peer key and a message.
PROTOCORE_NS MlKemNs MlKem PROTOCORE_UNUSED
Module namespace.
Definition mlkem.h:100
proto_bool protocore_ml_kem_decaps(uint8_t *work, const uint8_t *dk, const uint8_t *ct, uint8_t *ss)
The shared secret from a ciphertext, through the FO transform.
#define PROTOCORE_NS_LAYOUT(T,...)
Pin every dispatch slot of a table that is nothing but function pointers.
#define PROTOCORE_NS
Storage for a dispatch table. The const is load bearing.
Dispatch table. Addressed by offset, so the layout is asserted below.
Definition mlkem.h:62
proto_bool(* keygen)(uint8_t *, const uint8_t *, const uint8_t *, uint8_t *, uint8_t *)
Definition mlkem.h:63
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97