RosettaCodeData/Task/MD5-Implementation/Icon/md5-implementation.icon

103 lines
4.5 KiB
Plaintext

procedure main() # validate against the RFC test strings and more
testMD5("The quick brown fox jumps over the lazy dog", 16r9e107d9d372bb6826bd81d3542a419d6)
testMD5("The quick brown fox jumps over the lazy dog.", 16re4d909c290d0fb1ca068ffaddf22cbd0)
testMD5("", 16rd41d8cd98f00b204e9800998ecf8427e) #R = MD5 test suite from RFC
testMD5("a", 16r0cc175b9c0f1b6a831c399e269772661) #R
testMD5("abc", 16r900150983cd24fb0d6963f7d28e17f72) #R
testMD5("message digest", 16rf96b697d7cb7938d525a2f31aaf161d0) #R
testMD5("abcdefghijklmnopqrstuvwxyz", 16rc3fcd3d76192e4007dfb496cca67e13b) #R
testMD5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 16rd174ab98d277d9f5a5611c2c9f419d9f) #R
testMD5("12345678901234567890123456789012345678901234567890123456789012345678901234567890", 16r57edf4a22be3c955ac49da2e2107b67a) #R
end
procedure testMD5(s,rh) # compute the MD5 hash and compare it to reference value
write("Message(length=",*s,") = ",image(s))
write("Digest = ",hexstring(h := MD5(s)),if h = rh then " matches reference hash" else (" does not match reference hash = " || hexstring(rh)),"\n")
end
link hexcvt # for testMD5
$define B32 4 # 32 bits
$define B64 8 # 64 bits in bytes
$define B512 64 # 512 bits in bytes
$define M32 16r100000000 # 2^32
$define M64 16r10000000000000000 # 2^64
procedure MD5(s) #: return MD5 hash of message s
local w,a,b,c,d,i,t,m
local mlength,message,hash
static rs,ks,istate,maxpad,g
initial {
every (rs := []) |||:=
(t := [ 7, 12, 17, 22] | [ 5, 9, 14, 20] | [ 4, 11, 16, 23] | [ 6, 10, 15, 21]) ||| t ||| t ||| t
every put(ks := [],integer(M32 * abs(sin(i := 1 to 64))))
istate := [ 16r67452301, 16rEFCDAB89, 16r98BADCFE, 16r10325476 ] # "Magic" IV
maxpad := left(char(16r80),B512+B64,char(16r00)) # maximum possible padding
g := []
every i := 0 to 63 do # precompute offsets
case round := i/16 of {
0 : put(g,i + 1)
1 : put(g,(5*i+1) % 16 + 1)
2 : put(g,(3*i+5) % 16 + 1)
3 : put(g,(7*i) % 16 + 1)
}
if not (*rs = *ks = 64) then runerr(500,"MD5 setup error")
}
# 1. Construct prefix
t := (*s*8)%M64 # original message length
s ||:= maxpad # append maximum padding
s[0-:*s%B512] := "" # trim to final length
s[0-:B64] := reverse(unsigned2string(t,B64) ) # as little endian length
message := [] # 2. Subdivide message
s ? while put(message,move(B512)) # into 512 bit blocks
# 3. Transform message ...
state := copy(istate) # Initialize hashes
every m := !message do { # For each message block
w := []
m ? while put(w,unsigned(reverse(move(B32)))) # break into little-endian words
a := state[1] # pick up hashes
b := state[2]
c := state[3]
d := state[4]
every i := 1 to 64 do { # Process 4 rounds of hashes
case round := (i-1)/16 of {
0 : a +:= ixor(d, iand(b,ixor(c,d))) # 0..15 - alternate F
1 : a +:= ixor(c,iand(d,ixor(b,c))) # 16..31 - alternate G
2 : a +:= ixor(b,ixor(c,d)) # 32..47 - H
3 : a +:= ixor(c,ior(b,ixor(d,16rffffffff))) # 48..64 - alternate I
} # Core of FF, GG, HH, II
a +:= ks[i] + w[g[i]] # and the rest
a %:= M32
a := ior( ishift(a,rs[i]), ishift(a,-(32-rs[i]))) # 32bit rotate
a +:= b
a :=: b :=: c :=: d # rotate variables
}
state[1] +:= a # Add back new hashes
state[2] +:= b
state[3] +:= c
state[4] +:= d
every !state %:= M32 # mod 2^32
}
every (hash := "") ||:= reverse(unsigned2string(!state,4)) # little-endian digest
return unsigned(hash)
end
procedure unsigned2string(i,w) # uint to string pad to w bytes
local s
if i < 0 then runerr(500,i)
s := ""
while (0 < i) | (*s < \w) do {
s ||:= char(i % 256)
i /:= 256
}
return reverse(s)
end
link unsigned # string to unsigned integer