DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
snmp_crypto.cpp
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 snmp_crypto.cpp
6 * @brief USM key localization (SHA-256) + AES-128-CFB implementation.
7 */
8
10
11#if DETWS_ENABLE_SNMP_V3
12
14#include <string.h>
15
16// Zero key material with a volatile loop the compiler cannot optimize away. A
17// plain memset() whose result is never observed (the buffer dies at return) may
18// be elided as a dead store, leaving secrets on the stack. Same idiom as ssh_wipe.
19static inline void snmp_wipe(void *p, size_t n)
20{
21 volatile uint8_t *v = (volatile uint8_t *)p;
22 while (n--)
23 *v++ = 0;
24}
25
26// ---------------------------------------------------------------------------
27// RFC 3414 §2.6 key localization (SHA-256)
28// ---------------------------------------------------------------------------
29
30// RFC 3414 passphrases are >= 8 chars with no formal upper bound; cap the localization input
31// defensively (well above any real passphrase) so a non-terminated password cannot over-read.
32static constexpr size_t SNMP_USM_PASS_MAX = 256;
33
34void snmp_usm_localize_key(const char *password, const uint8_t *engine_id, size_t engine_id_len,
35 uint8_t key_out[SNMP_USM_KEY_LEN])
36{
37 size_t pwlen = password ? strnlen(password, SNMP_USM_PASS_MAX) : 0;
38 if (pwlen == 0)
39 {
40 memset(key_out, 0, SNMP_USM_KEY_LEN);
41 return;
42 }
43
44 // Ku = SHA-256( password repeated to exactly 1 048 576 bytes ).
45 SshSha256Ctx ctx;
46 ssh_sha256_init(&ctx);
47 uint8_t block[64];
48 size_t pw_index = 0;
49 uint32_t count = 0;
50 while (count < 1048576u)
51 {
52 for (int i = 0; i < 64; i++)
53 {
54 block[i] = (uint8_t)password[pw_index];
55 pw_index = (pw_index + 1) % pwlen;
56 }
57 ssh_sha256_update(&ctx, block, 64);
58 count += 64;
59 }
60 uint8_t ku[SNMP_USM_KEY_LEN];
61 ssh_sha256_final(&ctx, ku);
62
63 // Kul = SHA-256( Ku || engineID || Ku ).
64 ssh_sha256_init(&ctx);
65 ssh_sha256_update(&ctx, ku, SNMP_USM_KEY_LEN);
66 ssh_sha256_update(&ctx, engine_id, engine_id_len);
67 ssh_sha256_update(&ctx, ku, SNMP_USM_KEY_LEN);
68 ssh_sha256_final(&ctx, key_out);
69
70 snmp_wipe(ku, sizeof(ku));
71 snmp_wipe(block, sizeof(block));
72}
73
74// ---------------------------------------------------------------------------
75// AES-128 (FIPS-197) block encrypt + CFB-128 mode
76// ---------------------------------------------------------------------------
77
79
80static const uint8_t kRcon[10] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36};
81
82// Expand a 128-bit key into 11 round keys (44 words).
83static void aes128_key_schedule(const uint8_t key[16], uint8_t rk[176])
84{
85 memcpy(rk, key, 16);
86 for (int i = 4; i < 44; i++)
87 {
88 uint8_t t[4];
89 memcpy(t, rk + (i - 1) * 4, 4);
90 if (i % 4 == 0)
91 {
92 uint8_t tmp = t[0]; // RotWord
93 t[0] = DET_AES_SBOX[t[1]];
94 t[1] = DET_AES_SBOX[t[2]];
95 t[2] = DET_AES_SBOX[t[3]];
96 t[3] = DET_AES_SBOX[tmp];
97 t[0] ^= kRcon[i / 4 - 1];
98 }
99 for (int j = 0; j < 4; j++)
100 rk[i * 4 + j] = rk[(i - 4) * 4 + j] ^ t[j];
101 }
102}
103
104static uint8_t xtime(uint8_t x)
105{
106 return (uint8_t)((x << 1) ^ ((x >> 7) * 0x1b));
107}
108
109static void aes128_encrypt_block(const uint8_t rk[176], const uint8_t in[16], uint8_t out[16])
110{
111 uint8_t s[16];
112 for (int i = 0; i < 16; i++)
113 s[i] = in[i] ^ rk[i];
114
115 for (int round = 1; round <= 10; round++)
116 {
117 // SubBytes
118 for (int i = 0; i < 16; i++)
119 s[i] = DET_AES_SBOX[s[i]];
120
121 // ShiftRows (state is column-major: s[r + 4c])
122 uint8_t t[16];
123 for (int r = 0; r < 4; r++)
124 for (int c = 0; c < 4; c++)
125 t[r + 4 * c] = s[r + 4 * ((c + r) % 4)];
126 memcpy(s, t, 16);
127
128 // MixColumns (skip in the final round)
129 if (round != 10)
130 {
131 for (int c = 0; c < 4; c++)
132 {
133 uint8_t *col = s + 4 * c;
134 uint8_t a0 = col[0];
135 uint8_t a1 = col[1];
136 uint8_t a2 = col[2];
137 uint8_t a3 = col[3];
138 col[0] = (uint8_t)(xtime(a0) ^ (xtime(a1) ^ a1) ^ a2 ^ a3);
139 col[1] = (uint8_t)(a0 ^ xtime(a1) ^ (xtime(a2) ^ a2) ^ a3);
140 col[2] = (uint8_t)(a0 ^ a1 ^ xtime(a2) ^ (xtime(a3) ^ a3));
141 col[3] = (uint8_t)((xtime(a0) ^ a0) ^ a1 ^ a2 ^ xtime(a3));
142 }
143 }
144
145 // AddRoundKey
146 for (int i = 0; i < 16; i++)
147 s[i] ^= rk[round * 16 + i];
148 }
149 memcpy(out, s, 16);
150}
151
152void snmp_aes128_cfb(const uint8_t key[16], const uint8_t iv[16], const uint8_t *in, uint8_t *out, size_t len,
153 bool encrypt)
154{
155 uint8_t rk[176];
156 aes128_key_schedule(key, rk);
157 uint8_t fb[16];
158 memcpy(fb, iv, 16);
159 uint8_t ks[16];
160
161 size_t off = 0;
162 while (off < len)
163 {
164 aes128_encrypt_block(rk, fb, ks);
165 size_t bl = len - off;
166 if (bl > 16)
167 bl = 16;
168 // Ciphertext feedback must be captured before an in-place XOR overwrites
169 // the input (matters when out == in).
170 uint8_t cipher[16];
171 if (encrypt)
172 {
173 for (size_t j = 0; j < bl; j++)
174 {
175 out[off + j] = in[off + j] ^ ks[j];
176 cipher[j] = out[off + j];
177 }
178 }
179 else
180 {
181 for (size_t j = 0; j < bl; j++)
182 {
183 cipher[j] = in[off + j];
184 out[off + j] = in[off + j] ^ ks[j];
185 }
186 }
187 if (bl == 16)
188 memcpy(fb, cipher, 16);
189 off += bl;
190 }
191
192 snmp_wipe(rk, sizeof(rk));
193 snmp_wipe(ks, sizeof(ks));
194 snmp_wipe(fb, sizeof(fb));
195}
196
197#endif // DETWS_ENABLE_SNMP_V3
The AES forward S-box (FIPS 197 Figure 7) - one shared copy.
USM cryptographic primitives for SNMPv3 (DETWS_ENABLE_SNMP_V3).
void ssh_sha256_init(SshSha256Ctx *ctx)
Initialize a streaming SHA-256 context.
void ssh_sha256_update(SshSha256Ctx *ctx, const uint8_t *data, size_t len)
Feed len bytes of data into the running hash.
void ssh_sha256_final(SshSha256Ctx *ctx, uint8_t digest[SSH_SHA256_DIGEST_LEN])
Finalize the hash and write the 32-byte digest.
SHA-256 (FIPS 180-4) - streaming context and one-shot API.
Streaming SHA-256 context.
Definition ssh_sha256.h:44