"hashlib" --- 安全雜湊與訊息摘要
********************************

**原始碼：**Lib/hashlib.py

======================================================================

該模組實作了許多不同安全雜湊和訊息摘要演算法的通用介面，其中包括 FIPS
安全雜湊演算法 SHA1、SHA224、SHA256、SHA384、SHA512（定義於 FIPS 180-4
標準）、SHA-3 系列（定義於 FIPS 202 標準）以及 RSA 的 MD5 演算法（定義
於網際網路 **RFC 1321**）。「安全雜湊 (secure hash)」和「訊息摘要
(message digest)」這兩個術語是可以互換的。較舊的演算法稱為訊息摘要、現
代則較常稱之安全雜湊。

備註:

  如果你需要 adler32 或 crc32 雜湊函式，可以在 "zlib" 模組中找到它們。


雜湊演算法
==========

每種種類的 *hash* 都有一個以其命名的建構函式方法。全部都會回傳具有相同
簡單介面的雜湊物件。例如：可使用 "sha256()" 來建立 SHA-256 雜湊物件。
現在你可以使用 "update" 方法向此物件提供*類位元組物件 (bytes-like
objects)*（通常是 "bytes"）。在任何時候，你都可以使用 "digest()" 或
"hexdigest()" 方法來要求它提供迄今為止傳遞給它的資料串聯的*摘要
(digest)*。

為了允許多執行緒 (multithreading)，Python *GIL* 被釋放，同時在其建構函
式或 ".update" 方法中計算一次提供超過 2047 位元組資料的雜湊值。

此模組中始終存在的雜湊演算法之建構函式有 "sha1()"、"sha224()"、
"sha256()"、"sha384()"、"sha512()"、"sha3_224()"、"sha3_256()"、
"sha3_384()"、"sha3_512()"、"shake_128()"、"shake_256()"、"blake2b()"
和 "blake2s()"。"md5()" 通常也可用，但如果你使用罕見的「符合 FIPS
(FIPS compliant)」的 Python 建置版本，它可能不存在或者不被允許使用。以
上會對應到 "algorithms_guaranteed"。

如果你的 Python 發行版的 "hashlib" 與提供其他演算法的 OpenSSL 版本鏈結
，也可能有其他演算法可用。其他則*不保證可用*於在所有安裝上，並且只能以
名稱來透過 "new()" 存取。請參閱 "algorithms_available"。

警告:

  某些演算法具有已知的雜湊碰撞 (hash collision) 弱點（包括 MD5 和 SHA1
  ）。請參閱 Attacks on cryptographic hash algorithms 和本文件後面的也
  參考部分。

在 3.6 版被加入: 新增了 SHA3 (Keccak) 和 SHAKE 建構函式 "sha3_224()"、
"sha3_256()"、"sha3_384()"、"sha3_512()"、"shake_128()"、"shake_256()"
。也新增了 "blake2b()" 和 "blake2s()"。

在 3.9 版的變更: 所有 hashlib 建構函式都採用一個僅限關鍵字引數
(keyword-only argument) *usedforsecurity*，其預設值為 "True"。False 值
則會允許在受限環境中使用不安全 (insecure) 和阻塞的 (blocked) 雜湊演算
法。"False" 表示雜湊演算法未在安全情境中使用，例如作為一種非加密用途的
單向壓縮函式。

在 3.9 版的變更: Hashlib 現在使用 OpenSSL 中的 SHA3 和 SHAKE（如果有提
供的話）。

在 3.12 版的變更: 對於鏈結之 OpenSSL 未提供的任何 MD5、SHA1、SHA2 或
SHA3 演算法，我們會回退使用 HACL* 專案中經過驗證的實作。


用法
====

獲取位元組字串 "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.hexdigest()
   '031edd7d41651593c5fe5c006fa5752b37fddff7bc4e843aa6af0c950f4b9406'

更濃縮：

