Шифрование 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).
Матвеев Игорь Владимирович
DelphiWorld 6.0