Реализация алгоритмов хеширования семейства SHA-2

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Введение

На сегодняшний день, как никогда, актуальна проблема защиты информации и проверки её целостности. С каждым новым днём возрастает количество сайтов, программ, соцсетей и различных коммерческих структур, собирающих и хранящих данные своих пользователей. Каждый из нас должен быть уверен в том, что вводя свои персональные данные на каком-либо сайте, доступ к этим данным никто не получит. Как раз вопросом обеспечения безопасного хранения и проверки целостности данных активно занимается наука под названием криптография.

Криптографические методы широко применяются в задачах идентификации и аутентификации пользователей, защиты каналов связей от проникновения ложных данных, а также защиты электронных документов (различных форматов и типов) от незаконного распространения и подделывания. В данной статье хотелось бы рассмотреть и разобрать одни из популярных криптографических хеш-функций семейства SHA-2.

О хеш-функциях

Криптографическая хеш-функция представляет собой некоторый математический алгоритм, который преобразует последовательность данных произвольной длины в строку фиксированной длины, состоящей из ограниченного набора цифр и букв. Такие алгоритмы должны обладать рядом свойств:

  • Одно и тоже сообщение всегда должно приводить к одинаковому хеш-значению;

  • Скорость вычисления хеш-функции должна быть быстрой;

  • Не должно возникать коллизий, то есть не должно быть 2 таких сообщений, которые имели бы одинаковое хеш-значение;

  • Небольшие изменения в наборе данных должны приводить к абсолютно новому хеш-значению так, чтобы это значение не казалось связанным с предыдущим значением;

  • Сложность (невозможность) вычисления исходных данных по хеш-значению.

Наиболее частое применение хеш-функций - хранение хешированных паролей на стороне сервера. Это делается с той целью, чтобы никто не мог узнать оригинальный пароль и осуществить по нему вход в систему в случае получения доступа к базе. Хотя хеширование паролей не должно вызывать огромных проблем, но такое правило соблюдают не все компании, однако с каждым годом организации начинают больше заботиться о безопасности данных своих пользователей. Также многие компании защищают свои цифровые документы (музыку, фотографии, программы) при помощи вычисления хеш-суммы. В случае, если в программу был встроен вирус или документ был хоть немного изменен, контрольная сумма документа будет изменена и использование такого контента может сразу дать понять, что используются фальсифицированные данные.

Реализация алгоритмов семейства SHA-2

SHA-256

Первым делом реализуем битовые функции, которые будут использоваться в ходе хеширования, а именно: сложение (в случае алгоритма sha-256 сложение будет идти по модулю 2^32, в sha-512 по модулю 2^64), побитовое И, исключающее ИЛИ (xor), логический сдвиг вправо, циклический сдвиг вправо.

Примечания к реализации

В данных примерах для более легкого восприятия кода я работаю с битовой последовательность, как со строкой из 0 и 1. Все функции реализованы средствами посимвольного сравнения двух строк, содержащих только 0 и 1. В Python существуют уже готовые функции для битового сдвига (>>, <<) или для xor(^).

# константа для суммы по mod 2**32
VAL_MOD = 2 ** 32
def set_zero_in_end(bites, len_str):
   """ метод для вставки нулей в конец битовой последовательности"""
   while len(bites) % len_str != 0:
       bites += '0'
   return bites
def set_zero_in_start(bites, len_str):
   """ метод для вставки нулей в начало битовой последовательности"""
   while len(bites) % len_str != 0:
       bites = '0' + bites
   return bites
def set_len_str_in_end(bits, bits_len, max_mod=512):
   """ метод для вставки в конец битовой последовательности текста длины сообщения"""
   while (len(bits) + len(bits_len)) % max_mod != 0:
       bits += '0'
   bits += bits_len
   return bits
def hex_to_bin(hex_num, len_bits=32):
   """ метод для перевода числа из 16-ричного представления в бинарное"""
   bits = bin(hex_num)[2:]
   # добавляем в начало нули для получения нужной длины последовательности
   while len(bits) < len_bits:
       bits = '0' + bits
   return bits
