DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
ghash.h
Go to the documentation of this file.
1// 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 * Shared by ssh_aesgcm (AES-256-GCM) and quic_aead (AES-128-GCM, also DTLS 1.3). The textbook GHASH
9 * is a 128-iteration bitwise GF(2^128) multiply per 16-byte block (~3,700 cyc/byte on an ESP32-S3),
10 * which made AES-GCM ~350x slower than raw AES-CTR and is the throughput floor of every AEAD record
11 * layer (measured, docs/FEATURE_PERFORMANCE.md). There is no hardware GF-multiply on the S3 (unlike
12 * the RSA/MPI MODMULT that accelerates curve25519), so the lever is algorithmic: the standard 4-bit
13 * table method (Shoup) - build a 16-entry table of i*H once per key, then fold four bits of the
14 * accumulator per step.
15 *
16 * The 128-bit state is held as FOUR uint32 words (z[0] most significant), NOT two uint64: on the
17 * 32-bit Xtensa LX7 a uint64 `>>4`/`<<60` compiles to a libgcc call (__lshrdi3/__ashldi3), which
18 * dominated an early uint64 version (~8,100 cyc/block); the 32-bit-word shifts are native register
19 * ops. Byte-exact versus the bitwise reference (the NIST/McGrew GCM KATs plus a direct fuzz
20 * cross-check, test_ssh_crypto). Header-only, zero heap, deterministic; the 256-byte table lives in
21 * the per-direction context (SSH, built once at key install) or on the stack (QUIC/DTLS, per packet).
22 */
23
24#ifndef DETERMINISTICESPASYNCWEBSERVER_GHASH_H
25#define DETERMINISTICESPASYNCWEBSERVER_GHASH_H
26
27#include <stddef.h>
28#include <stdint.h>
29
30/** @brief 4-bit GHASH table for a fixed subkey H = E(K, 0^128): M[i] = i*H as four big-endian
31 * uint32 words (M[i][0] most significant). 256 bytes. */
33{
34 uint32_t M[16][4];
35};
36
38{
39inline uint32_t be32(const uint8_t *p)
40{
41 return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3];
42}
43inline void put_be32(uint8_t *p, uint32_t v)
44{
45 p[0] = (uint8_t)(v >> 24);
46 p[1] = (uint8_t)(v >> 16);
47 p[2] = (uint8_t)(v >> 8);
48 p[3] = (uint8_t)v;
49}
50} // namespace detws_ghash_detail
51
52/** @brief Build the 4-bit multiplication table from the 16-byte subkey @p h. Call once per key. */
53inline void ghash_key_init(GhashKey *t, const uint8_t h[16])
54{
56 // M[8] = H; M[4]=H/x, M[2]=H/x^2, M[1]=H/x^3 (one GF right-shift each, reducing by R=0xe1<<120).
57 uint32_t z0 = be32(h), z1 = be32(h + 4), z2 = be32(h + 8), z3 = be32(h + 12);
58 t->M[8][0] = z0;
59 t->M[8][1] = z1;
60 t->M[8][2] = z2;
61 t->M[8][3] = z3;
62 t->M[0][0] = t->M[0][1] = t->M[0][2] = t->M[0][3] = 0;
63 for (int i = 4; i > 0; i >>= 1)
64 {
65 uint32_t lsb = z3 & 1u;
66 z3 = (z3 >> 1) | (z2 << 31);
67 z2 = (z2 >> 1) | (z1 << 31);
68 z1 = (z1 >> 1) | (z0 << 31);
69 z0 = (z0 >> 1) ^ (lsb ? 0xe1000000u : 0u);
70 t->M[i][0] = z0;
71 t->M[i][1] = z1;
72 t->M[i][2] = z2;
73 t->M[i][3] = z3;
74 }
75 // Composite entries: (i + j) * H = i*H XOR j*H (i a power of two, 0 < j < i).
76 for (int i = 2; i < 16; i <<= 1)
77 for (int j = 1; j < i; j++)
78 {
79 t->M[i + j][0] = t->M[i][0] ^ t->M[j][0];
80 t->M[i + j][1] = t->M[i][1] ^ t->M[j][1];
81 t->M[i + j][2] = t->M[i][2] ^ t->M[j][2];
82 t->M[i + j][3] = t->M[i][3] ^ t->M[j][3];
83 }
84}
85
86/** @brief acc = acc * H in GF(2^128) with the GCM reduction, using the precomputed table @p t. */
87inline void ghash_mul(const GhashKey *t, uint8_t acc[16])
88{
89 // Reduction contribution (into the top 16 bits of word 0) of the low nibble shifted out per step.
90 static const uint16_t LAST4[16] = {0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
91 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0};
92 uint8_t idx = acc[15] & 0x0f;
93 uint32_t z0 = t->M[idx][0], z1 = t->M[idx][1], z2 = t->M[idx][2], z3 = t->M[idx][3];
94 for (int i = 15; i >= 0; i--)
95 {
96 uint8_t lo = acc[i] & 0x0f;
97 uint8_t hi = (acc[i] >> 4) & 0x0f;
98 if (i != 15)
99 {
100 uint32_t rem = z3 & 0x0f;
101 z3 = (z3 >> 4) | (z2 << 28);
102 z2 = (z2 >> 4) | (z1 << 28);
103 z1 = (z1 >> 4) | (z0 << 28);
104 z0 = (z0 >> 4) ^ ((uint32_t)LAST4[rem] << 16);
105 z0 ^= t->M[lo][0];
106 z1 ^= t->M[lo][1];
107 z2 ^= t->M[lo][2];
108 z3 ^= t->M[lo][3];
109 }
110 uint32_t rem = z3 & 0x0f;
111 z3 = (z3 >> 4) | (z2 << 28);
112 z2 = (z2 >> 4) | (z1 << 28);
113 z1 = (z1 >> 4) | (z0 << 28);
114 z0 = (z0 >> 4) ^ ((uint32_t)LAST4[rem] << 16);
115 z0 ^= t->M[hi][0];
116 z1 ^= t->M[hi][1];
117 z2 ^= t->M[hi][2];
118 z3 ^= t->M[hi][3];
119 }
121 detws_ghash_detail::put_be32(acc + 4, z1);
122 detws_ghash_detail::put_be32(acc + 8, z2);
123 detws_ghash_detail::put_be32(acc + 12, z3);
124}
125
126/** @brief Fold @p len bytes of @p data into @p acc: acc = (acc XOR block) * H per 16 bytes, a final
127 * short block MSB-zero-padded. */
128inline void ghash_update(const GhashKey *t, uint8_t acc[16], const uint8_t *data, size_t len)
129{
130 size_t off = 0;
131 while (off < len)
132 {
133 size_t take = len - off;
134 if (take > 16)
135 take = 16;
136 for (size_t i = 0; i < take; i++)
137 acc[i] ^= data[off + i];
138 ghash_mul(t, acc);
139 off += take;
140 }
141}
142
143#endif // DETERMINISTICESPASYNCWEBSERVER_GHASH_H
void ghash_key_init(GhashKey *t, const uint8_t h[16])
Build the 4-bit multiplication table from the 16-byte subkey h. Call once per key.
Definition ghash.h:53
void ghash_update(const GhashKey *t, uint8_t acc[16], const uint8_t *data, size_t len)
Fold len bytes of data into acc: acc = (acc XOR block) * H per 16 bytes, a final short block MSB-zero...
Definition ghash.h:128
void ghash_mul(const GhashKey *t, uint8_t acc[16])
acc = acc * H in GF(2^128) with the GCM reduction, using the precomputed table t.
Definition ghash.h:87
void put_be32(uint8_t *p, uint32_t v)
Definition ghash.h:43
uint32_t be32(const uint8_t *p)
Definition ghash.h:39
4-bit GHASH table for a fixed subkey H = E(K, 0^128): M[i] = i*H as four big-endian uint32 words (M[i...
Definition ghash.h:33
uint32_t M[16][4]
Definition ghash.h:34