1 (* Copyright (C
) 2001 Daniel Wang
. All rights reserved
.
2 Derived from the RSA Data Security
, Inc
. MD5 Message
-Digest Algorithm
.
7 (* type slice
= (Word8Vector
.vector
* int * int option
) *)
9 (* val updateSlice
: (md5state
* slice
) -> md5state
11 val update
: (md5state
* Word8Vector
.vector
) -> md5state
12 val final
: md5state
-> Word8Vector
.vector
13 val toHexString
: Word8Vector
.vector
-> string
16 (* Quick
and dirty transliteration
of C code
*)
17 structure MD5
:> MD5
=
19 structure W32
= Word32
23 fun extract (vec
, s
, l
) =
27 NONE
=> length vec
- s
30 tabulate (n
, fn i
=> sub (vec
, s
+ i
))
33 type word64
= {hi
:W32
.word,lo
:W32
.word}
34 type word128
= {A
:W32
.word, B
:W32
.word, C
:W32
.word, D
:W32
.word}
35 type md5state
= {digest
:word128
,
37 buf
:Word8Vector
.vector
}
41 val w64_zero
= ({hi
=0w0
,lo
=0w0
}:word64
)
42 fun mul8add ({hi
,lo
},n
) = let
43 val mul8lo
= W32
.<< (W32
.fromInt (n
),0w3
)
44 val mul8hi
= W32
.>> (W32
.fromInt (n
),0w29
)
45 val lo
= W32
.+ (lo
,mul8lo
)
46 val cout
= if W32
.< (lo
,mul8lo
) then 0w1
else 0w0
47 val hi
= W32
.+ (mul8hi
,W32
.+ (hi
,cout
))
51 fun packLittle wrds
= let
54 val b0
= Word8.fromLarge (W32
.toLarge w
)
55 val b1
= Word8.fromLarge (W32
.toLarge (W32
.>> (w
,0w8
)))
56 val b2
= Word8.fromLarge (W32
.toLarge (W32
.>> (w
,0w16
)))
57 val b3
= Word8.fromLarge (W32
.toLarge (W32
.>> (w
,0w24
)))
58 in b0
::b1
::b2
::b3
:: (loop ws
)
60 in W8V
.fromList (loop wrds
)
80 fun PADDING i
= W8V
.tabulate (i
,(fn 0 => 0wx80 | _
=> 0wx0
))
82 fun F (x
,y
,z
) = W32
.orb (W32
.andb (x
,y
),
83 W32
.andb (W32
.notb x
,z
))
84 fun G (x
,y
,z
) = W32
.orb (W32
.andb (x
,z
),
85 W32
.andb (y
,W32
.notb z
))
86 fun H (x
,y
,z
) = W32
.xorb (x
,W32
.xorb (y
,z
))
87 fun I (x
,y
,z
) = W32
.xorb (y
,W32
.orb (x
,W32
.notb z
))
88 fun ROTATE_LEFT (x
,n
) =
89 W32
.orb (W32
.<< (x
,n
), W32
.>> (x
,0w32
- n
))
91 fun XX
f (a
,b
,c
,d
,x
,s
,ac
) = let
92 val a
= W32
.+ (a
,W32
.+ (W32
.+ (f (b
,c
,d
),x
),ac
))
93 val a
= ROTATE_LEFT (a
,s
)
102 val empty_buf
= W8V
.tabulate (0,(fn x
=> raise (Fail
"buf")))
103 val init
= {digest
= {A
=0wx67452301
,
108 buf
=empty_buf
} : md5state
110 fun update ({buf
,digest
,mlen
}:md5state
,input
) = let
111 val inputLen
= W8V
.length input
112 val needBytes
= 64 - W8V
.length buf
113 fun loop (i
,digest
) =
114 if i
+ 63 < inputLen
then
115 loop (i
+ 64,transform (digest
,i
,input
))
117 val (buf
,(i
,digest
)) =
118 if inputLen
>= needBytes
then let
119 val buf
= W8V
.concat
[buf
,W8V
.extract (input
,0,SOME needBytes
)]
120 val digest
= transform (digest
,0,buf
)
121 in (empty_buf
,loop (needBytes
,digest
))
123 else (buf
,(0,digest
))
124 val buf
= W8V
.concat
[buf
, W8V
.extract (input
,i
,SOME (inputLen
-i
))]
125 val mlen
= mul8add (mlen
,inputLen
)
126 in {buf
=buf
,digest
=digest
,mlen
=mlen
}
128 and final (state
:md5state
) = let
129 val {mlen
= {lo
,hi
},buf
,...} = state
130 val bits
= packLittle
[lo
,hi
]
131 val index
= W8V
.length buf
132 val padLen
= if index
< 56 then 56 - index
else 120 - index
133 val state
= update (state
,PADDING padLen
)
134 val {digest
= {A
,B
,C
,D
},...} = update (state
,bits
)
135 in packLittle
[A
,B
,C
,D
]
137 and transform ({A
,B
,C
,D
},i
,buf
) = let
138 val off
= i
div PackWord32Little
.bytesPerElem
139 fun x (n
) = Word32
.fromLarge (PackWord32Little
.subVec (buf
,n
+ off
))
140 val (a
,b
,c
,d
) = (A
,B
,C
,D
)
141 (* fetch to avoid range checks
*)
142 val x_00
= x (0) val x_01
= x (1) val x_02
= x (2) val x_03
= x (3)
143 val x_04
= x (4) val x_05
= x (5) val x_06
= x (6) val x_07
= x (7)
144 val x_08
= x (8) val x_09
= x (9) val x_10
= x (10) val x_11
= x (11)
145 val x_12
= x (12) val x_13
= x (13) val x_14
= x (14) val x_15
= x (15)
147 val a
= FF (a
, b
, c
, d
, x_00
, S11
, 0wxd76aa478
) (* 1 *)
148 val d
= FF (d
, a
, b
, c
, x_01
, S12
, 0wxe8c7b756
) (* 2 *)
149 val c
= FF (c
, d
, a
, b
, x_02
, S13
, 0wx242070db
) (* 3 *)
150 val b
= FF (b
, c
, d
, a
, x_03
, S14
, 0wxc1bdceee
) (* 4 *)
151 val a
= FF (a
, b
, c
, d
, x_04
, S11
, 0wxf57c0faf
) (* 5 *)
152 val d
= FF (d
, a
, b
, c
, x_05
, S12
, 0wx4787c62a
) (* 6 *)
153 val c
= FF (c
, d
, a
, b
, x_06
, S13
, 0wxa8304613
) (* 7 *)
154 val b
= FF (b
, c
, d
, a
, x_07
, S14
, 0wxfd469501
) (* 8 *)
155 val a
= FF (a
, b
, c
, d
, x_08
, S11
, 0wx698098d8
) (* 9 *)
156 val d
= FF (d
, a
, b
, c
, x_09
, S12
, 0wx8b44f7af
) (* 10 *)
157 val c
= FF (c
, d
, a
, b
, x_10
, S13
, 0wxffff5bb1
) (* 11 *)
158 val b
= FF (b
, c
, d
, a
, x_11
, S14
, 0wx895cd7be
) (* 12 *)
159 val a
= FF (a
, b
, c
, d
, x_12
, S11
, 0wx6b901122
) (* 13 *)
160 val d
= FF (d
, a
, b
, c
, x_13
, S12
, 0wxfd987193
) (* 14 *)
161 val c
= FF (c
, d
, a
, b
, x_14
, S13
, 0wxa679438e
) (* 15 *)
162 val b
= FF (b
, c
, d
, a
, x_15
, S14
, 0wx49b40821
) (* 16 *)
165 val a
= GG (a
, b
, c
, d
, x_01
, S21
, 0wxf61e2562
) (* 17 *)
166 val d
= GG (d
, a
, b
, c
, x_06
, S22
, 0wxc040b340
) (* 18 *)
167 val c
= GG (c
, d
, a
, b
, x_11
, S23
, 0wx265e5a51
) (* 19 *)
168 val b
= GG (b
, c
, d
, a
, x_00
, S24
, 0wxe9b6c7aa
) (* 20 *)
169 val a
= GG (a
, b
, c
, d
, x_05
, S21
, 0wxd62f105d
) (* 21 *)
170 val d
= GG (d
, a
, b
, c
, x_10
, S22
, 0wx2441453
) (* 22 *)
171 val c
= GG (c
, d
, a
, b
, x_15
, S23
, 0wxd8a1e681
) (* 23 *)
172 val b
= GG (b
, c
, d
, a
, x_04
, S24
, 0wxe7d3fbc8
) (* 24 *)
173 val a
= GG (a
, b
, c
, d
, x_09
, S21
, 0wx21e1cde6
) (* 25 *)
174 val d
= GG (d
, a
, b
, c
, x_14
, S22
, 0wxc33707d6
) (* 26 *)
175 val c
= GG (c
, d
, a
, b
, x_03
, S23
, 0wxf4d50d87
) (* 27 *)
176 val b
= GG (b
, c
, d
, a
, x_08
, S24
, 0wx455a14ed
) (* 28 *)
177 val a
= GG (a
, b
, c
, d
, x_13
, S21
, 0wxa9e3e905
) (* 29 *)
178 val d
= GG (d
, a
, b
, c
, x_02
, S22
, 0wxfcefa3f8
) (* 30 *)
179 val c
= GG (c
, d
, a
, b
, x_07
, S23
, 0wx676f02d9
) (* 31 *)
180 val b
= GG (b
, c
, d
, a
, x_12
, S24
, 0wx8d2a4c8a
) (* 32 *)
183 val a
= HH (a
, b
, c
, d
, x_05
, S31
, 0wxfffa3942
) (* 33 *)
184 val d
= HH (d
, a
, b
, c
, x_08
, S32
, 0wx8771f681
) (* 34 *)
185 val c
= HH (c
, d
, a
, b
, x_11
, S33
, 0wx6d9d6122
) (* 35 *)
186 val b
= HH (b
, c
, d
, a
, x_14
, S34
, 0wxfde5380c
) (* 36 *)
187 val a
= HH (a
, b
, c
, d
, x_01
, S31
, 0wxa4beea44
) (* 37 *)
188 val d
= HH (d
, a
, b
, c
, x_04
, S32
, 0wx4bdecfa9
) (* 38 *)
189 val c
= HH (c
, d
, a
, b
, x_07
, S33
, 0wxf6bb4b60
) (* 39 *)
190 val b
= HH (b
, c
, d
, a
, x_10
, S34
, 0wxbebfbc70
) (* 40 *)
191 val a
= HH (a
, b
, c
, d
, x_13
, S31
, 0wx289b7ec6
) (* 41 *)
192 val d
= HH (d
, a
, b
, c
, x_00
, S32
, 0wxeaa127fa
) (* 42 *)
193 val c
= HH (c
, d
, a
, b
, x_03
, S33
, 0wxd4ef3085
) (* 43 *)
194 val b
= HH (b
, c
, d
, a
, x_06
, S34
, 0wx4881d05
) (* 44 *)
195 val a
= HH (a
, b
, c
, d
, x_09
, S31
, 0wxd9d4d039
) (* 45 *)
196 val d
= HH (d
, a
, b
, c
, x_12
, S32
, 0wxe6db99e5
) (* 46 *)
197 val c
= HH (c
, d
, a
, b
, x_15
, S33
, 0wx1fa27cf8
) (* 47 *)
198 val b
= HH (b
, c
, d
, a
, x_02
, S34
, 0wxc4ac5665
) (* 48 *)
201 val a
= II (a
, b
, c
, d
, x_00
, S41
, 0wxf4292244
) (* 49 *)
202 val d
= II (d
, a
, b
, c
, x_07
, S42
, 0wx432aff97
) (* 50 *)
203 val c
= II (c
, d
, a
, b
, x_14
, S43
, 0wxab9423a7
) (* 51 *)
204 val b
= II (b
, c
, d
, a
, x_05
, S44
, 0wxfc93a039
) (* 52 *)
205 val a
= II (a
, b
, c
, d
, x_12
, S41
, 0wx655b59c3
) (* 53 *)
206 val d
= II (d
, a
, b
, c
, x_03
, S42
, 0wx8f0ccc92
) (* 54 *)
207 val c
= II (c
, d
, a
, b
, x_10
, S43
, 0wxffeff47d
) (* 55 *)
208 val b
= II (b
, c
, d
, a
, x_01
, S44
, 0wx85845dd1
) (* 56 *)
209 val a
= II (a
, b
, c
, d
, x_08
, S41
, 0wx6fa87e4f
) (* 57 *)
210 val d
= II (d
, a
, b
, c
, x_15
, S42
, 0wxfe2ce6e0
) (* 58 *)
211 val c
= II (c
, d
, a
, b
, x_06
, S43
, 0wxa3014314
) (* 59 *)
212 val b
= II (b
, c
, d
, a
, x_13
, S44
, 0wx4e0811a1
) (* 60 *)
213 val a
= II (a
, b
, c
, d
, x_04
, S41
, 0wxf7537e82
) (* 61 *)
214 val d
= II (d
, a
, b
, c
, x_11
, S42
, 0wxbd3af235
) (* 62 *)
215 val c
= II (c
, d
, a
, b
, x_02
, S43
, 0wx2ad7d2bb
) (* 63 *)
216 val b
= II (b
, c
, d
, a
, x_09
, S44
, 0wxeb86d391
) (* 64 *)
218 val A
= Word32
.+ (A
,a
)
219 val B
= Word32
.+ (B
,b
)
220 val C
= Word32
.+ (C
,c
)
221 val D
= Word32
.+ (D
,d
)
225 val hxd
= "0123456789abcdef"
226 fun toHexString v
= let
227 fun byte2hex (b
,acc
) =
228 (String.sub (hxd
,(Word8.toInt b
) div 16))::
229 (String.sub (hxd
,(Word8.toInt b
) mod 16))::acc
230 val digits
= Word8Vector
.foldr byte2hex
[] v
231 in String.implode (digits
)
238 [("", "d41d8cd98f00b204e9800998ecf8427e"),
239 ("a", "0cc175b9c0f1b6a831c399e269772661"),
240 ("abc", "900150983cd24fb0d6963f7d28e17f72"),
241 ("message digest", "f96b697d7cb7938d525a2f31aaf161d0"),
242 ("abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b"),
243 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
244 "d174ab98d277d9f5a5611c2c9f419d9f"),
245 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890",
246 "57edf4a22be3c955ac49da2e2107b67a")]
248 fun do_tests () = let
250 val mstate
= MD5
.update (MD5
.init
,Byte
.stringToBytes x
)
251 val hash
= MD5
.final (mstate
)
252 in print (" input: "^x^
"\n");
253 print ("expected: "^s^
"\n");
254 print ("produced: "^MD5
.toHexString (hash
)^
"\n")
258 val BLOCK_LEN
= 10000
259 val BLOCK_COUNT
= 100000
260 fun time_test () = let
261 val block
= Word8Vector
.tabulate (BLOCK_LEN
,Word8.fromInt
)
263 if n
< BLOCK_COUNT
then
264 loop (n
+1,MD5
.update (s
,block
))
276 else (Test
.time_test ()