В данном тексте хотелось бы затронуть такую животрепещущую тему, как шифрование
файлов. Вообще нужно различать два вида шифрования файлов:
- шифрование для себя (чтобы ваши файлы никто, кроме вас не "читал")
- шифрование для других (чтобы ваши файлы "читал" только адресат)
Первый способ довольно актуальный для защиты собственной информации и для него
используются симметричные алгоритмы шифрования с секретным ключом. Так как
данный ключ будет доступен только тем, кому адресовано сообщение, в данном
случае только владельцу.
Во втором же способе необходимо использование ассиметричных алгоритмов, с
открытым и секретным ключом.
Блочные шифры используют симметричные алгоритмы и шифруют данные блоками.
Помимо блочных шифров существуют потоковые шифры, они часто применяются
военными для защиты каналов передачи информации. И имеют чаще всего аппаратную,
а не программную реализацию. Этим и объясняется довольно частое использование
в шифровании файлов блочных шифров. На блочных шифрах реализованы практически
все криптосистемы.
Реального способа доказать криптостойкость блочного шифра нет, а вот
опровергнуть её достаточно легко. И для доказательства не криптостойкости
алгоритма используется линейный и дифференциальный криптоанализ.
Годами зарекомендовавшими себя криптостойкими блочными шифрами являются:
IDEA, CAST, BlowFish, TwoFish, TEA, MARS, Serpent, Rijndael, ГОСТ 28147-89, Triple
DES, RC6.
В 80-х годах в США был принят стандарт симметричного криптоалгоритма для
внутреннего применения DES (Data Encryption Standard), который получил
достаточно широкое распространение в свое время. Но в данный момент он очень
сильно устарел. Все это побудило Американский институт стандартизации NIST - National
Institute of Standards & Technology на объявление в 1997 году конкурса на
новый стандарт симметричного криптоалгоритма. Тем самым, победитель этого
соревнования, названного AES - Advanced Encryption
Standard, станет де-факто мировым криптостандартом на ближайшие 10-20 лет.
Алгоритм Rijandel в 2000 г. сменил предыдущий американский стандарт - алгоритм
DES. Победил же данный алгоритм на открытом конкурсе в конце которого
соревновался с MARS, TwoFish, Rijndael, RC6. Теперь Rijandel называется -
AES.
Для примера мы реализуем на паскале и ассемблере блочный шифр TEA
(Tiny Encryption Algorithm), разработанный в Кэмбридже в 1985 году.
Параметры шифра: длина блока - 64 бита, длина ключа - 128 бит.
Оптимизирован под 32 битные процессоры. Испытан временем и является довольно криптостойким.
В алгоритме использована сеть Фейштеля с двумя ветвями в 32 бита каждая.
Образующая функция F обратима. Сеть Фейштеля несимметрична из-за использования
в качестве операции наложения не исключающего "ИЛИ", а арифметического сложения.
Сеть Фейштеля является модификацией метода смешивания текущей части шифруемого
блока с результатом некоторой функции. Данная функция вычисляется от другой
не зависимой части блока. Этот метод часто используется, потому что обеспечивает
многократное использовании ключа и материала исходного блока информации.
Недостатком алгоритма является некоторая медлительность, вызванная
необходимостью повторять цикл Фейштеля 32 раза (это необходимо для тщательного
"перемешивания данных" из-за отсутствия табличных подстановок).
Если же у читателя возникли сомнения в криптостойкости данного алгоритма, то
я рекомендую обратится к фундаментальным работам в области криптографии.
Если же и после этого доверия не возникает, то стоит прибегнуть к
самостоятельному криптоанализу данного алгоритма.
Рассмотрим листинг на паскале:
const Delta=$9E3779B9;
procedure EnCrypt(var y,z:longword; k0,k1,k2,k3:longword);
var a,sum:longword;
begin
sum:=0;
for a:=0 to 31 do
begin
inc(sum,Delta);
inc(y,((z shl 4)+k0) xor (z+sum) xor ((z shr 5)+k1));
inc(z,((y shl 4)+k2) xor (y+sum) xor ((y shr 5)+k3));
end;
end;
procedure DeCrypt(var y,z:longword; k0,k1,k2,k3:longword);
var a,sum:longword;
begin
sum:=Delta shl 5;
for a:=0 to 31 do
begin
dec(z,((y shl 4)+k2) xor (y+sum) xor ((y shr 5)+k3));
dec(y,((z shl 4)+k0) xor (z+sum) xor ((z shr 5)+k1));
dec(sum,Delta);
end;
end;
Рассмотрим листинг на ассемблере:
; ;
; [TINY ENCRYPTION ALGORITHM] ;
; ;
; DEZIGNED BY SLON ;
; ;
; FOR MS WINDOWS ;
; ;
;---;
; BUFFER TO ENCRYPT -> EDX ;
; KEY TO ENCRYPT -> EAX ;
; SIZE OF BUFFER (div 4 = 0) -> ECX ;
;---;
total_encrypt:
pusha ; Сохраняем всё в стеке
mov esi,eax ; Кладём в esi - eax
mov edi,edx ; Кладём в edi - edx
work__:
pusha ; Сохраняем всё в стеке
call Encrypt ; Шифруем первые 64 бита данных
popa ; Восстанавливаем из стека
add edi,8 ; Добавляем к edi - 8
sub ecx,7 ; Отнимаем от ecx - 7
loop work__ ; Продолжаем шифровать
popa ; Восстанавливаем из стека
ret ; Возврат из подпрограммы
;---;
; BUFFER TO DECRYPT -> EDX ;
; KEY TO DECRYPT -> EAX ;
; SIZE OF BUFFER (div 4 = 0) -> ECX ;
;---;
total_decrypt:
pusha ; Сохраняем всё в стеке
mov esi,eax ; Кладём в esi - eax
mov edi,edx ; Кладём в edi - edx
work2__:
pusha ; Сохраняем всё в стеке
call decrypt ; Шифруем первые 64 бита данных
popa ; Восстанавливаем из стека
add edi,8 ; Добавляем к edi - 8
sub ecx,7 ; Отнимаем от ecx - 7
loop work2__ ; Продолжаем шифровать
popa ; Восстанавливаем из стека
ret ; Возврат из подпрограммы
;---;
Encrypt:
push edi ; Сохраняем edi в стэке
mov ebx,v0 ; Кладём в ebx первые 32 бита данных
mov ecx,v1 ; В ecx кладём вторые 32 бита данных
xor eax,eax ; Обнуляем eax
mov edx,9e3779b9h ; В edx ->
sqr(5)-1 * 2^31
mov edi,32 ; Кладём в edi - 32
ELoopR:
add eax,edx ; Добавляем к eax - edx
mov ebp,ecx ; Кладём в ebp - ecx
shl ebp,4 ; Сдвиг ebp на 4 бита влево
add ebx,ebp ; Добавляем к ebx - ebp
mov ebp,k0 ; Кладём в ebx первые 32 бита ключа
xor ebp,ecx ; XOR'им их вторыми 32 битами данных
add ebx,ebp ; Добавляем к 1-и 32 битам данных р-т
mov ebp,ecx ; Кладём в ebp - ecx
shr ebp,5 ; Делим ebp на 32
xor ebp,eax ; XOR'им ebp - eax'ом
add ebx,ebp ; Добавляем к ebx - ebp
add ebx,k1 ; Добавляем к ebx - 2-е 32 бита ключа
;
mov ebp,ebx ; Кладём в ebp - ebx
shl ebp,4 ; Сдвиг ebp на 4 бита влево
add ecx,ebp ; Добавляем к ecx - ebp
mov ebp,k2 ; Кладём в ebp 3-и 32 бита ключа
xor ebp,ebx ; XOR'им ebp - ebx'ом
add ecx,ebp ; Добавляем к ecx - ebp
mov ebp,ebx ; Кладём в ebp - ebx
shr ebp,5 ; Сдвиг ebp вправо на 5 бит
xor ebp,eax ; XOR'им ebp - eax'ом
add ecx,ebp ; Добавляем к ecx - ebp
add ecx,k3 ; Добавляем к ecx - 4-е 32 бита ключа
dec edi ; Уменьшаем edi на единицу
jnz ELoopR ; Шифруем дальше
pop edi ; Вынимаем из стека edi
mov v0,ebx ; Кладём результаты шифрования
mov v1,ecx ; В отведённое для них место
ret ; Возврат из подпрограммы
;---;
Decrypt:
push edi ; Сохраняем edi в стэке
mov ebx,v0 ; Кладём в ebx первые 32 бита данных
mov ecx,v1 ; В ecx кладём вторые 32 бита данных
mov edx,9e3779b9h ; В edx ->
sqr(5)-1 * 2^31
mov eax,edx ; Кладём в eax - edx
shl eax,5 ; Сдвиг eax в лево на 5 бит
mov edi,32 ; Кладём в edi - 32
DLoopR:
mov ebp,ebx ; Кладём в ebp - ebx
shl ebp,4 ; Сдвиг ebp на 4 бита влево
sub ecx,ebp ; Отнимаем от ecx - ebp
mov ebp,k2 ; Кладём в ebp 3-и 32 бита ключа
xor ebp,ebx ; XOR'им ebp - ebx'ом
sub ecx,ebp ; Отнимаем от ecx - ebp
mov ebp,ebx ; Кладём в ebp - ebx
shr ebp,5 ; Сдвиг ebp вправо на 5 бит
xor ebp,eax ; XOR'им ebp - eax'ом
sub ecx,ebp ; Отнимаем от ecx - ebp
sub ecx,k3 ; Отнимаем от ecx - 4-е 32 бита ключа
;
mov ebp,ecx ; Кладём в ebp - ecx
shl ebp,4 ; Сдвиг ebp на 4 бита влево
sub ebx,ebp ; Отнимаем от ebx - ebp
mov ebp,k0 ; Кладём в ebx первые 32 бита ключа
xor ebp,ecx ; XOR'им ebp - eсx'ом
sub ebx,ebp ; Отнимаем от ebx - ebp
mov ebp,ecx ; Кладём в ebp - ecx
shr ebp,5 ; Сдвиг ebp вправо на 5 бит
xor ebp,eax ; XOR'им ebp - eax'ом
sub ebx,ebp ; Отнимаем от ebx - ebp
sub ebx,k1 ; Отнимаем от ebx - 2-е 32 бита ключа
sub eax,edx ; Отнимаем от eax - edx
dec edi ; Уменьшаем edi на единицу
jnz DLoopR ; Дешифруем дальше
pop edi ; Вынимаем из стека edi
mov v0,ebx ; Кладём результаты шифрования
mov v1,ecx ; В отведённое для них место
ret ; Возврат из подпрограммы
;---;
v0 equ dword ptr [edi]
v1 equ dword ptr [edi+4]
k0 equ dword ptr [esi]
k1 equ dword ptr [esi+4]
k2 equ dword ptr [esi+8]
k3 equ dword ptr [esi+12]
;-8<---[tea_128.asm]---8<-;
Как вы могли заметить, алгоритм довольно таки простой и легко реализуем на
ассемблере. Теперь на базе данного алгоритма разработаем утилиту для шифрования
файлов, ориентированную на ОС Windows.
Рассмотрим листинг:
;-8<---[fencu.asm]---8<-;
; ;
;xxx;
; ;
; [FILE ENCRYPTION UTILITE] ;
; ;
; DEZIGNED BY SLON ;
; ;
; FOR MS WINDOWS ;
; ;
;xxx;
.386
.model flat, stdcall
callx macro x ;
extrn x:proc ; Макрос для упрощения
call x ; использования WIN API
endm ;
.data
start:
;---;
push offset usage__ ;
callx printf ; Выводим сообщение об использовании
add esp,4 ; данной программы
callx GetCommandLineA ; Получаем командную строку
mov esi,eax ; Помещаем указатель на
; командную строку в esi
call t__ ; Получаем указатель на первый параметр
lodsw ; Загружаем параметр в ax
cmp ax,'E-' ; Проверяем мы будем шифровать?
jne d__ ; Нет, идём на следующую проверку
mov flag,1 ; Устанавливаем флаг в 1
jmp w__ ; И переходим к шифрованию
d__: ;
cmp ax,'D-' ; Проверяем мы будем дешифровать?
jne ex__ ; Нет, идём на выход
mov flag,2 ;
w__:
call f_open ; Работаем с файлом
ex__:
mov al,flag ; Помещаем в al - флаг
test al,al ; Проверяем его
jnz ex2__ ; Если всё нормально, то на выход
push offset invalid1_ ;
callx printf ; Выводим сообщение, об ошибке
add esp,4 ;
ex2__:
push 0 ;
callx ExitProcess ; Завершение процесса
;---;
t__: ;
lodsb ; Проверяем все символы на
cmp al,20h ; равенство пробелу
jne t__ ; Если нашли пробел, то
; теперь esi указывает на
; argv[1]
ret
;---;
f_open:
pusha ; Сохраняем всё в стэке
call t__ ; Находим имя файла
push esi ; Сохраняем указатель в стэке
call t__ ; Находим следующий параметр
dec esi ; Переходим на разделяющий пробел
mov byte ptr[esi],0 ; И заменяем его нулём
pop esi ; Восстанавливаем указатель из стека
push esi ; И тут же кладём его обратно в стек
xor eax,eax ;
push eax ;
push 00000080h ;
push 3 ;
push eax ;
push 00000001h OR 00000002h ;
push 40000000h OR 80000000h ;
push esi ; Открываем существующий
callx CreateFileA ; файл
(esi)
inc eax ;
test eax,eax ; Если возникла ошибка, то
jnz g__ ; переходим на error_
push offset file_err_ ; и выводим
callx printf ; Сообщение
add esp,4 ;
g__:
dec eax ; Уменьшаем eax на 1
mov fHnd,eax ; Сохраняем хэндл файла
push s2read ;
push eax ;
callx GetFileSize ; Получаем его размер
mov sz_,eax ; и сохраняем его в sz_
; выделяем память
push 0 ; имя файла хэндл = 0
push sz_ ; макс. размер = memory
push 0 ; минимальный размер = 0
push 4 ; доступ чтение/запись
push 0 ;
push fHnd ;
callx CreateFileMappingA ;
mov mHnd,eax ; Сохраняем хэндл памяти
or eax,eax ; если ошибка, то на выход
jz error2_ ;
push sz_ ; количество памяти
; для работы
push 0 ;
push 0 ;
push 2 ; Режим записи
push eax ; хэндл
callx MapViewOfFile ; Вызываем функцию
test eax,eax ; Если ошибка, то
je error3_ ; На выход
mov mHnd2,eax ; Сохраняем указатель на память
cmp flag,1 ;
je d2__ ;
lea ebx,total_decrypt ; Проверяем какая нам функция нужна
jmp d3__ ; Шифровки или дешифровки
d2__: ; и кладём её смещение в ebx
lea ebx,total_encrypt ;
d3__:
pop esi ; Получаем указатель на
call t__ ; наш 128 битный ключ
mov edx,mHnd2 ; Дешифруем данные
mov eax,esi ; Нашим ключом (128 бит)
mov ecx,sz_ ; sz_ байт - длина данных
call ebx ;
push mHnd2 ;
callx UnmapViewOfFile ; Закончиваем изменение
; файла в памяти и ложим
; его обратно
error3_:
push mHnd ;
callx CloseHandle ; Закрываем память
error2_:
push fHnd ;
callx CloseHandle ; Закрываем файл
error_:
popa ; Вынимаем всё из стэка
ret ; Возврат из подпрограммы
;---;
fHnd dd 0 ;
sz_ dd 0 ;
mHnd dd 0 ;
s2read dd 0 ; Данные
mHnd2 dd 0 ;
flag db 0 ;
usage__:
db '|----------------------------------------------------------------|',0ah,0dh
db '| [FILE ENCRYPTION UTILITE (BASED ON TEA) BY SLON] |',0ah,0dh
db '|----------------------------------------------------------------|',0ah,0dh
db '| USAGE: FENCU.EXE [-D OR -E] [FILENAME] [128 BIT KEY] |',0ah,0dh
db '| -D : DECRYPT FILE |',0ah,0dh
db '| -E : ENCRYPT FILE |',0ah,0dh
db '| EXAMPLE: FENCU.EXE -E HELLO.TXT 1234567890abcdef |',0ah,0dh
db '|----------------------------------------------------------------|',0ah,0dh
db 0ah,0dh,0
invalid1_:
db '[INVALID PARAMETER, EXITING ...]',0ah,0dh,0
file_err_:
db '[FILE ERROR, EXITING ...]',0ah,0dh,0
;---;
include tea_128.asm
;---;
.code
nop
end start
end
;-8<---[fencu.asm]---8<-;
Итак, к чему мы пришли в итоге: мы смогли написать утилиту, которая шифрует
файлы по алгоритму TEA на основе 128 битного ключа. Вскрытие таких файлов нельзя
назвать невозможным, но можно назвать крайне трудоёмким и
долгим по временм.
Данную утилиту можно было бы оптимизировать следующим образом:
- чтобы все ключи с именами файлов хранились на дискете (не нужно будет помнить все ключи)
- случайно генерировать ключ для шифрования файла (не возможность атаки по словарю)
Но это только упростит использование данного метода шифрования. Если же дискета
попадёт в руки к злоумышленнику, потеряется или испортится, то доступ к
зашифрованным файлам станет невозможным. Именно поэтому решать использовать
внешний носитель для хранения секретного ключа или нет дело каждого из нас.
Что касается меня, то я больше доверяю своей памяти.
На совести читателя остаётся изучение и разработка, таких алгоритмов, как:
IDEA, CAST, BlowFish, TwoFish, MARS, Serpent, Rijndael.
Освоение данной области криптографии открывает громадные перспективы перед
нами.