script (⏱,💰)

script (⏱,💰)

NG#9 - 多重簽署帳戶

關於合約帳戶有 3 個子任務,共同組成了 bad account 系列。本文是系列的第二個任務 Bendy Signatures。

分析

題目裡有 2 個合約,sphinx 是抽象帳戶合約,由帳戶去調用 gates 合約的 open 方法。sphinx 有很多限制,給了 3 個公鑰,其中第 1 個有私鑰。題目模擬的是 2/3 多簽問題,需要傳入 2 組簽名都認證成功才能發送驗證交易。

在帳戶合約的__validate__中使用了starknet::get_tx_info().unbox()拿到了交易的簽名,需要我們構造長度為 6 的 raw_sig,其中 raw_sig [0] 和 raw_sig [3] 是公鑰,raw_sig [1] 和 raw_sig [4] 是簽名的 r,raw_sig [2] 和 raw_sig [5] 是 簽名的 s。需要公鑰是預設的 3 個公鑰之一,且 r 和 s 不能重複。同時 __execute__ 中限制了calls.len() == 1,所以不能用唯一的私鑰生成 2 筆交易去解題。

由於只有 1 個私鑰,需要用這個私鑰簽名生成兩組 r 和 s 都能通過驗證且不重複。問題變成了如何用 SDK,發送一筆交易,但是有 2 組有效簽名。

過程

我首先嘗試在 Starknet.js 中用 2 個不同的 maxFee 生成了兩組 open 交易並分別簽名,發現無法被測試網驗證。

詢問導師後,發現這題實際要破解的問題是 ECDSA malleability 問題,具體可參考ZK book

我花了一段時間學習 ECDSA,其中涉及了很多數學知識。
如果你不知道 ECDSA 原理,建議也進行深入學習。上面的 ZK Book 內容很棒。

StarkNet 也是使用了 ECDSA 來生成簽名,ChatGPT 輸出的 ECDSA 簽名流程是:

1. 首先,需要一個私鑰,這是一個隨機選擇的整數。同時,還需要一個公鑰,這是私鑰和基點G的乘積,記作 Q = dG,其中 d 是私鑰,G 是基點。
2. 當你要簽名一個消息時,首先將消息通過哈希函數轉化為一個整數,記作 z。
3. 然後,選擇一個隨機整數 k,並計算點 R = kG。R 的x坐標就是簽名的一部分,記作 r。
4. 接著,計算 s = (z + r * d) / k。這個 s 就是簽名的另一部分。
最終簽名就是 (r, s)。

對應每一步在 Starknet.js 中是:

  1. ec.starkCurve.getStarkKey(privateKey)獲取公鑰,公鑰是已知的,可以跳過這步
  2. hash.calculateTransactionHash把交易詳情轉換為一個 messageHash。交易需要的 6 個參數需要自己設置,其中 calldata 用 transaction.getExecuteCalldata獲得
  3. r 是一個隨機數,不用自己設置,下一步可直接獲取
  4. 通過 ec.starkCurve.sign(msgHash, privateKey) 或者 signer.signTransaction來得到 r 和 s。

通過上面 ECDSA malleability 的參考資料可知由於對稱性,1 個 r 對應了 2 個 s 都可以使簽名有效。第二個 s 的計算很簡單,所需的另一個參數可在 SDK 的源碼中找到。最終生成的 r1 == r2, s1 != s2。

另外一個方法是用不同的 seed 生成兩個 r,python 庫 裡有對應的方法。最終生成的 r1 != r2, s1 != s2。

按題目要求構造一個長度為 6 的列表,傳入account.invokeFunction的 signature 中,填上其他所需參數,發送即可完成任務。

總結

NG9 end

這題一方面介紹了多簽錢包合約是如何工作的,另一方面涉及 ECDSA malleability 問題,還需要了解 transaction 的構成,如果能完成會很有收獲。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。