hashlib --- 安全哈希與消息摘要?
源碼: Lib/hashlib.py
這個模塊針對不同的安全哈希和消息摘要算法實現了一個通用的接口。包括 FIPS 的 SHA1, SHA224, SHA256, SHA384, and SHA512 (定義于 FIPS 180-2) 算法,以及 RSA 的 MD5 算法( 定義于 Internet RFC 1321)。術語“安全哈希”和“消息摘要”是可互換的,較舊的算法被稱為消息摘要,現代術語是安全哈希。
注解
如果你想找到 adler32 或 crc32 哈希函數,它們在 zlib 模塊中。
警告
有些算法已知存在哈希碰撞弱點,請參考最后的“另請參閱”段。
哈希算法?
每種類型的 hash 都有一個構造器方法。 它們都返回一個具有相同的簡單接口的 hash 對象。 例如,使用 use sha256() 創建一個 SHA-256 hash 對象。 你可以使用 update() 方法向這個對象輸入 字節類對象 (通常是 bytes)。 在任何時候你都可以使用 digest() 或 hexdigest() 方法獲得到目前為止輸入這個對象的拼接數據的 digest。
注解
為了更好的多線程性能,在對象創建或者更新時,若數據大于2047字節則 Python 的 GIL 會被釋放。
注解
向 update() 輸入字符串對象是不被支持的,因為哈希基于字節而非字符。
此模塊中總是可用的哈希算法構造器有 sha1(), sha224(), sha256(), sha384(), sha512(), blake2b() 和 blake2s()。 md5() 通常也是可用的,但如果你在使用少見的 "FIPS 兼容" 的 Python 編譯版本則可能會找不到它。 此外還可能有一些附加的算法,具體取決于你的平臺上的 Python 所使用的 OpenSSL 庫。 在大部分平臺上可用的還有 sha3_224(), sha3_256(), sha3_384(), sha3_512(), shake_128(), shake_256() 等等。
3.6 新版功能: SHA3 (Keccak) 和 SHAKE 構造器 sha3_224(), sha3_256(), sha3_384(), sha3_512(), shake_128(), shake_256().
例如,如果想獲取字節串 b'Nobody inspects the spammish repetition' 的摘要:
>>> import hashlib
>>> m = hashlib.sha256()
>>> m.update(b"Nobody inspects")
>>> m.update(b" the spammish repetition")
>>> m.digest()
b'\x03\x1e\xdd}Ae\x15\x93\xc5\xfe\\\x00o\xa5u+7\xfd\xdf\xf7\xbcN\x84:\xa6\xaf\x0c\x95\x0fK\x94\x06'
>>> m.digest_size
32
>>> m.block_size
64
更簡要的寫法:
>>> hashlib.sha224(b"Nobody inspects the spammish repetition").hexdigest()
'a4337bc45a8fc544c03f52dc550cd6e1e87021bc896588bd79e901e2'
-
hashlib.new(name[, data])? 一個接受所希望的算法對應的字符串 name 作為第一個形參的通用構造器。 它還允許訪問上面列出的哈希算法以及你的 OpenSSL 庫可能提供的任何其他算法。 同名的構造器要比
new()更快所以應當優先使用。
使用 new() 并附帶由 OpenSSL 所提供了算法:
>>> h = hashlib.new('ripemd160')
>>> h.update(b"Nobody inspects the spammish repetition")
>>> h.hexdigest()
'cc4a5ce1b3df48aec5d22d1f16b894a0b894eccc'
Hashlib 提供下列常量屬性:
-
hashlib.algorithms_guaranteed? 一個集合,其中包含此模塊在所有平臺上都保證支持的哈希算法的名稱。 請注意 'md5' 也在此清單中,雖然某些上游廠商提供了一個怪異的排除了此算法的 "FIPS 兼容" Python 編譯版本。
3.2 新版功能.
-
hashlib.algorithms_available? 一個集合,其中包含在所運行的 Python 解釋器上可用的哈希算法的名稱。 將這些名稱傳給
new()時將可被識別。algorithms_guaranteed將總是它的一個子集。 同樣的算法在此集合中可能以不同的名稱出現多次(這是 OpenSSL 的原因)。3.2 新版功能.
下列值會以構造器所返回的哈希對象的常量屬性的形式被提供:
-
hash.digest_size? 以字節表示的結果哈希對象的大小。
-
hash.block_size? 以字節表示的哈希算法的內部塊大小。
hash 對象具有以下屬性:
-
hash.name? 此哈希對象的規范名稱,總是為小寫形式并且總是可以作為
new()的形參用來創建另一個此類型的哈希對象。在 3.4 版更改: 該屬性名稱自被引入起即存在于 CPython 中,但在 Python 3.4 之前并未正式指明,因此可能不存在于某些平臺上。
哈希對象具有下列方法:
-
hash.update(data)? 用 bytes-like object 來更新哈希對象。 重復調用相當于單次調用并傳入所有參數的拼接結果:
m.update(a); m.update(b)等價于m.update(a+b)。在 3.1 版更改: 當使用 OpenSSL 提供的哈希算法在大于 2047 字節的數據上執行哈希更新時 Python GIL 會被釋放以允許其他線程運行。
-
hash.digest()? 返回當前已傳給
update()方法的數據摘要。 這是一個大小為digest_size的字節串對象,字節串中可包含 0 至 255 的完整取值范圍。
-
hash.copy()? 返回哈希對象的副本(“克隆”)。 這可被用來高效地計算共享相同初始子串的數據的摘要。
SHAKE 可變長度摘要?
shake_128() 和 shake_256() 算法提供安全的 length_in_bits//2 至 128 或 256 位可變長度摘要。 為此,它們的摘要需指定一個長度。 SHAKE 算法不限制最大長度。
-
shake.digest(length)? 返回當前已傳給
update()方法的數據摘要。 這是一個大小為 length 的字節串對象,字節串中可包含 0 to 255 的完整取值范圍。
密鑰派生?
密鑰派生和密鑰延展算法被設計用于安全密碼哈希。 sha1(password) 這樣的簡單算法無法防御暴力攻擊。 好的密碼哈希函數必須可以微調、放慢步調,并且包含 加鹽。
-
hashlib.pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None)? 此函數提供 PKCS#5 基于密碼的密鑰派生函數 2。 它使用 HMAC 作為偽隨機函數。
字符串 hash_name 是要求用于 HMAC 的哈希摘要算法的名稱,例如 'sha1' 或 'sha256'。 password 和 salt 會以字節串緩沖區的形式被解析。 應用和庫應當將 password 限制在合理長度 (例如 1024)。 salt 應當為適當來源例如
os.urandom()的大約 16 個或更多的字節串數據。iterations 數值應當基于哈希算法和算力來選擇。 在 2013 年時,建議至少為 100,000 次 SHA-256 迭代。
dklen 為派生密鑰的長度。 如果 dklen 為
None則會使用哈希算法 hash_name 的摘要大小,例如 SHA-512 為 64。>>> import hashlib, binascii >>> dk = hashlib.pbkdf2_hmac('sha256', b'password', b'salt', 100000) >>> binascii.hexlify(dk) b'0394a2ede332c9a13eb82e9b24631604c31df978b4e2f0fbd2c549944f9d79a5'
3.4 新版功能.
注解
隨同 OpenSSL 提供了一個快速的 pbkdf2_hmac 實現。 Python 實現是使用
hmac的內聯版本。 它的速度大約要慢上三倍并且不會釋放 GIL。
-
hashlib.scrypt(password, *, salt, n, r, p, maxmem=0, dklen=64)? 此函數提供基于密碼加密的密鑰派生函數,其定義參見 RFC 7914。
password 和 salt 必須為 字節類對象。 應用和庫應當將 password 限制在合理長度 (例如 1024)。 salt 應當為適當來源例如
os.urandom()的大約 16 個或更多的字節串數據。n 為 CPU/內存開銷因子,r 為塊大小,p 為并行化因子,maxmem 為內存限制 (OpenSSL 1.1.0 默認為 32 MiB)。 dklen 為派生密鑰的長度。
可用性: OpenSSL 1.1+。
3.6 新版功能.
BLAKE2?
BLAKE2 是在 RFC 7693 中定義的加密哈希函數,它有兩種形式:
BLAKE2b,針對 64 位平臺進行優化,并會生成長度介于 1 和 64 字節之間任意大小的摘要。
BLAKE2s,針對 8 至 32 位平臺進行優化,并會生成長度介于 1 和 32 字節之間任意大小的摘要。
BLAKE2 支持 keyed mode (HMAC 的更快速更簡單的替代), salted hashing, personalization 和 tree hashing.
此模塊的哈希對象遵循標準庫 hashlib 對象的 API。
創建哈希對象?
新哈希對象可通過調用構造器函數來創建:
-
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: 要哈希的初始數據塊,它必須為 bytes-like object。 它只能作為位置參數傳入。
digest_size: 以字節數表示的輸出摘要大小。
key: 用于密鑰哈希的密鑰(對于 BLAKE2b 最多 64 字節,對于 BLAKE2s 最多 32 字節)。
salt: 用于隨機哈希的鹽值(對于 BLAKE2b 最長 16 字節,對于 BLAKE2s 最長 8 字節)。
person: 個性化字符串(對于 BLAKE2b 最長 16 字節,對于 BLAKE2s 最長 8 字節)。
下表顯示了常規參數的限制(以字節為單位):
Hash |
目標長度 |
長度(鍵) |
長度(鹽) |
長度(個人) |
|---|---|---|---|---|
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,對于 BLAKE2s 為 0 至 32,連續模式下則為 0)。
last_node: 一個布爾值,指明所處理的節點是否為最后一個 (連續模式下則為 False)。
請參閱 BLAKE2 規格描述 第 2.10 節了解有關樹形哈希的完整介紹。
常量?
-
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?
哈希函數可輸出的最大摘要長度。
例子?
簡單哈希?
要計算某個數據的哈希值,你應該首先通過調用適當的構造器函數 (blake2b() 或 blake2s()) 來構造一個哈希對象,然后通過在該對象上調用 update() 來更新目標數據,最后通過調用 digest() (或針對十六進制編碼字符串的 hexdigest()) 來獲取該對象的摘要。
>>> from hashlib import blake2b
>>> h = blake2b()
>>> h.update(b'Hello world')
>>> h.hexdigest()
'6ff843ba685842aa82031d3f53c48b66326df7639a63d128974c5c14f31a0f33343a8c65551134ed1ae0f2b0dd2bb495dc81039e3eeb0aa1bb0388bbeac29183'
作為快捷方式,你可以直接以位置參數的形式向構造器傳入第一個數據塊來直接更新:
>>> 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'
使用不同的摘要大小?
BLAKE2 具有可配置的摘要大小,對于 BLAKE2b 最多 64 字節,對于 BLAKE2s 最多 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
不同摘要大小的哈希對象具有完全不同的輸出(較短哈希值 并非 較長哈希值的前綴);即使輸出長度相同,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 所繼承的不可區分特性。
這個例子演示了如何使用密鑰 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 應用可為發送給用戶的 cookies 進行對稱簽名,并在之后對其進行驗證以確保它們沒有被篡改:
>>> 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'
隨機哈希?
用戶可通過設置 salt 形參來為哈希函數引入隨機化。 隨機哈希適用于防止對數字簽名中使用的哈希函數進行碰撞攻擊。
隨機哈希被設計用來處理當一方(消息準備者)要生成由另一方(消息簽名者)進行簽名的全部或部分消息的情況。 如果消息準備者能夠找到加密哈希函數的碰撞現象(即兩條消息產生相同的哈希值),則他們就可以準備將產生相同哈希值和數字簽名但卻具有不同結果的有意義的消息版本(例如向某個賬戶轉入 $1,000,000 而不是 $10)。 加密哈希函數的設計都是以防碰撞性能為其主要目標之一的,但是當前針對加密哈希函數的集中攻擊可能導致特定加密哈希函數所提供的防碰撞性能低于預期。 隨機哈希為簽名者提供了額外的保護,可以降低準備者在數字簽名生成過程中使得兩條或更多條消息最終產生相同哈希值的可能性 --- 即使為特定哈希函數找到碰撞現象是可行的。 但是,當消息的所有部分均由簽名者準備時,使用隨機哈希可能降低數字簽名所提供的安全性。
在 BLAKE2 中,鹽值會在初始化期間作為對哈希函數的一次性輸入而不是對每個壓縮函數的輸入來處理。
警告
使用 BLAKE2 或任何其他通用加密哈希函數例如 SHA-256 進行 加鹽哈希 (或純哈希) 并不適用于哈希密碼。 請參閱 BLAKE2 FAQ 了解更多信息。
>>> 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
個性化?
出于不同的目的強制讓哈希函數為相同的輸入生成不同的摘要有時也是有用的。 正如 Skein 哈希函數的作者所言:
我們建議所有應用設計者慎重考慮這種做法;我們已看到有許多協議在協議的某一部分中計算出來的哈希值在另一個完全不同的部分中也可以被使用,因為兩次哈希計算是針對類似或相關的數據進行的,這樣攻擊者可以強制應用為相同的輸入生成哈希值。 個性化協議中所使用的每個哈希函數將有效地阻止這種類型的攻擊。
(Skein 哈希函數族, p. 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=
樹形模式?
以下是對包含兩個葉子節點的最小樹進行哈希的例子:
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'
開發人員?
BLAKE2 是由 Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn 和 Christian Winnerlein 基于 Jean-Philippe Aumasson, Luca Henzen, Willi Meier 和 Raphael C.-W. Phan 所創造的 SHA-3 入圍方案 BLAKE 進行設計的。
它使用的核心算法來自由 Daniel J. Bernstein 所設計的 ChaCha 加密。
stdlib 實現是基于 pyblake2 模塊的。 它由 Dmitry Chestnykh 在 Samuel Neves 所編寫的 C 實現的基礎上編寫。 此文檔拷貝自 pyblake2 并由 Dmitry Chestnykh 撰寫。
C 代碼由 Christian Heimes 針對 Python 進行了部分的重寫。
以下公共領域貢獻同時適用于 C 哈希函數實現、擴展代碼和本文檔:
在法律許可的范圍內,作者已將此軟件的全部版權以及關聯和鄰接權利貢獻到全球公共領域。 此軟件的發布不附帶任何擔保。
你應該已收到此軟件附帶的 CC0 公共領域專屬證書的副本。 如果沒有,請參閱 https://creativecommons.org/publicdomain/zero/1.0/。
根據創意分享公共領域貢獻 1.0 通用規范,下列人士為此項目的開發提供了幫助或對公共領域的修改作出了貢獻:
Alexandr Sokolovskiy
參見
- 模塊
hmac 使用哈希運算來生成消息驗證代碼的模塊。
- 模塊
base64 針對非二進制環境對二進制哈希值進行編輯的另一種方式。
- https://blake2.net
BLAKE2 官方網站
- https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf
有關安全哈希算法的 FIPS 180-2 出版物。
- https://en.wikipedia.org/wiki/Cryptographic_hash_function#Cryptographic_hash_algorithms
包含關于哪些算法存在已知問題以及對其使用所造成的影響的信息的 Wikipedia 文章。
- https://www.ietf.org/rfc/rfc2898.txt
PKCS #5: 基于密碼的加密規范描述 2.0 版
