IDEA шифрование

Материал из DRKB


Часто в свои проекты необходимо включать шифрование данных. Самый простой способ - xor шифрование, но он подходит только когда необходимо обеспечить малый уровень защиты. Но иногда необходимы более серьезные алгоритмы.

Работая над архиватором файлов (вроде WinRar), у меня встал вопрос о шифровании, в таких программах как архиватор это просто необходимо.

Итак, существует ряд алгоритмов симметричного шифрования - когда один и тот же ключ используется для шифрования и дешифрования. Эти алгоритмы, как правило, очень хорошо изучены и их стойкость к различного рода атакам подтверждена результатами математических исследований.

Кроме того, 2 октября 2000 года NIST (Национальный институт стандартов и технологий, правопреемник прежнего НБС), утвердил алгоритм Rijndael Джоана Димена и Винсента Риджмена как AES (Усовершенствованный алгоритм шифрования, который должен стать заменой прежнего стандарта - DES). Алгоритм Rijndael свободен как для коммерческого, так и для некоммерческого использования и, по видимому, является наилучшим выбором если необходима достаточная стойкость шифра наряду с высокой скоростью работы и относительной простотой реализации.

Но я выбрал для своего архиватора алгоритм IDEA (International Data Encryption Algorithm). Этот алгоритм был разработан для простого воплощения как программно, так и аппаратно. Стойкость IDEA основывается на использовании трех несовместимых типов арифметических операций над 16-битными словами. IDEA очень распространен в Европе и используется в популярной программе шифрования электронных писем PGP (Pretty Good Privacy).

Нижепредставленный модуль полностью реализует в себе метода IDEA шифрования. Главными функциями являются:

// Зашифровать данные из одного потока в другой
function EncryptCopy(DestStream, SourseStream: TStream; Count: Int64; Key: string): Boolean;

// Расшифровать данные из одного потока в другой
function DecryptCopy(DestStream, SourseStream: TStream; Count: Int64; Key: string): Boolean;

// Зашифровать содержимое потока
function EncryptStream(DataStream: TStream; Count: Int64; Key: string): Boolean;

// Расшифровать содержимое потока
function DecryptStream(DataStream: TStream; Count: Int64; Key: string): Boolean;


А теперь сам модуль:

{ *********************************************************************** }

{ Delphi Encryption Library                                               }
{ Encryption / Decryption stream - IDEA                                   }

{ Copyright (c) 2004 by Matveev Igor Vladimirovich                        }
{ With offers and wishes write: teap_leap@mail.ru                         }

{ *********************************************************************** }

unit IDEA;

interface

uses
  SysUtils, Classes, Math;

const
  Rounds = 8;
  KeyLength = (Rounds * 6) + 4;
  Maxim = 65537;

type
  TIDEAKey = array[0..KeyLength - 1] of Word;
  TIDEABlock = array[1..4] of Word;

var
  Z: TIDEAKey;
  K: TIDEAKey;

  FBlockSize: Integer;
  FKey: string;
  FBufferSize: Integer;
  FKeySize: Integer;
  FKeyPtr: PChar;

////////////////////////////////////////////////////////////////////////////////
// Дополнительные функции

procedure Initialize(AKey: string);          // Инициализация
procedure CalculateSubKeys;                  // Подготовка субключей
function EncipherBlock(var Block): Boolean;  // Шифрация блока (8 байт)
function DecipherBlock(var Block): Boolean;  // Дешифрация блока

////////////////////////////////////////////////////////////////////////////////
// Основные функции

// Зашифровать данные из одного потока в другой
function EncryptCopy(DestStream, SourseStream: TStream; Count: Int64;
  Key: string): Boolean;

// Расшифровать данные из одного потока в другой
function DecryptCopy(DestStream, SourseStream: TStream; Count: Int64;
  Key: string): Boolean;

// Зашифровать содержимое потока
function EncryptStream(DataStream: TStream; Count: Int64; Key: string): Boolean;

// Расшифровать содержимое потока
function DecryptStream(DataStream: TStream; Count: Int64; Key: string): Boolean;

implementation

////////////////////////////////////////////////////////////////////////////////

function ROL(a, s: LongWord): LongWord;
asm
         MOV     ECX, s
         ROL     EAX, CL
end;

////////////////////////////////////////////////////////////////////////////////

procedure InvolveKey;
var
  TempKey: string;
  i, j: Integer;
  K1, K2: LongWord;
