本文將探討 Poseidon 雜湊函數在 Iden3 密碼學函式庫中存在的延展性問題。
作者: Johan,慢霧安全團隊
編輯: Liz
背景
先前我們在雜湊函數的隱藏危險:長度擴展攻擊與服務端驗證的安全隱患一文中探討過雜湊函數的長度擴展攻擊,指出了 md5/sha1/sha2 演算法存在的延展性問題和可能帶來的安全風險。
近日,有開發者在 X 上揭露了存在於 Iden3 密碼學庫的一個哈希延展性問題。

慢霧安全團隊對此問題展開了深入研究。
什麼是 Poseidon
Poseidon 是一種專為零知識證明系統最佳化設計的密碼學雜湊函數。它具有以下主要特點:
- 在有限域上運行,特別適合基於算術電路的零知識證明系統
- 運算效率高,gas 消耗低
- 主要用於 Merkle 樹建置與資料承諾方案
- 廣泛應用於 zkRollup、身份驗證等區塊鏈應用場景
例如在目前流行的 Snark 程式語言 Circom 中,就廣泛使用了 Poseidon 演算法。
Poseidon 的延展性
Poseidon 雜湊本身不具有延展性或長度擴展攻擊 (Length Extension Attack) 問題,這得益於 Poseidon 海綿構造的填充方式 (Sponge Padding)[1]:
- 先在訊息字串後加入單一元素 1
- 然後根據需要添加足夠的 0 元素
- 直到訊息長度是 (tc) 的整數倍
- 其中 t 是狀態寬度,c 是容量
與具有延展性的哈希對比(如 SHA256):
SHA256(具有延展性):message -> [ 原始消息 | 填充长度] -> 迭代压缩函数 -> hash 值攻击者可以继续使用最终状态继续添加数据
Poseidon(不具有延展性):message -> [ 固定 t-1 长度] -> 置换函数 -> hash 值无法从 hash 值恢复内部状态
但 Iden3 實現的 Poseidon 函式庫與標準演算法卻有一些區別,主要體現在它的資料填充方案不同,也導致了它出現延展性問題。
讓我們來分析一下它的程式碼實作 [2]:
// HashBytes returns a sponge hash of a msg byte slice split into blocks of 31 bytesfunc HashBytes(msg []byte) (*big.Int, error) { // not used inputs default to zero inputs := make([]*big.Int, spongeInputs) for j := 0; j < spongeInputs; j++ { inputs[j] = new(big.Int) } dirty := false var hash *big.Int var err error
k := 0 for i := 0; i < len(msg)/spongeChunkSize; i++ { dirty = true inputs[k].SetBytes(msg[spongeChunkSize*i : spongeChunkSize*(i+1)]) if k == spongeInputs-1 { hash, err = Hash(inputs) dirty = false if err != nil { return nil, err } inputs = make([]*big.Int, spongeInputs) inputs[0] = hash for j := 1; j < spongeInputs; j++ { inputs[j] = new(big.Int) } k = 1 } else { k++ } }
if len(msg)%spongeChunkSize != 0 { // the last chunk of the message is less than 31 bytes // zero padding it, so that 0xdeadbeaf becomes // 0xdeadbeaf000000000000000000000000000000000000000000000000000000 var buf [spongeChunkSize]byte copy(buf[:], msg[(len(msg)/spongeChunkSize)*spongeChunkSize:]) inputs[k] = new(big.Int).SetBytes(buf[:]) dirty = true }
if dirty { // we haven't hashed something in the main sponge loop and need to do hash here hash, err = Hash(inputs) if err != nil { return nil, err } }
return hash, nil}
HashBytes 函數用於對給定的位元組切片 msg 進行海綿雜湊 (sponge hash),並將其分割成 31 位元組的區塊。首先,函數初始化了一個 inputs 切片,長度為 spongeInputs,並將每個元素設定為新的大整數 big.Int。這些未使用的輸入預設為零。
假設有一個訊息需要填充:
标准 Poseidon: [ 消息] + [1] + [0,0,0...]Iden3 实现: [ 消息] + [0,0,0...]
這種填充方案的差異看似微小,但在密碼學中是很關鍵的。標準 Poseidon 透過添加 1 再補 0 的方式可以確保不同長度的輸入訊息會產生不同的雜湊值,而純補 0 的方式卻會導致安全隱患。
漏洞危害
Iden3 Poseidon 的延展性有哪些具體的危害,讓我們寫一個範例來示範:
msg1 := []byte("9")hash1, _ := poseidon.HashBytes(msg1)fmt.Printf("Hash of 1: %s\n", hash1.String())fmt.Println("Value: ", new(big.Int).SetBytes(msg1))
msg2 := []byte("9\x00\x00\x00\x00")hash2, _ := poseidon.HashBytes(msg2)fmt.Printf("Hash of 9\\x00\\x00\\x00\\x00: %s\n", hash2.String())fmt.Println("Value: ", new(big.Int).SetBytes(msg2))
運行輸出:
Hash of 9: 11642804437010365980265264676069673149904017141487814048230421306886008365708Value: 57Hash of 9\x00\x00\x00\x00: 11642804437010365980265264676069673149904017141487814048230421306886008365708Value: 244813135872
基於程式碼範例的結果,我們可以看到這個漏洞可能帶來以下危害:
- 雜湊碰撞:利用延展性漏洞構造的不同輸入值(57 和 244813135872)產生了相同的雜湊值,這可能導致在零知識證明系統中出現驗證繞過
- 資料完整性問題:由於 Iden3 的實作採用了純補 0 的填充方案,攻擊者可能透過建構特定的輸入來產生碰撞,從而破壞系統的安全性驗證
這在實際應用中可能會影響:
- zkRollup、身份驗證等依賴 Poseidon 哈希的區塊鏈應用
- 使用 Circom 編寫的智能合約
總結
本文探討了 Poseidon 雜湊函數在 Iden3 密碼學函式庫中存在的延展性問題。 Poseidon 是一種為零知識證明系統優化的雜湊函數,雖然標準 Poseidon 不具有延展性,但 Iden3 的實現由於採用了純補 0 的填充方案而非標準的填充方式,導致可能出現哈希碰撞問題,這對依賴 Poseidon 哈希的 zkRollup、身份驗證等區塊鏈應用以及使用 Circom 編寫的智能合約都可能造成安全隱患。
參考資料[1] https://eprint.iacr.org/2019/458.pdf[2] https://github.com/iden3/go-iden3-crypto/blob/master/poseidon/poseidon.go
免責聲明:作為區塊鏈資訊平台,本站所發布文章僅代表作者及來賓個人觀點,與 Web3Caff 立場無關。文章內的資訊僅供參考,均不構成任何投資建議及要約,並請您遵守所在國家或地區的相關法律法規。