ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
rng.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/**
5 * @file rng.h
6 * @brief The seed a worker holds, and the draw over it.
7 *
8 * One draw: give me @p len bytes, and that is what comes back. The generator keeps its own schedule -
9 * it redraws from the platform once its budget is spent, so no caller sets the pace and no entropy
10 * source is drained by whichever module asks most often.
11 *
12 * ## What belongs here and what does not
13 *
14 * An algorithm whose specification dictates how it expands its randomness keeps that expansion and
15 * does not call this: ECDSA derives its nonce with the RFC 6979 HMAC-SHA256 DRBG (ecdsa.c), and
16 * ML-KEM is the FIPS 203 derandomized form, so its caller passes (d, z) and m in as values. This is
17 * the draw for the cases no specification pins down - a Diffie-Hellman private, SSH packet padding,
18 * a GUID, a WebSocket mask, a nonce - which would otherwise be one copy of the same expansion per
19 * caller.
20 *
21 * ## The expansion
22 *
23 * ChaCha20 keystream (RFC 8439) under the seed, the counter incrementing per 64-byte block. A stream
24 * cipher is what a keystream-from-a-seed is, it is already in the tree, and it costs one ARX
25 * permutation per 64 bytes rather than one HMAC per 32.
26 *
27 * After every draw the seed is replaced with 32 fresh keystream bytes, so the state that produced a
28 * value is gone before the value is returned and a later disclosure does not recover an earlier
29 * draw. Independently of that ratchet, the seed is redrawn from the platform once the draw budget
30 * @ref PROTOCORE_RAND_RESEED_BYTES is spent.
31 *
32 * The seed lives in the caller's borrow, one span per worker, so two workers never share a generator
33 * and the draw path takes no lock.
34 *
35 * @author Douglas Quigg (dstroy0)
36 * @date 2026
37 */
38
39#ifndef PROTOCORE_RNG_H
40#define PROTOCORE_RNG_H
41
42#include "protocore_config.h" // the entry point: protocore_types.h for the widths
43
44#if PROTOCORE_ENABLE_RNG
45
47
48/** @brief The seed: a ChaCha20 key. */
49#define PROTOCORE_RAND_SEED_LEN 32
50
51// PROTOCORE_RNG_BORROW - the bytes a generator runs out of - is stated in protocore_config.h, which
52// sums it into the secure arena. A caller takes them once, for the life of the program, and passes
53// the pointer to every call.
54
55/** @brief Where a draw lands. */
56typedef struct
57{
58 uint8_t *out; ///< destination
59 size_t len; ///< how many; any length, the block counter carries across 64-byte boundaries
60} RngFillArgs;
61
62/**
63 * @brief The general-purpose draw (ChaCha20 keystream, RFC 8439).
64 *
65 * A caller sets the members a call takes, invokes it through ::Rng with the bytes it runs out of, and
66 * reads the outcome off the same handle. How those bytes are carved is this module's and is never
67 * named here.
68 *
69 * Rng.fill_args.out = nonce;
70 * Rng.fill_args.len = nonce_len;
71 * Rng.fill(protocore_rng_span());
72 *
73 * @var RngNs::fill_args where a draw lands
74 * @var RngNs::ok a call's true/false outcome
75 * @var RngNs::fill write @c len keystream bytes out, then ratchet the seed
76 * @var RngNs::reseed redraw the seed and its nonce from the platform, and start the budget over
77 *
78 * A caller with no reason to hold a generator of its own passes @ref protocore_rng_span, the one the
79 * whole program shares. A caller that took its own PROTOCORE_RNG_BORROW span passes that instead and
80 * is a separate generator.
81 *
82 * @ref RngNs::fill draws the seed from the platform itself on the first call over a borrow, and again
83 * once @ref PROTOCORE_RAND_RESEED_BYTES is spent. @ref RngNs::reseed forces that redraw at a moment
84 * the caller picks.
85 *
86 * @c work is PROTOCORE_RNG_BORROW secure bytes the CALLER took, at an address it knows. It is not held past the call,
87 * so nothing here aliases it. The seed is in those bytes rather than in this module, so a caller takes them once for
88 * the life of the program and every draw runs out of the same span. The caller releases it, and the pool wipes on
89 * release; this module neither takes it, holds it, nor releases it, and the only bytes of it this module erases are the
90 * ratchet's replacement copy, after every draw. The borrow IS the generator, so two workers are two
91 * borrows and never collide.
92 *
93 * No storage member and no context: a caller sets operands and reads @ref RngNs::ok, and that is all
94 * the surface there is.
95 */
96typedef struct
97{
98 RngFillArgs fill_args;
99 proto_bool ok;
100} RngVars;
101
102/** @brief The operands and the outcome. */
103extern RngVars RngV;
104
105/** @brief The entries. */
106typedef struct
107{
108 void (*const fill)(uint8_t *work);
109 void (*const reseed)(uint8_t *work);
110} RngNs;
111
112// What the table binds, defined once in the .c and taking one parameter each: everything
113// else an entry needs is an operand in RngV or a region of the borrow at a fixed offset.
114void protocore_rng_fill(uint8_t *work);
115void protocore_rng_reseed(uint8_t *work);
116
117// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
118// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
119// `Rng.fill(work)` resolves to a named function and becomes a DIRECT call. An extern table
120// leaves the call indirect and the symbol live at every level, -O2 -flto included.
121static const RngNs Rng __attribute__((unused)) = {
122 .fill = protocore_rng_fill,
123 .reseed = protocore_rng_reseed,
124};
125
126/**
127 * @brief The PROTOCORE_RNG_BORROW bytes the whole program's generator runs out of.
128 *
129 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where that
130 * borrow comes from. Taken once from the end of the secure pool, which no mark and no release walks,
131 * so the seed and the ratchet last the life of the program.
132 *
133 * @return the span.
134 */
135uint8_t *protocore_rng_span(void);
136
138
139#endif // PROTOCORE_ENABLE_RNG
140
141#endif // PROTOCORE_RNG_H
#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