Skip to main content

15.2. hashlib — BLAKE2哈希函数

BLAKE2RFC-7693 中定义的加密散列函数,有两种类型:

  • BLAKE2b,针对64位平台进行了优化,可生成1到64个字节之间的任何大小的摘要,

  • BLAKE2s,针对8到32位平台进行了优化,可生成1到32个字节之间的任何大小的摘要。

BLAKE2支持 键控模式 (对 HMAC 更快更简单的替换),盐渍散列个性化树散列

此模块的哈希对象遵循标准库的 hashlib 对象的API。

15.3. 模块

15.3.1. 创建哈希对象

新的哈希对象通过调用构造函数创建:

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)
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)

这些函数返回用于计算BLAKE2b或BLAKE2s的相应哈希对象。他们可以选择这些一般参数:

  • data:数据的初始块以进行哈希,其必须可解释为字节的缓冲区。

  • digest_size:输出摘要的大小(以字节为单位)。

  • key:键控散列的键(BLAKE2b最多为64字节,BLAKE2最多为32个字节)。

  • salt:随机散列的盐(BLAKE2b最多为16字节,BLAKE2最多为8字节)。

  • person:个性化字符串(BLAKE2b最多为16字节,BLAKE2最多为8个字节)。

下表显示了常规参数的限制(以字节为单位):

哈希

digest_size

len(key)

伦盐

len(人)

BLAKE2b

64

64

16

16

BLAKE2s

32

32

8

8

注解

BLAKE2规范定义了盐和个性化参数的常量长度,然而,为了方便,此实现接受任何大小直到指定长度的字节串。如果参数的长度小于指定的长度,则用零填充,因此,例如,b'salt'b'salt\x00' 是相同的值。 (key 不是这样)。

这些尺寸可作为下述 constants 模块提供。

构造函数也接受以下树哈希参数:

  • fanout:扇出(0至255,0如果无限制,1在顺序模式)。

  • depth:树的最大深度(1到255,255,如果无限制,1在顺序模式)。

  • leaf_size:叶的最大字节长度(0到2 ** 32-1,0如果无限制或在顺序模式下)。

  • node_offset:节点偏移(对于BLAKE2b为0至2**64-1,对于BLAKE2s为0至2**48-1,对于开始,最左边,叶或顺序模式为0)。

  • node_depth:节点深度(0到255,0为叶,或在顺序模式)。

  • inner_size:内部摘要大小(BLAKE2b为0到64,BLAKE2为0到32,顺序模式为0)。

  • last_node:布尔值,表示处理的节点是否是最后一个(顺序模式的 False)。

Explanation of tree mode parameters.

参见 BLAKE2规范 中的2.10节,对树哈希进行全面检查。

15.3.2. 常量

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

散列函数可以输出的最大摘要大小。

15.4. 例子

15.4.1. 简单散列

要计算一些数据的哈希,你应该首先通过调用相应的构造函数(blake2b()blake2s())构造一个哈希对象,然后通过在对象上调用 update() 来更新数据,最后,从对象中获取摘要通过调用 digest() (或 hexdigest() 十六进制编码字符串)。

>>> from hashlib import blake2b
>>> h = blake2b()
>>> h.update(b'Hello world')
>>> h.hexdigest()
'6ff843ba685842aa82031d3f53c48b66326df7639a63d128974c5c14f31a0f33343a8c65551134ed1ae0f2b0dd2bb495dc81039e3eeb0aa1bb0388bbeac29183'

作为一个快捷方式,您可以将第一个数据块直接更新为构造函数作为第一个参数(或作为 data 关键字参数):

>>> from hashlib import blake2b
>>> blake2b(b'Hello world').hexdigest()
'6ff843ba685842aa82031d3f53c48b66326df7639a63d128974c5c14f31a0f33343a8c65551134ed1ae0f2b0dd2bb495dc81039e3eeb0aa1bb0388bbeac29183'