def str_to_bin(text):
   """ метод для перевода строки в бинарное представление """
   binary = ''
   # приводим текст к виде списка байт
   byte_array = bytearray(text, "utf8")
   # каждый байт преобразуем в битовую строчку
   for byte in byte_array:
       binary_repr = bin(byte)[2:]
       while len(binary_repr) < 8:
           binary_repr = '0' + binary_repr
       binary += binary_repr
   return binary
def rotate_right(list_bits, count):
   """ метод для циклического сдвига вправо бинарной последовательности"""
   for _ in range(count):
       list_bits = list_bits[-1] + list_bits[:-1]
   return list_bits
def shift_right(list_bits, count):
   """ метод для логического сдивга вправо бинарной последовательности"""
   for _ in range(count):
       list_bits = '0' + list_bits[:-1]
   return list_bits
def xor(list_bits1, list_bits2):
   """ метод для вычисления исключающего или"""
   # дополняем последовательности до одинаковой длины
   max_len = max(len(list_bits1), len(list_bits2))
   list_bits1 = '0' * (max_len - len(list_bits1)) + list_bits1
   list_bits2 = '0' * (max_len - len(list_bits2)) + list_bits2
   rez_bits = []
   # xor: 1 если хотя бы один бит 1, но не оба вместе, иначе 0
   for i in range(max_len):
       new_bit = '1' if list_bits1[i] != list_bits2[i] else '0'
       rez_bits.append(new_bit)
   return ''.join(rez_bits)
def log_and(bits1, bits2):
   """ метод для вычисления логического И """
   max_len = max(len(bits1), len(bits2))
   bits1 = '0' * (max_len - len(bits1)) + bits1
   bits2 = '0' * (max_len - len(bits2)) + bits2
   rez_bits = []
   # and: 1, если оба бита = 1, иначе 0
   for i in range(max_len):
       new_bit = '1' if bits1[i] == bits2[i] == '1' else '0'
       rez_bits.append(new_bit)
   return ''.join(rez_bits)
def summator(*list_bits):
   """ метод для суммирования битовых последовательностей"""
   summa = 0
   # для получения суммы всё переводится к обычным 10-чным числам
   for bit in list_bits:
       summa += int(bit, base=2)
   # в sha-256 сумма берется по mod 2**32
   summa = summa % VAL_MOD
   # приводим обратно к битовой последовательности
   binary = bin(summa)[2:]
   while len(binary) < 32:
       binary = '0' + binary
   return binary
def log_not(bits):
   """ метод для получения отрицания от битовой последовательности"""
   binary = ''
   for bit in bits:
       binary += '1' if bit == '0' else '0'
   return binary

Теперь определим 8 констант, которые являются первыми 32 битами дробных частей квадратных корней первых восьми простых чисел:

# заполняем список первоначальных значений хеш-функции
# первые 32 бита дробных частей квадратных корней первых восьми простых чисел
h0 = hex_to_bin(0x6A09E667)
h1 = hex_to_bin(0xBB67AE85)
h2 = hex_to_bin(0x3C6EF372)
h3 = hex_to_bin(0xA54FF53A)
h4 = hex_to_bin(0x510E527F)
h5 = hex_to_bin(0x9B05688C)
h6 = hex_to_bin(0x1F83D9AB)
h7 = hex_to_bin(0x5BE0CD19)

Также заполним таблицу констант, которые являются первыми 32 битами кубических корней первых 64 простых чисел:

# таблица констант
# первые 32 бита дробных частей кубических корней первых 64 простых чисел
constants = [
   hex_to_bin(0x428A2F98), hex_to_bin(0x71374491), hex_to_bin(0xB5C0FBCF), hex_to_bin(0xE9B5DBA5),
   hex_to_bin(0x3956C25B), hex_to_bin(0x59F111F1), hex_to_bin(0x923F82A4), hex_to_bin(0xAB1C5ED5),
   hex_to_bin(0xD807AA98), hex_to_bin(0x12835B01), hex_to_bin(0x243185BE), hex_to_bin(0x550C7DC3),
   hex_to_bin(0x72BE5D74), hex_to_bin(0x80DEB1FE), hex_to_bin(0x9BDC06A7), hex_to_bin(0xC19BF174),
   hex_to_bin(0xE49B69C1), hex_to_bin(0xEFBE4786), hex_to_bin(0x0FC19DC6), hex_to_bin(0x240CA1CC),
   hex_to_bin(0x2DE92C6F), hex_to_bin(0x4A7484AA), hex_to_bin(0x5CB0A9DC), hex_to_bin(0x76F988DA),
   hex_to_bin(0x983E5152), hex_to_bin(0xA831C66D), hex_to_bin(0xB00327C8), hex_to_bin(0xBF597FC7),
   hex_to_bin(0xC6E00BF3), hex_to_bin(0xD5A79147), hex_to_bin(0x06CA6351), hex_to_bin(0x14292967),
   hex_to_bin(0x27B70A85), hex_to_bin(0x2E1B2138), hex_to_bin(0x4D2C6DFC), hex_to_bin(0x53380D13),
   hex_to_bin(0x650A7354), hex_to_bin(0x766A0ABB), hex_to_bin(0x81C2C92E), hex_to_bin(0x92722C85),
   hex_to_bin(0xA2BFE8A1), hex_to_bin(0xA81A664B), hex_to_bin(0xC24B8B70), hex_to_bin(0xC76C51A3),
   hex_to_bin(0xD192E819), hex_to_bin(0xD6990624), hex_to_bin(0xF40E3585), hex_to_bin(0x106AA070),
   hex_to_bin(0x19A4C116), hex_to_bin(0x1E376C08), hex_to_bin(0x2748774C), hex_to_bin(0x34B0BCB5),
   hex_to_bin(0x391C0CB3), hex_to_bin(0x4ED8AA4A), hex_to_bin(0x5B9CCA4F), hex_to_bin(0x682E6FF3),
   hex_to_bin(0x748F82EE), hex_to_bin(0x78A5636F), hex_to_bin(0x84C87814), hex_to_bin(0x8CC70208),
   hex_to_bin(0x90BEFFFA), hex_to_bin(0xA4506CEB), hex_to_bin(0xBEF9A3F7), hex_to_bin(0xC67178F2),
]

Приступим к реализации самого алгоритма. Алгоритм работает только с последовательностями бит, длина которых кратна 512. Для этого первым делом необходимо перевести исходное сообщение в последовательность бит. Затем добавить в конец ‘1’ и дополнять нулями до тех пор, пока длина битовой последовательности сообщения + 64 не станет делиться на 512 без остатка. Это необходимо для того, чтобы в конец сообщения вставить длину исходного сообщения в виде последовательности из 64 бит.

# НАЧАЛО АЛГОРИТМА
# сообщение для хеширования
msg = "Euler is held to be one of the greatest mathematicians in history."
# переводим сообщение в битовую последовательность
m = str_to_bin(msg)
# добавляем в конец '1'
m = m + "1"
# добавляем в конец исходную длину сообщения в виде 64 битной последователньости
bits_len = set_zero_in_start(bin(len(msg) * 8)[2:], 64)
# добавляем в конец длину строки
m = set_len_str_in_end(m, bits_len)
print(len(m))
>>> 1024
print(m)
>>> 0100010101110101011011000110010101110010001000000110100101110011001000000110100001100101011011000110010000100000011101000110111100100000011000100110010100100000011011110110111001100101001000000110111101100110001000000111010001101000011001010010000001100111011100100110010101100001011101000110010101110011011101000010000001101101011000010111010001101000011001010110110101100001011101000110100101100011011010010110000101101110011100110010000001101001011011100010000001101000011010010111001101110100011011110111001001111001001011101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000010000

Далее текущее сообщение будет обрабатываться кусочками по 512 бит. Теперь каждый кусочек длиной 512 бит необходимо разбить еще на 16 частей по 32 бита и все это занести в список.

for i in range(0, len(m), 512):
   part = m[i:i + 512]
   parts = []
   # разбиваем исходное сообщение на 16 кусочкой длиной 32 бита
   for j in range(0, 512, 32):
       parts.append(part[j:j + 32])

