nx_hmac_md5.nx source
↩ module page · 101 lines · 2945 B
1// hmac_md5.nx -- HMAC with MD5 (RFC 2104).
2//
3// license_tier: INDEPENDENT_REDERIVE
4// genealogy_id: international-research-sources/ietf/rfc_2104
5//
6// Like HMAC-SHA1: even though plain MD5 is broken for signatures,
7// HMAC-MD5 remains safe for authentication because HMAC relies
8// on pseudo-randomness of the keyed mix, not collision resistance.
9// Still on the wire in:
10// - RADIUS Access-Request Message-Authenticator (RFC 3579)
11// - CRAM-MD5 SASL auth (IMAP, POP3, SMTP legacy)
12// - Microsoft MS-CHAP-v2 password challenge
13// - NTLMv1 / NTLMv2 (the real world's Windows auth)
14//
15// Composes md5.nx with standard ipad/opad construction.
16//
17// Invariants:
18// HM1 Output = 16 bytes (MD5 tag length).
19// HM2 Matches RFC 2104 Appendix test vectors.
20
21// nx_safety_envelope:
22// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
23// sil_target: SIL1
24// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
25// verdict: NOT_YET_EVALUATED
26
27import "nx_syscalls.nx"
28import "nx_md5_canonical.nx"
29
30const HMAC_MD5_BLOCK: i64 = 64
31const HMAC_MD5_OUT: i64 = 16
32
33func hmac_md5(key: *u8, key_len: i64,
34 msg: *u8, msg_len: i64,
35 out: *u8) -> i64 {
36 let k_prime: *u8 = sys_mmap(HMAC_MD5_BLOCK + 16)
37 var i: i64 = 0
38 while i < HMAC_MD5_BLOCK { k_prime[i] = 0; i = i + 1 }
39
40 if key_len > HMAC_MD5_BLOCK {
41 md5(key, key_len, k_prime)
42 } else {
43 i = 0
44 while i < key_len {
45 k_prime[i] = key[i]
46 i = i + 1
47 }
48 }
49
50 // Inner: MD5(ipad || msg).
51 let inner_len: i64 = HMAC_MD5_BLOCK + msg_len
52 let inner_buf: *u8 = sys_mmap(inner_len + 16)
53 i = 0
54 while i < HMAC_MD5_BLOCK {
55 inner_buf[i] = k_prime[i] ^ 0x36
56 i = i + 1
57 }
58 i = 0
59 while i < msg_len {
60 inner_buf[HMAC_MD5_BLOCK + i] = msg[i]
61 i = i + 1
62 }
63 let inner_hash: *u8 = sys_mmap(32)
64 md5(inner_buf, inner_len, inner_hash)
65
66 // Outer: MD5(opad || inner_hash).
67 let outer_len: i64 = HMAC_MD5_BLOCK + HMAC_MD5_OUT
68 let outer_buf: *u8 = sys_mmap(outer_len + 16)
69 i = 0
70 while i < HMAC_MD5_BLOCK {
71 outer_buf[i] = k_prime[i] ^ 0x5C
72 i = i + 1
73 }
74 i = 0
75 while i < HMAC_MD5_OUT {
76 outer_buf[HMAC_MD5_BLOCK + i] = inner_hash[i]
77 i = i + 1
78 }
79 md5(outer_buf, outer_len, out)
80 return 0
81}
82
83// Compile-only smoke.
84func main() -> i64 {
85 let out: *u8 = sys_mmap(32)
86 // RFC 2104 test vector 1:
87 // key = 0x0b repeated 16 times, msg = "Hi There"
88 // HMAC-MD5 = 9294727a3638bb1c13f48ef8158bfc9d
89 let key: *u8 = sys_mmap(32)
90 var i: i64 = 0
91 while i < 16 {
92 key[i] = 0x0B
93 i = i + 1
94 }
95 hmac_md5(key, 16, "Hi There", 8, out)
96 if out[0] != 0x92 { return 1 }
97 if out[1] != 0x94 { return 2 }
98 if out[2] != 0x72 { return 3 }
99 if out[15] != 0x9D { return 4 }
100 return 0
101}