零知識證明 - zkSNARK應用的Nullifier Hash攻擊

買賣虛擬貨幣
早上很多朋友@我,安比實驗室發表了一篇文章zkSNARK的“輸入假名”的攻擊。迅速看了看,很贊。這個攻擊原理其實比較簡單,但是,不深入理解zkSNARK以及使用場景的朋友確實很難發現和理解。本文講講我對這個攻擊的分析和理解。源於三天前

這種攻擊方式一直潛伏著,沒被發現。直到三天前:

俄羅斯開發者poma,在專案semaphore提交了一個issue,公開了這個攻擊方法。poma同時也在kovan測試網路驗證了這種攻擊方式。

先從semaphore專案的程式碼開始:

話說,semaphore是個很有意思的專案,它提供了一套方法能讓使用者不暴露自己身份的情況下廣播訊息。暫不深入介紹這個專案的內容,直接看函式。broadcastSignal函式提交了證明,某個使用者傳送某個訊息(signal)。只有具體的某個broadcaster(server)才會呼叫這個函式。

signal - 廣播的訊息內容

a/b/c以及input - 是zkSNARK的證明以及公開資訊(statement資訊)

函式實現(第82行),呼叫verifyProof函式驗證證明以及statement資訊是否是合理的證明。第83行,檢視nullifier_hash是否被用過。

什麼是Nullifier?

熟悉ZCash的朋友,估計對Nullifier比較熟悉。

為了保護交易的隱私,在鏈上只儲存Note和Nullifier對應的hash資訊。Note代表可以花的錢,Nullifier表示某筆錢已經消費。Note和Nullifier一一對應,一個Note只存在一個Nullifier。透過zkSNARK生成證明,證明Note和Nullifier的正確性以及存在某種聯絡。在鏈上,為了防止雙花,在執行某個交易時,必須確定某個Note是否已經消費。確定的方法就是記錄下Nullifier對應的hash資訊。

verifyProof的計算

verifyProof的函式實現在snarkjs專案的templates/verifier_groth.sol(以Groth26為例)。verifyProof只是個簡單的wrapper,具體計算的實現時verify函式。

verify就是驗證Groth26的驗證等式是否成立,再看Groth26的驗證等式:

其中橙色部分就是input,藍色部分就是vk.IC。scalar_mul是橢圓曲線的“標量乘法”計算。vk.IC是橢圓曲線上的一個點(假設為P),input是個標量(假設x)。scalar_mul(P, x) 表示為xP。如果橢圓曲線的階為q的話,下面的等式成立:

(x+q)P = xP + qP = xP

也就是說,x+p和x作為input的話,scalar_mul的計算結果相等。也就是說,Groth26的等式依然成立。

如何攻擊?

在智慧合約中,輸入input是用uint表示。以太坊上一般採用bn254的曲線,q為:

21888242871839275222246405745257275088548364400416034343698204186575808495617。雖然這個q比較大,但是,uint的最大值還是比q大不少。

攻擊方法就形成了:

在一個使用者提交了證明以及公開的input資訊後,攻擊者修改input中的nullifier_hash即可。雖然邏輯上在花費同一筆錢,但是,智慧合約卻認為是花費不同的費用。智慧合約認為一個Note只能對應一個Nullfier,事實上,在這樣的情況下,一個Note對應了多個Nullfier。

如何修復?

修復的方式有好多種,目前最簡單的修復方式是在verify函式加限制:

限制input不能超過q。

總結:

三天前,俄羅斯開發者poma公開了zkSNARK應用模型下的一種攻擊方式。攻擊不需要重新生成證明資訊,只需要修改Statement中Nullfier對應的hash資料。原理是,橢圓曲線是個迴圈群,scalarMul計算在輸入的標量加上橢圓曲線的階的情況下,結果相等。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;