ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
ghash.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 ghash.h
6 * @brief GHASH (the GF(2^128) universal hash under AES-GCM, NIST SP 800-38D sec 6.3), 4-bit table.
7 *
8 * The shared GHASH primitive for the whole library (AES-256-GCM, AES-128-GCM, DTLS 1.3). The textbook
9 * GHASH is a 128-iteration bitwise GF(2^128) multiply per 16-byte block, which makes AES-GCM the
10 * throughput floor of every AEAD record layer. There is no hardware GF-multiply on any die in the list,
11 * so the lever is algorithmic: the 4-bit table method (Shoup) builds a 16-entry table of i*H once per
12 * key, then folds four bits of the accumulator per step.
13 *
14 * @author Douglas Quigg (dstroy0)
15 * @date 2026
16 */
17
18#ifndef PROTOCORE_GHASH_H
19#define PROTOCORE_GHASH_H
20
21#include "protocore_config.h" // the entry point: protocore_types.h for the widths
22
23#if PROTOCORE_ENABLE_GHASH
24
26
27/** @brief GHASH subkey length in bytes. */
28#define PROTOCORE_GHASH_KEY_LEN 16
29
30/** @brief GHASH accumulator length in bytes. */
31#define PROTOCORE_GHASH_ACC_LEN 16
32
33// PROTOCORE_GHASH_BORROW - the bytes a bound subkey runs out of - is stated in protocore_config.h,
34// which sums it into the secure arena. A caller takes them once and passes the pointer to every call.
35
36/** @brief The subkey a table is built from. */
37typedef struct
38{
39 const uint8_t *h; ///< PROTOCORE_GHASH_KEY_LEN bytes, H = E(K, 0^128)
40} GhashKeyArgs;
41
42/** @brief The accumulator one multiply runs in. */
43typedef struct
44{
45 uint8_t *acc; ///< PROTOCORE_GHASH_ACC_LEN bytes, multiplied by H in place
46} GhashMulArgs;
47
48/** @brief The accumulator and the bytes a fold runs over. */
49typedef struct
50{
51 uint8_t *acc; ///< PROTOCORE_GHASH_ACC_LEN bytes, folded in place
52 const uint8_t *data; ///< the bytes, NULL when @c len is 0
53 size_t len; ///< how many
54} GhashUpdateArgs;
55
56/**
57 * @brief GHASH (NIST SP 800-38D sec 6.3), 4-bit table.
58 *
59 * A caller sets the members a call takes, invokes it through ::Ghash with the bytes it runs out of, and
60 * reads the outcome off the same handle. How those bytes are carved is this module's and is never named
61 * here.
62 *
63 * Ghash.key_args.h = h;
64 * Ghash.key_init(work);
65 * Ghash.update_args.acc = acc;
66 * Ghash.update_args.data = aad;
67 * Ghash.update_args.len = aad_len;
68 * Ghash.update(work);
69 * Ghash.mul_args.acc = acc;
70 * Ghash.mul(work);
71 *
72 * @var GhashNs::key_args the subkey a table is built from
73 * @var GhashNs::mul_args the accumulator one multiply runs in
74 * @var GhashNs::update_args the accumulator and the bytes a fold runs over
75 * @var GhashNs::ok a call's true/false outcome
76 * @var GhashNs::key_init build the 4-bit table for the subkey, once per key
77 * @var GhashNs::mul acc = acc * H in GF(2^128) under that table
78 * @var GhashNs::update fold the bytes into acc 16 at a time, a final short block MSB-zero-padded
79 *
80 * The accumulator is the CALLER's 16 bytes: both folding entries work in place on the buffer their args
81 * name and hold it no longer than the call.
82 *
83 * @c work is PROTOCORE_GHASH_BORROW secure bytes the CALLER took, at an address it knows. It is not held past the call,
84 * so nothing here aliases it. The caller releases it, and the pool wipes on release; this module neither takes it,
85 * holds it, releases it, nor wipes it. The borrow IS the table, so two subkeys are two borrows and never collide, and
86 * the table dies with the release.
87 *
88 * No storage member and no context: a caller sets operands and reads @ref GhashNs::ok, and that is all
89 * the surface there is.
90 */
91typedef struct
92{
93 GhashKeyArgs key_args;
94 GhashMulArgs mul_args;
95 GhashUpdateArgs update_args;
96 proto_bool ok;
97} GhashVars;
98
99/** @brief The operands and the outcome. */
100extern GhashVars GhashV;
101
102/** @brief The entries. */
103typedef struct
104{
105 void (*const key_init)(uint8_t *work);
106 void (*const mul)(uint8_t *work);
107 void (*const update)(uint8_t *work);
108} GhashNs;
109
110// What the table binds, defined once in the .c and taking one parameter each: everything
111// else an entry needs is an operand in GhashV or a region of the borrow at a fixed offset.
112void protocore_ghash_key_init(uint8_t *work);
113void protocore_ghash_mul(uint8_t *work);
114void protocore_ghash_update(uint8_t *work);
115
116// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
117// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
118// `Ghash.key_init(work)` resolves to a named function and becomes a DIRECT call. An extern table
119// leaves the call indirect and the symbol live at every level, -O2 -flto included.
120static const GhashNs Ghash __attribute__((unused)) = {
121 .key_init = protocore_ghash_key_init,
122 .mul = protocore_ghash_mul,
123 .update = protocore_ghash_update,
124};
125
127
128#endif // PROTOCORE_ENABLE_GHASH
129
130#endif // PROTOCORE_GHASH_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