如何計算 SHA-256 “中間狀態”?
最近我一直在嘗試實現一些與比特幣相關的程式碼,並且偶然發現了一個奇怪的概念,即 SHA-256 “中間狀態”。這裡給出了一些解釋。
一般概念是比特幣依賴於多次對 128 字節數據塊執行 SHA 散列,但只有該塊的後半部分發生變化。這就是使用“中間狀態”概念的原因。據我了解,為了對 128 字節的數據執行 SHA,需要將散列劃分為對數據的第一個和第二個 64 字節的操作。由於第一次散列的結果是恆定的,因此可以將它們保存為此“中間狀態”,並且在對更改的後半字節進行散列之前,將恢復“中間狀態”而不是再次計算它。
誰能解釋我將如何計算這個“中間狀態”,它有一個計算整個 SHA-256 雜湊的庫,或者哪些庫支持計算類似的東西?
SHA-256 使用內部壓縮功能 $ f $ 它接受兩個輸入,分別為 512 位和 256 位,輸出 256 位。散列的工作方式如下:
- 輸入資訊 $ M $ 首先通過附加 129 和 640 位(含)之間的值來填充,從而生成填充消息 $ M’ $ 其長度(以位為單位)是 512 的倍數。
- $ M’ $ 分為 $ n $ 512 位塊 $ M_1 $ , $ M_2 $ ,… $ M_n $ (每個塊的長度為 64字節)。
- 放 $ X_0 $ (一個 256 位值)到一個傳統的初始值(在SHA-256 標準,第 5.3.3 節中指定)。
- 通過計算按適當的順序處理每個塊 $ X_i = f(M_i, X_{i-1}) $ 對全部 $ i $ 從 $ 1 $ 到 $ n $ .
- 雜湊值為 $ X_n $ .
這 $ X_i $ 值可以看作是狀態變數的連續內容 $ X $ (事實上,當你計算出 $ X_i $ , 你可以丟棄 $ X_{i-1} $ 因為此後將不再使用該值)。
你的“中間狀態”是 $ X_1 $ :這是處理完第一個塊後“狀態”的內容。由於所有 128 字節消息都以完全相同的 64 字節標頭開頭,因此所有雜湊計算正式開始於處理相同的塊 $ M_1 $ , 同 $ X_0 $ (正常初始值),結果相同 $ X_1 = f(M_1, X_0) $ . 您想從該值“重新啟動”每個計算 $ X_1 $ 直接,以避免一次又一次地重新計算它。
在實現 SHA-256 的現有庫上執行此操作可能容易也可能不容易,具體取決於庫提供的設施。對於基本的 SHA-256 庫,有兩個可能的問題:
- 圖書館可能拒絕輸出 $ X_1 $ ,因為它堅持只在填充消息上計算雜湊值;你想要輸出 $ f $ 在給定的 $ M_1 $ 沒有填充的塊。
- 庫可能會拒絕開始一個新的計算 $ X_1 $ 您提供而不是傳統的初始值 $ X_0 $ .
一些圖書館提供更多。例如,考慮sphlib。使用該庫,有兩種方法可以實現您的目標:
- 該實現在表示目前狀態的上下文結構(類型)上工作。
sph_sha256_context
因此,您可以通過處理標頭開始計算,然後複製上下文以進行許多從該確切點開始的雜湊計算。它看起來像這樣:sph_sha256_context sc; sph_sha256_init(&sc); sph_sha256(&sc, header, 64); for (/* all 128-byte messages */) { sph_sha256_context sc2; unsigned char second_half[64]; unsigned char out[32]; /* set second_half[] to the second half of the 128-byte message */ sc2 = sc; sph_sha256(&sc2, second_half, 64); sph_sha256_close(&sc2, out); /* SHA-256 output is in out[] */ }
- sphlib 提供了一個
sph_sha256_comp()
完全實現壓縮功能的功能 $ f $ . 您可以使用它從任何狀態值開始“手動”逐塊計算 SHA-256 $ X $ 你希望。您必須處理編碼問題(SHA-256 始終是大端,使用sph_dec32be()
並sph_enc32be()
正確且可移植地執行此操作)和填充。如果所有消息的長度正好為 128 字節,則填充始終是一個完整的 64 字節塊,由一個值為 0x80 的字節組成,然後是 61 個值為 0x00 的字節,然後是兩個值為 0x04 0x00 的字節。用 16 個 32 位字表示,第一個字的數值為 0x80000000,然後是 14 個零值字,最後一個字的值為 0x00000400。對於繁重的雜湊運算(我明白這就是重點),您最好的選擇可能仍然是從開源實現中提取內部循環,並將其直接集成到您的程式碼中,以避免庫 API 的任何成本。您可能還希望避免繁重的編碼/解碼,並直接處理 32 位字(雖然 SHA-256 將位序列作為輸入,但它很快將其轉換為 32 位字序列並對其進行處理)。我邀請您根據標准進行自己的 SHA-256 實施:這並不難,該標準相當清晰,並且它將為您提供有關 SHA-256 如何優化您的程式碼的足夠知識(即使您結束重用其他一些實現的部分)。