Гошко С.В., Блочные шифры, Системный администратор, апрель 2004, стр.64-68
;------------------------------[Блочные шифры]---------------------------------; В данном тексте хотелось бы затронуть такую животрепещущую тему, как шифрование файлов. Вообще нужно различать два вида шифрования файлов: - шифрование для себя (чтобы ваши файлы никто, кроме вас не "читал") - шифрование для других (чтобы ваши файлы "читал" только адресат) Первый способ довольно актуальный для защиты собственной информации и для него используются симметричные алгоритмы шифрования с секретным ключом. Так как данный ключ будет доступен только тем, кому адресовано сообщение, в данном случае только владельцу. Во втором же способе необходимо использование ассиметричных алгоритмов, с открытым и секретным ключом. Блочные шифры используют симметричные алгоритмы и шифруют данные блоками. Помимо блочных шифров существуют потоковые шифры, они часто применяются военными для защиты каналов передачи информации. И имеют чаще всего аппаратную, а не программную реализацию. Этим и объясняется довольно частое использование в шифровании файлов блочных шифров. На блочных шифрах реализованы практически все криптосистемы. Реального способа доказать криптостойкость блочного шифра нет, а вот опровергнуть её достаточно легко. И для доказательства не криптостойкости алгоритма используется линейный и дифференциальный криптоанализ. Годами зарекомендовавшими себя криптостойкими блочными шифрами являются: 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 раза (это необходимо для тщательного "перемешивания данных" из-за отсутствия табличных подстановок). Если же у читателя возникли сомнения в криптостойкости данного алгоритма, то я рекомендую обратится к фундаментальным работам в области криптографии. Если же и после этого доверия не возникает, то стоит прибегнуть к самостоятельному криптоанализу данного алгоритма. Рассмотрим листинг на паскале: {-------------------------------[tea.pas]--------------------------------------} 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; {-------------------------------[tea.pas]--------------------------------------} Рассмотрим листинг на ассемблере: ;-8<---------------------------[tea_128.asm]--------------------------------8<-; ; ; ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; ; ; ; [TINY ENCRYPTION ALGORITHM] ; ; ; ; ############ ############ ####### ; ; ############ ############# ######### ; ; ### ## ## #### ; ; ### ############# ########### ; ; ### ############# ########### ; ; ### ## ### ### ; ; ### ############# ### ### ; ; ### ############ ### ### ; ; ; ; DEZIGNED BY SLON ; ; ; ; FOR MS WINDOWS ; ; ; ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; ; ; ; ; ;------------------------------------------------------------------------------; ; 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<-; ; ; ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; ; ; ; [FILE ENCRYPTION UTILITE] ; ; ; ; ############# ############ ###### ## ############ ### ### ; ; ############# ############# ######## ## ############# ### ### ; ; ## ## ### ## ## ## ### ### ; ; ############# ############# ### ## ## ## ### ### ; ; ############# ############# ### ## ## ## ### ### ; ; ## ## ### ## ## ## ### ### ; ; ## ############# ### ######## ############# ############## ; ; ## ############ ### ###### ############ ############ ; ; ; ; ; ; DEZIGNED BY SLON ; ; ; ; FOR MS WINDOWS ; ; ; ;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; .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. Освоение данной области криптографии открывает громадные перспективы перед нами. ;-------------------------------------------------------------[slon (2003)]----;