unit sha1DK;

// implementation of SHA1 hash algo - pretty ugly, but working - of course you can use any other SHA1 library instead of this one

interface

uses Windows;

procedure SHA1_starts(pSHA_context: pointer);
procedure SHA1_update(pSHA_context: pointer; psrc: pointer; size: dword);
procedure SHA1_finish(pSHA_context: pointer; pdest: pointer);

// no special comments for these functions :)

type
  SHA1_context = record
    key: array [0..19] of byte;
    var1: dword;
    var2: dword;
    buffer: array [0..63] of byte;
  end;


implementation

procedure copy_block(psrc: pointer; pdest: pointer; size: dword);
type
  tdata = array [0..65535] of byte;
var
  src:  ^tdata;
  dest: ^tdata;
  i:    dword;
begin
  if (size = 0) then
    exit;
  src  := psrc;
  dest := pdest;
  for i := 0 to (size - 1) do
    dest^[i] := src^[i];
end;

procedure SHA1_process(pdata: pointer; var H1: dword; var H2: dword;
  var H3: dword; var H4: dword; var H5: dword);
type
  tbuffer = array [0..63] of byte;
const
  K: array [0..3] of Dword = ($5A827999, $6ED9EBA1, $8F1BBCDC, $CA62C1D6);
var
  t:      dword;
  buffer: ^tbuffer;
  W:      array [0..79] of dword;
  tmp, tmp2: dword;
  A, B, C, D, E: dword;

begin
  buffer := pdata;

  A := H1;
  B := H2;
  C := H3;
  D := H4;
  E := H5;

  for t := 0 to 15 do
    W[t] := (buffer^[t * 4] shl 24) or (buffer^[t * 4 + 1] shl 16) or
      (buffer^[t * 4 + 2] shl 8) or (buffer^[t * 4 + 3]);

  for t := 16 to 79 do
  begin
    tmp2 := (W[t - 3] xor W[t - 8] xor W[t - 14] xor W[t - 16]);
    W[t] := ((tmp2 shl 1) or (tmp2 shr 31));
  end;

  for t := 0 to 19 do
  begin
    tmp := ((A shl 5) or (A shr 27)) + ((B and C) or ((not B) and D)) + E + W[t] + K[0];
    E   := D;
    D   := C;
    C   := ((B shl 30) or (B shr 2));
    B   := A;
    A   := tmp;
  end;

  for t := 20 to 39 do
  begin
    tmp := ((A shl 5) or (A shr 27)) + (B xor C xor D) + E + W[t] + K[1];
    E   := D;
    D   := C;
    C   := ((B shl 30) or (B shr 2));
    B   := A;
    A   := tmp;
  end;

  for t := 40 to 59 do
  begin
    tmp := ((A shl 5) or (A shr 27)) + ((B and C) or (B and D) or (C and D)) +
      E + W[t] + K[2];
    E   := D;
    D   := C;
    C   := ((B shl 30) or (B shr 2));
    B   := A;
    A   := tmp;
  end;

  for t := 60 to 79 do
  begin
    tmp := ((A shl 5) or (A shr 27)) + (B xor C xor D) + E + W[t] + K[3];
    E   := D;
    D   := C;
    C   := ((B shl 30) or (B shr 2));
    B   := A;
    A   := tmp;
  end;

  H1 := H1 + A;
  H2 := H2 + B;
  H3 := H3 + C;
  H4 := H4 + D;
  H5 := H5 + E;
end;

procedure SHA1_starts;
var
  tmp: ^SHA1_context;
begin
  tmp := pSHA_context;
  tmp^.key[$00] := $01;
  tmp^.key[$01] := $23;
  tmp^.key[$02] := $45;
  tmp^.key[$03] := $67;

  tmp^.key[$04] := $89;
  tmp^.key[$05] := $AB;
  tmp^.key[$06] := $CD;
  tmp^.key[$07] := $EF;

  tmp^.key[$08] := $FE;
  tmp^.key[$09] := $DC;
  tmp^.key[$0A] := $BA;
  tmp^.key[$0B] := $98;

  tmp^.key[$0C] := $76;
  tmp^.key[$0D] := $54;
  tmp^.key[$0E] := $32;
  tmp^.key[$0F] := $10;

  tmp^.key[$10] := $F0;
  tmp^.key[$11] := $E1;
  tmp^.key[$12] := $D2;
  tmp^.key[$13] := $C3;

  tmp^.var1 := 0;
  tmp^.var2 := 0;
