Модуль для IDEA шифрования
Автор: Матвеев Игорь
Часто в свои проекты необходимо включать шифрование данных.
Самый простой способ - 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 Еncryption Library }
{ Еncryption / 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 );
EncryptStream(SourseStream, SourseStream.Size, Edit2.Text);
SourseStream.Free;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
SourseStream : TFileStream;
begin
SourseStream := TFileStream.Create(Edit1.Text, fmOpenReadWrite );
DecryptStream(SourseStream, SourseStream.Size, Edit2.Text);
SourseStream.Free;
end;
ПРИМЕЧАНИЕ: Так как алгоритм шифрует данные блоками по 8 байт, а размер шифруемых
данных не всегда кратен 8, поэтому в данном модуле последний блок, если он размером
больше нодя и меньше восьми, не шифруется. Поэтому, если функцию шифрования
обозначить e(x), а Srt1 и Str2 - шифруемые данные, то
e(Str1) + e(Str2) не всегда равно e(Str1 + Str2).
Матвеев Игорь Владимирович
|