При каждой итерации по кусочкам сообщения длиной 512 бит необходимо будет создать дополнительные 48 слов длиной 32 бита из уже имеющихся переменных (h0...h7 и полученных 16 частей)

Примечание по коду

Для упрощения читаемости кода и понимания алгоритма, каждое действие при подсчете слагаемых выносилось отдельной записью. Это не целесообразно с точки зрения используемой памяти и времени, но положительно влияет на понимание алгоритма.

# генерируем дополнительные 48 слов для хеширования
for k in range(16, 64):
   # sigma0 = right_rotate(parts[k-15], 7) xor right_rotate(parts[k-15], 18) xor shift_right(parts[k-15], 3)
   rr1 = rotate_right(parts[k - 15], 7)
   rr2 = rotate_right(parts[k - 15], 18)
   sr = shift_right(parts[k - 15], 3)
   x1 = xor(rr1, rr2)
   s0 = xor(x1, sr)
   # sigma1 = right_rotate(parts[k-2], 17) xor right_rotate(parts[k-2], 19) xor shift_right(parts[k-2], 10)
   rr1 = rotate_right(parts[k - 2], 17)
   rr2 = rotate_right(parts[k - 2], 19)
   sh = shift_right(parts[k - 2], 10)
   x1 = xor(rr1, rr2)
   s1 = xor(x1, sh)
   # новое слово: parts[k - 16] + sigma0 + parts[k - 7] + sigma1
   new_part = summator(parts[k - 16], s0, parts[k - 7], s1)
   parts.append(new_part)

Для дальнейших вычислений хеш-значения одной 512-битовой части необходимо объявить еще несколько дополнительных переменных

# инициализируем дополнительные переменные
a = h0
b = h1
c = h2
d = h3
e = h4
f = h5
g = h6
h = h7

Теперь можно реализовать основную часть алгоритма, которая будет выполняться 64 раза:

# весь алгоритм хеширования выполняется 64 раза
for k in range(64):
   # sigma1 = rotate_right(e, 6) xor rotate_right(e, 11) xor rotate_right(e, 25)
   rr1 = rotate_right(e, 6)
   rr2 = rotate_right(e, 11)
   rr3 = rotate_right(e, 25)
   x1 = xor(rr1, rr2)
   s1 = xor(x1, rr3)
   # ch = log_and(e, f) xor log_and(log_not(e), g)
   rr1 = log_and(e, f)
   rr3 = log_and(log_not(e), g)
   ch = xor(rr1, rr3)
   # temp1 = h + s1 + ch + constants[k] + parts[k]
   t1 = summator(h, s1, ch, constants[k], parts[k])
   # sigma0 = rotate_right(a, 2) xor rotate_right(a, 13) xor rotate_right(a, 22)
   rr1 = rotate_right(a, 2)
   rr2 = rotate_right(a, 13)
   rr3 = rotate_right(a, 22)
   x1 = xor(rr1, rr2)
   s0 = xor(x1, rr3)
   # maj = log_and(a, b) xor log_and(a, c) xor log_and(b, c)
   rr1 = log_and(a, b)
   rr2 = log_and(a, c)
   rr3 = log_and(b, c)
   x1 = xor(rr1, rr2)
   maj = xor(x1, rr3)
   # temp2 = sigma0 + maj
   t2 = summator(s0, maj)
   # теперь необходимо изменить значения временных переменных
   h = g
   g = f
   f = e
   e = summator(d, t1)
   d = c
   c = b
   b = a
   a = summator(t1, t2)

После цикла for k in range(64): необходимо выполнить последние преобразования для изменения переменных h0...h7, отвечающих за итоговое состояние хеш-функции:

# в конце необходимо изменить начальные переменные для хранения хеша
h0 = summator(h0, a)
h1 = summator(h1, b)
h2 = summator(h2, c)
h3 = summator(h3, d)
h4 = summator(h4, e)
h5 = summator(h5, f)
h6 = summator(h6, g)
h7 = summator(h7, h)

