ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
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 pc_snmp_crypto.cpp
6 * @brief USM key localization (SHA-256) + AES-128-CFB implementation.
7 */
8
10#include "server/mmgr/secure.h"
11
12#if PC_ENABLE_SNMP_V3
13
15#include "crypto/crypto_scratch.h" // pc_secure_wipe (the canonical secure wipe)
16#include "crypto/hash/sha256.h"
17#include <string.h>
18
19// ---------------------------------------------------------------------------
20// RFC 3414 ยง2.6 key localization (SHA-256)
21// ---------------------------------------------------------------------------
22
23// RFC 3414 passphrases are >= 8 chars with no formal upper bound; cap the localization input
24// defensively (well above any real passphrase) so a non-terminated password cannot over-read.
25#define PC_SNMP_USM_PASS_MAX 256
26
27void pc_snmp_usm_localize_key(const char *password, const uint8_t *engine_id, size_t engine_id_len,
28 uint8_t key_out[SNMP_USM_KEY_LEN])
29{
30 size_t pwlen = password ? strnlen(password, PC_SNMP_USM_PASS_MAX) : 0;
31 if (pwlen == 0)
32 {
33 memset(key_out, 0, SNMP_USM_KEY_LEN);
34 return;
35 }
36
37 // Ku = SHA-256( password repeated to exactly 1 048 576 bytes ).
38 pc_sha256_ctx ctx;
39 pc_sha256_init(&ctx);
40 uint8_t block[64];
41 size_t pw_index = 0;
42 uint32_t count = 0;
43 while (count < 1048576u)
44 {
45 for (int i = 0; i < 64; i++)
46 {
47 block[i] = (uint8_t)password[pw_index];
48 pw_index = (pw_index + 1) % pwlen;
49 }
50 pc_sha256_update(&ctx, block, 64);
51 count += 64;
52 }
53 uint8_t ku[SNMP_USM_KEY_LEN];
54 pc_sha256_final(&ctx, ku);
55
56 // Kul = SHA-256( Ku || engineID || Ku ).
57 pc_sha256_init(&ctx);
58 pc_sha256_update(&ctx, ku, SNMP_USM_KEY_LEN);
59 pc_sha256_update(&ctx, engine_id, engine_id_len);
60 pc_sha256_update(&ctx, ku, SNMP_USM_KEY_LEN);
61 pc_sha256_final(&ctx, key_out);
62
63 pc_secure_wipe(ku, sizeof(ku));
64 pc_secure_wipe(block, sizeof(block));
65}
66
67// ---------------------------------------------------------------------------
68// AES-128 (FIPS-197) block encrypt + CFB-128 mode
69// ---------------------------------------------------------------------------
70
71static const uint8_t kRcon[10] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36};
72
73// Expand a 128-bit key into 11 round keys (44 words).
74static void aes128_key_schedule(const uint8_t key[16], uint8_t rk[176])
75{
76 memcpy(rk, key, 16);
77 for (int i = 4; i < 44; i++)
78 {
79 uint8_t t[4];
80 memcpy(t, rk + (i - 1) * 4, 4);
81 if (i % 4 == 0)
82 {
83 uint8_t tmp = t[0]; // RotWord
84 t[0] = PC_AES_SBOX[t[1]];
85 t[1] = PC_AES_SBOX[t[2]];
86 t[2] = PC_AES_SBOX[t[3]];
87 t[3] = PC_AES_SBOX[tmp];
88 t[0] ^= kRcon[i / 4 - 1];
89 }
90 for (int j = 0; j < 4; j++)
91 {
92 rk[i * 4 + j] = rk[(i - 4) * 4 + j] ^ t[j];
93 }
94 }
95}
96
97static uint8_t xtime(uint8_t x)
98{
99 return (uint8_t)((x << 1) ^ ((x >> 7) * 0x1b));
100}
101
102static void aes128_encrypt_block(const uint8_t rk[176], const uint8_t in[16], uint8_t out[16])
103{
104 uint8_t s[16];
105 for (int i = 0; i < 16; i++)
106 {
107 s[i] = in[i] ^ rk[i];
108 }
109
110 for (int round = 1; round <= 10; round++)
111 {
112 // SubBytes
113 for (int i = 0; i < 16; i++)
114 {
115 s[i] = PC_AES_SBOX[s[i]];
116 }
117
118 // ShiftRows (state is column-major: s[r + 4c])
119 uint8_t t[16];
120 for (int r = 0; r < 4; r++)
121 {
122 for (int c = 0; c < 4; c++)
123 {
124 t[r + 4 * c] = s[r + 4 * ((c + r) % 4)];
125 }
126 }
127 memcpy(s, t, 16);
128
129 // MixColumns (skip in the final round)
130 if (round != 10)
131 {
132 for (int c = 0; c < 4; c++)
133 {
134 uint8_t *col = s + 4 * c;
135 uint8_t a0 = col[0];
136 uint8_t a1 = col[1];
137 uint8_t a2 = col[2];
138 uint8_t a3 = col[3];
139 col[0] = (uint8_t)(xtime(a0) ^ (xtime(a1) ^ a1) ^ a2 ^ a3);
140 col[1] = (uint8_t)(a0 ^ xtime(a1) ^ (xtime(a2) ^ a2) ^ a3);
141 col[2] = (uint8_t)(a0 ^ a1 ^ xtime(a2) ^ (xtime(a3) ^ a3));
142 col[3] = (uint8_t)((xtime(a0) ^ a0) ^ a1 ^ a2 ^ xtime(a3));
143 }
144 }
145
146 // AddRoundKey
147 for (int i = 0; i < 16; i++)
148 {
149 s[i] ^= rk[round * 16 + i];
150 }
151 }
152 memcpy(out, s, 16);
153}
154
155void pc_snmp_aes128_cfb(const uint8_t key[16], const uint8_t iv[16], const uint8_t *in, uint8_t *out, size_t len,
156 bool encrypt)
157{
158 uint8_t rk[176];
159 aes128_key_schedule(key, rk);
160 uint8_t fb[16];
161 memcpy(fb, iv, 16);
162 uint8_t ks[16];
163
164 size_t off = 0;
165 while (off < len)
166 {
167 aes128_encrypt_block(rk, fb, ks);
168 size_t bl = len - off;
169 if (bl > 16)
170 {
171 bl = 16;
172 }
173 // Ciphertext feedback must be captured before an in-place XOR overwrites
174 // the input (matters when out == in).
175 uint8_t cipher[16];
176 if (encrypt)
177 {
178 for (size_t j = 0; j < bl; j++)
179 {
180 out[off + j] = in[off + j] ^ ks[j];
181 cipher[j] = out[off + j];
182 }
183 }
184 else
185 {
186 for (size_t j = 0; j < bl; j++)
187 {
188 cipher[j] = in[off + j];
189 out[off + j] = in[off + j] ^ ks[j];
190 }
191 }
192 if (bl == 16)
193 {
194 memcpy(fb, cipher, 16);
195 }
196 off += bl;
197 }
198
199 pc_secure_wipe(rk, sizeof(rk));
200 pc_secure_wipe(ks, sizeof(ks));
201 pc_secure_wipe(fb, sizeof(fb));
202}
203
204#endif // PC_ENABLE_SNMP_V3
The AES forward S-box (FIPS 197 Figure 7) - one shared copy.
Constant-time comparison for secret-dependent checks.
Secure pool accessor - borrows that hold key material.
PC_CRYPTO_HOT void pc_sha256_init(pc_sha256_ctx *ctx)
Initialize a streaming SHA-256 context (ctx must not be NULL).
Definition sha256.cpp:29
void pc_sha256_final(pc_sha256_ctx *ctx, uint8_t digest[PC_SHA256_DIGEST_LEN])
Finalize the hash and write the 32-byte digest. The context is undefined afterwards; call init() agai...
Definition sha256.cpp:48
void pc_sha256_update(pc_sha256_ctx *ctx, const uint8_t *data, size_t len)
Feed len bytes of data into the running hash.
Definition sha256.cpp:39
SHA-256 (FIPS 180-4) - streaming context and one-shot API.
Streaming SHA-256 context.
Definition sha256.h:40