{-------------------------------------------------------------------------------
    MD4.PAS - implementation of MD4 hash function
    Copyright (C) 1998  Jan Tomasek <xtomasej@fel.cvut.cz>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.


Creation :    25.08.1998
Version  :    1.01
History  :
  25.08.98 Created
  28.10.98 Released under GNU licence
-------------------------------------------------------------------------------}
unit MD4;

interface
{ MD4 context }
Type
  uint      = word;
  { UINT2 defines a two byte word }
  UINT2     = word;
  { UINT4 defines a four byte word }
  UINT4     = longint;

  PMD4_CTX  =^TMD4_CTX;
  PMD4State =^TMD4State;
  PMD4Buffer=^TMD4Buffer;
  PMD4Digest=^TMD4Digest;

  TMD4State = array[0..4-1] of UINT4;
  TMD4Buffer= array[0..64-1] of Char;
  TMD4_CTX  = record
    state   : TMD4State;              {state (ABCD)}
    count   : array[0..2-1] of UINT4; {number of bits, modulo 2^64 (lsb first)}
    buffer  : TMD4Buffer;             { input buffer }
  end;
  TMD4Digest= array[0..15] of Char;


Procedure MD4Init(context:PMD4_CTX);
Procedure MD4Update(context:PMD4_CTX; input:PChar; inputLen:uint);
Procedure MD4Final(digest:PMD4Digest; context:PMD4_CTX);