您可以调用 hash.update() 多次,因为您需要迭代更新散列:

>>> from hashlib import blake2b
>>> items = [b'Hello', b' ', b'world']
>>> h = blake2b()
>>> for item in items:
...     h.update(item)
>>> h.hexdigest()
'6ff843ba685842aa82031d3f53c48b66326df7639a63d128974c5c14f31a0f33343a8c65551134ed1ae0f2b0dd2bb495dc81039e3eeb0aa1bb0388bbeac29183'

15.4.2. 使用不同的摘要大小

BLAKE2具有可配置的摘要的大小,对于BLAKE2b最多为64字节,对于BLAKE2最多为32字节。例如,要在不改变输出大小的情况下用BLAKE2b替换SHA-1,我们可以告诉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

具有不同摘要大小的哈希对象具有完全不同的输出(较短的散列是较长散列的 not 前缀);即使输出长度相同,BLAKE2b和BLAKE2也会产生不同的输出:

>>> 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'

15.4.3. 键控散列

键控散列可用于认证,作为 基于哈希的消息认证码 (HMAC)的更快和更简单的替换。 BLAKE2可以安全地使用前缀MAC模式由于从BLAKE继承的indifferentiability属性。

此示例显示如何为具有密钥 b'pseudorandom key' 的消息 b'message data' 获取(十六进制编码)128位认证代码:

>>> from hashlib import blake2b
>>> h = blake2b(key=b'pseudorandom key', digest_size=16)
>>> h.update(b'message data')
>>> h.hexdigest()
'3d363ff7401e02026f4a4687d4863ced'

作为一个实际示例,Web应用程序可以对称地签署发送给用户的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(data=cookie, digest_size=AUTH_SIZE, key=SECRET_KEY)
...     return h.hexdigest()
>>>
>>> cookie = b'user:vatrogasac'
>>> sig = sign(cookie)
>>> print("{0},{1}".format(cookie.decode('utf-8'), sig))
user:vatrogasac,349cf904533767ed2d755279a8df84d0
>>> compare_digest(cookie, sig)
True
>>> compare_digest(b'user:policajac', sig)
False
>>> compare_digesty(cookie, '0102030405060708090a0b0c0d0e0f00')
False

即使有一个本地键控散列模式,BLAKE2当然可以用于具有 hmac 模块的HMAC构造:

>>> import hmac, hashlib
>>> m = hmac.new(b'secret key', digestmod=hashlib.blake2s)
>>> m.update(b'message')
>>> m.hexdigest()
'e3c8102868d28b5ff85fc35dda07329970d1a01e273c37481326fe0c861c8142'

15.4.4. 随机散列

通过设置 salt 参数,用户可以向散列函数引入随机化。随机散列对于防止对数字签名中使用的散列函数的冲突攻击是有用的。

随机散列被设计用于一方(消息准备器)生成要由第二方(消息签名者)签名的消息的全部或部分的情况。如果消息准备器能够找到密码散列函数冲突(即,两个消息产生相同的散列值),则她可以准备将产生相同的散列值和数字签名但具有不同结果的消息的有意义版本(例如, ,将$ 1,000,000转帐到帐户,而不是$ 10)。已经设计了加密散列函数作为主要目标的冲突抵抗,但是攻击加密散列函数的当前集中可以导致给定的加密散列函数提供比预期更小的抗冲突性。随机散列通过减少准备者可以生成两个或更多个消息,在数字签名生成过程期间最终产生相同的散列值,即使可以为散列函数找到冲突的可能性,也为签名者提供了附加保护。然而,当消息的所有部分由签名者准备时,随机散列的使用可以减少由数字签名提供的安全性的量。

NIST SP-800-106“Randomized Hashing for Digital Signatures”

在BLAKE2中,盐在初始化期间作为一次性输入被处理为散列函数,而不是作为每个压缩函数的输入。

警告

使用BLAKE2或任何其他通用加密散列函数(例如SHA-256)的 盐渍散列 (或只是散列)不适用于散列密码。有关详细信息,请参阅 BLAKE2常见问题

