Расчет хеша SHA1 на Ассемблере

Алгоритм криптографического хеширования SHA1 (Secure Hash Algorithm 1) используется во многих криптографических приложениях и протоколах. В его основе лежат методы, очень похожие на MD5. В Интернете есть много реализаций этого алгоритма на разных языках программирования, но я не нашел ни одного нормального решения на Ассемблере. Пришлось разбираться самому и в результате у меня получилась достаточно быстрая функция расчета SHA1 участка памяти произвольной длины. Для работы в сегменте данных потребуются некоторые дополнительные переменные и массивы, они вынесены в глобальную область видимости, чтобы не загромождать код.
section '.data' data readable writeable
 
SHA1_h0         dd ?  ; Переменные, в которых будет создан хеш SHA1
SHA1_h1         dd ?
SHA1_h2         dd ?
SHA1_h3         dd ?
SHA1_h4         dd ?
 
SHA1_a          dd ?  ; Вспомогательные переменные для промежуточных
SHA1_b          dd ?  ; вычислений
SHA1_c          dd ?
SHA1_d          dd ?
SHA1_e          dd ?
 
SHA1_W          rd 80  ; Массивы для промежуточных данных
SHA1_Buff       rb 64

Я постарался сохранить оригинальные названия переменных, которые используются в описании алгоритма, так что с этим вопросов возникнуть не должно.

И, собственно, сама функция расчета SHA1. Параметры вызова: lpData - указатель на участок памяти, хеш от которого надо получить, и dSize - размер этого участка. Кроме основной функции расчета здесь используются еще две вспомогательные функции - SHA1_BE для преобразования строки к формату Big-endian и SHA1_Calc для цикла трансформации хеша.
;-----------------------------------------------------------------------
; Функция расчета хэша SHA1
; Copyright (C) ManHunter / PCL
; http://www.manhunter.ru
;-----------------------------------------------------------------------
; Параметры:
;      lpData - указатель на блок данных
;      dSize  - размер блока
; На выходе: заполненные переменные SHA1_h0 - SHA1_h0
;-----------------------------------------------------------------------
proc    SHA1 lpData:DWORD, dSize:DWORD
        locals
                len     dd ?
                padding dd ?
                index   dd ?
        endl
 
        pusha
 
        ; Инициализация
        mov     [SHA1_h0],0x67452301
        mov     [SHA1_h1],0xEFCDAB89
        mov     [SHA1_h2],0x98BADCFE
        mov     [SHA1_h3],0x10325476
        mov     [SHA1_h4],0xC3D2E1F0
 
        ; Получить количество 64-байтных блоков
        mov     eax,[dSize]
        push    eax
        shr     eax,6
        mov     [len],eax
        pop     eax
 
        ; Получить остаток от деления на 64
        and     eax,3Fh
        ; Расчет количества добавляемых байт
        mov     [padding],eax
        ; Подсчет SHA1 основных данных
        mov     [index],0
.main_loop:
        mov     eax,[index]
        cmp     eax,[len]
        je      .main_done
 
        ; Указатель * 64
        shl     eax,6
        mov     esi,[lpData]
        add     esi,eax
        mov     edi,SHA1_Buff
        mov     ecx,16
        rep     movsd
 
        ; Привести строку к Big-endian виду
        stdcall SHA1_BE
        stdcall SHA1_Calc
 
        inc     [index]
        jmp     .main_loop
.main_done:
        ; Подсчет SHA1 остаточных данных
        xor     eax,eax
        mov     edi,SHA1_Buff
        mov     ecx,16
        rep     stosd
 
        ; Скопировать остаточные данные в буфер
        mov     eax,[index]
        shl     eax,6
        mov     esi,[lpData]
        add     esi,eax
        mov     edi,SHA1_Buff
        mov     ecx,[padding]
        rep     movsb
        ; Дописать финальный бит
        mov     al,80h
        stosb
 
        ; Привести строку к Big-endian виду
        stdcall SHA1_BE
 
        ; Проверка на пограничные случаи, когда остаток
        ; строки меньше, чем требуется для записи финального
        ; бита и длины строки
        cmp     [padding],56
        jae     @f
 
        ; В конец буфера записать длину строки
        mov     eax,[dSize]
        shl     eax,3
        mov     dword [SHA1_Buff+60],eax
 
        stdcall SHA1_Calc
        jmp     .sha1_done
@@:
        stdcall SHA1_Calc
 
        ; Очистка буфера
        mov     edi,SHA1_Buff
        xor     eax,eax
        mov     ecx,15
        rep     stosd
 
        ; В конец буфера записать длину строки
        mov     eax,[dSize]
        shl     eax,3
        stosd
 
        stdcall SHA1_Calc
 
.sha1_done:
        popa
        ret
endp

;-----------------------------------------------------------------------
; Вспомогательная функция
; Приведение строки к Big-endian виду
;-----------------------------------------------------------------------
proc    SHA1_BE
        pusha
 
        mov     ecx,16
        mov     esi,SHA1_Buff
        mov     edi,esi
@@:
        lodsd
        bswap   eax
        stosd
        loop    @b
 
        popa
        ret
endp