>>> hashlib.sha256(b"Nobody inspects the spammish repetition").hexdigest()
'031edd7d41651593c5fe5c006fa5752b37fddff7bc4e843aa6af0c950f4b9406'


建構函式
========

hashlib.new(name, [data, ]*, usedforsecurity=True)

   是一個通用建構函式，它將目標演算法的字串 *name* 作為其第一個參數。
   它還允許使用者取得上面列出的雜湊值以及 OpenSSL 函式庫可能提供的任何
   其他演算法。

使用 "new()" 和演算法名稱：

>>> h = hashlib.new('sha256')
>>> h.update(b"Nobody inspects the spammish repetition")
>>> h.hexdigest()
'031edd7d41651593c5fe5c006fa5752b37fddff7bc4e843aa6af0c950f4b9406'

hashlib.md5([data, ]*, usedforsecurity=True)

hashlib.sha1([data, ]*, usedforsecurity=True)

hashlib.sha224([data, ]*, usedforsecurity=True)

hashlib.sha256([data, ]*, usedforsecurity=True)

hashlib.sha384([data, ]*, usedforsecurity=True)

hashlib.sha512([data, ]*, usedforsecurity=True)

hashlib.sha3_224([data, ]*, usedforsecurity=True)

hashlib.sha3_256([data, ]*, usedforsecurity=True)

hashlib.sha3_384([data, ]*, usedforsecurity=True)

hashlib.sha3_512([data, ]*, usedforsecurity=True)

諸如此類的附名建構函式比將演算法名稱傳遞給 "new()" 更快。


屬性
====

Hashlib 提供以下常數模組屬性：

hashlib.algorithms_guaranteed

   包含所有平台上該模組保證支援的雜湊演算法名稱的集合。請注意，'md5'
   有出現在此列表中，儘管有一些上游供應商提供了奇怪的「符合 FIPS (FIPS
   compliant)」的 Python 建置，並將其排除在外。

   在 3.2 版被加入.

hashlib.algorithms_available

   包含正在運行的 Python 直譯器中可用的雜湊演算法名稱的集合。這些名稱
   在傳遞給 "new()" 時將被識別。"algorithms_guaranteed" 都會是它的一個
   子集。相同的演算法可能會以不同的名稱多次出現在該集合中（多虧了
   OpenSSL）。

   在 3.2 版被加入.


雜湊物件
========

以下的值皆為建構函式回傳之雜湊物件的常數屬性：

hash.digest_size

   生成雜湊的大小（以位元組為單位）。

hash.block_size

   雜湊演算法的內部區塊大小（以位元組為單位）。

雜湊物件具有以下屬性：

hash.name

   該雜湊的規範名稱，都會是小寫，且都會適合作為 "new()" 的參數以建立此
   型別的另一個雜湊。

   在 3.4 版的變更: name 屬性自 CPython 誕生以來就存在於其中，但直到
   Python 3.4 才正式確立，因此在某些平台上可能不存在。

雜湊物件具有以下方法：

hash.update(data)

   使用*類位元組物件*來更新雜湊物件。重複呼叫相當於連接所有引數的單一
   呼叫："m.update(a); m.update(b)" 等價於 "m.update(a+b)"。

hash.digest()

   回傳目前有傳遞給 "update()" 方法的資料之摘要。這是一個大小為
   "digest_size" 的位元組物件，它可能包含從 0 到 255 的整個範圍內的位
   元組。

hash.hexdigest()

   與 "digest()" 類似，只不過摘要會是作為雙倍長度的字串物件回傳，僅包
   含十六進位數字。這可用於在電子郵件或其他非二進位環境中安全地交換值
   。

hash.copy()

   回傳雜湊物件的副本（「複製 (clone)」），這可用於高效率地計算出共享
   同一初始子字串的資料之摘要。


SHAKE 可變長度摘要
==================

hashlib.shake_128([data, ]*, usedforsecurity=True)

hashlib.shake_256([data, ]*, usedforsecurity=True)

