272 lines
6 KiB
Standard ML
272 lines
6 KiB
Standard ML
/*
|
|
* This file is part of the Fun programming language.
|
|
* https://hanez.org/project/fun/
|
|
*
|
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
|
* Licensed under the terms of the Apache-2.0 license.
|
|
* https://opensource.org/license/apache-2-0
|
|
*
|
|
* Added: 2025-10-01
|
|
*/
|
|
|
|
#include <strings.fun>
|
|
|
|
// 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)
|