MD5 in Delphi / Pascal / FreePascal for short strings

I'm trying to implement a simple MD5 for short strings (64 bytes shorter). I am using an algorithm from Wikipedia. ... Everything compiles, but my result for the line is:

"hello world" 

      

is an:

BB3BB65ED0EE1EE0BB22CB93C3CD5A8F

      

while it should be:

5EB63BBBE01EEED093CB22BB8F5ACDC3

      

The complete code is here:

program Prog;

uses Classes, SysUtils;

function leftrotate(x, c: Cardinal): Cardinal;
begin
  leftrotate := (x shl c) or (x shr (32-c));
end;

const s: array[0..63] of Cardinal = (
    7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,
    5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,
    4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,
    6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21 );
K: array[0..63] of Cardinal = (
    $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 );

var a0,b0,c0,d0, a,b,c,d, f,g,dTemp: Cardinal;
   Len: Integer;
   Msg: array[0..63] of Char;
   M: array[0..15] of Cardinal absolute Msg; //break chunk into sixteen 32-bit words M[j]
   Str: String;
   i: Integer;
   ff: TFileStream;
   wait: Char;
begin
  a0 := $67452301;
  b0 := $efcdab89;
  c0 := $98badcfe;
  d0 := $10325476;

  Str := 'hello world';
  Len := Length(Str);

  FillChar(Msg, 64, 0);

  for i:=1 to Len do Msg[i-1] := Str[i];

//append "1" bit to message
  Msg[Len] := chr(128);

//append original length in bits mod (2 pow 64) to message
  Msg[63-7] := chr(8*Len);  //Update thanks to @MBo

//Process each 512-bit chunk of message- 1 only have 1 chunk

//TEST dump
//  ff := TFileStream.create('test.txt', fmCreate);
//  ff.write(msg, 64);
//  ff.free;

//Initialize hash value for this chunk:
    A := a0;
    B := b0;
    C := c0;
    D := d0;

//Main loop:
    for i := 0 to 63 do begin

        if (i>=0) and (i<=15) then begin
            F := (B and C) or ((not B) and D);
            g := i;
        end
        else if (i>=16) and (i<=31) then begin
            F := (D and B) or ((not D) and C);
            g := (5*i + 1) mod 16;
        end
        else if (i>=32) and (i<=47) then begin
            F := B xor C xor D;
            g := (3*i + 5) mod 16;
        end
        else if (i>=48) and (i<=63) then begin
            F := C xor (B or (not D));
            g := (7*i) mod 16;
        end;

        dTemp := D;
        D := C;
        C := B;
        B := B + leftrotate((A + F + K[i] + M[g]), s[i]);
        A := dTemp;
    end;

//Add this chunk hash to result so far:
  a0 := a0 + A;
  b0 := b0 + B;
  c0 := c0 + C;
  d0 := d0 + D;

  //This should give 5EB63BBBE01EEED093CB22BB8F5ACDC3
  Writeln( IntToHex(a0,8) + IntToHex(b0,8) + IntToHex(c0,8)  +IntToHex(d0,8) );

  Readln(wait);
end.

      

You can try the code here: http://ideone.com/qdYQ6q

And here is a dump of my prepared chunk before the main path (test.txt):

enter image description here

+3


source to share


3 answers


The last step is wrong:

  a0 := a0 + A;
  b0 := b0 + B;
  c0 := c0 + C;
  d0 := d0 + D;

      

it should change endianess:



  a0 := Swap32(a0 + A);
  b0 := Swap32(b0 + B);
  c0 := Swap32(c0 + C);
  d0 := Swap32(d0 + D);

function Swap32(ALong: Cardinal): Cardinal; Assembler; 
asm 
  BSWAP eax 
end;

      

and then that's good.

+5


source


You might want to consider using a third party implementation instead of creating your own. For example, the Indy class TIdHashMessageDigest5 produces the correct value, for example:



uses
  ..., IdHashMessageDigest;

var
  S: string;
begin
  with TIdHashMessageDigest5.Create do
  try
    S := HashStringAsHex('hello world'); // returns '5EB63BBBE01EEED093CB22BB8F5ACDC3'
  finally
    Free;
  end;
end;

      

+3


source


How about these steps:

append "0" bit until message length in bits ≡ 448 (mod 512)

      

(56 bytes, 64 + 56, etc.) and

append original length

in bits mod (2 pow 64) to message

but you added Len in bytes

PS I checked the last one with Delphi. I changed the char types to AnsiChar and the result is as expected. Note that no byte swapping is required for a correct binary result. This can only help for constructing a hex string from Int32 values

Int32s are already a little geared towards Intel hardware, so BB3BB65E

(hexadecimal string representation) matches a sequence of bytes 5E B6 3B BB

, etc.

+2


source







All Articles