"shake_128()" 和 "shake_256()" 演算法提供了可變長度摘要，其
length_in_bits//2 最高為 128 或 256 位元安全性。因此，他們的摘要方法會
需要長度。最大長度不受 SHAKE 演算法限制。

shake.digest(length)

   回傳目前有傳遞給 "update()" 方法的資料之摘要。這是一個大小為
   *length* 的位元組物件，可能包含從 0 到 255 的整個範圍內的位元組。

shake.hexdigest(length)

   與 "digest()" 類似，只不過摘要作為雙倍長度的字串物件回傳，僅包含十
   六進位數字。這可用於交換電子郵件或其他非二進位環境中的值。

範例：

>>> h = hashlib.shake_256(b'Nobody inspects the spammish repetition')
>>> h.hexdigest(20)
'44709d6fcb83d92a76dcb0b668c98e1b1d3dafe7'


檔案雜湊
========

hashlib 模組提供了一個輔助函式，用於對檔案或類檔案物件 (file-like
object) 進行有效率的雜湊。

hashlib.file_digest(fileobj, digest, /)

   回傳已用檔案物件內容更新的摘要物件。

   *fileobj* 必須是以二進位模式讀取的類檔案物件。它接受來自內建
   "open()" 的檔案物件、"BytesIO" 實例、來自
   "socket.socket.makefile()" 的 SocketIO 物件等。該函式可以繞過
   Python 的 I/O 並直接使用 "fileno()" 中的檔案描述器 (file
   descriptor)。在此函式回傳或引發後，必須假定 *fileobj* 處於未知狀態
   (unknown state)。由呼叫者決定是否關閉 *fileobj*。

   *digest* 必須是名稱作為 *str* 的雜湊演算法、雜湊建構函式或會回傳雜
   湊物件的可呼叫函式。

   範例：

   >>> import io, hashlib, hmac
   >>> with open(hashlib.__file__, "rb") as f:
   ...     digest = hashlib.file_digest(f, "sha256")
   ...
   >>> digest.hexdigest()
   '...'

   >>> buf = io.BytesIO(b"somedata")
   >>> mac1 = hmac.HMAC(b"key", digestmod=hashlib.sha512)
   >>> digest = hashlib.file_digest(buf, lambda: mac1)

   >>> digest is mac1
   True
   >>> mac2 = hmac.HMAC(b"key", b"somedata", digestmod=hashlib.sha512)
   >>> mac1.digest() == mac2.digest()
   True

   在 3.11 版被加入.


密鑰的生成
==========

密鑰生成 (key derivation) 和密鑰延伸 (key stretching) 演算法專為安全密
碼雜湊而設計。像是 "sha1(password)" 這樣過於單純的演算法無法抵抗暴力攻
擊。一個好的密碼雜湊函式必須是可調校的 (tunable)、緩慢的，並且有包含
salt（鹽）。

hashlib.pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None)

   該函式提供基於密碼的 PKCS#5 密鑰生成函式 2。它使用 HMAC 作為偽隨機
   函式 (pseudorandom function)。

   字串 *hash_name* 是 HMAC 的雜湊摘要演算法所需的名稱，例如 "sha1" 或
   "sha256"。*password* 和 *salt* 被直譯為位元組緩衝區。應用程式和函式
   庫應為 *password* 設下合理的長度限制（例如 1024）。*salt* 應該是來
   自適當來源（例如 "os.urandom()"）且大約 16 或更多位元組。

   應根據雜湊演算法和計算能力選擇 *iterations* 次數。截至 2022 年，建
   議進行數十萬次 SHA-256 疊代。有關為什麼以及如何選擇最適合你的應用程
   式的基本原理，請閱讀 NIST-SP-800-132 的 *Appendix A.2.2*。
   stackexchange pbkdf2 疊代問題上的答案有詳細解釋。

   *dklen* 是生成的密鑰長度。如果 *dklen* 為 "None"，則會使用雜湊演算
   法 *hash_name* 的摘要大小，例如 SHA-512 為 64。

   >>> from hashlib import pbkdf2_hmac
   >>> our_app_iters = 500_000  # Application specific, read above.
   >>> dk = pbkdf2_hmac('sha256', b'password', b'bad salt' * 2, our_app_iters)
   >>> dk.hex()
   '15530bba69924174860db778f2c6f8104d3aaf9d26241840c8c4a641c8d000a9'

   僅當有使用 OpenSSL 編譯 Python 時該函式才可用。

   在 3.4 版被加入.

   在 3.12 版的變更: 該函式現在僅在有使用 OpenSSL 建置 Python 時可用。
   緩慢的純 Python 實作已被刪除。

