From: pandeng@telepath.com (Steve Schafer (TeamB)) Subject: Re: MD5 algorithm Date: 09 Sep 1999 00:00:00 GMT Message-ID: <37db211b.109425043@90.0.0.40> Content-Transfer-Encoding: 7bit References: <37D70126.38AD6737@xoommail.com> Content-Type: text/plain; charset=us-ascii Organization: TeamB Mime-Version: 1.0 Reply-To: pandeng@telepath.com Newsgroups: borland.public.delphi.winapi On Thu, 09 Sep 1999 03:36:54 +0300, Steelglass wrote: >how do i calculate it, its a lot stronger then CRC32 since its 128bits >checksum. >I've seen the RFC for it which includes the C code, >but i failed to convert it , any ideas would be appreciated. The following unit implements MD5: --------8<-------- unit uMd5; {$Q-} {$R-} interface uses Windows, SysUtils, Classes; type TMd5Digest = array[0..3] of LongWord; TMd5Stream = class(TStream) private BlockCount: Int64; BufCount: Cardinal; Buffer: array[0..15] of LongWord; A: LongWord; B: LongWord; C: LongWord; D: LongWord; function F(X, Y, Z: LongWord): LongWord; function G(X, Y, Z: LongWord): LongWord; function H(X, Y, Z: LongWord): LongWord; function I(X, Y, Z: LongWord): LongWord; function Rotate(L: LongWord; NumBits: Cardinal): LongWord; protected procedure AddByte(B: Byte); virtual; function GetDigest: TMd5Digest; virtual; function GetDigestString: String; virtual; procedure Initialize; virtual; procedure UpdateDigest; virtual; public constructor Create; function Read(var Buffer; Count: Longint): Longint; override; function Seek(Offset: Longint; Origin: Word): Longint; override; function Write(const Buffer; Count: Longint): Longint; override; property Digest: TMd5Digest read GetDigest; property DigestString: String read GetDigestString; end; implementation const T: array[1..64] of LongWord = ($D76AA478, $E8C7B756, $242070DB, $C1BDCEEE, $F57C0FAF, $4787C62A, $A8304613, $FD469501, $698098D8, $8B44F7AF, $FFFF5BB1, $895CD7BE, $6B901122, $FD987193, $A679438E, $49B40821, $F61E2562, $C040B340, $265E5A51, $E9B6C7AA, $D62F105D, $02441453, $D8A1E681, $E7D3FBC8, $21E1CDE6, $C33707D6, $F4D50D87, $455A14ED, $A9E3E905, $FCEFA3F8, $676F02D9, $8D2A4C8A, $FFFA3942, $8771F681, $6D9D6122, $FDE5380C, $A4BEEA44, $4BDECFA9, $F6BB4B60, $BEBFBC70, $289B7EC6, $EAA127FA, $D4EF3085, $04881D05, $D9D4D039, $E6DB99E5, $1FA27CF8, $C4AC5665, $F4292244, $432AFF97, $AB9423A7, $FC93A039, $655B59C3, $8F0CCC92, $FFEFF47D, $85845DD1, $6FA87E4F, $FE2CE6E0, $A3014314, $4E0811A1, $F7537E82, $BD3AF235, $2AD7D2BB, $EB86D391); constructor TMd5Stream.Create; begin inherited Create; Initialize end; procedure TMd5Stream.AddByte(B: Byte); begin case BufCount mod 4 of 0: Buffer[BufCount div 4] := Buffer[BufCount div 4] or B; 1: Buffer[BufCount div 4] := Buffer[BufCount div 4] or (B shl 8); 2: Buffer[BufCount div 4] := Buffer[BufCount div 4] or (B shl 16); 3: Buffer[BufCount div 4] := Buffer[BufCount div 4] or (B shl 24) end; Inc(BufCount); if BufCount = 64 then UpdateDigest end; function TMd5Stream.F(X, Y, Z: LongWord): LongWord; begin Result := (X and Y) or ((not X) and Z) end; function TMd5Stream.G(X, Y, Z: LongWord): LongWord; begin Result := (X and Z) or (Y and (not Z)) end; function TMd5Stream.H(X, Y, Z: LongWord): LongWord; begin Result := X xor Y xor Z end; function TMd5Stream.I(X, Y, Z: LongWord): LongWord; begin Result := Y xor (X or (not Z)) end; function TMd5Stream.GetDigest: TMd5Digest; var L: Int64; MsgLen: Int64; begin MsgLen := BlockCount * 512 + BufCount * 8; AddByte($80); L := 0; if BufCount > 56 then Write(L, SizeOf(L)); while BufCount < 56 do AddByte(0); Write(MsgLen, SizeOf(MsgLen)); Result[0] := A; Result[1] := B; Result[2] := C; Result[3] := D; Initialize end; function TMd5Stream.GetDigestString: String; var D: TMd5Digest; begin D := GetDigest; SetLength(Result, SizeOf(D)); Move(D, Result[1], Length(Result)) end; procedure TMd5Stream.Initialize; begin BlockCount := 0; BufCount := 0; FillChar(Buffer, SizeOf(Buffer), 0); A := $67452301; B := $EFCDAB89; C := $98BADCFE; D := $10325476 end; function TMd5Stream.Read(var Buffer; Count: LongInt): LongInt; begin Result := 0 end; function TMd5Stream.Rotate(L: LongWord; NumBits: Cardinal): LongWord; begin Result := (L shl NumBits) + (L shr (32 - NumBits)) end; function TMd5Stream.Seek(Offset: LongInt; Origin: Word): LongInt; begin Result := BlockCount * 16 + BufCount end; procedure TMd5Stream.UpdateDigest; var AA: LongWord; BB: LongWord; CC: LongWord; DD: LongWord; begin Inc(BlockCount); AA := A; BB := B; CC := C; DD := D; A := B + Rotate(A + F(B, C, D) + Buffer[ 0] + T[ 1], 7); D := A + Rotate(D + F(A, B, C) + Buffer[ 1] + T[ 2], 12); C := D + Rotate(C + F(D, A, B) + Buffer[ 2] + T[ 3], 17); B := C + Rotate(B + F(C, D, A) + Buffer[ 3] + T[ 4], 22); A := B + Rotate(A + F(B, C, D) + Buffer[ 4] + T[ 5], 7); D := A + Rotate(D + F(A, B, C) + Buffer[ 5] + T[ 6], 12); C := D + Rotate(C + F(D, A, B) + Buffer[ 6] + T[ 7], 17); B := C + Rotate(B + F(C, D, A) + Buffer[ 7] + T[ 8], 22); A := B + Rotate(A + F(B, C, D) + Buffer[ 8] + T[ 9], 7); D := A + Rotate(D + F(A, B, C) + Buffer[ 9] + T[10], 12); C := D + Rotate(C + F(D, A, B) + Buffer[10] + T[11], 17); B := C + Rotate(B + F(C, D, A) + Buffer[11] + T[12], 22); A := B + Rotate(A + F(B, C, D) + Buffer[12] + T[13], 7); D := A + Rotate(D + F(A, B, C) + Buffer[13] + T[14], 12); C := D + Rotate(C + F(D, A, B) + Buffer[14] + T[15], 17); B := C + Rotate(B + F(C, D, A) + Buffer[15] + T[16], 22); A := B + Rotate(A + G(B, C, D) + Buffer[ 1] + T[17], 5); D := A + Rotate(D + G(A, B, C) + Buffer[ 6] + T[18], 9); C := D + Rotate(C + G(D, A, B) + Buffer[11] + T[19], 14); B := C + Rotate(B + G(C, D, A) + Buffer[ 0] + T[20], 20); A := B + Rotate(A + G(B, C, D) + Buffer[ 5] + T[21], 5); D := A + Rotate(D + G(A, B, C) + Buffer[10] + T[22], 9); C := D + Rotate(C + G(D, A, B) + Buffer[15] + T[23], 14); B := C + Rotate(B + G(C, D, A) + Buffer[ 4] + T[24], 20); A := B + Rotate(A + G(B, C, D) + Buffer[ 9] + T[25], 5); D := A + Rotate(D + G(A, B, C) + Buffer[14] + T[26], 9); C := D + Rotate(C + G(D, A, B) + Buffer[ 3] + T[27], 14); B := C + Rotate(B + G(C, D, A) + Buffer[ 8] + T[28], 20); A := B + Rotate(A + G(B, C, D) + Buffer[13] + T[29], 5); D := A + Rotate(D + G(A, B, C) + Buffer[ 2] + T[30], 9); C := D + Rotate(C + G(D, A, B) + Buffer[ 7] + T[31], 14); B := C + Rotate(B + G(C, D, A) + Buffer[12] + T[32], 20); A := B + Rotate(A + H(B, C, D) + Buffer[ 5] + T[33], 4); D := A + Rotate(D + H(A, B, C) + Buffer[ 8] + T[34], 11); C := D + Rotate(C + H(D, A, B) + Buffer[11] + T[35], 16); B := C + Rotate(B + H(C, D, A) + Buffer[14] + T[36], 23); A := B + Rotate(A + H(B, C, D) + Buffer[ 1] + T[37], 4); D := A + Rotate(D + H(A, B, C) + Buffer[ 4] + T[38], 11); C := D + Rotate(C + H(D, A, B) + Buffer[ 7] + T[39], 16); B := C + Rotate(B + H(C, D, A) + Buffer[10] + T[40], 23); A := B + Rotate(A + H(B, C, D) + Buffer[13] + T[41], 4); D := A + Rotate(D + H(A, B, C) + Buffer[ 0] + T[42], 11); C := D + Rotate(C + H(D, A, B) + Buffer[ 3] + T[43], 16); B := C + Rotate(B + H(C, D, A) + Buffer[ 6] + T[44], 23); A := B + Rotate(A + H(B, C, D) + Buffer[ 9] + T[45], 4); D := A + Rotate(D + H(A, B, C) + Buffer[12] + T[46], 11); C := D + Rotate(C + H(D, A, B) + Buffer[15] + T[47], 16); B := C + Rotate(B + H(C, D, A) + Buffer[ 2] + T[48], 23); A := B + Rotate(A + I(B, C, D) + Buffer[ 0] + T[49], 6); D := A + Rotate(D + I(A, B, C) + Buffer[ 7] + T[50], 10); C := D + Rotate(C + I(D, A, B) + Buffer[14] + T[51], 15); B := C + Rotate(B + I(C, D, A) + Buffer[ 5] + T[52], 21); A := B + Rotate(A + I(B, C, D) + Buffer[12] + T[53], 6); D := A + Rotate(D + I(A, B, C) + Buffer[ 3] + T[54], 10); C := D + Rotate(C + I(D, A, B) + Buffer[10] + T[55], 15); B := C + Rotate(B + I(C, D, A) + Buffer[ 1] + T[56], 21); A := B + Rotate(A + I(B, C, D) + Buffer[ 8] + T[57], 6); D := A + Rotate(D + I(A, B, C) + Buffer[15] + T[58], 10); C := D + Rotate(C + I(D, A, B) + Buffer[ 6] + T[59], 15); B := C + Rotate(B + I(C, D, A) + Buffer[13] + T[60], 21); A := B + Rotate(A + I(B, C, D) + Buffer[ 4] + T[61], 6); D := A + Rotate(D + I(A, B, C) + Buffer[11] + T[62], 10); C := D + Rotate(C + I(D, A, B) + Buffer[ 2] + T[63], 15); B := C + Rotate(B + I(C, D, A) + Buffer[ 9] + T[64], 21); A := A + AA; B := B + BB; C := C + CC; D := D + DD; BufCount := 0; FillChar(Buffer, SizeOf(Buffer), 0) end; function TMd5Stream.Write(const Buffer; Count: LongInt): LongInt; var I: Cardinal; P: PByteArray; begin P := @Buffer; for I := 0 to Count - 1 do AddByte(P^[I]); Result := Count end; end. --------8<-------- After creating a TMd5Stream object, just write your data to it, as you would to any stream. After you've written all of the data, read the Digest or DigestString properties to get the digest. -Steve ======================================================================= Postscript (21 April 2004) > -----Original Message----- > From: Steve Schafer [mailto:s t e v e @ f e n e s t r a . c o m] > Sent: Wednesday, April 21, 2004 3:41 PM > To: den4b > Cc: efg2@efg2.com > Subject: Re: MD5 algorithm (warning!!!) >i'm writing in reference to the "MD5 algorithm" topic: > http://www.efg2.com/Lab/Library/UseNet/1999/0909.txt The test suite included in RFC1321 specifies the following results: > MD5 ("") = d41d8cd98f00b204e9800998ecf8427e > MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661 > MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72 > MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0 > MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b > MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = d174ab98d277d9f5a5611c2c9f419d9f > MD5 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = 57edf4a22be3c955ac49da2e2107b67a I just now tested the code posted at http://www.efg2.com/ using the following Delphi 6.0 code: uses uMD5; function TestMD5(const S: AnsiString): AnsiString; var BinaryResult: AnsiString; I: Integer; MD5: TMD5Stream; begin MD5 := TMD5Stream.Create; try MD5.WriteBuffer(S[1], Length(S)); BinaryResult := MD5.DigestString; Result := ''; for I := 1 to Length(BinaryResult) do Result := Format('%s%.2x', [Result, Ord(BinaryResult[I])]); Result := Format('MD5 ("%s") = %s', [S, AnsiLowerCase(Result)]); finally MD5.Free end end; procedure TForm1.Button1Click(Sender: TObject); var L: TStringList; begin L := TStringList.Create; try L.Add(TestMD5('')); L.Add(TestMD5('a')); L.Add(TestMD5('abc')); L.Add(TestMD5('message digest')); L.Add(TestMD5('abcdefghijklmnopqrstuvwxyz')); L.Add(TestMD5('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')); L.Add(TestMD5('12345678901234567890123456789012345678901234567890123456789012345678901234567890')); L.SaveToFile('c:\MD5Results.txt'); finally L.Free end end; If you run this code, you will see that it produces exactly the results specified by RFC1321. -Steve