unit UAthena;
{
  Athena: cryptor module for Miranda SecuredMMAP Database driver
  Copyright 2007-2008 Klyde

  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
}


interface
uses
   md5_unit, windows;
Type
   Arr  = array of byte;
   PArr = ^Arr;
   
   Function MD5_Mod(const s: AnsiString; block_count: byte): AnsiString;
   Procedure MakeKey(key: PArr; len: word; const pwd: AnsiString);
   Procedure EncryptData(key: PArr; data: PByte; size: LongWord);
   Procedure DecryptData(key: PArr; data: PByte; size: LongWord);

implementation
//==============================================================================
Function str_back(const s: AnsiString): AnsiString;
Var
   i: integer;
Begin
   result := '';
   for i := Length(s) downto 1 do result := result + s[i];
end;
//==============================================================================
Function MD5_Mod(const s: AnsiString; block_count: byte): AnsiString;
Var
   s1, s2, sb : AnsiString;
   k          : word;
Begin
   sb := str_back(s);
   s2 := '';
   For k := 1 to block_count do
   Begin
      s1 := md5(s + sb);
      s2 := str_back(s2 + md5(s1+sb+s2));
   End;
   result := s2;
end;
//==============================================================================
Procedure MakeKey(key: PArr; len: word; const pwd: AnsiString);
Var
   s : AnsiString;
   i : word;
   dummy: integer;
Begin
   if len > 64 then Len := ((Len div 16) + 1)*16 else Len := 64;
   SetLength(key^, Len);
   s := MD5_mod(pwd, len div 16);
   for i := 1 to length(s) div 2 do
   begin
     val('$' + copy(s, i*2 - 1, 2),key^[i-1],dummy);
   end;
end;
//==============================================================================
Procedure GetNENum(key: arr; var n1, n2: LongWord);
Var
   i: LongWord;
Begin
   n1 := 0;
   n2 := 0;
   for i := 0 to Length(key) - 1 do
   Begin
      n1 := n1 + key[i] + (i + 1)*(n1+1);
      n2 := n2 + key[i] - (i + 1)*(n2+1);
   end;
   n1 := n1*2 + 1;
   n2 := n2*2 + 3;
end;

//==============================================================================
Procedure SimGamm(key: PArr; data: PByte; size: LongWord);
Var
   kg : Arr;
   i, n1, n2 : LongWord;
   lk, k1, k2 : word;
Begin
   lk := Length(key^);
   SetLength(kg, lk);
   for i := 0 to lk - 1 do kg[i] := key^[i];
   GetNENum(kg, n1, n2);
   For i := 1 to size - 1 do
   Begin
      if (i mod lk) = 0 then GetNENum(kg, n1, n2);
      k1 := (i+n1+7)*n2 mod lk;
      k2 := (i+n2+3)*n1 mod lk;

      PByte(uint_ptr(data)+i)^ := PByte(uint_ptr(data)+i)^ xor kg[k1] xor kg[k2];

      kg[k1] := kg[k1]*k1 + kg[k2] + i*k2;
      kg[k2] := kg[k2]*k2 + kg[k1] + i*k1;
   end;
end;
//==============================================================================
Procedure Left(key: PArr; data: PByte; size: LongWord);
Var
   k : Arr;
   i, n1, n2 : LongWord;
   lk, k1, k2 : word;

Begin
   lk := Length(key^);

   SetLength(k, lk);
   for i := 0 to lk - 1 do k[i] := key^[i];
   GetNENum(k, n1, n2);
   //---------------------------------------------------------------------------
   k1 := (n2 + lk)*n1 mod lk;
   k2 := (n1 + lk)*n2 mod lk;
   data^ := data^ xor k[k1] xor k[k2];

   //---------------------------------------------------------------------------
   For i := 1 to size - 1 do
   Begin
      k1 := (i+n1)*n2 mod lk;
      k2 := (i+n2)*n1 mod lk;

      PByte(uint_ptr(data)+i)^ := PByte(uint_ptr(data)+i)^ xor ((PByte(uint_ptr(data)+i-1)^ xor k[k1]) xor k[k2]);
   end;
end;
//==============================================================================
Procedure Right(key: PArr; data: PByte; size: LongWord);
Var
   k : Arr;
   i, n1, n2 : LongWord;
   lk, k1, k2 : word;
Begin
   lk := Length(key^);
   SetLength(k, lk);
   for i := 0 to lk - 1 do k[i] := key^[i];
   GetNENum(k, n1, n2);
   //---------------------------------------------------------------------------
   For i := size - 1 downto 1 do
   Begin
      k1 := (i+n1)*n2 mod lk;
      k2 := (i+n2)*n1 mod lk;
      PByte(uint_ptr(data) + i)^ := PByte(uint_ptr(data)+i)^ xor ((PByte(uint_ptr(data) + i - 1)^ xor k[k1]) xor k[k2]);
   end;
   //---------------------------------------------------------------------------
   k1 := (n2 + lk)*n1 mod lk;
   k2 := (n1 + lk)*n2 mod lk;
   data^ := data^ xor k[k1] xor k[k2];
end;
//==============================================================================
Procedure EncryptData(key: PArr; data: PByte; size: LongWord);
Begin
   Left(key, data, size);
   SimGamm(key, data, size);
end;
//==============================================================================
Procedure DecryptData(key: PArr; data: PByte; size: LongWord);
Begin
   SimGamm(key, data, size);
   Right(key, data, size);
end;
//==============================================================================
end.