hashlib.scrypt(password, *, salt, n, r, p, maxmem=0, dklen=64)

   該函式提供（如 **RFC 7914** 中所定義的）scrypt 基於密碼的密鑰衍生函
   式。

   *password* 和 *salt* 必須是*類位元組物件*。應用程式和函式庫應為
   *password* 設下合理的長度限制（例如 1024）。*salt* 應該是來自適當來
   源（例如 "os.urandom()"）且大約 16 或更多位元組。

   *n* 是 CPU/記憶體開銷係數、*r* 是區塊大小、*p* 為平行化係數、
   *maxmem* 為記憶體限制（OpenSSL 1.1.0 預設為 32 MiB）。*dklen* 是生
   成密鑰的長度。

   在 3.6 版被加入.


BLAKE2
======

BLAKE2 是在 **RFC 7693** 中定義的加密雜湊函式，有兩種類型：

* **BLAKE2b**，針對 64 位元平台進行了最佳化，可生成 1 到 64 位元組之間
  任意大小的摘要，

* **BLAKE2s**，針對 8 至 32 位元平台進行了最佳化，可生成 1 至 32 位元
  組之間任意大小的摘要。

BLAKE2 支援**密鑰模式 (keyed mode)** （更快、更簡單的 HMAC 替代品）、
**加鹽雜湊 (salted 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, 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)

這些函式回傳相應的雜湊物件以計算 BLAKE2b 或 BLAKE2s。他們可以選擇採用
這些通用參數：

* *data*：要雜湊的初始資料塊 (data chunk)，它必須是*類位元組物件*。它
  只能作為位置引數傳遞。

* *digest_size*：輸出摘要的大小（以位元組為單位）。

* *key*：用於密鑰雜湊的密鑰（BLAKE2b 最多 64 位元組、BLAKE2s 最多 32
  位元組）。

* *salt*：用於隨機雜湊的鹽（BLAKE2b 最多 16 個位元組、BLAKE2s 最多 8
  個位元組）。

* *person*：個人化字串（BLAKE2b 最多 16 個位元組、BLAKE2s 最多 8 個位
  元組）。

下表顯示了一般參數的限制（以位元組為單位）：

+---------+-------------+----------+-----------+-------------+
| 雜湊    | digest_size | len(key) | len(salt) | len(person) |
|=========|=============|==========|===========|=============|
| BLAKE2b | 64          | 64       | 16        | 16          |
+---------+-------------+----------+-----------+-------------+
| BLAKE2s | 32          | 32       | 8         | 8           |
+---------+-------------+----------+-----------+-------------+

備註:

  BLAKE2 規範定義了鹽和個人化參數的恆定長度，但為了方便起見，此實作接
  受任意大小的位元組字串，最多可達指定長度。如果參數的長度小於指定的長
  度，則用零補滿，所以像是 "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*：節點偏移量（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'


密鑰雜湊 (Keyed hashing)
~~~~~~~~~~~~~~~~~~~~~~~~

密鑰雜湊可用於身份驗證，作為基於雜湊的訊息驗證碼 (Hash-based message
authentication code) (HMAC) 的更快、更簡單的替代方案。由於繼承自 BLAKE
的不可微特性 (indifferentiability property)，BLAKE2 可以安全地用於
prefix-MAC 模式。

