You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MD5.pas 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. // tabs = 2
  2. // -----------------------------------------------------------------------------------------------
  3. //
  4. // MD5 Message-Digest for Delphi 4
  5. //
  6. // Delphi 4 Unit implementing the
  7. // RSA Data Security, Inc. MD5 Message-Digest Algorithm
  8. //
  9. // Implementation of Ronald L. Rivest's RFC 1321
  10. //
  11. // Copyright � 1997-1999 Medienagentur Fichtner & Meyer
  12. // Written by Matthias Fichtner
  13. //
  14. // -----------------------------------------------------------------------------------------------
  15. // See RFC 1321 for RSA Data Security's copyright and license notice!
  16. // -----------------------------------------------------------------------------------------------
  17. //
  18. // 14-Jun-97 mf Implemented MD5 according to RFC 1321 RFC 1321
  19. // 16-Jun-97 mf Initial release of the compiled unit (no source code) RFC 1321
  20. // 28-Feb-99 mf Added MD5Match function for comparing two digests RFC 1321
  21. // 13-Sep-99 mf Reworked the entire unit RFC 1321
  22. // 17-Sep-99 mf Reworked the "Test Driver" project RFC 1321
  23. // 19-Sep-99 mf Release of sources for MD5 unit and "Test Driver" project RFC 1321
  24. //
  25. // -----------------------------------------------------------------------------------------------
  26. // The latest release of md5.pas will always be available from
  27. // the distribution site at: http://www.fichtner.net/delphi/md5/
  28. // -----------------------------------------------------------------------------------------------
  29. // Please send questions, bug reports and suggestions
  30. // regarding this code to: mfichtner@fichtner-meyer.com
  31. // -----------------------------------------------------------------------------------------------
  32. // This code is provided "as is" without express or
  33. // implied warranty of any kind. Use it at your own risk.
  34. // -----------------------------------------------------------------------------------------------
  35. unit md5;
  36. {$MODE Delphi}
  37. // -----------------------------------------------------------------------------------------------
  38. INTERFACE
  39. // -----------------------------------------------------------------------------------------------
  40. uses
  41. Windows;
  42. type
  43. MD5Count = array[0..1] of DWORD;
  44. MD5State = array[0..3] of DWORD;
  45. MD5Block = array[0..15] of DWORD;
  46. MD5CBits = array[0..7] of byte;
  47. MD5Digest = array[0..15] of byte;
  48. MD5Buffer = array[0..63] of byte;
  49. MD5Context = record
  50. State: MD5State;
  51. Count: MD5Count;
  52. Buffer: MD5Buffer;
  53. end;
  54. procedure MD5Init(var Context: MD5Context);
  55. procedure MD5Update(var Context: MD5Context; Input: pChar; Length: longword);
  56. procedure MD5Final(var Context: MD5Context; var Digest: MD5Digest);
  57. function MD5String(M: string): MD5Digest;
  58. function MD5File(N: string): MD5Digest;
  59. function MD5Print(D: MD5Digest): string;
  60. function MD5Match(D1, D2: MD5Digest): boolean;
  61. // -----------------------------------------------------------------------------------------------
  62. IMPLEMENTATION
  63. // -----------------------------------------------------------------------------------------------
  64. var
  65. PADDING: MD5Buffer = (
  66. $80, $00, $00, $00, $00, $00, $00, $00,
  67. $00, $00, $00, $00, $00, $00, $00, $00,
  68. $00, $00, $00, $00, $00, $00, $00, $00,
  69. $00, $00, $00, $00, $00, $00, $00, $00,
  70. $00, $00, $00, $00, $00, $00, $00, $00,
  71. $00, $00, $00, $00, $00, $00, $00, $00,
  72. $00, $00, $00, $00, $00, $00, $00, $00,
  73. $00, $00, $00, $00, $00, $00, $00, $00
  74. );
  75. function F(x, y, z: DWORD): DWORD;
  76. begin
  77. Result := (x and y) or ((not x) and z);
  78. end;
  79. function G(x, y, z: DWORD): DWORD;
  80. begin
  81. Result := (x and z) or (y and (not z));
  82. end;
  83. function H(x, y, z: DWORD): DWORD;
  84. begin
  85. Result := x xor y xor z;
  86. end;
  87. function I(x, y, z: DWORD): DWORD;
  88. begin
  89. Result := y xor (x or (not z));
  90. end;
  91. procedure rot(var x: DWORD; n: BYTE);
  92. begin
  93. x := (x shl n) or (x shr (32 - n));
  94. end;
  95. procedure FF(var a: DWORD; b, c, d, x: DWORD; s: BYTE; ac: DWORD);
  96. begin
  97. inc(a, F(b, c, d) + x + ac);
  98. rot(a, s);
  99. inc(a, b);
  100. end;
  101. procedure GG(var a: DWORD; b, c, d, x: DWORD; s: BYTE; ac: DWORD);
  102. begin
  103. inc(a, G(b, c, d) + x + ac);
  104. rot(a, s);
  105. inc(a, b);
  106. end;
  107. procedure HH(var a: DWORD; b, c, d, x: DWORD; s: BYTE; ac: DWORD);
  108. begin
  109. inc(a, H(b, c, d) + x + ac);
  110. rot(a, s);
  111. inc(a, b);
  112. end;
  113. procedure II(var a: DWORD; b, c, d, x: DWORD; s: BYTE; ac: DWORD);
  114. begin
  115. inc(a, I(b, c, d) + x + ac);
  116. rot(a, s);
  117. inc(a, b);
  118. end;
  119. // -----------------------------------------------------------------------------------------------
  120. // Encode Count bytes at Source into (Count / 4) DWORDs at Target
  121. procedure Encode(Source, Target: pointer; Count: longword);
  122. var
  123. S: PByte;
  124. T: PDWORD;
  125. I: longword;
  126. begin
  127. S := Source;
  128. T := Target;
  129. for I := 1 to Count div 4 do begin
  130. T^ := S^;
  131. inc(S);
  132. T^ := T^ or (S^ shl 8);
  133. inc(S);
  134. T^ := T^ or (S^ shl 16);
  135. inc(S);
  136. T^ := T^ or (S^ shl 24);
  137. inc(S);
  138. inc(T);
  139. end;
  140. end;
  141. // Decode Count DWORDs at Source into (Count * 4) Bytes at Target
  142. procedure Decode(Source, Target: pointer; Count: longword);
  143. var
  144. S: PDWORD;
  145. T: PByte;
  146. I: longword;
  147. begin
  148. S := Source;
  149. T := Target;
  150. for I := 1 to Count do begin
  151. T^ := S^ and $ff;
  152. inc(T);
  153. T^ := (S^ shr 8) and $ff;
  154. inc(T);
  155. T^ := (S^ shr 16) and $ff;
  156. inc(T);
  157. T^ := (S^ shr 24) and $ff;
  158. inc(T);
  159. inc(S);
  160. end;
  161. end;
  162. // Transform State according to first 64 bytes at Buffer
  163. procedure Transform(Buffer: pointer; var State: MD5State);
  164. var
  165. a, b, c, d: DWORD;
  166. Block: MD5Block;
  167. begin
  168. Encode(Buffer, @Block, 64);
  169. a := State[0];
  170. b := State[1];
  171. c := State[2];
  172. d := State[3];
  173. FF (a, b, c, d, Block[ 0], 7, $d76aa478);
  174. FF (d, a, b, c, Block[ 1], 12, $e8c7b756);
  175. FF (c, d, a, b, Block[ 2], 17, $242070db);
  176. FF (b, c, d, a, Block[ 3], 22, $c1bdceee);
  177. FF (a, b, c, d, Block[ 4], 7, $f57c0faf);
  178. FF (d, a, b, c, Block[ 5], 12, $4787c62a);
  179. FF (c, d, a, b, Block[ 6], 17, $a8304613);
  180. FF (b, c, d, a, Block[ 7], 22, $fd469501);
  181. FF (a, b, c, d, Block[ 8], 7, $698098d8);
  182. FF (d, a, b, c, Block[ 9], 12, $8b44f7af);
  183. FF (c, d, a, b, Block[10], 17, $ffff5bb1);
  184. FF (b, c, d, a, Block[11], 22, $895cd7be);
  185. FF (a, b, c, d, Block[12], 7, $6b901122);
  186. FF (d, a, b, c, Block[13], 12, $fd987193);
  187. FF (c, d, a, b, Block[14], 17, $a679438e);
  188. FF (b, c, d, a, Block[15], 22, $49b40821);
  189. GG (a, b, c, d, Block[ 1], 5, $f61e2562);
  190. GG (d, a, b, c, Block[ 6], 9, $c040b340);
  191. GG (c, d, a, b, Block[11], 14, $265e5a51);
  192. GG (b, c, d, a, Block[ 0], 20, $e9b6c7aa);
  193. GG (a, b, c, d, Block[ 5], 5, $d62f105d);
  194. GG (d, a, b, c, Block[10], 9, $2441453);
  195. GG (c, d, a, b, Block[15], 14, $d8a1e681);
  196. GG (b, c, d, a, Block[ 4], 20, $e7d3fbc8);
  197. GG (a, b, c, d, Block[ 9], 5, $21e1cde6);
  198. GG (d, a, b, c, Block[14], 9, $c33707d6);
  199. GG (c, d, a, b, Block[ 3], 14, $f4d50d87);
  200. GG (b, c, d, a, Block[ 8], 20, $455a14ed);
  201. GG (a, b, c, d, Block[13], 5, $a9e3e905);
  202. GG (d, a, b, c, Block[ 2], 9, $fcefa3f8);
  203. GG (c, d, a, b, Block[ 7], 14, $676f02d9);
  204. GG (b, c, d, a, Block[12], 20, $8d2a4c8a);
  205. HH (a, b, c, d, Block[ 5], 4, $fffa3942);
  206. HH (d, a, b, c, Block[ 8], 11, $8771f681);
  207. HH (c, d, a, b, Block[11], 16, $6d9d6122);
  208. HH (b, c, d, a, Block[14], 23, $fde5380c);
  209. HH (a, b, c, d, Block[ 1], 4, $a4beea44);
  210. HH (d, a, b, c, Block[ 4], 11, $4bdecfa9);
  211. HH (c, d, a, b, Block[ 7], 16, $f6bb4b60);
  212. HH (b, c, d, a, Block[10], 23, $bebfbc70);
  213. HH (a, b, c, d, Block[13], 4, $289b7ec6);
  214. HH (d, a, b, c, Block[ 0], 11, $eaa127fa);
  215. HH (c, d, a, b, Block[ 3], 16, $d4ef3085);
  216. HH (b, c, d, a, Block[ 6], 23, $4881d05);
  217. HH (a, b, c, d, Block[ 9], 4, $d9d4d039);
  218. HH (d, a, b, c, Block[12], 11, $e6db99e5);
  219. HH (c, d, a, b, Block[15], 16, $1fa27cf8);
  220. HH (b, c, d, a, Block[ 2], 23, $c4ac5665);
  221. II (a, b, c, d, Block[ 0], 6, $f4292244);
  222. II (d, a, b, c, Block[ 7], 10, $432aff97);
  223. II (c, d, a, b, Block[14], 15, $ab9423a7);
  224. II (b, c, d, a, Block[ 5], 21, $fc93a039);
  225. II (a, b, c, d, Block[12], 6, $655b59c3);
  226. II (d, a, b, c, Block[ 3], 10, $8f0ccc92);
  227. II (c, d, a, b, Block[10], 15, $ffeff47d);
  228. II (b, c, d, a, Block[ 1], 21, $85845dd1);
  229. II (a, b, c, d, Block[ 8], 6, $6fa87e4f);
  230. II (d, a, b, c, Block[15], 10, $fe2ce6e0);
  231. II (c, d, a, b, Block[ 6], 15, $a3014314);
  232. II (b, c, d, a, Block[13], 21, $4e0811a1);
  233. II (a, b, c, d, Block[ 4], 6, $f7537e82);
  234. II (d, a, b, c, Block[11], 10, $bd3af235);
  235. II (c, d, a, b, Block[ 2], 15, $2ad7d2bb);
  236. II (b, c, d, a, Block[ 9], 21, $eb86d391);
  237. inc(State[0], a);
  238. inc(State[1], b);
  239. inc(State[2], c);
  240. inc(State[3], d);
  241. end;
  242. // -----------------------------------------------------------------------------------------------
  243. // Initialize given Context
  244. procedure MD5Init(var Context: MD5Context);
  245. begin
  246. with Context do begin
  247. State[0] := $67452301;
  248. State[1] := $efcdab89;
  249. State[2] := $98badcfe;
  250. State[3] := $10325476;
  251. Count[0] := 0;
  252. Count[1] := 0;
  253. ZeroMemory(@Buffer, SizeOf(MD5Buffer));
  254. end;
  255. end;
  256. // Update given Context to include Length bytes of Input
  257. procedure MD5Update(var Context: MD5Context; Input: pChar; Length: longword);
  258. var
  259. Index: longword;
  260. PartLen: longword;
  261. I: longword;
  262. begin
  263. with Context do begin
  264. Index := (Count[0] shr 3) and $3f;
  265. inc(Count[0], Length shl 3);
  266. if Count[0] < (Length shl 3) then inc(Count[1]);
  267. inc(Count[1], Length shr 29);
  268. end;
  269. PartLen := 64 - Index;
  270. if Length >= PartLen then begin
  271. CopyMemory(@Context.Buffer[Index], Input, PartLen);
  272. Transform(@Context.Buffer, Context.State);
  273. I := PartLen;
  274. while I + 63 < Length do begin
  275. Transform(@Input[I], Context.State);
  276. inc(I, 64);
  277. end;
  278. Index := 0;
  279. end else I := 0;
  280. CopyMemory(@Context.Buffer[Index], @Input[I], Length - I);
  281. end;
  282. // Finalize given Context, create Digest and zeroize Context
  283. procedure MD5Final(var Context: MD5Context; var Digest: MD5Digest);
  284. var
  285. Bits: MD5CBits;
  286. Index: longword;
  287. PadLen: longword;
  288. begin
  289. Decode(@Context.Count, @Bits, 2);
  290. Index := (Context.Count[0] shr 3) and $3f;
  291. if Index < 56 then PadLen := 56 - Index else PadLen := 120 - Index;
  292. MD5Update(Context, @PADDING, PadLen);
  293. MD5Update(Context, @Bits, 8);
  294. Decode(@Context.State, @Digest, 4);
  295. ZeroMemory(@Context, SizeOf(MD5Context));
  296. end;
  297. // -----------------------------------------------------------------------------------------------
  298. // Create digest of given Message
  299. function MD5String(M: string): MD5Digest;
  300. var
  301. Context: MD5Context;
  302. begin
  303. MD5Init(Context);
  304. MD5Update(Context, pChar(M), length(M));
  305. MD5Final(Context, Result);
  306. end;
  307. // Create digest of file with given Name
  308. function MD5File(N: string): MD5Digest;
  309. var
  310. FileHandle: THandle;
  311. MapHandle: THandle;
  312. ViewPointer: pointer;
  313. Context: MD5Context;
  314. begin
  315. MD5Init(Context);
  316. FileHandle := CreateFile(pChar(N), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE,
  317. nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_SEQUENTIAL_SCAN, 0);
  318. if FileHandle <> INVALID_HANDLE_VALUE then try
  319. MapHandle := CreateFileMapping(FileHandle, nil, PAGE_READONLY, 0, 0, nil);
  320. if MapHandle <> 0 then try
  321. ViewPointer := MapViewOfFile(MapHandle, FILE_MAP_READ, 0, 0, 0);
  322. if ViewPointer <> nil then try
  323. MD5Update(Context, ViewPointer, GetFileSize(FileHandle, nil));
  324. finally
  325. UnmapViewOfFile(ViewPointer);
  326. end;
  327. finally
  328. CloseHandle(MapHandle);
  329. end;
  330. finally
  331. CloseHandle(FileHandle);
  332. end;
  333. MD5Final(Context, Result);
  334. end;
  335. // Create hex representation of given Digest
  336. function MD5Print(D: MD5Digest): string;
  337. var
  338. I: byte;
  339. const
  340. Digits: array[0..15] of char =
  341. ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f');
  342. begin
  343. Result := '';
  344. for I := 0 to 15 do Result := Result + Digits[(D[I] shr 4) and $0f] + Digits[D[I] and $0f];
  345. end;
  346. // -----------------------------------------------------------------------------------------------
  347. // Compare two Digests
  348. function MD5Match(D1, D2: MD5Digest): boolean;
  349. var
  350. I: byte;
  351. begin
  352. I := 0;
  353. Result := TRUE;
  354. while Result and (I < 16) do begin
  355. Result := D1[I] = D2[I];
  356. inc(I);
  357. end;
  358. end;
  359. end.