;-----------------------------------------------------------------------
; Вспомогательная функция для расчета SHA1
;-----------------------------------------------------------------------
proc    SHA1_Calc
        pusha
 
        mov     eax,[SHA1_h0]
        mov     [SHA1_a],eax
        mov     eax,[SHA1_h1]
        mov     [SHA1_b],eax
        mov     eax,[SHA1_h2]
        mov     [SHA1_c],eax
        mov     eax,[SHA1_h3]
        mov     [SHA1_d],eax
        mov     eax,[SHA1_h4]
        mov     [SHA1_e],eax
 
        xor     ecx,ecx ; i
.cycle_loop:
        mov     eax,ecx
        shl     eax,2
 
        cmp     ecx,16
        jae     @f
 
        mov     ebx,dword [SHA1_Buff+eax]
        mov     dword [SHA1_W+eax],ebx
        jmp     .@1
@@:
        ; rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1)
        mov     eax,ecx
        sub     eax,3
        shl     eax,2
        mov     ebx,dword [SHA1_W+eax]
 
        mov     eax,ecx
        sub     eax,8
        shl     eax,2
        xor     ebx,dword [SHA1_W+eax]
 
        mov     eax,ecx
        sub     eax,14
        shl     eax,2
        xor     ebx,dword [SHA1_W+eax]
 
        mov     eax,ecx
        sub     eax,16
        shl     eax,2
        xor     ebx,dword [SHA1_W+eax]
 
        rol     ebx,1
 
        mov     eax,ecx
        shl     eax,2
        mov     dword [SHA1_W+eax],ebx
.@1:
        mov     edx,[SHA1_a]
        rol     edx,5
 
        ; Расчет t и k
        cmp     ecx,20
        jae     @f
        ; (b & c) | ((~b) & d)
        mov     eax,[SHA1_b]
        and     eax,[SHA1_c]
        mov     ebx,[SHA1_b]
        not     ebx
        and     ebx,[SHA1_d]
        or      eax,ebx
        add     edx,0x5A827999
        jmp     .@2
@@:
        cmp     ecx,40
        jae     @f
        ; b ^ c ^ d
        mov     eax,[SHA1_b]
        xor     eax,[SHA1_c]
        xor     eax,[SHA1_d]
        add     edx,0x6ED9EBA1
        jmp     .@2
@@:
        cmp     ecx,60
        jae     @f
        ; (b & c) | (b & d) | (c & d)
        mov     eax,[SHA1_b]
        and     eax,[SHA1_c]
        mov     ebx,[SHA1_b]
        and     ebx,[SHA1_d]
        or      eax,ebx
        mov     ebx,[SHA1_c]
        and     ebx,[SHA1_d]
        or      eax,ebx
        add     edx,0x8F1BBCDC
        jmp     .@2
@@:
        mov     eax,[SHA1_b]
        xor     eax,[SHA1_c]
        xor     eax,[SHA1_d]
        add     edx,0xCA62C1D6
.@2:
        add     edx,eax
        add     edx,[SHA1_e]
 
        mov     eax,ecx
        shl     eax,2
        add     edx,[SHA1_W+eax]
 
        mov     eax,[SHA1_d]         ; e = d
        mov     [SHA1_e],eax
 
        mov     eax,[SHA1_c]         ; d = c
        mov     [SHA1_d],eax
 
        mov     eax,[SHA1_b]         ; c = rol(b,30)
        rol     eax,30
        mov     [SHA1_c],eax
 
        mov     eax,[SHA1_a]         ; b = a
        mov     [SHA1_b],eax
 
        mov     [SHA1_a],edx         ; a = t
 
        inc     ecx
        cmp     ecx,80
        jne     .cycle_loop
 
        mov     eax,[SHA1_a]
        add     [SHA1_h0],eax
        mov     eax,[SHA1_b]
        add     [SHA1_h1],eax
        mov     eax,[SHA1_c]
        add     [SHA1_h2],eax
        mov     eax,[SHA1_d]
        add     [SHA1_h3],eax
        mov     eax,[SHA1_e]
        add     [SHA1_h4],eax
 
        popa
        ret
endp

Пример использования:
; Сегмент данных
section '.data' data readable writeable  
...
somedata db 'Yeah! I like Flat Assembler!',0 ; Исходные данные для хеширования
mask     db '%.8x%.8x%.8x%.8x%.8x',0         ; Маска для вывода строки
 
; Сегмент кода
section '.code' code readable executable
        ...
        ; Расчет длины строки. Для бинарных данных lstrlen лучше не использовать
        invoke  lstrlen,somedata
        ; Расчет SHA1
        stdcall SHA1,somedata,eax
        ; Перевод в строку, буквы строчные
        invoke  wsprintf, buff, mask, [SHA1_h0], [SHA1_h1], [SHA1_h2],\
                [SHA1_h3], [SHA1_h4]
        ...

Функция, конечно, не идеальная, часть кода можно было оптимизировать. Еще один недостаток в том, что она считает хеш сразу за один проход, а не накопительно, то есть, например, с ее помощью не получится посчитать SHA1 от большого файла, подгружая его по частям. Но такая задача и не ставилась, мне нужен был именно однопроходный вариант. Вы же можете доработать функцию SHA1 как вам угодно.
Теги:
Расчет хеша, SHA1
Добавлено: 10 Апреля 2018 09:06:47 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...