Гошко С.В., Блочные шифры, Системный администратор, апрель 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)]----;