此範例示範了如何使用密鑰 "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'

舉一個實際的例子，網頁應用程式可以對發送給使用者的 cookie 進行對稱簽名
(symmetrically sign)，然後驗證它們以確保它們沒有被篡改：

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


隨機雜湊 (Randomized hashing)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

透過設定 *salt* 參數，使用者可以向雜湊函式引入隨機化。隨機雜湊在防止針
對數位簽章中雜湊函式的碰撞攻擊 (collision attacks) 非常有用。

   隨機雜湊是為這樣的情況而設計的：一方（訊息準備者）生成全部或部分訊
   息並由另一方（訊息簽名者）簽名。如果訊息準備者能夠發現加密雜湊函式
   發生碰撞（collision，即兩條訊息產生相同的雜湊值），那麼他們可能會準
   備有意義的訊息版本，該版本將產生相同的雜湊值和數位簽章，但結果不同
   （例如，將 $1,000,000 轉入賬戶，而不是 $10）。加密雜湊函式的設計以
   抗碰撞性為主要目標，但當前對加密雜湊函式攻擊的關注可能會導致給定的
   加密雜湊函式所提供的抗碰撞性低於預期。隨機雜湊透過降低準備者在數位
   簽章生成過程中生成最終產生相同雜湊值的兩個或多個訊息的可能性，為簽
   名者提供額外的保護 —— 即便嘗試去找到雜湊函式碰撞的發生是實際可行的
   。然而，若訊息的所有部分都是由簽名者所準備好的，使用隨機雜湊可能會
   降低數位簽章提供的安全性。

   （NIST SP-800-106 「數位簽章的隨機雜湊 (Randomized Hashing for
   Digital Signatures)」）

在 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 雜湊函式系列，第 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-byte 內部摘要，並回傳 32-byte 最終摘要：

   >>> 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 密碼的核心演算法。

標準函式庫實作是基於 pyblake2 模組。它是由 *Dmitry Chestnykh* 在
*Samuel Neves* 的 C 版本實作為基礎所編寫的。該文件是由 *Dmitry
Chestnykh* 編寫並從 pyblake2 複製過來的。

*Christian Heimes* 為 Python 重寫了部分 C 程式碼。

以下公開領域貢獻適用於 C 雜湊函式實作、擴充程式碼和此文件：

   在法律允許的範圍內，作者已將該軟體的所有版權以及相關和鄰接權利奉獻
   給全球的公開領域。該軟體的發布沒有任何授權 (warranty)。

   你應會隨本軟體一起收到一份 CC0 公眾領域貢獻宣告 (CC0 Public Domain
   Dedication) 的副本。如果沒有，請參閱
   https://creativecommons.org/publicdomain/zero/1.0/ 。

以下人員根據創用 CC 通用公眾領域貢獻宣告 1.0 (Creative Commons Public
Domain Dedication 1.0 Universal) 於專案和公開領域做出了開發或貢獻：

* *Alexandr Sokolovskiy*

也參考:

  "hmac" 模組
     使用雜湊生成訊息驗證程式碼的模組。

  "base64" 模組
     另一種在非二進位環境中編碼二進位雜湊的方法。

  https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.180-4.pdf
     有關安全雜湊演算法的 FIPS 180-4 出版物。

  https://csrc.nist.gov/pubs/fips/202/final
     有關 SHA-3 標準的 FIPS 202 出版物。

  https://www.blake2.net/
     BLAKE2 官方網站。

  https://en.wikipedia.org/wiki/Cryptographic_hash_function
     包含有關哪些演算法存在已知問題以及這些問題對其使用意味著什麼資訊
     的維基百科文章。

  https://www.ietf.org/rfc/rfc8018.txt
     PKCS #5：基於密碼的加密規範版本 2.1

  https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication8
  00-132.pdf
     NIST（美國國家標準技術研究院）針對基於密碼的密鑰衍生的建議。
