code wiki / (root) / nx_hmac_md5.nx

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}