ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
md.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 md.cpp
6 * @brief MD4 / MD5 / HMAC-MD5 implementation (see md.h). Little-endian word order.
7 */
8
9#include "crypto/hash/md.h"
10#include "crypto/crypto_opt.h"
11#include "server/mmgr/secure.h" // the secure pool: digest state, wiped on release
13#include <string.h>
15
16// The one definition of MdCtx - private to this TU (md.h forward-declares it). External callers hold it
17// only via pointer and get their storage from pc_md_wants() below, so the size never leaves this file.
18struct MdCtx
19{
20 uint32_t state[4];
21 uint64_t bits; ///< total message length in bits
22 uint8_t buf[64]; ///< partial block
23 uint32_t buf_len; ///< bytes currently in buf
24};
25static_assert(sizeof(MdCtx) <= PC_WORK_MD,
26 "MdCtx outgrew PC_WORK_MD - raise it in protocore_config.h, which derives PC_SECURE_ARENA_SIZE from it");
27
29{
30 pc_span ws = pc_secure_span(sizeof(MdCtx), alignof(MdCtx));
31 return pc_span_ok(ws) ? reinterpret_cast<MdCtx *>(ws.buf) : nullptr;
32}
33
34static inline uint32_t rotl(uint32_t v, unsigned n)
35{
36 return (v << n) | (v >> (32 - n));
37}
38
39// --- MD5 (RFC 1321) --------------------------------------------------------
40
41static const uint32_t MD5_K[64] = {
42 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
43 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
44 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
45 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
46 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
47 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
48 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
49 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
50
51static const uint8_t MD5_S[64] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
52 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
53 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
54 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
55
56static void pc_md5_compress(uint32_t s[4], const uint8_t block[64])
57{
58 uint32_t m[16];
59 for (int i = 0; i < 16; i++)
60 {
61 m[i] = pc_rd32le(block + i * 4);
62 }
63 uint32_t a = s[0];
64 uint32_t b = s[1];
65 uint32_t c = s[2];
66 uint32_t d = s[3];
67 for (int i = 0; i < 64; i++)
68 {
69 uint32_t f;
70 int g;
71 if (i < 16)
72 {
73 f = (b & c) | (~b & d);
74 g = i;
75 }
76 else if (i < 32)
77 {
78 f = (d & b) | (~d & c);
79 g = (5 * i + 1) & 15;
80 }
81 else if (i < 48)
82 {
83 f = b ^ c ^ d;
84 g = (3 * i + 5) & 15;
85 }
86 else
87 {
88 f = c ^ (b | ~d);
89 g = (7 * i) & 15;
90 }
91 f += a + MD5_K[i] + m[g];
92 a = d;
93 d = c;
94 c = b;
95 b += rotl(f, MD5_S[i]);
96 }
97 s[0] += a;
98 s[1] += b;
99 s[2] += c;
100 s[3] += d;
101}
102
104{
105 c->state[0] = 0x67452301;
106 c->state[1] = 0xefcdab89;
107 c->state[2] = 0x98badcfe;
108 c->state[3] = 0x10325476;
109 c->bits = 0;
110 c->buf_len = 0;
111}
112
113// --- MD4 (RFC 1320) --------------------------------------------------------
114
115static void pc_md4_compress(uint32_t s[4], const uint8_t block[64])
116{
117 uint32_t x[16];
118 for (int i = 0; i < 16; i++)
119 {
120 x[i] = pc_rd32le(block + i * 4);
121 }
122 uint32_t a = s[0];
123 uint32_t b = s[1];
124 uint32_t c = s[2];
125 uint32_t d = s[3];
126#define F4(X, Y, Z) (((X) & (Y)) | (~(X) & (Z)))
127#define G4(X, Y, Z) (((X) & (Y)) | ((X) & (Z)) | ((Y) & (Z)))
128#define H4(X, Y, Z) ((X) ^ (Y) ^ (Z))
129#define R1(A, B, C, D, K, S) A = rotl((uint32_t)(A + F4(B, C, D) + x[K]), S)
130#define R2(A, B, C, D, K, S) A = rotl((uint32_t)(A + G4(B, C, D) + x[K] + 0x5a827999u), S)
131#define R3(A, B, C, D, K, S) A = rotl((uint32_t)(A + H4(B, C, D) + x[K] + 0x6ed9eba1u), S)
132 R1(a, b, c, d, 0, 3);
133 R1(d, a, b, c, 1, 7);
134 R1(c, d, a, b, 2, 11);
135 R1(b, c, d, a, 3, 19);
136 R1(a, b, c, d, 4, 3);
137 R1(d, a, b, c, 5, 7);
138 R1(c, d, a, b, 6, 11);
139 R1(b, c, d, a, 7, 19);
140 R1(a, b, c, d, 8, 3);
141 R1(d, a, b, c, 9, 7);
142 R1(c, d, a, b, 10, 11);
143 R1(b, c, d, a, 11, 19);
144 R1(a, b, c, d, 12, 3);
145 R1(d, a, b, c, 13, 7);
146 R1(c, d, a, b, 14, 11);
147 R1(b, c, d, a, 15, 19);
148 R2(a, b, c, d, 0, 3);
149 R2(d, a, b, c, 4, 5);
150 R2(c, d, a, b, 8, 9);
151 R2(b, c, d, a, 12, 13);
152 R2(a, b, c, d, 1, 3);
153 R2(d, a, b, c, 5, 5);
154 R2(c, d, a, b, 9, 9);
155 R2(b, c, d, a, 13, 13);
156 R2(a, b, c, d, 2, 3);
157 R2(d, a, b, c, 6, 5);
158 R2(c, d, a, b, 10, 9);
159 R2(b, c, d, a, 14, 13);
160 R2(a, b, c, d, 3, 3);
161 R2(d, a, b, c, 7, 5);
162 R2(c, d, a, b, 11, 9);
163 R2(b, c, d, a, 15, 13);
164 R3(a, b, c, d, 0, 3);
165 R3(d, a, b, c, 8, 9);
166 R3(c, d, a, b, 4, 11);
167 R3(b, c, d, a, 12, 15);
168 R3(a, b, c, d, 2, 3);
169 R3(d, a, b, c, 10, 9);
170 R3(c, d, a, b, 6, 11);
171 R3(b, c, d, a, 14, 15);
172 R3(a, b, c, d, 1, 3);
173 R3(d, a, b, c, 9, 9);
174 R3(c, d, a, b, 5, 11);
175 R3(b, c, d, a, 13, 15);
176 R3(a, b, c, d, 3, 3);
177 R3(d, a, b, c, 11, 9);
178 R3(c, d, a, b, 7, 11);
179 R3(b, c, d, a, 15, 15);
180#undef F4
181#undef G4
182#undef H4
183#undef R1
184#undef R2
185#undef R3
186 s[0] += a;
187 s[1] += b;
188 s[2] += c;
189 s[3] += d;
190}
191
193{
194 c->state[0] = 0x67452301;
195 c->state[1] = 0xefcdab89;
196 c->state[2] = 0x98badcfe;
197 c->state[3] = 0x10325476;
198 c->bits = 0;
199 c->buf_len = 0;
200}
201
202// --- shared absorb / finish (MD4 and MD5 share the framing) ----------------
203
204using md_compress_fn = void (*)(uint32_t[4], const uint8_t[64]);
205
206static void md_absorb(MdCtx *c, const uint8_t *data, size_t len, md_compress_fn compress)
207{
208 c->bits += (uint64_t)len * 8;
209 while (len > 0)
210 {
211 uint32_t take = 64 - c->buf_len;
212 if ((size_t)take > len)
213 {
214 take = (uint32_t)len;
215 }
216 memcpy(c->buf + c->buf_len, data, take);
217 c->buf_len += take;
218 data += take;
219 len -= take;
220 if (c->buf_len == 64)
221 {
222 compress(c->state, c->buf);
223 c->buf_len = 0;
224 }
225 }
226}
227
228static void md_finish(MdCtx *c, uint8_t out[16], md_compress_fn compress)
229{
230 uint64_t bits = c->bits;
231 uint8_t pad = 0x80;
232 md_absorb(c, &pad, 1, compress);
233 uint8_t zero = 0x00;
234 while (c->buf_len != 56)
235 {
236 md_absorb(c, &zero, 1, compress);
237 }
238 uint8_t lenbuf[8];
239 for (int i = 0; i < 8; i++)
240 {
241 lenbuf[i] = (uint8_t)(bits >> (8 * i)); // little-endian bit length
242 }
243 md_absorb(c, lenbuf, 8, compress); // triggers the final compress
244 for (int i = 0; i < 4; i++)
245 {
246 pc_wr32le(out + i * 4, c->state[i]);
247 }
248}
249
250void pc_md5_update(MdCtx *c, const uint8_t *data, size_t len)
251{
252 md_absorb(c, data, len, pc_md5_compress);
253}
254void pc_md5_final(MdCtx *c, uint8_t out[16])
255{
256 md_finish(c, out, pc_md5_compress);
257}
258void pc_md4_update(MdCtx *c, const uint8_t *data, size_t len)
259{
260 md_absorb(c, data, len, pc_md4_compress);
261}
262void pc_md4_final(MdCtx *c, uint8_t out[16])
263{
264 md_finish(c, out, pc_md4_compress);
265}
266
267void md5(const uint8_t *data, size_t len, uint8_t out[16])
268{
269 MdCtx c;
270 pc_md5_init(&c);
271 pc_md5_update(&c, data, len);
272 pc_md5_final(&c, out);
273}
274void md4(const uint8_t *data, size_t len, uint8_t out[16])
275{
276 MdCtx c;
277 pc_md4_init(&c);
278 pc_md4_update(&c, data, len);
279 pc_md4_final(&c, out);
280}
281
282// --- HMAC-MD5 (RFC 2104) ---------------------------------------------------
283
284void pc_hmac_md5(const uint8_t *key, size_t key_len, const uint8_t *msg, size_t msg_len, uint8_t out[16])
285{
286 uint8_t k[64];
287 memset(k, 0, sizeof(k));
288 if (key_len > 64)
289 {
290 md5(key, key_len, k); // keys longer than the block are hashed down (leaves 16 bytes, rest zero)
291 }
292 else
293 {
294 memcpy(k, key, key_len);
295 }
296
297 uint8_t ipad[64];
298 uint8_t opad[64];
299 for (int i = 0; i < 64; i++)
300 {
301 ipad[i] = (uint8_t)(k[i] ^ 0x36);
302 opad[i] = (uint8_t)(k[i] ^ 0x5c);
303 }
304
305 uint8_t inner[16];
306 MdCtx c;
307 pc_md5_init(&c);
308 pc_md5_update(&c, ipad, 64);
309 pc_md5_update(&c, msg, msg_len);
310 pc_md5_final(&c, inner);
311
312 pc_md5_init(&c);
313 pc_md5_update(&c, opad, 64);
314 pc_md5_update(&c, inner, 16);
315 pc_md5_final(&c, out);
316}
Per-translation-unit optimization override for hot, pure-integer crypto.
Fixed-width integer serializers into a raw uint8_t* buffer - one source of truth.
uint32_t pc_rd32le(const uint8_t *p)
Read a little-endian u32 at p.
Definition endian.h:66
size_t pc_wr32le(uint8_t *p, uint32_t v)
Write v little-endian at p.
Definition endian.h:40
void pc_md5_init(MdCtx *c)
Definition md.cpp:103
#define R1(A, B, C, D, K, S)
void pc_md4_init(MdCtx *c)
Definition md.cpp:192
void pc_md4_update(MdCtx *c, const uint8_t *data, size_t len)
Definition md.cpp:258
MdCtx * pc_md_wants(void)
Storage this module wants for one MD4/MD5 context.
Definition md.cpp:28
void pc_md5_update(MdCtx *c, const uint8_t *data, size_t len)
Definition md.cpp:250
void pc_md5_final(MdCtx *c, uint8_t out[16])
Definition md.cpp:254
void md4(const uint8_t *data, size_t len, uint8_t out[16])
One-shot MD4 (the NT-hash primitive).
Definition md.cpp:274
#define R2(A, B, C, D, K, S)
#define R3(A, B, C, D, K, S)
void md5(const uint8_t *data, size_t len, uint8_t out[16])
One-shot MD5.
Definition md.cpp:267
void pc_md4_final(MdCtx *c, uint8_t out[16])
Definition md.cpp:262
void(*)(uint32_t[4], const uint8_t[64]) md_compress_fn
Definition md.cpp:204
void pc_hmac_md5(const uint8_t *key, size_t key_len, const uint8_t *msg, size_t msg_len, uint8_t out[16])
HMAC-MD5 (RFC 2104): the NTLMv2 MAC primitive.
Definition md.cpp:284
MD4 (RFC 1320), MD5 (RFC 1321), and HMAC-MD5 (RFC 2104) - the legacy digests NTLM needs.
#define PC_WORK_MD
pc_span pc_secure_span(size_t n, size_t align)
Borrow n secure bytes as a span whose capacity is bound to the allocation.
Definition secure.cpp:142
Secure pool accessor - borrows that hold key material.
bool pc_span_ok(const pc_span &s)
True when the span refers to real storage and every write so far has fit.
Definition span.h:114
Definition md.cpp:19
uint32_t state[4]
Definition md.cpp:20
uint32_t buf_len
bytes currently in buf
Definition md.cpp:23
uint64_t bits
total message length in bits
Definition md.cpp:21
uint8_t buf[64]
partial block
Definition md.cpp:22
A writable byte region: the storage, the capacity that belongs to it, and what has been produced into...
Definition span.h:65
uint8_t * buf
first byte, or nullptr when the region could not be obtained
Definition span.h:66