362 lines
9.3 KiB
ObjectPascal
362 lines
9.3 KiB
ObjectPascal
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ****************************************************************************
|
|
// * Project : FWZip
|
|
// * Unit Name : FWZipCrypt
|
|
// * Purpose : Реализация криптографии по методу PKWARE
|
|
// * Author : Александр (Rouse_) Багель
|
|
// * Copyright : © Fangorn Wizards Lab 1998 - 2015.
|
|
// * Version : 1.0.11
|
|
// * Home Page : http://rouse.drkb.ru
|
|
// * Home Blog : http://alexander-bagel.blogspot.ru
|
|
// ****************************************************************************
|
|
// * Stable Release : http://rouse.drkb.ru/components.php#fwzip
|
|
// * Latest Source : https://github.com/AlexanderBagel/FWZip
|
|
// ****************************************************************************
|
|
//
|
|
// Используемые источники:
|
|
// ftp://ftp.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip
|
|
// http://zlib.net/zlib-1.2.5.tar.gz
|
|
// http://www.base2ti.com/
|
|
//
|
|
|
|
unit FWZipCrypt;
|
|
{$mode delphi}
|
|
{$codepage UTF8}
|
|
|
|
interface
|
|
|
|
// Переполнения и выход за диапазон неизбежны
|
|
// поэтому отключаем данные проверки
|
|
{$OVERFLOWCHECKS OFF}
|
|
{$RANGECHECKS OFF}
|
|
|
|
uses
|
|
LCLIntf, LCLType, LMessages, windows,
|
|
Classes,
|
|
FWZipConsts;
|
|
|
|
{
|
|
XIII. Decryption
|
|
----------------
|
|
|
|
The encryption used in PKZIP was generously supplied by Roger
|
|
Schlafly. PKWARE is grateful to Mr. Schlafly for his expert
|
|
help and advice in the field of data encryption.
|
|
|
|
PKZIP encrypts the compressed data stream. Encrypted files must
|
|
be decrypted before they can be extracted.
|
|
|
|
Each encrypted file has an extra 12 bytes stored at the start of
|
|
the data area defining the encryption header for that file. The
|
|
encryption header is originally set to random values, and then
|
|
itself encrypted, using three, 32-bit keys. The key values are
|
|
initialized using the supplied encryption password. After each byte
|
|
is encrypted, the keys are then updated using pseudo-random number
|
|
generation techniques in combination with the same CRC-32 algorithm
|
|
used in PKZIP and described elsewhere in this document.
|
|
|
|
The following is the basic steps required to decrypt a file:
|
|
|
|
1) Initialize the three 32-bit keys with the password.
|
|
2) Read and decrypt the 12-byte encryption header, further
|
|
initializing the encryption keys.
|
|
3) Read and decrypt the compressed data stream using the
|
|
encryption keys.
|
|
|
|
|
|
Step 1 - Initializing the encryption keys
|
|
-----------------------------------------
|
|
|
|
Key(0) <- 305419896
|
|
Key(1) <- 591751049
|
|
Key(2) <- 878082192
|
|
|
|
loop for i <- 0 to length(password)-1
|
|
update_keys(password(i))
|
|
end loop
|
|
|
|
|
|
Where update_keys() is defined as:
|
|
|
|
|
|
update_keys(char):
|
|
Key(0) <- crc32(key(0),char)
|
|
Key(1) <- Key(1) + (Key(0) & 000000ffH)
|
|
Key(1) <- Key(1) * 134775813 + 1
|
|
Key(2) <- crc32(key(2),key(1) >> 24)
|
|
end update_keys
|
|
|
|
|
|
Where crc32(old_crc,char) is a routine that given a CRC value and a
|
|
character, returns an updated CRC value after applying the CRC-32
|
|
algorithm described elsewhere in this document.
|
|
|
|
|
|
Step 2 - Decrypting the encryption header
|
|
-----------------------------------------
|
|
|
|
The purpose of this step is to further initialize the encryption
|
|
keys, based on random data, to render a plaintext attack on the
|
|
data ineffective.
|
|
|
|
|
|
Read the 12-byte encryption header into Buffer, in locations
|
|
Buffer(0) thru Buffer(11).
|
|
|
|
loop for i <- 0 to 11
|
|
C <- buffer(i) ^ decrypt_byte()
|
|
update_keys(C)
|
|
buffer(i) <- C
|
|
end loop
|
|
|
|
|
|
Where decrypt_byte() is defined as:
|
|
|
|
|
|
unsigned char decrypt_byte()
|
|
local unsigned short temp
|
|
temp <- Key(2) | 2
|
|
decrypt_byte <- (temp * (temp ^ 1)) >> 8
|
|
end decrypt_byte
|
|
|
|
|
|
After the header is decrypted, the last 1 or 2 bytes in Buffer
|
|
should be the high-order word/byte of the CRC for the file being
|
|
decrypted, stored in Intel low-byte/high-byte order, or the high-order
|
|
byte of the file time if bit 3 of the general purpose bit flag is set.
|
|
Versions of PKZIP prior to 2.0 used a 2 byte CRC check; a 1 byte CRC check is
|
|
used on versions after 2.0. This can be used to test if the password
|
|
supplied is correct or not.
|
|
|
|
|
|
Step 3 - Decrypting the compressed data stream
|
|
----------------------------------------------
|
|
|
|
The compressed data stream can be decrypted as follows:
|
|
|
|
|
|
loop until done
|
|
read a character into C
|
|
Temp <- C ^ decrypt_byte()
|
|
update_keys(temp)
|
|
output Temp
|
|
end loop
|
|
}
|
|
|
|
const
|
|
EncryptedHeaderSize = 12;
|
|
LastEncryptedHeaderByte = EncryptedHeaderSize - 1;
|
|
|
|
type
|
|
TZipKeys = array [0..2] of Cardinal;
|
|
|
|
TFWZipKeys = class
|
|
private
|
|
FKeys: TZipKeys;
|
|
protected
|
|
procedure UpdateKeys(Value: Byte);
|
|
function DecryptByte: Byte;
|
|
public
|
|
constructor Create(const Password: AnsiString);
|
|
end;
|
|
|
|
TFWZipCryptor = class(TFWZipKeys)
|
|
protected
|
|
function EncryptByte(Value: Byte): Byte;
|
|
public
|
|
procedure GenerateEncryptionHeader(Stream: TStream;
|
|
IsDescryptorFlagPresent: Boolean;
|
|
CRC32, FileDate: Cardinal);
|
|
procedure EncryptBuffer(Buffer: PByte; Size: Int64);
|
|
end;
|
|
|
|
TFWZipDecryptor = class(TFWZipKeys)
|
|
public
|
|
function LoadEncryptionHeader(Stream: TStream;
|
|
IsDescryptorFlagPresent: Boolean;
|
|
CRC32, FileDate: Cardinal): Boolean;
|
|
procedure DecryptBuffer(Buffer: PByte; Size: Int64);
|
|
end;
|
|
|
|
implementation
|
|
|
|
const
|
|
DefaultKeys: TZipKeys = (305419896, 591751049, 878082192);
|
|
|
|
{ TFWZipKeys }
|
|
|
|
constructor TFWZipKeys.Create(const Password: AnsiString);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
inherited Create;
|
|
{
|
|
Step 1 - Initializing the encryption keys
|
|
-----------------------------------------
|
|
|
|
Key(0) <- 305419896
|
|
Key(1) <- 591751049
|
|
Key(2) <- 878082192
|
|
|
|
loop for i <- 0 to length(password)-1
|
|
update_keys(password(i))
|
|
end loop
|
|
|
|
}
|
|
FKeys := DefaultKeys;
|
|
for I := 1 to Length(Password) do
|
|
UpdateKeys(Byte(Password[I]));
|
|
end;
|
|
|
|
function TFWZipKeys.DecryptByte: Byte;
|
|
var
|
|
temp: Word;
|
|
begin
|
|
{
|
|
Where decrypt_byte() is defined as:
|
|
|
|
unsigned char decrypt_byte()
|
|
local unsigned short temp
|
|
temp <- Key(2) | 2
|
|
decrypt_byte <- (temp * (temp ^ 1)) >> 8
|
|
end decrypt_byte
|
|
|
|
}
|
|
temp := FKeys[2] or 2;
|
|
Result := (temp * (temp xor 1)) shr 8;
|
|
end;
|
|
|
|
procedure TFWZipKeys.UpdateKeys(Value: Byte);
|
|
begin
|
|
{
|
|
Key(0) <- crc32(key(0),char)
|
|
Key(1) <- Key(1) + (Key(0) & 000000ffH)
|
|
Key(1) <- Key(1) * 134775813 + 1
|
|
Key(2) <- crc32(key(2),key(1) >> 24)
|
|
}
|
|
FKeys[0] := ((FKeys[0] shr 8) and $FFFFFF) xor
|
|
CRC32Table[(FKeys[0] xor Value) and $FF];
|
|
FKeys[1] := FKeys[1] + (FKeys[0] and $FF);
|
|
FKeys[1] := FKeys[1] * 134775813 + 1;
|
|
FKeys[2] := ((FKeys[2] shr 8) and $FFFFFF) xor
|
|
CRC32Table[(FKeys[2] xor (FKeys[1] shr 24)) and $FF];
|
|
end;
|
|
|
|
{ TFWZipCryptor }
|
|
|
|
procedure TFWZipCryptor.EncryptBuffer(Buffer: PByte; Size: Int64);
|
|
var
|
|
temp: Byte;
|
|
begin
|
|
// реверсированный вариант TFWZipDecryptor.DecryptBuffer
|
|
while Size > 0 do
|
|
begin
|
|
Dec(Size);
|
|
temp := DecryptByte;
|
|
UpdateKeys(Buffer^);
|
|
Buffer^ := temp xor Buffer^;
|
|
Inc(Buffer);
|
|
end;
|
|
end;
|
|
|
|
function TFWZipCryptor.EncryptByte(Value: Byte): Byte;
|
|
var
|
|
temp: Byte;
|
|
begin
|
|
temp := DecryptByte;
|
|
UpdateKeys(Value);
|
|
Result := temp xor Value;
|
|
end;
|
|
|
|
procedure TFWZipCryptor.GenerateEncryptionHeader(Stream: TStream;
|
|
IsDescryptorFlagPresent: Boolean; CRC32, FileDate: Cardinal);
|
|
var
|
|
Buffer: array [0..EncryptedHeaderSize - 1] of Byte;
|
|
I: Integer;
|
|
begin
|
|
// реверсированный вариант TFWZipDecryptor.LoadEncryptionHeader
|
|
Randomize;
|
|
for I := 0 to LastEncryptedHeaderByte - 2 do
|
|
Buffer[I] := EncryptByte(Byte(Random(MAXBYTE)));
|
|
if IsDescryptorFlagPresent then
|
|
begin
|
|
Buffer[10] := EncryptByte(LoByte(LoWord(FileDate)));
|
|
Buffer[11] := EncryptByte(HiByte(LoWord(FileDate)));
|
|
end
|
|
else
|
|
begin
|
|
Buffer[10] := EncryptByte(LoByte(HiWord(CRC32)));
|
|
Buffer[11] := EncryptByte(HiByte(HiWord(CRC32)));
|
|
end;
|
|
Stream.WriteBuffer(Buffer[0], EncryptedHeaderSize);
|
|
end;
|
|
|
|
{ TFWZipDecryptor }
|
|
|
|
procedure TFWZipDecryptor.DecryptBuffer(Buffer: PByte; Size: Int64);
|
|
var
|
|
temp: Byte;
|
|
begin
|
|
{
|
|
Step 3 - Decrypting the compressed data stream
|
|
----------------------------------------------
|
|
|
|
The compressed data stream can be decrypted as follows:
|
|
|
|
loop until done
|
|
read a character into C
|
|
Temp <- C ^ decrypt_byte()
|
|
update_keys(temp)
|
|
output Temp
|
|
end loop
|
|
}
|
|
while Size > 0 do
|
|
begin
|
|
Dec(Size);
|
|
temp := Buffer^ xor DecryptByte;
|
|
UpdateKeys(temp);
|
|
Buffer^ := temp;
|
|
Inc(Buffer);
|
|
end;
|
|
end;
|
|
|
|
function TFWZipDecryptor.LoadEncryptionHeader(Stream: TStream;
|
|
IsDescryptorFlagPresent: Boolean; CRC32, FileDate: Cardinal): Boolean;
|
|
var
|
|
Buffer: array [0..EncryptedHeaderSize - 1] of Byte;
|
|
I: Integer;
|
|
C: Byte;
|
|
begin
|
|
{
|
|
Read the 12-byte encryption header into Buffer, in locations
|
|
Buffer(0) thru Buffer(11).
|
|
|
|
loop for i <- 0 to 11
|
|
C <- buffer(i) ^ decrypt_byte()
|
|
update_keys(C)
|
|
buffer(i) <- C
|
|
end loop
|
|
}
|
|
Stream.ReadBuffer(Buffer[0], EncryptedHeaderSize);
|
|
for I := 0 to LastEncryptedHeaderByte do
|
|
begin
|
|
C := Buffer[I] xor DecryptByte;
|
|
UpdateKeys(C);
|
|
Buffer[I] := C;
|
|
end;
|
|
|
|
{
|
|
After the header is decrypted, the last 1 or 2 bytes in Buffer
|
|
should be the high-order word/byte of the CRC for the file being
|
|
decrypted, stored in Intel low-byte/high-byte order, or the high-order
|
|
byte of the file time if bit 3 of the general purpose bit flag is set.
|
|
}
|
|
if IsDescryptorFlagPresent then
|
|
Result := Buffer[LastEncryptedHeaderByte] = HiByte(LoWord(FileDate))
|
|
else
|
|
Result := Buffer[LastEncryptedHeaderByte] = HiByte(HiWord(CRC32));
|
|
end;
|
|
|
|
end.
|