code wiki / (root) / hmac_md5.nx

hmac_md5.nx source

↩ module page · 104 lines · 3563 B

1// hmac_md5.nx -- HMAC with MD5 (RFC 2104). 2// 3// Like HMAC-SHA1: even though plain MD5 is broken for signatures, 4// HMAC-MD5 remains safe for authentication because HMAC relies 5// on pseudo-randomness of the keyed mix, not collision resistance. 6// Still on the wire in: 7// - RADIUS Access-Request Message-Authenticator (RFC 3579) 8// - CRAM-MD5 SASL auth (IMAP, POP3, SMTP legacy) 9// - Microsoft MS-CHAP-v2 password challenge 10// - NTLMv1 / NTLMv2 (the real world's Windows auth) 11// 12// Composes md5.nx with standard ipad/opad construction. 13// 14// Invariants: 15// HM1 Output = 16 bytes (MD5 tag length). 16// HM2 Matches RFC 2104 Appendix test vectors. 17 18import "syscalls.nx" 19// 2026-08-01 -- REPOINTED FROM md5.nx TO nx_md5_canonical.nx. SECOND sev-8 OF THE SAME SHAPE AS sha1.nx. 20// 21// md5.nx COMPUTES WRONG DIGESTS. Adjudicated by RFC 2202 section 2 (7 published HMAC-MD5 vectors, in a 22// document already pinned and cross-stack corroborated): 23// nx_hmacmd5_extvec_gate -> nx_hmac_md5.nx -> nx_md5_canonical.nx 7/7 GREEN 24// nx_hmacmd5alt_extvec_gate -> hmac_md5.nx -> md5.nx 0/7 RED 25// One import apart, identical readers, identical document. * A DUPLICATE PAIR IS A COIN FLIP UNTIL AN 26// AUTHORITY ADJUDICATES IT -- and the un-prefixed twin lost, exactly as sha1.nx did. 27// 28// VERIFIED DROP-IN BEFORE EDITING: both files export the SAME symbol set 29// (md5, md5_rotl, md5_s, md5_k, md5_g, md5_process_block, MD5_MASK32) -- nothing is lost by repointing. 30// * A "DROP-IN REPLACEMENT" IS A CLAIM ABOUT SYMBOL SETS; CHECK THEM. 31import "nx_md5_canonical.nx" 32 33const HMAC_MD5_BLOCK: i64 = 64 34const HMAC_MD5_OUT: i64 = 16 35 36func hmac_md5(key: *u8, key_len: i64, 37 msg: *u8, msg_len: i64, 38 out: *u8) -> i64 { 39 let k_prime: *u8 = sys_mmap(HMAC_MD5_BLOCK + 16) 40 var i: i64 = 0 41 while i < HMAC_MD5_BLOCK { k_prime[i] = 0; i = i + 1 } 42 43 if key_len > HMAC_MD5_BLOCK { 44 md5(key, key_len, k_prime) 45 } else { 46 i = 0 47 while i < key_len { 48 k_prime[i] = key[i] 49 i = i + 1 50 } 51 } 52 53 // Inner: MD5(ipad || msg). 54 let inner_len: i64 = HMAC_MD5_BLOCK + msg_len 55 let inner_buf: *u8 = sys_mmap(inner_len + 16) 56 i = 0 57 while i < HMAC_MD5_BLOCK { 58 inner_buf[i] = k_prime[i] ^ 0x36 59 i = i + 1 60 } 61 i = 0 62 while i < msg_len { 63 inner_buf[HMAC_MD5_BLOCK + i] = msg[i] 64 i = i + 1 65 } 66 let inner_hash: *u8 = sys_mmap(32) 67 md5(inner_buf, inner_len, inner_hash) 68 69 // Outer: MD5(opad || inner_hash). 70 let outer_len: i64 = HMAC_MD5_BLOCK + HMAC_MD5_OUT 71 let outer_buf: *u8 = sys_mmap(outer_len + 16) 72 i = 0 73 while i < HMAC_MD5_BLOCK { 74 outer_buf[i] = k_prime[i] ^ 0x5C 75 i = i + 1 76 } 77 i = 0 78 while i < HMAC_MD5_OUT { 79 outer_buf[HMAC_MD5_BLOCK + i] = inner_hash[i] 80 i = i + 1 81 } 82 md5(outer_buf, outer_len, out) 83 return 0 84} 85 86// Compile-only smoke. 87func main() -> i64 { 88 let out: *u8 = sys_mmap(32) 89 // RFC 2104 test vector 1: 90 // key = 0x0b repeated 16 times, msg = "Hi There" 91 // HMAC-MD5 = 9294727a3638bb1c13f48ef8158bfc9d 92 let key: *u8 = sys_mmap(32) 93 var i: i64 = 0 94 while i < 16 { 95 key[i] = 0x0B 96 i = i + 1 97 } 98 hmac_md5(key, 16, "Hi There", 8, out) 99 if out[0] != 0x92 { return 1 } 100 if out[1] != 0x94 { return 2 } 101 if out[2] != 0x72 { return 3 } 102 if out[15] != 0x9D { return 4 } 103 return 0 104}