implementation
{ Constants for MD4Transform routine. }
Const S11       = 3;
Const S12       = 7;
Const S13       = 11;
Const S14       = 19;
Const S21       = 3;
Const S22       = 5;
Const S23       = 9;
Const S24       = 13;
Const S31       = 3;
Const S32       = 9;
Const S33       = 11;
Const S34       = 15;
Const PADDING   : array[0..64-1] of char = (
  #$80, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
  #0,   #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
  #0,   #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
  #0,   #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0);



{ F, G and H are basic MD4 functions. }
function F(X, Y, Z: UINT4):UINT4;
begin
  result := (X and Y) or ((not X) and Z);
end;

function G(X, Y, Z: UINT4):UINT4;
begin
  result := (X and Y) or (X and Z) or (Y and Z);
end;

function H(X, Y, Z:UINT4):UINT4;
begin
  result := X xor Y xor Z;
end;


{ ROTATE_LEFT rotates x left n bits. }
function ROTATE_LEFT(X:UINT4; n:byte):UINT4;
begin
  result:= (X shl n) or (X shr (32-n));
end;

{ FF, GG and HH are transformations for rounds 1, 2 and 3 }
{ Rotation is separate from addition to prevent recomputation }
procedure FF(var a:UINT4; b, c, d, x:UINT4; s:byte);
begin
  inc(a, F(b, c, d) + x);
  a := ROTATE_LEFT(a, s);
end;

procedure GG(var a:UINT4; b, c, d, x:UINT4; s:byte);
begin
  inc(a, G(b, c, d) + x + UINT4($5a827999));
  a := ROTATE_LEFT(a, s);
end;

procedure HH(var a:UINT4; b, c, d, x:UINT4; s:byte);
begin
  inc(a, H(b, c, d) + x + UINT4($6ed9eba1));
  a := ROTATE_LEFT(a, s);
end;

procedure MD4Transform(state:PMD4State; block:PMD4Buffer); forward;



{ MD4 initialization. Begins an MD4 operation, writing a new context. }
procedure MD4Init(context:PMD4_CTX);
begin
  context^.count[0] := 0;
  context^.count[1] := 0;
  { Load magic initialization constants. }
  context^.state[0] := UINT4($67452301);
  context^.state[1] := UINT4($efcdab89);
  context^.state[2] := UINT4($98badcfe);
  context^.state[3] := UINT4($10325476);
  { Clear buffer }
  FillChar(context^.buffer, sizeof(context^.buffer), 0);
end;

{ MD4 block update operation. Continues an MD4 message-digest
  operation, processing another message block, and updating the
  context. }
Procedure MD4Update(context:PMD4_CTX; input:PChar; inputLen:uint);
var
  I, index, partLen : UINT4;
begin
  { Compute number of bytes mod 64 }
  index := (context^.count[0] shr 3) and $3F;
  { Update number of bits }
  inc(context^.count[0], inputLen shl 3);
  if context^.count[0]<(inputLen shl 3) Then inc(context^.count[1]);
  inc(context^.count[1], inputLen shr 29);

  partLen := 64 - index;

  { Transform as many times as possible. }
  if (inputLen >= partLen) then begin
    move(input^, context^.buffer[index], partLen);
    MD4Transform(@context^.state, @context^.buffer);

    i := partLen;
    while i + 63 < inputLen do begin
      MD4Transform(@context^.state, @input[i]);
      inc(i, 64);
    end;
    index := 0;
  end else
    i := 0;
  { Buffer remaining input }
  move(input[i], context^.buffer[index], inputLen-i);
end;

{ MD4 finalization. Ends an MD4 message-digest operation, writing the
  the message digest and zeroizing the context. }
procedure MD4Final(digest:PMD4Digest; context:PMD4_CTX);
var
  bits   : array[0..8-1] of char;
  index,
  padLen : uint;
begin
  { Save number of bits }
  move (context^.count, bits, 8);

  { Pad out to 56 mod 64. }
  index  := (context^.count[0] shr 3) and $3f;
  if (index < 56) then padLen := (56 - index)
  else padLen := (120 - index);
  MD4Update (context, PADDING, padLen);

  { Append length (before padding) }
  MD4Update (context, bits, 8);
  { Store state in digest }
  move (context^.state, digest^, 16);

  { Zeroize sensitive information. }
  FillChar(context^.state, sizeof(context^.state), 0);
end;

{ MD4 basic transformation. Transforms state based on block. }
procedure MD4Transform(state:PMD4State; block:PMD4Buffer);
var
  a, b, c, d : UINT4;
  x          : array[0..16-1] of UINT4;
begin
  a := state^[0];
  b := state^[1];
  c := state^[2];
  d := state^[3];

  move(block^, x, sizeof(block^));

  { Round 1 }

  FF (a, b, c, d, x[ 0], S11); {  1   }
  FF (d, a, b, c, x[ 1], S12); {  2   }
  FF (c, d, a, b, x[ 2], S13); {  3   }
  FF (b, c, d, a, x[ 3], S14); {  4   }
  FF (a, b, c, d, x[ 4], S11); {  5   }
  FF (d, a, b, c, x[ 5], S12); {  6   }
  FF (c, d, a, b, x[ 6], S13); {  7   }
  FF (b, c, d, a, x[ 7], S14); {  8   }
  FF (a, b, c, d, x[ 8], S11); {  9   }
  FF (d, a, b, c, x[ 9], S12); {  10  }
  FF (c, d, a, b, x[10], S13); {  11  }
  FF (b, c, d, a, x[11], S14); {  12  }
  FF (a, b, c, d, x[12], S11); {  13  }
  FF (d, a, b, c, x[13], S12); {  14  }
  FF (c, d, a, b, x[14], S13); {  15  }
  FF (b, c, d, a, x[15], S14); {  16  }

  { Round 2 }
  GG (a, b, c, d, x[ 0], S21); {  17  }
  GG (d, a, b, c, x[ 4], S22); {  18  }
  GG (c, d, a, b, x[ 8], S23); {  19  }
  GG (b, c, d, a, x[12], S24); {  20  }
  GG (a, b, c, d, x[ 1], S21); {  21  }
  GG (d, a, b, c, x[ 5], S22); {  22  }
  GG (c, d, a, b, x[ 9], S23); {  23  }
  GG (b, c, d, a, x[13], S24); {  24  }
  GG (a, b, c, d, x[ 2], S21); {  25  }
  GG (d, a, b, c, x[ 6], S22); {  26  }
  GG (c, d, a, b, x[10], S23); {  27  }
  GG (b, c, d, a, x[14], S24); {  28  }
  GG (a, b, c, d, x[ 3], S21); {  29  }
  GG (d, a, b, c, x[ 7], S22); {  30  }
  GG (c, d, a, b, x[11], S23); {  31  }
  GG (b, c, d, a, x[15], S24); {  32  }

  { Round 3 }
  HH (a, b, c, d, x[ 0], S31); {  33  }
  HH (d, a, b, c, x[ 8], S32); {  34  }
  HH (c, d, a, b, x[ 4], S33); {  35  }
  HH (b, c, d, a, x[12], S34); {  36  }
  HH (a, b, c, d, x[ 2], S31); {  37  }
  HH (d, a, b, c, x[10], S32); {  38  }
  HH (c, d, a, b, x[ 6], S33); {  39  }
  HH (b, c, d, a, x[14], S34); {  40  }
  HH (a, b, c, d, x[ 1], S31); {  41  }
  HH (d, a, b, c, x[ 9], S32); {  42  }
  HH (c, d, a, b, x[ 5], S33); {  43  }
  HH (b, c, d, a, x[13], S34); {  44  }
  HH (a, b, c, d, x[ 3], S31); {  45  }
  HH (d, a, b, c, x[11], S32); {  46  }
  HH (c, d, a, b, x[ 7], S33); {  47  }
  HH (b, c, d, a, x[15], S34); {  48  }

  inc(state^[0], a);
  inc(state^[1], b);
  inc(state^[2], c);
  inc(state^[3], d);
end;


end.
