Увидел пост от Майкла по поводу EuroAssembler, знал про него, все никак руки не доходили покодить на нем.
Все таки в 3 часа ночи руки решили дойти до этого ассемблера, пробежался по документации за 30 минут и написал алгоритм шифрования ChaCha20 на нем. Под x86.
Разбор алгоритма
plaintext - исходные данные шифрования
keystream - 64 байтовый блок псевдослучайных данных, полученный из состояния чачи и использумый для xor с plaintext;
nonce - уникальное значение, которое падается вместе с ключом и формирует начальное состояние алгоритма, не является секретным но обзяано быть уникальным при каждомс шифровании с одним и тем же ключом.
cчетчик counter - часть состояния, которая увеличивается на каждый 64-байтовый блок.
state - 16 * 32-bit, то есть 16 слов по 32 бита.
1. константы
2. ключ
3. счетчик
4. nonce
QuarterRound - ядро чачи, он перемешивает четыре 32-битных слова состояния, создавая диффузию.
Диффузия - это свойство преобразования, при котором изменение одного бита входных данных приводит к распространению изменений на множество битов выходного результата.
После 20 раундов из этого состояния формируется блок keystream, который затем используется в операции XOR с данными
DoubleRound - пара проходов(CR/DR), обеспечивающая полное перемешивание всех 16 слов состояния.
Сам алгоритм является симметричным потоковым алгоритмом шифрования который под капотом использует операцию xor но не с простым ключом а с псевдослучайным потоком байтов генерируемым на основе ключа nonce и счетчика.
Что значит потоковый? - это значит, что шифрование происходит не блоками фиксированного размера например как в AES, а последовательно по байтам, генерируется непрерывный поток псевдослучайных данных который затем поэлементно складывается с исходным сообщением через xor, благодаря этому можно шифровать данные произвольной длины без разбиения на блоки и обрабатывать поток данных в реальном времен.
Работает это так:
1. принимается 32 битное значение которое участвует в операции сложения по модулю 2^32, при этом результат всегда ограничен размером регистра и переполнение просто отбрасывается
2. далее результат проходит побитовое XOR преобразование с другим значением, что обеспечивает нелинейное смешивание битов и является обратимой операцией
3. затем применяется операция ROL, то есть циклический сдвиг битов влево внутри 32 битного слова, при котором вышедшие за старший бит значения не теряются а возвращаются в младшие разряды, это перераспределяет биты внутри слова и усиливает диффузию(диффузия это неравномерный процесс перешивания)
в результате этих трех операций формируется некое промежуточное состояние, которое все еще нельзя рассматривать как исходное значение, так как информация распределена по всему 32 битному пространству
4. далее это не шифрование само по себе, а часть процесса генерации ключевого потока, который в ChaCha20 формируется после многократного применения таких преобразований ко всей 16 словной матрице состояния
5. и уже только после получения ключевого потока выполняется финальный этап, где каждый байт данных поэлементно ксорится(xor) с байтом keystream, формируя шифротекст.
Результат реализации:
(Правда, еще не понял как использовать анонимные метки в этом диалекте, но как я понял их там вообще нет)
C:
;// for codeby
ChaCha20Program PROGRAM Format=PE, Width=32, Entry=Start:
IMPORT GetStdHandle, WriteFile, ExitProcess, LIB="kernel32.dll"
IMPORT _getch, LIB="msvcrt.dll"
Start:
CLD
PUSH -11
CALL GetStdHandle
MOV [hStdOut], EAX
CALL CHACHA20_INIT
PUSH 0
PUSH lpCharsWritten
PUSH 11
PUSH encrypts
PUSH [hStdOut]
CALL WriteFile
PUSH 0
PUSH lpCharsWritten
PUSH 2
PUSH szNewLine
PUSH [hStdOut]
CALL WriteFile
CALL CHACHA20_20_ROUNDS_ITERATE
LEA ESI, [state]
LEA EDI, [encrypts]
MOV ECX, 11
.loop_xor:
MOV AL, [ESI]
XOR [EDI], AL
INC ESI
INC EDI
LOOP .loop_xor
PUSH 0
PUSH lpCharsWritten
PUSH 11
PUSH encrypts
PUSH [hStdOut]
CALL WriteFile
PUSH 0
PUSH lpCharsWritten
PUSH 2
PUSH szNewLine
PUSH [hStdOut]
CALL WriteFile
CALL _getch
PUSH 0
CALL ExitProcess
CHACHA20_QUARTEROUND:
ADD EAX, EBX
XOR EDX, EAX
ROL EDX, 16
ADD ECX, EDX
XOR EBX, ECX
ROL EBX, 12
ADD EAX, EBX
XOR EDX, EAX
ROL EDX, 8
ADD ECX, EDX
XOR EBX, ECX
ROL EBX, 7
RET
CHACHA20_DOUBLEROUND:
LEA ESI, [state]
MOV EAX, [ESI+0]
MOV EBX, [ESI+16]
MOV ECX, [ESI+32]
MOV EDX, [ESI+48]
CALL CHACHA20_QUARTEROUND
MOV [ESI+0], EAX
MOV [ESI+16], EBX
MOV [ESI+32], ECX
MOV [ESI+48], EDX
MOV EAX, [ESI+4]
MOV EBX, [ESI+20]
MOV ECX, [ESI+36]
MOV EDX, [ESI+52]
CALL CHACHA20_QUARTEROUND
MOV [ESI+4], EAX
MOV [ESI+20], EBX
MOV [ESI+36], ECX
MOV [ESI+52], EDX
MOV EAX, [ESI+8]
MOV EBX, [ESI+24]
MOV ECX, [ESI+40]
MOV EDX, [ESI+56]
CALL CHACHA20_QUARTEROUND
MOV [ESI+8], EAX
MOV [ESI+24], EBX
MOV [ESI+40], ECX
MOV [ESI+56], EDX
MOV EAX, [ESI+12]
MOV EBX, [ESI+28]
MOV ECX, [ESI+44]
MOV EDX, [ESI+60]
CALL CHACHA20_QUARTEROUND
MOV [ESI+12], EAX
MOV [ESI+28], EBX
MOV [ESI+44], ECX
MOV [ESI+60], EDX
MOV EAX, [ESI+0]
MOV EBX, [ESI+20]
MOV ECX, [ESI+40]
MOV EDX, [ESI+60]
CALL CHACHA20_QUARTEROUND
MOV [ESI+0], EAX
MOV [ESI+20], EBX
MOV [ESI+40], ECX
MOV [ESI+60], EDX
MOV EAX, [ESI+4]
MOV EBX, [ESI+24]
MOV ECX, [ESI+44]
MOV EDX, [ESI+48]
CALL CHACHA20_QUARTEROUND
MOV [ESI+4], EAX
MOV [ESI+24], EBX
MOV [ESI+44], ECX
MOV [ESI+48], EDX
MOV EAX, [ESI+8]
MOV EBX, [ESI+28]
MOV ECX, [ESI+32]
MOV EDX, [ESI+52]
CALL CHACHA20_QUARTEROUND
MOV [ESI+8], EAX
MOV [ESI+28], EBX
MOV [ESI+32], ECX
MOV [ESI+52], EDX
MOV EAX, [ESI+12]
MOV EBX, [ESI+16]
MOV ECX, [ESI+36]
MOV EDX, [ESI+56]
CALL CHACHA20_QUARTEROUND
MOV [ESI+12], EAX
MOV [ESI+16], EBX
MOV [ESI+36], ECX
MOV [ESI+56], EDX
RET
CHACHA20_20_ROUNDS_ITERATE:
PUSH EBX
PUSH ESI
PUSH EDI
LEA ESI, [state]
LEA EDI, [initial_state]
MOV ECX, 16
REP MOVSD
MOV EBX, 10
.loop_round_20:
PUSH EBX
CALL CHACHA20_DOUBLEROUND
POP EBX
DEC EBX
JNZ .loop_round_20
LEA ESI, [state]
LEA EDI, [initial_state]
MOV ECX, 16
.loop_sum:
MOV EAX, [ESI]
ADD EAX, [EDI]
MOV [ESI], EAX
ADD ESI, 4
ADD EDI, 4
LOOP .loop_sum
POP EDI
POP ESI
POP EBX
RET
CHACHA20_INIT:
PUSH EBX
PUSH ESI
LEA ESI, [state]
MOV EAX, 'expa'
MOV [ESI+0], EAX
MOV EAX, 'nd 3'
MOV [ESI+4], EAX
MOV EAX, '2-by'
MOV [ESI+8], EAX
MOV EAX, 'te k'
MOV [ESI+12], EAX
LEA EBX, [key]
MOV EAX, [EBX+0]
MOV [ESI+16], EAX
MOV EAX, [EBX+4]
MOV [ESI+20], EAX
MOV EAX, [EBX+8]
MOV [ESI+24], EAX
MOV EAX, [EBX+12]
MOV [ESI+28], EAX
MOV EAX, [EBX+16]
MOV [ESI+32], EAX
MOV EAX, [EBX+20]
MOV [ESI+36], EAX
MOV EAX, [EBX+24]
MOV [ESI+40], EAX
MOV EAX, [EBX+28]
MOV [ESI+44], EAX
MOV EAX, 1
MOV [ESI+48], EAX
LEA EBX, [nonce]
MOV EAX, [EBX+0]
MOV [ESI+52], EAX
MOV EAX, [EBX+4]
MOV [ESI+56], EAX
MOV EAX, [EBX+8]
MOV [ESI+60], EAX
POP ESI
POP EBX
RET
key DB 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
nonce DB 0,0,0,0,0,0,0,0,0,0,0,2
encrypts DB "Hello Codeby", 0
szNewLine DB 13, 10
hStdOut DD 0
lpCharsWritten DD 0
ALIGN 16
state: DD 16 * 0
initial_state: DD 16 * 0
ENDPROGRAM ChaCha20Program