end;

procedure SHA1_step(pkey: pointer; pdata: pointer);
type
  tKey = array [0..4] of dword;
var
  key: ^tKey;

  buffer: array [0..63] of byte;
begin
  key := pkey;

  copy_block(pdata, @buffer, $40);

  SHA1_process(@buffer, key^[0], key^[1], key^[2], key^[3], key^[4]);
end;

procedure SHA1_update;
type
  tdata = array [0..65535] of byte;
var
  crypt_key: ^SHA1_context;
  src: ^tdata;

  tmp_alreadycopied: dword;
  tmp_currentpos:    dword;

begin
  crypt_key := pSHA_context;
  src := psrc;

  tmp_currentpos  := (crypt_key^.var1 and 511) shr 3;
  crypt_key^.var1 := crypt_key^.var1 + (size shl 3);

  if ((crypt_key^.var1 + (size * 8)) < (size * 8)) then
    crypt_key^.var2 := crypt_key^.var2 + 1;

  crypt_key^.var2 := crypt_key^.var2 + (size shr $1D);

  if ((tmp_currentpos + size) <= $3F) then
    copy_block(src, addr(crypt_key^.buffer[tmp_currentpos]), size)// not full
  else begin // full
    tmp_alreadycopied := $40 - (tmp_currentpos);
    copy_block(src, addr(crypt_key^.buffer[tmp_currentpos]), tmp_alreadycopied);

    SHA1_step(addr(crypt_key^.key), addr(crypt_key^.buffer));

    while ((tmp_alreadycopied + $3F) < size) do
    begin
      SHA1_step(addr(crypt_key^.key), addr(src^[tmp_alreadycopied]));
      tmp_alreadycopied := tmp_alreadycopied + $40;
    end;

    copy_block(addr(src^[tmp_alreadycopied]), addr(crypt_key^.buffer),
      (size - tmp_alreadycopied));
  end;
end;

procedure SHA1_finish;
type
  tdata = array [0..65535] of byte;
var
  crypt_key: ^SHA1_context;
  sp_key: array [0..7] of byte;
  dest: ^tdata;
  i: dword;

  tmp_val:  dword;
  tmp_pos1: dword;
  tmp_pos2: dword;

  tmp_crypt: dword;
begin
  crypt_key := pSHA_context;
  dest      := pdest;

  // part1
  for i := 0 to 7 do
  begin
    if i < 4 then
      tmp_val := crypt_key^.var2
    else
      tmp_val := crypt_key^.var1;
    tmp_pos1 := (3 - (i mod 4));
    sp_key[i] := (tmp_val shr (tmp_pos1 * 8));
  end;

  //part2
  tmp_crypt := $80;
  SHA1_update(crypt_key, addr(tmp_crypt), 1);
  tmp_crypt := $00;
  while (((crypt_key^.var1 shr 3) and 63) < $38) do
    SHA1_update(crypt_key, addr(tmp_crypt), 1);
  SHA1_update(crypt_key, addr(sp_key), 8);

  // part3
  for i := 0 to $13 do
  begin
    tmp_pos2 := (i div 4) * 4;
    tmp_val  := ((crypt_key^.key[tmp_pos2 + 0])) +
      ((crypt_key^.key[tmp_pos2 + 1]) shl $08) +
      ((crypt_key^.key[tmp_pos2 + 2]) shl $10) +
      ((crypt_key^.key[tmp_pos2 + 3]) shl $18);
    tmp_pos1 := (3 - (i mod 4));
    dest^[i] := (tmp_val shr (tmp_pos1 * 8));
  end;

  // part4
  for i := 0 to 7 do
    sp_key[i] := 0;
  for i := 0 to $13 do
    crypt_key^.key[i] := 0;
  for i := 0 to $3F do
    crypt_key^.buffer[i] := 0;
  crypt_key^.var1 := 0;
  crypt_key^.var2 := 0;
end;

end.