После вычисления хеша для каждого 512-битного кусочка необходимо конкатенировать 16ричные значения всех переменных h0...h7:

# приводим вычисленные бинарные последовательности к 16-ым значениям и конкатенируем их
sha256text = (hex(int(h0, base=2))[2:] +
             hex(int(h1, base=2))[2:] +
             hex(int(h2, base=2))[2:] +
             hex(int(h3, base=2))[2:] +
             hex(int(h4, base=2))[2:] +
             hex(int(h5, base=2))[2:] +
             hex(int(h6, base=2))[2:] +
             hex(int(h7, base=2))[2:])
print(sha256text)
>>> b20447c5281a7b4cf6d7dacaaf0e8ed77f1c4acfb9d7dbd64c8ccccbb5ec5bcd

На этом алгоритм SHA-256 можно считать реализованным, но хотелось рассмотреть другие алгоритмы из семейства SHA-2 и узнать, чем они все между собой отличаются.

SHA-224

Алгоритм SHA-224 в реализации полностью идентичен алгоритму SHA-256, но в качестве начальных значений h0..h7 берутся значения:

h0 = hex_to_bin(0xC1059ED8)
h1 = hex_to_bin(0x367CD507)
h2 = hex_to_bin(0x3070DD17)
h3 = hex_to_bin(0xF70E5939)
h4 = hex_to_bin(0xFFC00B31)
h5 = hex_to_bin(0x68581511)
h6 = hex_to_bin(0x64F98FA7)
h7 = hex_to_bin(0xBEFA4FA4)

И при получении итогового хеша не используется значение h7 (за счет чего уменьшается длина полученного хеш-значения до 56 символов):

sha224text = (hex(int(h0, base=2))[2:] +
             hex(int(h1, base=2))[2:] +
             hex(int(h2, base=2))[2:] +
             hex(int(h3, base=2))[2:] +
             hex(int(h4, base=2))[2:] +
             hex(int(h5, base=2))[2:] +
             hex(int(h6, base=2))[2:])
print(sha224text)
>>> b56b1dbbb3eab8ec1d3a850b6177fc163689493dc54d5b25e6d69ea9
print(len(sha224text))
>>> 56

SHA-512

Отличия sha-512 от sha-256:

  • Слова имеют длину 64 бита;

  • Алгоритм хеширования проводится 80 раз, а не 64;

  • Всё сообщение разбивается на кусочки длиной 1024 бита;

  • Используются кубические корни 80 первых простых чисел в отличии от 64 для sha-256;

  • Сдвиги, при вычислении дополнительных слов, происходят на другое число позиций;

  • Создается 64 дополнительных слова, а не 48;

  • Длина сообщения записывается в виде 128 битовой последовательности;

  • Вычисление суммы ведется по mod 2^64.

Первым делом изменится метод перевода числа в бинарное представление:

def hex_to_bin(hex_num, len_bits=64):
   """ метод для перевода числа из 16-ричного представления в бинарное"""
   bits = bin(hex_num)[2:]
   # добавляем в начало нули для получения нужной длины последовательности
   while len(bits) < len_bits:
       bits = '0' + bits
   return bits

И метод перевода строки в бинарное представление:

def set_len_str_in_end(bits, bits_len, max_mod=1024):
   """ метод для вставки в конец битовой последовательности текста длины сообщения"""
   while (len(bits) + len(bits_len)) % max_mod != 0:
       bits += '0'
   bits += bits_len
   return bits

Цикл по чанкам предложения:

# исходной сообщение обрабатывается частями по 1024 бита
for i in range(0, len(m), 1024):
   part = m[i:i + 1024]
   parts = []
   # разбиваем исходное сообщение на 16 кусочкой длиной 64 бита
   for j in range(0, 1024, 64):
       parts.append(part[j:j + 64])

Генерация новых слов (изменился цикл, так же изменилось вычисление s0, s1):

