[Информационная безопасность, Python, Программирование, Алгоритмы] Реализация алгоритмов хеширования семейства SHA-2
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
ВведениеНа сегодняшний день, как никогда, актуальна проблема защиты информации и проверки её целостности. С каждым новым днём возрастает количество сайтов, программ, соцсетей и различных коммерческих структур, собирающих и хранящих данные своих пользователей. Каждый из нас должен быть уверен в том, что вводя свои персональные данные на каком-либо сайте, доступ к этим данным никто не получит. Как раз вопросом обеспечения безопасного хранения и проверки целостности данных активно занимается наука под названием криптография.Криптографические методы широко применяются в задачах идентификации и аутентификации пользователей, защиты каналов связей от проникновения ложных данных, а также защиты электронных документов (различных форматов и типов) от незаконного распространения и подделывания. В данной статье хотелось бы рассмотреть и разобрать одни из популярных криптографических хеш-функций семейства SHA-2.О хеш-функцияхКриптографическая хеш-функция представляет собой некоторый математический алгоритм, который преобразует последовательность данных произвольной длины в строку фиксированной длины, состоящей из ограниченного набора цифр и букв. Такие алгоритмы должны обладать рядом свойств:
- Одно и тоже сообщение всегда должно приводить к одинаковому хеш-значению;
- Скорость вычисления хеш-функции должна быть быстрой;
- Не должно возникать коллизий, то есть не должно быть 2 таких сообщений, которые имели бы одинаковое хеш-значение;
- Небольшие изменения в наборе данных должны приводить к абсолютно новому хеш-значению так, чтобы это значение не казалось связанным с предыдущим значением;
- Сложность (невозможность) вычисления исходных данных по хеш-значению.
Наиболее частое применение хеш-функций - хранение хешированных паролей на стороне сервера. Это делается с той целью, чтобы никто не мог узнать оригинальный пароль и осуществить по нему вход в систему в случае получения доступа к базе. Хотя хеширование паролей не должно вызывать огромных проблем, но такое правило соблюдают не все компании, однако с каждым годом организации начинают больше заботиться о безопасности данных своих пользователей. Также многие компании защищают свои цифровые документы (музыку, фотографии, программы) при помощи вычисления хеш-суммы. В случае, если в программу был встроен вирус или документ был хоть немного изменен, контрольная сумма документа будет изменена и использование такого контента может сразу дать понять, что используются фальсифицированные данные.Реализация алгоритмов семейства SHA-2SHA-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/256SHA-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/224SHA-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 — используется для создания электронной цифровой подписи и во многих других системах для подтверждения подлинности документа, хеширования паролей, создания электронных цифровых подписей.Исходники алгоритмов можно найти тут.
===========
Источник:
habr.com
===========
Похожие новости:
- [Системное администрирование, Oracle, Программирование, *nix] Актуальность инициативы #BAAG — BattleAgainstAnyGuess
- [Python] Сокрытые драгоценности Python (перевод)
- [Ruby, Python, Программирование, Будущее здесь] Почему за интерпретируемыми языками будущее
- [C++, Программирование микроконтроллеров] Stm32 + USB на шаблонах C++. Продолжение. Делаем CDC
- [JavaScript, Программирование, Алгоритмы, Функциональное программирование] Решаем вопрос сортировки в JavaScript раз и навсегда
- [Open source, Python, Алгоритмы, Машинное обучение, Искусственный интеллект] Прогнозирование временных рядов с помощью AutoML
- [Программирование, Карьера в IT-индустрии, История IT] Последние четверть века развития в программировании нет
- [Habr, Программирование, Разработка игр, C#, Unity] Как обновить все сцены Unity-проекта в один клик
- [Занимательные задачки, Python, Алгоритмы, Математика] В аквариуме: вычислительная генетика на Python и Mathcad (часть 1)
- [Информационная безопасность, Мессенджеры, Законодательство в IT] WhatsApp отказался ограничивать работу аккаунтов из-за политики конфиденциальности
Теги для поиска: #_informatsionnaja_bezopasnost (Информационная безопасность), #_python, #_programmirovanie (Программирование), #_algoritmy (Алгоритмы), #_python_3, #_programmirovanie (программирование), #_sha2, #_heshfunktsii (хеш-функции), #_heshirovanie (хеширование), #_sha256, #_sha512, #_informatsionnaja_bezopasnost (
Информационная безопасность
), #_python, #_programmirovanie (
Программирование
), #_algoritmy (
Алгоритмы
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 13:06
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
ВведениеНа сегодняшний день, как никогда, актуальна проблема защиты информации и проверки её целостности. С каждым новым днём возрастает количество сайтов, программ, соцсетей и различных коммерческих структур, собирающих и хранящих данные своих пользователей. Каждый из нас должен быть уверен в том, что вводя свои персональные данные на каком-либо сайте, доступ к этим данным никто не получит. Как раз вопросом обеспечения безопасного хранения и проверки целостности данных активно занимается наука под названием криптография.Криптографические методы широко применяются в задачах идентификации и аутентификации пользователей, защиты каналов связей от проникновения ложных данных, а также защиты электронных документов (различных форматов и типов) от незаконного распространения и подделывания. В данной статье хотелось бы рассмотреть и разобрать одни из популярных криптографических хеш-функций семейства SHA-2.О хеш-функцияхКриптографическая хеш-функция представляет собой некоторый математический алгоритм, который преобразует последовательность данных произвольной длины в строку фиксированной длины, состоящей из ограниченного набора цифр и букв. Такие алгоритмы должны обладать рядом свойств:
# константа для суммы по 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 # заполняем список первоначальных значений хеш-функции
# первые 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 простых чисел 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), ] # НАЧАЛО АЛГОРИТМА
# сообщение для хеширования 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 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]) # генерируем дополнительные 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) # инициализируем дополнительные переменные
a = h0 b = h1 c = h2 d = h3 e = h4 f = h5 g = h6 h = h7 # весь алгоритм хеширования выполняется 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) # в конце необходимо изменить начальные переменные для хранения хеша
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) # приводим вычисленные бинарные последовательности к 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 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) 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
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]) # генерируем дополнительные 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 раз
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) 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 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)) 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 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 =========== Источник: habr.com =========== Похожие новости:
Информационная безопасность ), #_python, #_programmirovanie ( Программирование ), #_algoritmy ( Алгоритмы ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 13:06
Часовой пояс: UTC + 5