/* * This file is part of the Fun programming language. * https://fun-lang.xyz/ * * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 * * Added: 2025-10-01 */ #include // Legacy global API and helpers. Please use the MD5 class (see lib/crypt/md5.fun). fun u32(x) // bring x into [0 .. 2^32-1] by modular reduction m = 4294967296 while x < 0 x = x + m while x >= m x = x - m return x fun shl32(x, s) return shl(u32(x), s) fun shr32(x, s) return shr(u32(x), s) fun rol32(x, s) return rol(u32(x), s) fun and32(a, b) return band(u32(a), u32(b)) fun or32(a, b) return bor(u32(a), u32(b)) fun xor32(a, b) return bxor(u32(a), u32(b)) fun not32(x) return bnot(u32(x)) fun add32(a, b) return u32(a + b) fun add32_4(a, b, c, d) return u32(u32(u32(a + b) + c) + d) fun hex_val(ch) if (ch == "0") return 0 else if (ch == "1") return 1 else if (ch == "2") return 2 else if (ch == "3") return 3 else if (ch == "4") return 4 else if (ch == "5") return 5 else if (ch == "6") return 6 else if (ch == "7") return 7 else if (ch == "8") return 8 else if (ch == "9") return 9 else if (ch == "a" || ch == "A") return 10 else if (ch == "b" || ch == "B") return 11 else if (ch == "c" || ch == "C") return 12 else if (ch == "d" || ch == "D") return 13 else if (ch == "e" || ch == "E") return 14 else if (ch == "f" || ch == "F") return 15 else return 0 fun byte_from_hex_pair(hh) // hh is 2-char string hi = hex_val(substr(hh, 0, 1)) lo = hex_val(substr(hh, 1, 1)) return hi * 16 + lo fun from_hex(hex) // parse even-length hex string to array of bytes (numbers 0..255) arr = [] i = 0 n = len(hex) while i + 1 < n b = byte_from_hex_pair(substr(hex, i, 2)) push(arr, b) i = i + 2 return arr fun two_hex(n) n = n % 256 d = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"] hi = n / 16 lo = n % 16 // build with join parts = [d[hi], d[lo]] return join(parts, "") fun bytes_to_hex(arr) i = 0 out = [] while i < len(arr) push(out, two_hex(arr[i])) i = i + 1 return join(out, "") // MD5 auxiliary functions fun F(x, y, z) return or32(and32(x, y), and32(not32(x), z)) fun G(x, y, z) return or32(and32(x, z), and32(y, not32(z))) fun H(x, y, z) return xor32(xor32(x, y), z) fun I(x, y, z) return xor32(y, or32(x, not32(z))) // T constants (K) = floor(abs(sin(i+1)) * 2^32), i=0..63 K = [ 3614090360, 3905402710, 606105819, 3250441966, 4118548399, 1200080426, 2821735955, 4249261313, 1770035416, 2336552879, 4294925233, 2304563134, 1804603682, 4254626195, 2792965006, 1236535329, 4129170786, 3225465664, 643717713, 3921069994, 3593408605, 38016083, 3634488961, 3889429448, 568446438, 3275163606, 4107603335, 1163531501, 2850285829, 4243563512, 1735328473, 2368359562, 4294588738, 2272392833, 1839030562, 4259657740, 2763975236, 1272893353, 4139469664, 3200236656, 681279174, 3936430074, 3572445317, 76029189, 3654602809, 3873151461, 530742520, 3299628645, 4096336452, 1126891415, 2878612391, 4237533241, 1700485571, 2399980690, 4293915773, 2240044497, 1873313359, 4264355552, 2734768916, 1309151649, 4149444226, 3174756917, 718787259, 3951481745 ] // per-round rotation amounts S = [ 7,12,17,22, 7,12,17,22, 7,12,17,22, 7,12,17,22, 5, 9,14,20, 5, 9,14,20, 5, 9,14,20, 5, 9,14,20, 4,11,16,23, 4,11,16,23, 4,11,16,23, 4,11,16,23, 6,10,15,21, 6,10,15,21, 6,10,15,21, 6,10,15,21 ] fun pad_bytes(bytes) // returns a new array with MD5 padding L = len(bytes) out = [] // copy original bytes i = 0 while i < L push(out, bytes[i]) i = i + 1 // append 0x80 push(out, 128) // pad zeros until length % 64 == 56 while (len(out) % 64) != 56 push(out, 0) // append length (bits) as 64-bit little-endian len_bits = L * 8 j = 0 while j < 8 b = (len_bits / pow(2, 8 * j)) % 256 push(out, b) j = j + 1 return out fun word32_le(b0, b1, b2, b3) return u32(b0 + b1 * 256 + b2 * 65536 + b3 * 16777216) fun process_block(state, block) // state: [a0,b0,c0,d0], block: 64 bytes array a0 = state[0] b0 = state[1] c0 = state[2] d0 = state[3] // M[16] little-endian words M = [] i = 0 while i < 16 j = i * 4 w = word32_le(block[j], block[j+1], block[j+2], block[j+3]) push(M, w) i = i + 1 A = a0 B = b0 C = c0 D = d0 k = 0 while k < 64 if (k < 16) f = F(B, C, D) g = k else if (k < 32) f = G(B, C, D) g = (5 * k + 1) % 16 else if (k < 48) f = H(B, C, D) g = (3 * k + 5) % 16 else f = I(B, C, D) g = (7 * k) % 16 tmp = D sum = add32_4(A, f, K[k], M[g]) D = C C = B B = add32(B, rol32(sum, S[k])) A = tmp k = k + 1 state[0] = add32(state[0], A) state[1] = add32(state[1], B) state[2] = add32(state[2], C) state[3] = add32(state[3], D) return state fun md5_bytes(bytes) // returns array of 16 bytes of digest data = pad_bytes(bytes) // initial state a0 = 1732584193 // 0x67452301 b0 = 4023233417 // 0xEFCDAB89 c0 = 2562383102 // 0x98BADCFE d0 = 271733878 // 0x10325476 state = [a0, b0, c0, d0] off = 0 N = len(data) while off < N block = [] i = 0 while i < 64 push(block, data[off + i]) i = i + 1 state = process_block(state, block) off = off + 64 // output digest as little-endian bytes of A,B,C,D out = [] j = 0 while j < 4 v = state[j] // 4 bytes little-endian push(out, (v) % 256) push(out, (v / 256) % 256) push(out, (v / 65536) % 256) push(out, (v / 16777216) % 256) j = j + 1 return out fun md5_hex(hexStr) bytes = from_hex(hexStr) digest = md5_bytes(bytes) return bytes_to_hex(digest) fun md5_str(this, str) bytes = string_to_bytes_ascii(str) digest = this.md5_bytes(bytes) return this.bytes_to_hex(digest)