# генерируем дополнительные 64 слова для хеширования
for k in range(16, 80):
   # sigma0 = right_rotate(parts[k-15], 1) xor right_rotate(parts[k-15], 8) xor shift_right(parts[k-15], 7)
   rr1 = rotate_right(parts[k - 15], 1)
   rr2 = rotate_right(parts[k - 15], 8)
   sr = shift_right(parts[k - 15], 7)
   x1 = xor(rr1, rr2)
   s0 = xor(x1, sr)
   # sigma1 = right_rotate(parts[k-2], 19) xor right_rotate(parts[k-2], 61) xor shift_right(parts[k-2], 6)
   rr1 = rotate_right(parts[k - 2], 19)
   rr2 = rotate_right(parts[k - 2], 61)
   sh = shift_right(parts[k - 2], 6)
   x1 = xor(rr1, rr2)
   s1 = xor(x1, sh)
   # новое слово: parts[k - 16] + sigma0 + parts[k - 7] + sigma1
   new_part = summator(parts[k - 16], s0, parts[k - 7], s1)
   parts.append(new_part)

Изменилось и основное тело функции, цикло повторяется 80 раз и сдвиги считаются по другому:

# весь алгоритм хеширования выполняется 80 раз
for k in range(80):
   # sigma1 = rotate_right(e, 14) xor rotate_right(e, 18) xor rotate_right(e, 41)
   rr1 = rotate_right(e, 14)
   rr2 = rotate_right(e, 18)
   rr3 = rotate_right(e, 41)
   x1 = xor(rr1, rr2)
   s1 = xor(x1, rr3)
   # ch = log_and(e, f) xor log_and(log_not(e), g)
   rr1 = log_and(e, f)
   rr3 = log_and(log_not(e), g)
   ch = xor(rr1, rr3)
   # temp1 = h + s1 + ch + constants[k] + parts[k]
   t1 = summator(h, s1, ch, constants[k], parts[k])
   # sigma0 = rotate_right(a, 28) xor rotate_right(a, 34) xor rotate_right(a, 39)
   rr1 = rotate_right(a, 28)
   rr2 = rotate_right(a, 34)
   rr3 = rotate_right(a, 39)
   x1 = xor(rr1, rr2)
   s0 = xor(x1, rr3)
   # maj = log_and(a, b) xor log_and(a, c) xor log_and(b, c)
   rr1 = log_and(a, b)
   rr2 = log_and(a, c)
   rr3 = log_and(b, c)
   x1 = xor(rr1, rr2)
   maj = xor(x1, rr3)
   # temp2 = sigma0 + maj
   t2 = summator(s0, maj)
   # теперь необходимо изменить значения временных переменных
   h = g
   g = f
   f = e
   e = summator(d, t1)
   d = c
   c = b
   b = a
   a = summator(t1, t2)

Как итог для функции sha-512 получаем:

sha512text = (hex(int(h0, base=2))[2:] +
             hex(int(h1, base=2))[2:] +
             hex(int(h2, base=2))[2:] +
             hex(int(h3, base=2))[2:] +
             hex(int(h4, base=2))[2:] +
             hex(int(h5, base=2))[2:] +
             hex(int(h6, base=2))[2:] +
             hex(int(h7, base=2))[2:])
print(sha512text)
>>> d18979a3b0071f6ff34af0be222ef9b2d727fa20311af7d4e1d21d374217b4ecd776d572f696509525678b05399da69966f867bb39f317902dea1f0b293ce77c
print(len(sha512text))
>>> 128

SHA-512/256

SHA-512/256 идентичен SHA-512 за исключением того, что используются другие значения h0…h7 и при получении итогового хеша берутся только 256 левых бит результата:

h0 = hex_to_bin(0x22312194FC2BF72C)
h1 = hex_to_bin(0x9F555FA3C84C64C2)
h2 = hex_to_bin(0x2393B86B6F53B151)
h3 = hex_to_bin(0x963877195940EABD)
h4 = hex_to_bin(0x96283EE2A88EFFE3)
h5 = hex_to_bin(0xBE5E1E2553863992)
h6 = hex_to_bin(0x2B0199FC2C85B8AA)
h7 = hex_to_bin(0x0EB72DDC81C52CA2)

Результат:

sha512_256_bin = h0+h1+h2+h3+h4+h5+h6+h7
sha512_256_bin = sha512_256_bin[:256]
sha512_256_text = (hex(int(sha512_256_bin, base=2))[2:])
print(sha512_256_text)
print(len(sha512_256_text))

SHA-512/224

SHA-512/224 идентичен SHA-512 за исключением того, что используются другие значения h0…h7 и при получении итогового хеша берутся 224 левых бита результата:

h0 = hex_to_bin(0x8C3D37C819544DA2)
h1 = hex_to_bin(0x73E1996689DCD4D6)
h2 = hex_to_bin(0x1DFAB7AE32FF9C82)
h3 = hex_to_bin(0x679DD514582F9FCF)
h4 = hex_to_bin(0x0F6D2B697BD44DA8)
h5 = hex_to_bin(0x77E36F7304C48942)
h6 = hex_to_bin(0x3F9D85A86A1D36C8)
h7 = hex_to_bin(0x1112E6AD91D692A1)

Результат:

sha512_224_bin = h0+h1+h2+h3+h4+h5+h6+h7
sha512_224_bin = sha512_224_bin[:224]
sha512_224_text = (hex(int(sha512_224_bin, base=2))[2:])
print(sha512_224_text)
>>> 6018fc16ecc97bf9844eb8a5c7a70346028f9fb5c5ffdfb80a30c3af
print(len(sha512_224_text))
>>> 56

SHA-384

Аналогичен sha-512 за исключением того, что используются другие значения h0...h7 и опускаются значения h7, h6 при выводе итогового хеша:

h0 = hex_to_bin(0xCBBB9D5DC1059ED8)
h1 = hex_to_bin(0x629A292A367CD507)
h2 = hex_to_bin(0x9159015A3070DD17)
h3 = hex_to_bin(0x152FECD8F70E5939)
h4 = hex_to_bin(0x67332667FFC00B31)
h5 = hex_to_bin(0x8EB44A8768581511)
h6 = hex_to_bin(0xDB0C2E0D64F98FA7)
h7 = hex_to_bin(0x47B5481DBEFA4FA4)

Результат:

sha384text = (hex(int(h0, base=2))[2:] +
             hex(int(h1, base=2))[2:] +
             hex(int(h2, base=2))[2:] +
             hex(int(h3, base=2))[2:] +
             hex(int(h4, base=2))[2:] +
             hex(int(h5, base=2))[2:])

print(sha384text)
>>> 09829bbc49e3bdd6d47a954f4ea74853579c0d9f743900d939162fed45b4a2c7ef670501bb195b9b1275830a985aa3d3

Вывод

В данной статье были рассмотрены и реализованы алгоритмы семейства SHA-2. Эти алгоритмы используются в различных системах, например, в PGP — используются для создания электронной цифровой подписи; в DSA — используется для создания электронной цифровой подписи и во многих других системах для подтверждения подлинности документа, хеширования паролей, создания электронных цифровых подписей.

Исходники алгоритмов можно найти тут.

Источник: https://habr.com/ru/post/560040/


Интересные статьи

Интересные статьи

Публикуя перевод первой статьи из цикла материалов о реализации epoll, мы провели опрос, посвящённый целесообразности перевода продолжения цикла. Более 90% участников опроса высказались з...
Мне давно было интересно, как сжимаются данные, в том числе в Zip-файлах. Однажды я решил удовлетворить своё любопытство: узнать, как работает сжатие, и написать собственную Zip-программу. Ре...
Среди советов по улучшению юзабилити интернет-магазина, которые можно встретить в инете, один из явных лидеров — совет «сообщайте посетителю стоимость доставки как можно раньше».
Иллюстрация из работы Г.М. Адельсон-Вельского и Е.М. Ландиса 1962 года Деревья поиска — это структуры данных для упорядоченного хранения и простого поиска элементов. Широко применяются двоичны...
Представим типичную ситуацию на первом курсе: вы прочитали про алгоритм Диница, реализовали, а он не заработал, и вы не знаете, почему. Стандартное решение — это начать отлаживать по шагам, кажды...