>>> import os
>>> from hashlib import blake2b
>>> msg = b'some message'
>>> # Calculate the first hash with a random salt.
>>> salt1 = os.urandom(blake2b.SALT_SIZE)
>>> h1 = blake2b(salt=salt1)
>>> h1.update(msg)
>>> # Calculate the second hash with a different random salt.
>>> salt2 = os.urandom(blake2b.SALT_SIZE)
>>> h2 = blake2b(salt=salt2)
>>> h2.update(msg)
>>> # The digests are different.
>>> h1.digest() != h2.digest()
True

15.4.5. 个性化

有时,强制哈希函数为不同目的的相同输入产生不同的摘要是有用的。引用Skein散列函数的作者:

我们建议所有应用程序设计者认真考虑这样做;我们已经看到许多协议,其中在协议的一部分中计算的散列可以在完全不同的部分中使用,因为对相似或相关数据进行了两次散列计算,并且攻击者可以强制应用使散列输入相同。个性化协议中使用的每个哈希函数总是停止这种类型的攻击。

Skein哈希函数家族,第21页)

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=

15.4.6. 树模式

这里有一个使用两个叶节点对最小树进行散列的示例:

  10
 /  \
00  01

此示例使用64字节的内部摘要,并返回32字节的最终摘要:

>>> from hashlib import blake2b
>>>
>>> FANOUT = 2
>>> DEPTH = 2
>>> LEAF_SIZE = 4096
>>> INNER_SIZE = 64
>>>
>>> buf = bytearray(6000)
>>>
>>> # Left leaf
... h00 = blake2b(buf[0:LEAF_SIZE], fanout=FANOUT, depth=DEPTH,
...               leaf_size=LEAF_SIZE, inner_size=INNER_SIZE,
...               node_offset=0, node_depth=0, last_node=False)
>>> # Right leaf
... h01 = blake2b(buf[LEAF_SIZE:], fanout=FANOUT, depth=DEPTH,
...               leaf_size=LEAF_SIZE, inner_size=INNER_SIZE,
...               node_offset=1, node_depth=0, last_node=True)
>>> # Root node
... h10 = blake2b(digest_size=32, fanout=FANOUT, depth=DEPTH,
...               leaf_size=LEAF_SIZE, inner_size=INNER_SIZE,
...               node_offset=0, node_depth=1, last_node=True)
>>> h10.update(h00.digest())
>>> h10.update(h01.digest())
>>> h10.hexdigest()
'3ad2a9b37c6070e374c7a8c508fe20ca86b6ed54e286e93a0318e95e881db5aa'

15.5. 积分

BLAKE2Jean-Philippe Aumasson塞缪尔·内维斯Zooko Wilcox-O’Hearn基督徒Winnerlein 设计,基于 SHA-3 决赛入围的由 Jean-Philippe AumassonLuca HenzenWilli Meier拉斐尔C.藩 创建的 BLAKE

它使用由 丹尼尔·伯恩斯坦 设计的 ChaCha 密码的核心算法。

stdlib实现基于 pyblake2 模块。它是由 Dmitry Chestnykh 基于 塞缪尔·内维斯 编写的C实现写的。文档从 pyblake2 中复制并由 Dmitry Chestnykh 编写。

CAC代码被 基督教Heimes 的Python部分重写了。

以下公共域名奉献适用于C哈希函数实现,扩展代码和本文档:

在法律允许的范围内,作者将所有版权,相关和邻近的权利都授予了全世界的公共领域。此软件的分发没有任何保证。

您应该已经收到CC0公共域名的复制本软件。如果没有,请参阅 http://creativecommons.org/publicdomain/zero/1.0/

以下人员已根据知识共享公共域专用1.0通用帮助开发或贡献他们对项目和公共领域的更改:

  • Alexandr Sokolovskiy

参见

官方BLAKE2网站:https://blake2.net