begin
  // Разворачивание ключа до длинны 51 символ
  TempKey := FKey;
  i := 1;
  while ((Length(TempKey) mod FKeySize) <> 0) do
  begin
    TempKey := TempKey + TempKey[i];
    Inc(i);
  end;

  // Now shorten the key down to one KeySize block by combining the bytes
  i := 1;
  j := 0;
  while (i < Length(TempKey)) do
  begin
    Move((FKeyPtr + j)^, K1, 4);
    Move(TempKey[i], K2, 4);
    K1 := ROL(K1, K2) xor K2;
    Move(K1, (FKeyPtr + j)^, 4);
    j := (j + 4) mod FKeySize;
    Inc(i, 4);
  end;
end;

////////////////////////////////////////////////////////////////////////////////

{$R-,Q-}
procedure ExpandKeys;
var
  i: Integer;
begin
  // Копирование ключа в Z
  Move(FKeyPtr^, Z, FKeySize);

  // Генерация подключа зашифрование
  for i := 8 to KeyLength - 1 do
  begin
    if (((i + 2) mod 8) = 0) then
      Z[i] := (Z[i - 7] shl 9) xor (Z[i - 14] shr 7)
    else if (((i + 1) mod 8) = 0) then
      Z[i] := (Z[i - 15] shl 9) xor (Z[i - 14] shr 7)
    else
      Z[i] := (Z[i - 7] shl 9) xor (Z[i - 6] shr 7);
  end;
end;

////////////////////////////////////////////////////////////////////////////////

procedure InvertKeys;
type
  PWord = ^Word;
var
  j: Integer;
  pz, pp: PWord;
  t1, t2, t3: Word;

  ////////////////////////////////////////

  function Inv(I: Integer): Integer;
  var
    n1, n2, q, r, b1, b2, t: Integer;
  begin
    if (I = 0) then
      Result := 0
    else
    begin
      n1 := Maxim;
      n2 := I;
      b2 := 1;
      b1 := 0;
      repeat
        r := (n1 mod n2);
        q := (n1 - r) div n2;
        if (r = 0) then
        begin
          if (b2 < 0) then b2 := Maxim + b2;
        end
        else
        begin
          n1 := n2;
          n2 := r;
          t := b2;
          b2 := b1 - q * b2;
          b1 := t;
        end;
      until (r = 0);
      Result := b2;
    end;
    Result := (Result and $ffff);
  end;

  ////////////////////////////////////////

begin
  pz := @Z;
  pp := @K;
  Inc(pp, KeyLength);

  //  t1 = inv(*Z++);
  t1 := Inv(pz^);
  Inc(pz);

  //  t2 = -*Z++;
  t2 := -pz^;
  Inc(pz);

  //  t3 = -*Z++;
  t3 := -pz^;
  Inc(pz);

  //  *--p = inv(*Z++);
  Dec(pp);
  pp^ := Inv(pz^);
  Inc(pz);

  //  *--p = t3;
  Dec(pp);
  pp^ := t3;

  //  *--p = t2;
  Dec(pp);
  pp^ := t2;

  //  *--p = t1;
  Dec(pp);
  pp^ := t1;

  for j := 1 to Rounds - 1 do
  begin
    //      t1 = *Z++;
    t1 := pz^;
    Inc(pz);

    //      *--p = *Z++;
    Dec(pp);
    pp^ := pz^;
    Inc(pz);

    //      *--p = t1;
    Dec(pp);
    pp^ := t1;

    //      t1 = inv(*Z++);
    t1 := Inv(pz^);
    Inc(pz);

    //      t2 = -*Z++;
    t2 := -pz^;
    Inc(pz);

    //      t3 = -*Z++;
    t3 := -pz^;
    Inc(pz);

    //      *--p = inv(*Z++);
    Dec(pp);
    pp^ := Inv(pz^);
    Inc(pz);

    //      *--p = t2;
    Dec(pp);
    pp^ := t2;

    //      *--p = t3;
    Dec(pp);
    pp^ := t3;

    //      *--p = t1;
    Dec(pp);
    pp^ := t1;
  end;

  //  t1 = *Z++;
  t1 := pz^;
  Inc(pz);

  //  *--p = *Z++;
  Dec(pp);
  pp^ := pz^;
  Inc(pz);

  //  *--p = t1;
  Dec(pp);
  pp^ := t1;

  //  t1 = inv(*Z++);
  t1 := Inv(pz^);
  Inc(pz);

  //  t2 = -*Z++;
  t2 := -pz^;
  Inc(pz);

  //  t3 = -*Z++;
  t3 := -pz^;
  Inc(pz);

  //  *--p = inv(*Z++);
  Dec(pp);
  pp^ := Inv(pz^);

  //  *--p = t3;
  Dec(pp);
  pp^ := t3;

  //  *--p = t2;
  Dec(pp);
  pp^ := t2;

  //  *--p = t1;
  Dec(pp);
  pp^ := t1;
