BLAKE2 - это криптографическая хеш-функция, определенная в RFC 7693, которая поставляется в двух вариантах:
BLAKE2b
, оптимизированный для 64-битных платформ и создающий хеши(дайджесты) любого размера от 1 до 64 байт,BLAKE2s
, оптимизированный для 8 и 32-битных платформ, создает хеши(дайджесты) любого размера от 1 до 32 байт.BLAKE2 поддерживает режим ключей, более быстрая и простая замена HMAC. Поддерживает хеширование с добавлением соли, персонализацию хеша и хеширование дерева.
Предупреждение.
Хеширование с добавлением соли или простого хеширования с помощью BLAKE2
или любой другой криптографической хеш-функции общего назначения, такой как SHA-256, не подходит для хеширования паролей.
import hashlib hashlib.blake2b(data=b'', *, digest_size=64, key=b'', salt=b'', person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, node_depth=0, inner_size=0, last_node=False, usedforsecurity=True) hashlib.blake2s(data=b'', *, digest_size=32, key=b'', salt=b'', person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, node_depth=0, inner_size=0, last_node=False, usedforsecurity=True)
Функции hashlib.blake2b()
и hashlib.blake2s()
модуля hashlib
возвращают соответствующие хеш-объекты для вычисления хешей BLAKE2b
или BLAKE2s
.
Они могут принимать следующие общие параметры:
data
: начальный кусок данных для вычисления хеша, должен быть байтоподобным объектом. Аргумент data
может быть передан только как позиционный аргумент.digest_size
: int
размер выходного хеша(дайджеста) в байтах.key
: ключ, для хеширования по ключу, должна быть до 64 байтов для BLAKE2b
, до 32 байтов для BLAKE2s
.salt
: соль для рандомизированного хеширования, должна быть до 16 байт для BLAKE2b
, до 8 байт для BLAKE2s
.person
: строка персонализации выходного хеша, должна быть до 16 байтов для BLAKE2b
, до 8 байтов для BLAKE2s
.В следующей таблице приведены ограничения для общих параметров, значения указаны в байтах:
Hash | digest_size | len(key) | len(salt) | len(person) |
BLAKE2b | 64 | 64 | 16 | 16 |
BLAKE2s | 32 | 32 | 8 | 8 |
Примечание.
Спецификация BLAKE2
определяет постоянные длины для параметров соли salt
и строки персонализации person
, однако для удобства эта реализация принимает байтовые строки любого размера вплоть до указанной длины. Если длина параметра меньше указанной, она дополняется нулями, поэтому например b'salt'
и b'salt\\x00'
- это одно и то же значение, но это не так для ключа key
.
Эти размеры доступны как константы модуля, описанные ниже.
Функции конструктора также принимают следующие параметры хеширования дерева:
fanout
: разветвление (от 0 до 255, 0, если не ограничено, 1 в последовательном режиме).depth
: максимальная глубина дерева (от 1 до 255, 255 без ограничений, 1 в последовательном режиме).leaf_size
: максимальная длина байта листа (от 0 до 2**32-1, 0, если не ограничено или в последовательном режиме).node_offset
: смещение узла (от 0 до 2 ** 64-1 для BLAKE2b
, от 0 до 2**48-1 для BLAKE2s
, 0 для первого, крайнего левого, конечного или в последовательном режиме).node_depth
: глубина узла (от 0 до 255, 0 для листьев или в последовательном режиме).inner_size
: размер внутреннего хеша (от 0 до 64 для BLAKE2b
, от 0 до 32 для BLAKE2s
, 0 в последовательном режиме).last_node
: логическое значение, указывающее, является ли обработанный узел последним (False
для последовательного режима).usedforsecurity=True
: запрещает использовать небезопасные и заблокированные алгоритмы хеширования (новое в Python 3.9).BLAKE2b
и BLAKE2b
.blake2b.SALT_SIZE
blake2s.SALT_SIZE
:Длина соли (максимальная длина, принятая конструкторами).
blake2b.PERSON_SIZE
blake2s.PERSON_SIZE
:Длина строки персонализации (максимальная длина, принимаемая конструкторами).
blake2b.MAX_KEY_SIZE
blake2s.MAX_KEY_SIZE
:Максимальный размер ключа.
blake2b.MAX_DIGEST_SIZE
blake2s.MAX_DIGEST_SIZE
:Максимальный размер дайджеста, который может вывести хеш-функция.
Чтобы вычислить хеш некоторых данных, вы должны сначала создать хеш-объект, вызвав соответствующую функцию конструктора hashlib.blake2b()
или hashlib.blake2s()
, а затем обновить его данными, вызвав метод hash.update()
для объекта и наконец получить хеш объекта путем вызова hash.digest() для [байтовой строки][t-bytes] или
hash.hexdigest() для строки в шестнадцатеричном коде.
>>> from hashlib import blake2b >>> h = blake2b(digest_size=25) >>> h.update(b'Hello world') >>> h.hexdigest() # '70ac97a738e8f0ccdecc277268516235e1ac687019af3f192b'
В качестве аргумента вы можете передать первый фрагмент данных для обновления непосредственно конструктору в качестве позиционного аргумента:
>>> from hashlib import blake2b >>> blake2b(b'Hello world', digest_size=30).hexdigest() # '5e9196a5ceaac41fa1c545c0b95df361856cee39b7a56235f11145d3affc'
Вы можете вызывать hash.update()
столько раз, сколько нужно для итеративного обновления хеша:
>>> from hashlib import blake2b >>> items = [b'Hello', b' ', b'world'] >>> h = blake2b(digest_size=30) >>> for item in items: ... h.update(item) >>> h.hexdigest() # '5e9196a5ceaac41fa1c545c0b95df361856cee39b7a56235f11145d3affc'
BLAKE2
имеет настраиваемый размер получаемого хеша до 64 байт для BLAKE2b
и до 32 байт для BLAKE2s
. Например, чтобы заменить SHA-1
на BLAKE2b
без изменения размера вывода, мы можем указать BLAKE2b
создать 20-байтовые дайджесты:
>>> from hashlib import blake2b >>> h = blake2b(digest_size=20) >>> h.update(b'Replacing SHA1 with the more secure function') >>> h.hexdigest() # 'd24f26cf8de66472d58d4e1b1774b4c9158b1f4c' >>> h.digest_size # 20 >>> len(h.digest()) # 20
Хеш-объекты с разным размером получаемого хеша имеют совершенно разные выходные данные. Более короткие хеши не являются префиксами более длинных хешей. BLAKE2b
и BLAKE2s
производят разные выходы, даже если длина вывода одинакова:
>>> from hashlib import blake2b, blake2s >>> blake2b(digest_size=10).hexdigest() # '6fa1d8fcfd719046d762' >>> blake2b(digest_size=11).hexdigest() # 'eb6ec15daf9546254f0809' >>> blake2s(digest_size=10).hexdigest() # '1bf21a98c78a1c376ae9' >>> blake2s(digest_size=11).hexdigest() # '567004bf96e4a25773ebf4'
Хеширование на основе ключа может использоваться для аутентификации в качестве более быстрой и простой замены кода аутентификации сообщений на основе хеша HMAC. BLAKE2
может безопасно использоваться в режиме префикса MAC благодаря свойству безразличия, унаследованному от BLAKE
.
В этом примере показано, как получить (шестнадцатеричный) 128-битный код аутентификации для сообщения b'message data'
с ключом b'pseudorandom key'
:
>>> from hashlib import blake2b >>> h = blake2b(key=b'pseudorandom key', digest_size=16) >>> h.update(b'message data') >>> h.hexdigest() # '3d363ff7401e02026f4a4687d4863ced'
В качестве практического примера веб-приложение может симметрично подписывать файлы cookie
, отправляемые пользователям, а затем проверять их, чтобы убедиться, что они не были подделаны:
>>> from hashlib import blake2b >>> from hmac import compare_digest >>> SECRET_KEY = b'pseudorandomly generated server secret key' >>> AUTH_SIZE = 16 >>> def sign(cookie): ... h = blake2b(digest_size=AUTH_SIZE, key=SECRET_KEY) ... h.update(cookie) ... return h.hexdigest().encode('utf-8') >>> def verify(cookie, sig): ... good_sig = sign(cookie) ... return compare_digest(good_sig, sig) >>> cookie = b'user-alice' >>> sig = sign(cookie) >>> print("{0},{1}".format(cookie.decode('utf-8'), sig)) # user-alice,b'43b3c982cf697e0c5ab22172d1ca7421' >>> verify(cookie, sig) # True >>> verify(b'user-bob', sig) # False >>> verify(cookie, b'0102030405060708090a0b0c0d0e0f00') # False
Несмотря на то, что существует встроенный режим хеширования с ключом, BLAKE2
, конечно же может использоваться в конструкции HMAC с модулем hmac
:
>>> import hmac, hashlib >>> m = hmac.new(b'secret key', digestmod=hashlib.blake2s) >>> m.update(b'message') >>> m.hexdigest() # 'e3c8102868d28b5ff85fc35dda07329970d1a01e273c37481326fe0c861c8142'
Иногда полезно заставить хеш-функцию создавать разные дайджесты для одного и того же ввода для разных целей.
Цитируем авторов хеш-функции Скейна:
Мы рекомендуем всем дизайнерам приложений серьезно подумать об этом; Мы видели много протоколов, где хеш, который вычисляется в одной части протокола, может использоваться в совершенно другой части, потому что два вычисления хеша были выполнены для похожих или связанных данных, и злоумышленник может заставить приложение сделать один и тот же хеш-ввод. Персонализация каждой хеш-функции, используемой в протоколе, в целом останавливает этот тип атаки.
BLAKE2
можно персонализировать, передав байты аргументу person
:
>>> from hashlib import blake2b >>> FILES_HASH_PERSON = b'MyApp Files Hash' >>> BLOCK_HASH_PERSON = b'MyApp Block Hash' >>> h = blake2b(digest_size=32, person=FILES_HASH_PERSON) >>> h.update(b'the same content') >>> h.hexdigest() # '20d9cd024d4fb086aae819a1432dd2466de12947831b75c5a30cf2676095d3b4' >>> h = blake2b(digest_size=32, person=BLOCK_HASH_PERSON) >>> h.update(b'the same content') >>> h.hexdigest() # 'cf68fb5761b9c44e7878bfb2c4c9aea52264a80b75005e65619778de59f383a3'
Персонализация вместе с режимом работы с ключами также может использоваться для получения разных ключей из одного и того же хеша.
>>> from hashlib import blake2s >>> from base64 import b64decode, b64encode >>> orig_key = b64decode(b'Rm5EPJai72qcK3RGBpW3vPNfZy5OZothY+kHY6h21KM=') >>> enc_key = blake2s(key=orig_key, person=b'kEncrypt').digest() >>> mac_key = blake2s(key=orig_key, person=b'kMAC').digest() >>> print(b64encode(enc_key).decode('utf-8')) # rbPb15S/Z9t+agffno5wuhB77VbRi6F9Iv2qIxU7WHw= >>> print(b64encode(mac_key).decode('utf-8')) # G9GtHFE1YluXY1zWPlYk1e/nWfu0WSEb0KRcjhDeP/o=
Добавив соли, пользователи могут ввести рандомизацию в хеш-функцию. Рандомизированное хеширование полезно для защиты от коллизионных атак на хеш-функцию, используемую в цифровых подписях.
В BLAKE2
- соль обрабатывается как одноразовый вход для хеш-функции во время инициализации, а не как вход для каждой функции сжатия.
>>> import os >>> from hashlib import blake2b >>> msg = b'some message' # Вычислите первый хеш со случайной солью. >>> salt1 = os.urandom(blake2b.SALT_SIZE) >>> h1 = blake2b(salt=salt1) >>> h1.update(msg) # Вычислите второй хеш с другой случайной солью. >>> salt2 = os.urandom(blake2b.SALT_SIZE) >>> h2 = blake2b(salt=salt2) >>> h2.update(msg) # Хеши разные. >>> h1.digest() != h2.digest() # True