code wiki / (root) / nx_md5.nx

nx_md5.nx source

↩ module page · 246 lines · 9362 B

1// nx_md5.nx -- MD5 message digest (RFC 1321). 2// 3// MD5 is cryptographically broken (collisions) but still used for: 4// - Legacy file checksums (md5sum verification) 5// - Apache .htdigest auth files (which we may need to interop) 6// - ETag generation (when collision resistance isn't needed) 7// - Cache keys for systems that hardcode 16-byte digests 8// 9// DO NOT use MD5 for passwords, signatures, or anything where an 10// attacker controls the input. Use SHA-256 (already in tree) + 11// HMAC. 12// 13// Algorithm: 64-round MD-construction with 4-word state, processing 14// 64-byte blocks. Returns 16-byte digest. 15 16// nx_safety_envelope: 17// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 18// sil_target: SIL1 19// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 20// verdict: NOT_YET_EVALUATED 21 22import "syscalls.nx" 23 24// 64-bit rotate-left for 32-bit values (MD5 ops are 32-bit). 25func nx_md5_rotl(x: i64, k: i64) -> i64 { 26 let v: i64 = x & 0xFFFFFFFF 27 let lo: i64 = (v << k) & 0xFFFFFFFF 28 let hi: i64 = (v >> (32 - k)) & ((1 << (32 - k)) - 1) 29 return (lo | hi) & 0xFFFFFFFF 30} 31 32// MD5 round functions. Each operates on 32-bit words. 33func nx_md5_F(x: i64, y: i64, z: i64) -> i64 { return ((x & y) | ((~x) & z)) & 0xFFFFFFFF } 34func nx_md5_G(x: i64, y: i64, z: i64) -> i64 { return ((x & z) | (y & (~z))) & 0xFFFFFFFF } 35func nx_md5_H(x: i64, y: i64, z: i64) -> i64 { return (x ^ y ^ z) & 0xFFFFFFFF } 36func nx_md5_I(x: i64, y: i64, z: i64) -> i64 { return (y ^ (x | (~z))) & 0xFFFFFFFF } 37 38// FF/GG/HH/II step macros. 39func nx_md5_FF(a: i64, b: i64, c: i64, d: i64, m: i64, s: i64, t: i64) -> i64 { 40 let v: i64 = (a + nx_md5_F(b, c, d) + m + t) & 0xFFFFFFFF 41 return (b + nx_md5_rotl(v, s)) & 0xFFFFFFFF 42} 43func nx_md5_GG(a: i64, b: i64, c: i64, d: i64, m: i64, s: i64, t: i64) -> i64 { 44 let v: i64 = (a + nx_md5_G(b, c, d) + m + t) & 0xFFFFFFFF 45 return (b + nx_md5_rotl(v, s)) & 0xFFFFFFFF 46} 47func nx_md5_HH(a: i64, b: i64, c: i64, d: i64, m: i64, s: i64, t: i64) -> i64 { 48 let v: i64 = (a + nx_md5_H(b, c, d) + m + t) & 0xFFFFFFFF 49 return (b + nx_md5_rotl(v, s)) & 0xFFFFFFFF 50} 51func nx_md5_II(a: i64, b: i64, c: i64, d: i64, m: i64, s: i64, t: i64) -> i64 { 52 let v: i64 = (a + nx_md5_I(b, c, d) + m + t) & 0xFFFFFFFF 53 return (b + nx_md5_rotl(v, s)) & 0xFFFFFFFF 54} 55 56// Read u32 LE from byte buffer. 57func nx_md5_load_u32(p: *u8) -> i64 { 58 return (p[0] as i64) 59 | ((p[1] as i64) << 8) 60 | ((p[2] as i64) << 16) 61 | ((p[3] as i64) << 24) 62} 63 64// Process one 64-byte block, updating state[0..4]. 65func nx_md5_block(state: *i64, blk: *u8) -> i64 { 66 var a: i64 = state[0] 67 var b: i64 = state[1] 68 var c: i64 = state[2] 69 var d: i64 = state[3] 70 71 // Load 16 message words (LE). 72 let m_raw: *u8 = sys_mmap(64) 73 let m: *i64 = m_raw as *i64 74 var i: i64 = 0 75 while i < 16 { 76 let p: *u8 = (((blk as i64) + i * 4) as *u8) 77 m[i] = nx_md5_load_u32(p) 78 i = i + 1 79 } 80 81 // Round 1 82 a = nx_md5_FF(a, b, c, d, m[0], 7, 0xD76AA478) 83 d = nx_md5_FF(d, a, b, c, m[1], 12, 0xE8C7B756) 84 c = nx_md5_FF(c, d, a, b, m[2], 17, 0x242070DB) 85 b = nx_md5_FF(b, c, d, a, m[3], 22, 0xC1BDCEEE) 86 a = nx_md5_FF(a, b, c, d, m[4], 7, 0xF57C0FAF) 87 d = nx_md5_FF(d, a, b, c, m[5], 12, 0x4787C62A) 88 c = nx_md5_FF(c, d, a, b, m[6], 17, 0xA8304613) 89 b = nx_md5_FF(b, c, d, a, m[7], 22, 0xFD469501) 90 a = nx_md5_FF(a, b, c, d, m[8], 7, 0x698098D8) 91 d = nx_md5_FF(d, a, b, c, m[9], 12, 0x8B44F7AF) 92 c = nx_md5_FF(c, d, a, b, m[10], 17, 0xFFFF5BB1) 93 b = nx_md5_FF(b, c, d, a, m[11], 22, 0x895CD7BE) 94 a = nx_md5_FF(a, b, c, d, m[12], 7, 0x6B901122) 95 d = nx_md5_FF(d, a, b, c, m[13], 12, 0xFD987193) 96 c = nx_md5_FF(c, d, a, b, m[14], 17, 0xA679438E) 97 b = nx_md5_FF(b, c, d, a, m[15], 22, 0x49B40821) 98 99 // Round 2 100 a = nx_md5_GG(a, b, c, d, m[1], 5, 0xF61E2562) 101 d = nx_md5_GG(d, a, b, c, m[6], 9, 0xC040B340) 102 c = nx_md5_GG(c, d, a, b, m[11], 14, 0x265E5A51) 103 b = nx_md5_GG(b, c, d, a, m[0], 20, 0xE9B6C7AA) 104 a = nx_md5_GG(a, b, c, d, m[5], 5, 0xD62F105D) 105 d = nx_md5_GG(d, a, b, c, m[10], 9, 0x02441453) 106 c = nx_md5_GG(c, d, a, b, m[15], 14, 0xD8A1E681) 107 b = nx_md5_GG(b, c, d, a, m[4], 20, 0xE7D3FBC8) 108 a = nx_md5_GG(a, b, c, d, m[9], 5, 0x21E1CDE6) 109 d = nx_md5_GG(d, a, b, c, m[14], 9, 0xC33707D6) 110 c = nx_md5_GG(c, d, a, b, m[3], 14, 0xF4D50D87) 111 b = nx_md5_GG(b, c, d, a, m[8], 20, 0x455A14ED) 112 a = nx_md5_GG(a, b, c, d, m[13], 5, 0xA9E3E905) 113 d = nx_md5_GG(d, a, b, c, m[2], 9, 0xFCEFA3F8) 114 c = nx_md5_GG(c, d, a, b, m[7], 14, 0x676F02D9) 115 b = nx_md5_GG(b, c, d, a, m[12], 20, 0x8D2A4C8A) 116 117 // Round 3 118 a = nx_md5_HH(a, b, c, d, m[5], 4, 0xFFFA3942) 119 d = nx_md5_HH(d, a, b, c, m[8], 11, 0x8771F681) 120 c = nx_md5_HH(c, d, a, b, m[11], 16, 0x6D9D6122) 121 b = nx_md5_HH(b, c, d, a, m[14], 23, 0xFDE5380C) 122 a = nx_md5_HH(a, b, c, d, m[1], 4, 0xA4BEEA44) 123 d = nx_md5_HH(d, a, b, c, m[4], 11, 0x4BDECFA9) 124 c = nx_md5_HH(c, d, a, b, m[7], 16, 0xF6BB4B60) 125 b = nx_md5_HH(b, c, d, a, m[10], 23, 0xBEBFBC70) 126 a = nx_md5_HH(a, b, c, d, m[13], 4, 0x289B7EC6) 127 d = nx_md5_HH(d, a, b, c, m[0], 11, 0xEAA127FA) 128 c = nx_md5_HH(c, d, a, b, m[3], 16, 0xD4EF3085) 129 b = nx_md5_HH(b, c, d, a, m[6], 23, 0x04881D05) 130 a = nx_md5_HH(a, b, c, d, m[9], 4, 0xD9D4D039) 131 d = nx_md5_HH(d, a, b, c, m[12], 11, 0xE6DB99E5) 132 c = nx_md5_HH(c, d, a, b, m[15], 16, 0x1FA27CF8) 133 b = nx_md5_HH(b, c, d, a, m[2], 23, 0xC4AC5665) 134 135 // Round 4 136 a = nx_md5_II(a, b, c, d, m[0], 6, 0xF4292244) 137 d = nx_md5_II(d, a, b, c, m[7], 10, 0x432AFF97) 138 c = nx_md5_II(c, d, a, b, m[14], 15, 0xAB9423A7) 139 b = nx_md5_II(b, c, d, a, m[5], 21, 0xFC93A039) 140 a = nx_md5_II(a, b, c, d, m[12], 6, 0x655B59C3) 141 d = nx_md5_II(d, a, b, c, m[3], 10, 0x8F0CCC92) 142 c = nx_md5_II(c, d, a, b, m[10], 15, 0xFFEFF47D) 143 b = nx_md5_II(b, c, d, a, m[1], 21, 0x85845DD1) 144 a = nx_md5_II(a, b, c, d, m[8], 6, 0x6FA87E4F) 145 d = nx_md5_II(d, a, b, c, m[15], 10, 0xFE2CE6E0) 146 c = nx_md5_II(c, d, a, b, m[6], 15, 0xA3014314) 147 b = nx_md5_II(b, c, d, a, m[13], 21, 0x4E0811A1) 148 a = nx_md5_II(a, b, c, d, m[4], 6, 0xF7537E82) 149 d = nx_md5_II(d, a, b, c, m[11], 10, 0xBD3AF235) 150 c = nx_md5_II(c, d, a, b, m[2], 15, 0x2AD7D2BB) 151 b = nx_md5_II(b, c, d, a, m[9], 21, 0xEB86D391) 152 153 state[0] = (state[0] + a) & 0xFFFFFFFF 154 state[1] = (state[1] + b) & 0xFFFFFFFF 155 state[2] = (state[2] + c) & 0xFFFFFFFF 156 state[3] = (state[3] + d) & 0xFFFFFFFF 157 return 0 158} 159 160// Compute MD5 of `n` bytes from `data` into 16-byte `digest`. 161func nx_md5(data: *u8, n: i64, digest: *u8) -> i64 { 162 let state_raw: *u8 = sys_mmap(32) 163 let state: *i64 = state_raw as *i64 164 state[0] = 0x67452301 165 state[1] = 0xEFCDAB89 166 state[2] = 0x98BADCFE 167 state[3] = 0x10325476 168 169 var off: i64 = 0 170 while off + 64 <= n { 171 let p: *u8 = (((data as i64) + off) as *u8) 172 nx_md5_block(state, p) 173 off = off + 64 174 } 175 176 // Padding: 0x80 byte, then zeros, then length in bits as 8-byte LE. 177 let pad: *u8 = sys_mmap(128) 178 var pad_len: i64 = 0 179 let tail: i64 = n - off 180 var i: i64 = 0 181 while i < tail { 182 pad[i] = data[off + i] 183 i = i + 1 184 } 185 pad[tail] = 0x80 186 pad_len = tail + 1 187 // Pad with zeros until we land at length-mod-64 == 56. 188 var pad_done: i64 = 0 189 while pad_done == 0 { 190 let mod: i64 = pad_len & 63 191 if mod == 56 { pad_done = 1 } 192 else { pad[pad_len] = 0; pad_len = pad_len + 1 } 193 } 194 // Append 64-bit bit-length LE. 195 let bits: i64 = n * 8 196 pad[pad_len + 0] = bits & 0xFF 197 pad[pad_len + 1] = (bits >> 8) & 0xFF 198 pad[pad_len + 2] = (bits >> 16) & 0xFF 199 pad[pad_len + 3] = (bits >> 24) & 0xFF 200 pad[pad_len + 4] = (bits >> 32) & 0xFF 201 pad[pad_len + 5] = (bits >> 40) & 0xFF 202 pad[pad_len + 6] = (bits >> 48) & 0xFF 203 pad[pad_len + 7] = (bits >> 56) & 0xFF 204 pad_len = pad_len + 8 205 206 var po: i64 = 0 207 while po < pad_len { 208 let p: *u8 = (((pad as i64) + po) as *u8) 209 nx_md5_block(state, p) 210 po = po + 64 211 } 212 213 // Output digest LE. 214 var k: i64 = 0 215 while k < 4 { 216 digest[k * 4 + 0] = state[k] & 0xFF 217 digest[k * 4 + 1] = (state[k] >> 8) & 0xFF 218 digest[k * 4 + 2] = (state[k] >> 16) & 0xFF 219 digest[k * 4 + 3] = (state[k] >> 24) & 0xFF 220 k = k + 1 221 } 222 return 0 223} 224 225// ---- self-test --------------------------------------------------- 226 227func main() -> i64 { 228 let digest: *u8 = sys_mmap(16) 229 230 // MD5("") = D41D8CD98F00B204E9800998ECF8427E 231 let empty: *u8 = sys_mmap(8) 232 nx_md5(empty, 0, digest) 233 if digest[0] != 0xD4 { return __syscall(93, 1, 0, 0, 0, 0, 0) } 234 if digest[1] != 0x1D { return __syscall(93, 2, 0, 0, 0, 0, 0) } 235 if digest[15] != 0x7E { return __syscall(93, 3, 0, 0, 0, 0, 0) } 236 237 // MD5("abc") = 900150983CD24FB0D6963F7D28E17F72 238 let abc: *u8 = sys_mmap(8) 239 abc[0] = 0x61; abc[1] = 0x62; abc[2] = 0x63 240 nx_md5(abc, 3, digest) 241 if digest[0] != 0x90 { return __syscall(93, 4, 0, 0, 0, 0, 0) } 242 if digest[1] != 0x01 { return __syscall(93, 5, 0, 0, 0, 0, 0) } 243 if digest[15] != 0x72 { return __syscall(93, 6, 0, 0, 0, 0, 0) } 244 245 return 0 246}