end;

{$R+,Q+}

////////////////////////////////////////////////////////////////////////////////

procedure CalculateSubKeys;
begin
  ExpandKeys;
  InvertKeys;
end;

////////////////////////////////////////////////////////////////////////////////

procedure Initialize(AKey: string);
begin
  FBlockSize := 8;
  FBufferSize := 2048;
  FKey := AKey;
  FKeySize := 32;

  FillChar(Z, SizeOf(Z), 0);
  FillChar(K, SizeOf(K), 0);

  GetMem(FKeyPtr, FKeySize);
  FillChar(FKeyPtr^, FKeySize, #0);

  InvolveKey;
end;

////////////////////////////////////////////////////////////////////////////////

{$R-,Q-}
procedure Cipher(var Block: TIDEABlock; const Keys: TIDEAKey);
var
  x1, x2, x3, x4: Word;
  t1, t2: Word;
  pz: ^Word;
  r: Integer;

  ////////////////////////////////////////

  function Mul(a, b: Word): Word;
  var
    p: LongWord;
  begin
    if (a > 0) then
    begin
      if (b > 0) then
      begin
        p := longword(a) * b;
        b := p and $ffff;
        a := p shr 16;
        Result := ((b - a) + Ord(b < a));
      end
      else
        Result := 1 - a;
    end
    else
      Result := 1 - b;
  end;

  ////////////////////////////////////////

begin
  //  x1 = *in++;  x2 = *in++;
  x1 := Block[1];
  x2 := Block[2];
  //  x3 = *in++;  x4 = *in;
  x3 := Block[3];
  x4 := Block[4];

  pz := @Keys;
  for r := 1 to Rounds do
  begin
    //      MUL(x1,*Z++);
    x1 := Mul(x1, pz^);
    Inc(pz);

    //      x2 += *Z++;
    x2 := x2 + pz^;
    Inc(pz);

    //      x3 += *Z++;
    x3 := x3 + pz^;
    Inc(pz);

    //      MUL(x4, *Z++);
    x4 := Mul(x4, pz^);
    Inc(pz);

    //      t2 = x1^x3;
    t2 := x1 xor x3;

    //      MUL(t2, *Z++);
    t2 := Mul(t2, pz^);
    Inc(pz);

    //      t1 = t2 + (x2^x4);
    t1 := t2 + (x2 xor x4);

    //      MUL(t1, *Z++);
    t1 := Mul(t1, pz^);
    Inc(pz);

    //      t2 = t1+t2;
    t2 := (t1 + t2);

    //      x1 ^= t1;
    x1 := x1 xor t1;

    //      x4 ^= t2;
    x4 := x4 xor t2;

    //      t2 ^= x2;
    t2 := t2 xor x2;

    //      x2 = x3^t1;
    x2 := x3 xor t1;

    //      x3 = t2;
    x3 := t2;
  end;

  //  MUL(x1, *Z++);
  x1 := Mul(x1, pz^);
  Inc(pz);

  //  *out++ = x1;
  Block[1] := x1;

  //  *out++ = x3 + *Z++;
  Block[2] := x3 + pz^;
  Inc(pz);

  //  *out++ = x2 + *Z++;
  Block[3] := x2 + pz^;
  Inc(pz);

  //  MUL(x4, *Z);
  x4 := Mul(x4, pz^);

  //  *out = x4;
  Block[4] := x4;
end;

{$R+,Q+}

////////////////////////////////////////////////////////////////////////////////

function EncipherBlock(var Block): Boolean;
begin
  Cipher(TIDEABlock(Block), Z);
  Result := True;
end;

////////////////////////////////////////////////////////////////////////////////

function DecipherBlock(var Block): Boolean;
begin
  Cipher(TIDEABlock(Block), K);
  Result := True;
end;

////////////////////////////////////////////////////////////////////////////////
// Главные функции ...

function EncryptCopy(DestStream, SourseStream: TStream; Count: Int64;
  Key: string): Boolean;
var
  Buffer: TIDEABlock;
  PrCount: Int64;
  AddCount: Byte;
begin
  Result := True;
  try
    if Key = '' then
    begin
      DestStream.CopyFrom(SourseStream, Count);
      Exit;
    end;
    Initialize(Key);
    CalculateSubKeys;
    PrCount := 0;
    while Count - PrCount >= 8 do
    begin
      SourseStream.Read(Buffer, SizeOf(TIDEABlock));
      EncipherBlock(Buffer);
      DestStream.Write(Buffer, SizeOf(TIDEABlock));
      Inc(PrCount, 8);
    end;

    AddCount := Count - PrCount;
    if Count - PrCount <> 0 then
    begin
      SourseStream.Read(Buffer, AddCount);
      DestStream.Write(Buffer, AddCount);
    end;
  except
    Result := False;
  end;
end;

////////////////////////////////////////////////////////////////////////////////

function DecryptCopy(DestStream, SourseStream: TStream; Count: Int64;
  Key: string): Boolean;
var
  Buffer: TIDEABlock;
  PrCount: Int64;
  AddCount: Byte;
begin
  Result := True;
  try
    if Key = '' then
    begin
      DestStream.CopyFrom(SourseStream, Count);
      Exit;
    end;
    Initialize(Key);
    CalculateSubKeys;
    PrCount := 0;
    while Count - PrCount >= 8 do
    begin
      SourseStream.Read(Buffer, SizeOf(TIDEABlock));
      DecipherBlock(Buffer);
      DestStream.Write(Buffer, SizeOf(TIDEABlock));
      Inc(PrCount, 8);
    end;

    AddCount := Count - PrCount;
    if Count - PrCount <> 0 then
    begin
      SourseStream.Read(Buffer, AddCount);
      DestStream.Write(Buffer, AddCount);
    end;
  except
    Result := False;
  end;
end;

////////////////////////////////////////////////////////////////////////////////

function EncryptStream(DataStream: TStream; Count: Int64; Key: string): Boolean;
var
  Buffer: TIDEABlock;
  PrCount: Int64;
  AddCount: Byte;
begin
  Result := True;
  try
    if Key = '' then
    begin
      DataStream.Seek(Count, soFromCurrent);
      Exit;
    end;
    Initialize(Key);
    CalculateSubKeys;
    PrCount := 0;
    while Count - PrCount >= 8 do
    begin
      DataStream.Read(Buffer, SizeOf(TIDEABlock));
      EncipherBlock(Buffer);
      DataStream.Seek(-SizeOf(TIDEABlock), soFromCurrent);
      DataStream.Write(Buffer, SizeOf(TIDEABlock));
      Inc(PrCount, 8);
    end;
  except
    Result := False;
  end;
end;

////////////////////////////////////////////////////////////////////////////////

function DecryptStream(DataStream: TStream; Count: Int64; Key: string): Boolean;
var
  Buffer: TIDEABlock;
  PrCount: Int64;
begin
  Result := True;
  try
    if Key = '' then
    begin
      DataStream.Seek(Count, soFromCurrent);
      Exit;
    end;
    Initialize(Key);
    CalculateSubKeys;
    PrCount := 0;
    while Count - PrCount >= 8 do
    begin
      DataStream.Read(Buffer, SizeOf(TIDEABlock));
      DecipherBlock(Buffer);
      DataStream.Seek(-SizeOf(TIDEABlock), soFromCurrent);
      DataStream.Write(Buffer, SizeOf(TIDEABlock));
      Inc(PrCount, 8);
    end;
  except
    Result := False;
  end;
end;

// Завершение главных функций ...
////////////////////////////////////////////////////////////////////////////////

end.


А пользоваться этим модулем можно так. Нижеприведенный пример демонстрирует шифрование / дешифрование файла с использованием функций EncryptStream / DecryptStream:

procedure TForm1.Button1Click(Sender: TObject);
var
  SourseStream: TFileStream;
begin
  SourseStream := TFileStream.Create(Edit1.Text, fmOpenReadWrite);
  try
    EncryptStream(SourseStream, SourseStream.Size, Edit2.Text);
  finally
    SourseStream.Free;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  SourseStream: TFileStream;
begin
  SourseStream := TFileStream.Create(Edit1.Text, fmOpenReadWrite);
  try
    DecryptStream(SourseStream, SourseStream.Size, Edit2.Text);
  finally
    SourseStream.Free;
  end;
end;


ПРИМЕЧАНИЕ
Так как алгоритм шифрует данные блоками по 8 байт, а размер шифруемых данных не всегда кратен 8, поэтому в данном модуле последний блок, если он размером больше ноля и меньше восьми, не шифруется. Поэтому, если функцию шифрования обозначить e(x), а Srt1 и Str2 - шифруемые данные, то e(Str1) + e(Str2) не всегда равно e(Str1 + Str2).

Матвеев Игорь Владимирович


Author: Матвеев Игорь
Source: http://delphiworld.narod.ru/
ID: 04009