add text file read unit and test program to go with it
[lcore.git] / fastmd5.pas
1 { Copyright (C) 2005 Bas Steendijk and Peter Green\r
2   For conditions of distribution and use, see copyright notice in zlib_license.txt\r
3   Which is included in the package\r
4   ----------------------------------------------------------------------------- }\r
5 \r
6 unit fastmd5;\r
7 \r
8 {\r
9 pascal implementation of MD5\r
10 \r
11 written by Bas Steendijk - steendijk@xs4all.nl\r
12 \r
13 based on RFC1321 - The MD5 Message-Digest Algorithm\r
14 \r
15 optimized for speed: saved on copying and sub calls in the core routine\r
16 \r
17 verified on:\r
18 - Borland Delphi 3\r
19 - Borland Turbo Pascal 7\r
20 - Free Pascal 1.0.6 for i386 (on *nix)\r
21 - various other versions of freepascal on windows and linux i386\r
22 - various other versions of delphi\r
23 - free pascal 1.9.5 on powerpc darwin\r
24 \r
25 this unit is endian portable but is likely to be significantly slower on big endian systems\r
26 }\r
27 \r
28 {$Q-,R-}\r
29 \r
30 interface\r
31 \r
32 \r
33 \r
34 \r
35 \r
36 type\r
37   Tmd5=array[0..15] of byte;\r
38 \r
39 {$i uint32.inc}\r
40 \r
41 type\r
42   dvar=array[0..0] of byte;\r
43   Tmd5state=record\r
44     buf:array[0..63] of byte;\r
45     H:array[0..3] of uint32;\r
46     msglen:longint;\r
47     msglenhi:longint;\r
48   end;\r
49 \r
50 procedure md5processblock(var h:array of uint32;const data);\r
51 \r
52 procedure md5init(var state:tmd5state);\r
53 procedure md5process(var state:tmd5state;const data;len:longint);\r
54 procedure md5finish(var state:tmd5state;var result);\r
55 \r
56 procedure getmd5(const data;len:longint;var result);\r
57 \r
58 function md5tostr(const md5:tmd5):string;\r
59 \r
60 implementation\r
61 \r
62 function inttohex(val,bits:integer):string;\r
63 const\r
64   hexchar:array[0..15] of char='0123456789abcdef';\r
65 begin\r
66   inttohex := hexchar[val shr 4]+hexchar[val and $f];\r
67 end;\r
68 \r
69 {$ifdef cpu386}\r
70 function rol(w,bits:uint32): uint32; assembler;\r
71 asm\r
72   {cpu386 is not defined on freepascal. but fpc assembler is incompatible, uses different code}\r
73   {inline($89/$d1/$d3/$c0);}\r
74   mov   ecx,edx\r
75   rol   eax,cl\r
76 end;\r
77 {$else}\r
78 function rol(w,bits:uint32):uint32;\r
79 begin\r
80   rol := (w shl bits) or (w shr (32-bits));\r
81 end;\r
82 {$endif}\r
83 \r
84 \r
85 {function swapbytes(invalue:uint32):uint32;\r
86 var\r
87   inbytes  : array[0..3] of byte absolute invalue;\r
88   outbytes : array[0..3] of byte absolute result;\r
89 \r
90 \r
91 begin\r
92   outbytes[0] := inbytes[3];\r
93   outbytes[1] := inbytes[2];\r
94   outbytes[2] := inbytes[1];\r
95   outbytes[3] := inbytes[0];\r
96 end;}\r
97 \r
98 procedure md5processblock(var h:array of uint32;const data);\r
99 const\r
100   S11=7;  S12=12;  S13=17;  S14=22;\r
101   S21=5;  S22=9;   S23=14;  S24=20;\r
102   S31=4;  S32=11;  S33=16;  S34=23;\r
103   S41=6;  S42=10;  S43=15;  S44=21;\r
104 \r
105 var\r
106   A,B,C,D:uint32;\r
107   w:array[0..63] of byte absolute data;\r
108   x:array[0..15] of uint32 {$ifndef ENDIAN_BIG} absolute data{$endif} ;\r
109   y:array[0..63] of byte absolute x;\r
110   {$ifdef ENDIAN_BIG}counter : integer;{$endif}\r
111 begin\r
112   A := h[0];\r
113   B := h[1];\r
114   C := h[2];\r
115   D := h[3];\r
116   {$ifdef ENDIAN_BIG}\r
117     for counter := 0 to 63 do begin\r
118       y[counter] := w[counter xor 3];\r
119     end;\r
120   {$endif}\r
121   a := rol(a + ((b and c) or ((not b) and d)) + x[ 0] + $d76aa478, S11) + b;\r
122   d := rol(d + ((a and b) or ((not a) and c)) + x[ 1] + $e8c7b756, S12) + a;\r
123   c := rol(c + ((d and a) or ((not d) and b)) + x[ 2] + $242070db, S13) + d;\r
124   b := rol(b + ((c and d) or ((not c) and a)) + x[ 3] + $c1bdceee, S14) + c;\r
125   a := rol(a + ((b and c) or ((not b) and d)) + x[ 4] + $f57c0faf, S11) + b;\r
126   d := rol(d + ((a and b) or ((not a) and c)) + x[ 5] + $4787c62a, S12) + a;\r
127   c := rol(c + ((d and a) or ((not d) and b)) + x[ 6] + $a8304613, S13) + d;\r
128   b := rol(b + ((c and d) or ((not c) and a)) + x[ 7] + $fd469501, S14) + c;\r
129   a := rol(a + ((b and c) or ((not b) and d)) + x[ 8] + $698098d8, S11) + b;\r
130   d := rol(d + ((a and b) or ((not a) and c)) + x[ 9] + $8b44f7af, S12) + a;\r
131   c := rol(c + ((d and a) or ((not d) and b)) + x[10] + $ffff5bb1, S13) + d;\r
132   b := rol(b + ((c and d) or ((not c) and a)) + x[11] + $895cd7be, S14) + c;\r
133   a := rol(a + ((b and c) or ((not b) and d)) + x[12] + $6b901122, S11) + b;\r
134   d := rol(d + ((a and b) or ((not a) and c)) + x[13] + $fd987193, S12) + a;\r
135   c := rol(c + ((d and a) or ((not d) and b)) + x[14] + $a679438e, S13) + d;\r
136   b := rol(b + ((c and d) or ((not c) and a)) + x[15] + $49b40821, S14) + c;\r
137 \r
138   a := rol(a + ((b and d) or (c and (not d))) + x[ 1] + $f61e2562, S21) + b;\r
139   d := rol(d + ((a and c) or (b and (not c))) + x[ 6] + $c040b340, S22) + a;\r
140   c := rol(c + ((d and b) or (a and (not b))) + x[11] + $265e5a51, S23) + d;\r
141   b := rol(b + ((c and a) or (d and (not a))) + x[ 0] + $e9b6c7aa, S24) + c;\r
142   a := rol(a + ((b and d) or (c and (not d))) + x[ 5] + $d62f105d, S21) + b;\r
143   d := rol(d + ((a and c) or (b and (not c))) + x[10] + $02441453, S22) + a;\r
144   c := rol(c + ((d and b) or (a and (not b))) + x[15] + $d8a1e681, S23) + d;\r
145   b := rol(b + ((c and a) or (d and (not a))) + x[ 4] + $e7d3fbc8, S24) + c;\r
146   a := rol(a + ((b and d) or (c and (not d))) + x[ 9] + $21e1cde6, S21) + b;\r
147   d := rol(d + ((a and c) or (b and (not c))) + x[14] + $c33707d6, S22) + a;\r
148   c := rol(c + ((d and b) or (a and (not b))) + x[ 3] + $f4d50d87, S23) + d;\r
149   b := rol(b + ((c and a) or (d and (not a))) + x[ 8] + $455a14ed, S24) + c;\r
150   a := rol(a + ((b and d) or (c and (not d))) + x[13] + $a9e3e905, S21) + b;\r
151   d := rol(d + ((a and c) or (b and (not c))) + x[ 2] + $fcefa3f8, S22) + a;\r
152   c := rol(c + ((d and b) or (a and (not b))) + x[ 7] + $676f02d9, S23) + d;\r
153   b := rol(b + ((c and a) or (d and (not a))) + x[12] + $8d2a4c8a, S24) + c;\r
154 \r
155   a := rol(a + (b xor c xor d) + x[ 5] + $fffa3942, S31) + b;\r
156   d := rol(d + (a xor b xor c) + x[ 8] + $8771f681, S32) + a;\r
157   c := rol(c + (d xor a xor b) + x[11] + $6d9d6122, S33) + d;\r
158   b := rol(b + (c xor d xor a) + x[14] + $fde5380c, S34) + c;\r
159   a := rol(a + (b xor c xor d) + x[ 1] + $a4beea44, S31) + b;\r
160   d := rol(d + (a xor b xor c) + x[ 4] + $4bdecfa9, S32) + a;\r
161   c := rol(c + (d xor a xor b) + x[ 7] + $f6bb4b60, S33) + d;\r
162   b := rol(b + (c xor d xor a) + x[10] + $bebfbc70, S34) + c;\r
163   a := rol(a + (b xor c xor d) + x[13] + $289b7ec6, S31) + b;\r
164   d := rol(d + (a xor b xor c) + x[ 0] + $eaa127fa, S32) + a;\r
165   c := rol(c + (d xor a xor b) + x[ 3] + $d4ef3085, S33) + d;\r
166   b := rol(b + (c xor d xor a) + x[ 6] + $04881d05, S34) + c;\r
167   a := rol(a + (b xor c xor d) + x[ 9] + $d9d4d039, S31) + b;\r
168   d := rol(d + (a xor b xor c) + x[12] + $e6db99e5, S32) + a;\r
169   c := rol(c + (d xor a xor b) + x[15] + $1fa27cf8, S33) + d;\r
170   b := rol(b + (c xor d xor a) + x[ 2] + $c4ac5665, S34) + c;\r
171 \r
172   a := rol(a + (c xor (b or (not d))) + x[ 0] + $f4292244, S41) + b;\r
173   d := rol(d + (b xor (a or (not c))) + x[ 7] + $432aff97, S42) + a;\r
174   c := rol(c + (a xor (d or (not b))) + x[14] + $ab9423a7, S43) + d;\r
175   b := rol(b + (d xor (c or (not a))) + x[ 5] + $fc93a039, S44) + c;\r
176   a := rol(a + (c xor (b or (not d))) + x[12] + $655b59c3, S41) + b;\r
177   d := rol(d + (b xor (a or (not c))) + x[ 3] + $8f0ccc92, S42) + a;\r
178   c := rol(c + (a xor (d or (not b))) + x[10] + $ffeff47d, S43) + d;\r
179   b := rol(b + (d xor (c or (not a))) + x[ 1] + $85845dd1, S44) + c;\r
180   a := rol(a + (c xor (b or (not d))) + x[ 8] + $6fa87e4f, S41) + b;\r
181   d := rol(d + (b xor (a or (not c))) + x[15] + $fe2ce6e0, S42) + a;\r
182   c := rol(c + (a xor (d or (not b))) + x[ 6] + $a3014314, S43) + d;\r
183   b := rol(b + (d xor (c or (not a))) + x[13] + $4e0811a1, S44) + c;\r
184   a := rol(a + (c xor (b or (not d))) + x[ 4] + $f7537e82, S41) + b;\r
185   d := rol(d + (b xor (a or (not c))) + x[11] + $bd3af235, S42) + a;\r
186   c := rol(c + (a xor (d or (not b))) + x[ 2] + $2ad7d2bb, S43) + d;\r
187   b := rol(b + (d xor (c or (not a))) + x[ 9] + $eb86d391, S44) + c;\r
188 \r
189   inc(h[0],A);\r
190   inc(h[1],B);\r
191   inc(h[2],C);\r
192   inc(h[3],D);\r
193 end;\r
194 \r
195 procedure md5init(var state:tmd5state);\r
196 begin\r
197   state.h[0] := $67452301;\r
198   state.h[1] := $EFCDAB89;\r
199   state.h[2] := $98BADCFE;\r
200   state.h[3] := $10325476;\r
201   state.msglen := 0;\r
202   state.msglenhi := 0;\r
203 end;\r
204 \r
205 procedure md5process(var state:tmd5state;const data;len:longint);\r
206 var\r
207   a,b:longint;\r
208   ofs:longint;\r
209   p:dvar absolute data;\r
210 begin\r
211   b := state.msglen and 63;\r
212 \r
213   inc(state.msglen,len);\r
214   while (state.msglen > $20000000) do begin\r
215     dec(state.msglen,$20000000);\r
216     inc(state.msglenhi);\r
217   end;\r
218   ofs := 0;\r
219   if b > 0 then begin\r
220     a := 64-b;\r
221     if a > len then a := len;\r
222     move(p[0],state.buf[b],a);\r
223     inc(ofs,a);\r
224     dec(len,a);\r
225     if b+a = 64 then md5processblock(state.h,state.buf);\r
226     if len = 0 then exit;\r
227   end;\r
228   while len >= 64 do begin\r
229     md5processblock(state.h,p[ofs]);\r
230     inc(ofs,64);\r
231     dec(len,64);\r
232   end;\r
233   if len > 0 then move(p[ofs],state.buf[0],len);\r
234 end;\r
235 \r
236 procedure md5finish(var state:tmd5state;var result);\r
237 var\r
238   b       :integer;\r
239   {$ifdef endian_big}\r
240     h       :tmd5 absolute state.h;\r
241     r       :tmd5 absolute result;\r
242     counter :integer ;\r
243   {$endif}\r
244 begin\r
245   b := state.msglen and 63;\r
246   state.buf[b] := $80;\r
247   if b >= 56 then begin\r
248     {-- for a := b+1 to 63 do state.buf[a] := 0; }\r
249     fillchar(state.buf[b+1],63-b,0);\r
250     md5processblock(state.h,state.buf);\r
251     fillchar(state.buf,56,0);\r
252   end else begin\r
253     {-- for a := b+1 to 55 do state.buf[a] := 0; }\r
254     fillchar(state.buf[b+1],55-b,0);\r
255   end;\r
256   state.msglen := state.msglen shl 3;\r
257 \r
258   state.buf[56] := state.msglen;\r
259   state.buf[57] := state.msglen shr 8;\r
260   state.buf[58] := state.msglen shr 16;\r
261   state.buf[59] := state.msglen shr 24;\r
262   state.buf[60] := state.msglenhi;\r
263   state.buf[61] := state.msglenhi shr 8;\r
264   state.buf[62] := state.msglenhi shr 16;\r
265   state.buf[63] := state.msglenhi shr 24;\r
266 \r
267   md5processblock(state.h,state.buf);\r
268   {$ifdef ENDIAN_BIG}\r
269     for counter := 0 to 15 do begin\r
270       r[counter] := h[counter xor 3];\r
271     end;\r
272   {$else} \r
273     move(state.h,result,16);\r
274   {$endif}\r
275   fillchar(state,sizeof(state),0);\r
276 end;\r
277 \r
278 procedure getmd5(const data;len:longint;var result);\r
279 var\r
280   t:tmd5state;\r
281 begin\r
282   md5init(t);\r
283   md5process(t,data,len);\r
284   md5finish(t,result);\r
285 end;\r
286 \r
287 function md5tostr(const md5:tmd5):string;\r
288 var\r
289   a:integer;\r
290   s:string;\r
291 begin\r
292   s := '';\r
293   for a := 0 to 15 do s := s + inttohex(md5[a],2);\r
294   md5tostr := s;\r
295 end;\r
296 \r
297 end.\r