COLDCARD 熵值失效事件:一次靜默的 RNG 軟體回退如何令用戶損失 3,800 萬美元

AbbieAbbie
/更新於 2026年8月3日
COLDCARD 熵值失效事件:一次靜默的 RNG 軟體回退如何令用戶損失 3,800 萬美元

重點總結

  • 在 COLDCARD Mk3 上產生並使用過助記詞的用戶:請立即在另一個錢包上重新產生助記詞,並將資金遷移到新錢包。
  • 在 COLDCARD Mk4 或 Mk5 上產生並使用過助記詞的用戶:若產生原始助記詞的韌體版本低於 5.6.0,請立即在另一個錢包上重新產生助記詞,並將資金遷移到新錢包。
  • 在 COLDCARD Q 上產生並使用過助記詞的用戶:若產生原始助記詞的韌體版本低於 1.5.0Q,請立即在另一個錢包上重新產生助記詞,並將資金遷移到新錢包。
  • OneKey 硬體錢包用戶不受此漏洞影響,可以安心繼續使用。

在密碼學裡,「隨機」是一個嚴苛的指標。它是可以被量化的東西,也是必須在真實算力面前站得住腳的東西。

一組 12 個單詞的助記詞,背後是一個 128 位元的隨機數,並依照 BIP-39 規範編碼成人類可讀的單詞。這個數字有多難以預測,直接決定了錢包能否抵禦暴力破解。128 位元代表 2¹²⁸ 種可能。把地球上所有算力加在一起,直到宇宙終結你也枚舉不完。

因此理論上,硬體錢包在這件事上應該極為嚴謹。必須有一個專用的真隨機數產生器(TRNG),從電路噪聲中擷取物理世界的隨機性,搭配一枚通過 EAL 認證的安全元件(SE),最後再用一套成熟的演算法將這一切充分攪拌在一起。

2026 年 7 月 30 日,約 594 BTC(約 3,800 萬美元)在不到半小時內,被從將近 500 個 COLDCARD 錢包中一掃而空。翌日,製造商 Coinkite 在《Technical Deep Dive into the Entropy Issue》[1] 中確認了根本原因:自 2021 年 3 月的一次韌體改動起,一個建置設定錯誤一直悄悄地令程式碼回退到 MicroPython 內建的軟體偽隨機數產生器(PRNG)。

後果因型號而異。Coinkite 的初步估算是,Mk3 只剩下約 40 位元的有效搜尋空間,而 Mk4、Mk5 與 Q 約為 72 位元,對比 128 位元的設計目標。40 位元大約是 1.1 兆種可能,聽起來仍是天文數字,但對於一個租得起 GPU 叢集的攻擊者而言,它已經從「物理上不可能」下降為一個在期望上可行的工程專案。從 128 到 40,搜尋空間縮小了 2⁸⁸ 倍,也就是 3,000 億兆倍。

這宗 3,800 萬美元的盜竊,把這個「期望」變成了既成事實。

註:OneKey 用戶的資金是安全的。OneKey 的程式碼完全不涉及此問題,也沒有使用任何相關的實作或依賴。完整聲明:https://x.com/OneKeyHQ/status/2083106588022509974?s=20

TL;DR

  • 在 COLDCARD Mk3 上產生並使用過助記詞的用戶:請立即在另一個錢包上重新產生助記詞,並將資金遷移到新錢包。
  • 在 COLDCARD Mk4 或 Mk5 上產生並使用過助記詞的用戶:若產生原始助記詞的韌體版本低於 5.6.0,請立即在另一個錢包上重新產生助記詞,並將資金遷移到新錢包。
  • 在 COLDCARD Q 上產生並使用過助記詞的用戶:若產生原始助記詞的韌體版本低於 1.5.0Q,請立即在另一個錢包上重新產生助記詞,並將資金遷移到新錢包。
  • OneKey 硬體錢包用戶不受此漏洞影響,可以安心繼續使用。

接下來是硬核的技術部分 👇

根本原因:一次靜默的軟體回退

從硬體 RNG 介面到 libNgU

2021 年的密碼學堆疊遷移,把錢包助記詞的產生方式從 ckcc.rng_bytes() 改成了 ngu.random.bytes() [6]:

ngu.random.bytes() → libNgU rng_get() → 它最終連結到的那個 rng_get 符號

風險不在函式名稱或輸出長度,而在於究竟是哪個目標檔(object file)實際提供了最後那個 rng_get()

#ifndef 只檢查有沒有定義,不檢查它的值

目標開發板設定把 MICROPY_HW_ENABLE_RNG 定義為 0,意即 MicroPython 的硬體 RNG 路徑並未啟用。

但當時 libNgU 使用的是 [8]:

#ifndef MICROPY_HW_ENABLE_RNG
#error ...
#endif

#ifndef 只測試巨集是否存在。一個被定義為 0 的巨集依然是「已定義」,所以那個本應攔截錯誤建置的 #error 從未觸發。

一個同名符號讓錯誤的實作溜進了最終韌體

當硬體 RNG 未啟用時,MicroPython 仍會以相同名稱提供一個 rng_get(),但那是 Yasmarang 軟體 PRNG 的回退版本 [9]。

這造就了完整的失效鏈:

巨集存在,但其值為 0 → libNgU 的建置防護不會觸發 → 韌體仍然成功編譯 → 同名的 rng_get 解析到 MicroPython 的回退版本 → 助記詞產生始終沒有收到它本應獲得的硬體 RNG 輸入

逐一來看,這條鏈上的每一環都只是一個小疏忽。串在一起,卻讓建置系統的把關形同虛設:一個安全的實作存在於原始碼或二進位檔中,並不代表那個攸關安全的呼叫真的會抵達它。

混合多個熵源,無法彌補缺失的熵

回退版本的初始狀態涉及部分 UID、SysTick、RTC 時間與次秒級狀態,之後 Yasmarang 以決定性方式推進它。libNgU 還會把它與另一條從固定初始狀態開始的 Yasmarang 資料流混合。

兩條可被重建或枚舉的決定性資料流,無論是互相 XOR 還是丟進 SHA-256,都不會因此獲得任何新的物理不可預測性。雜湊可以整理(condition)輸入,卻無法把一個有限的候選集合擴張成一個真正的 2^256 未知空間。

40 位元與 72 位元到底代表什麼

Coinkite 所用的有效搜尋空間估算,並不是實測、經認證的熵值。這兩個數字描述的是攻擊者必須逐一處理的候選狀態空間的大小。它們不是對最小熵(min-entropy)的量測,不會換算成平均猜測次數,更不會得出某個具體的攻擊時間或成本。

真實的攻擊成本取決於一連串前提條件:攻擊者是否知道或能猜出裝置 UID;能否縮小 RTC、SysTick 與開機時間的範圍;在助記詞產生前發生了多少次 RNG 呼叫;手上是否有 xpub、位址或公鑰可以離線驗證候選;以及執行 BIP-39/BIP-32 推導與並行硬體的成本。

所以準確的說法是:在 Coinkite 目前的初步攻擊假設下,Mk3 的有效搜尋空間估計約為 40 位元,Mk4/Q/Mk5 約為 72 位元。這並不等同於「每個 Mk3 錢包都恰好有 40 位元的熵」,也不等同於「較新的型號已被證明具有 72 位元的密碼學安全性」,更不等同於「任何一台普通電腦都能在固定時間內還原每一個錢包」。

為何 Mk4、Mk5 與 Q 也受影響

Mk5 與 Q 主機板上的兩枚安全元件(來源:coldcard.com)Mk5 與 Q 主機板上的兩枚安全元件(來源:coldcard.com)

Mk5 與 Q 主機板上的兩枚安全元件(來源:coldcard.com)

Mk4、Mk5 與 Q 的確會從兩枚安全元件擷取隨機材料,但修復前的實作並未把這些輸出全部餵進一個密碼學 DRBG。

公開原始碼顯示,在對安全元件材料做雜湊之後,它只取了四個位元組,傳給 ngu.random.reseed(),僅替換了 Yasmarang 的一個 32 位元狀態字(state word)。

因此,當回退狀態的其餘部分與呼叫軌跡都固定時:

安全元件 reseed 可區分的輸出資料流數量 ≤ 2³²

這並不與官方「約 72 位元」相矛盾。72 位元的模型同樣把 UID、計時器、RTC 與呼叫歷史等狀態中的不確定性計算在內。Block Engineering 給出的寬鬆組合上界落在 2^73.3 以下,而他們也明確表示,這並非對 73 位元密碼學安全性的認證 [10]。

換句話說:Mk3 是最壞情況,因為它根本沒有任何安全 reseed。Mk4/Q/Mk5 上的安全元件輸入降低了風險,但四個位元組的 reseed 無法還原設計原本要達成的 128 位元安全性。

官方公告與實際影響範圍

Coinkite 目前的公告從 Mk3 4.0.1 起算。但不可變的 v4.0.0 原始碼已經包含相關的熵值產生路徑 [7],而 4.0.0 與 4.0.1 之間並沒有對應的 RNG 修復。

Block Engineering 走得更遠,把 Mk2/Mk3 v4.0.0–v4.1.9 也納入其原始碼分析 [10]。

所以要分開看兩個層次:

  • Coinkite 的官方範圍:Mk3 4.0.1+;
  • 原始碼所支持的技術範圍:Mk2/Mk3 v4.0.0–v4.1.9。

你不能說 Coinkite 已官方確認 Mk2 或 v4.0.0。但你也不能因為公告沒有點名它們,就推斷它們是安全的。

修復是如何運作的

Mk4/Mk5 5.6.0 與 Q 1.5.0Q 的緊急修復,不只是改了一個條件式,還加入了建置層級的強制檢查 [3][4][5]:

  • 排除 MicroPython 的回退 RNG 物件;
  • 確保開發板層級的物件提供全域的 rng_get()
  • 使用 nm 檢查目標檔中的符號;
  • 若回退版本仍然匯出該符號,或開發板層級的物件未正確提供 rng_get(),就直接讓建置失敗。

用戶應該怎麼做

Mk4、Mk5 與 Q

  1. 先更新到 5.6.0 或更新版本(Mk4/Mk5),或 1.5.0Q 或更新版本(Q)。
  2. 在已修復的韌體上產生一組全新的助記詞。
  3. 記錄備份並完成一次還原驗證。
  4. 在裝置螢幕上核對錢包指紋與收款位址。
  5. 在遷移其餘資金之前,先發送一筆小額測試交易。
  6. 在每一筆轉帳都確認之前,保留舊備份,但停止再向舊錢包收款。

Mk3

目前 Mk3 尚無已釋出的修復韌體,4.1.9 也不是修復版本。用戶不應等待日後可能推出的韌體,因為任何新韌體都無法為既有的助記詞增添熵值。

請遵循 Coinkite 目前的遷移指引:在可信路徑上產生一組新助記詞,驗證備份、位址與一筆小額交易,然後遷移你的資產。

兩個不同的骰子門檻

骰子有兩個數字:50 和 99。它們服務於兩個不同的目標,不能互換使用。

在原始助記詞產生時額外加入 50 次以上的骰子。 若在助記詞首次產生時,混入了至少 50 次公平、獨立、私密、且從未被記錄、儲存或洩露的六面骰結果,Coinkite 表示,僅就這個 RNG 問題而言,它不認為該助記詞處於風險之中 [11]。

50 × log₂(6) ≈ 129.25 位元

這是針對此次特定事件的約 128 位元門檻,而不是對你整個操作流程的絕對安全證明。

在空白 Mk3 上的 Dice Roll Import 流程。 Coinkite 還提供了另一種流程:在執行 4.1.9 的空白 Mk3 上,選擇 Import Existing > Dice Rolls,並輸入至少 99 次公平的骰子結果。這條專用路徑直接消耗骰子序列,不會使用受影響的裝置產生器。

99 × log₂(6) ≈ 255.91 位元

99 是官方流程中的門檻;若從字面上的「至少 256 位元」標準反推,你會需要 100 次。

通行密碼是另一道獨立防線,而非修復手段

一個強壯、獨一無二、隨機且保密的 BIP-39 通行密碼(passphrase)會推導出一個不同的錢包,並能增加一個獨立的搜尋因子;裝置 PIN 碼只控制本地存取,完全不參與 BIP-39 根金鑰的推導 [12]。

但通行密碼無法彌補助記詞從一開始就不具備的熵。短通行密碼、名言佳句、可預測的模式與重複使用的密碼,仍然可能被猜中。一個打字錯誤也會產生一個看似有效、實則完全不同的錢包,所以你必須核對指紋,並保留一份你已測試過還原的可靠備份。

結語

這次事件暴露的核心問題是:一個攸關安全的實作,其實際可達性(reachability)從未由建置系統來強制保證。「有人把某個巨集寫錯了」遠遠不足以描述它。

硬體錢包與其他金鑰產生系統,至少應該:

  • 驗證一個安全 API 最終連結到的是哪個目標檔與哪個符號,而不只是檢查函式簽章;
  • 讓能力檢查同時驗證巨集的存在與其數值;
  • 讓攸關安全的回退「故障即關閉」(fail closed),絕不靜默地提供一個普通的 PRNG;
  • 讓 CI 檢查目標檔組成、連結映射(link map)、符號來源與端到端的資料流;
  • 透過測試證明真正的硬體熵抵達了最終的助記詞,而不只是檢查輸出長度、是否非零或是否無重複;
  • 撰寫能清楚區分「產生助記詞的版本、當前韌體、修復版本、以及舊助記詞該如何處理」的公告。

COLDCARD 的開源,正是外部研究者得以重建這條呼叫鏈的原因,而 Coinkite 也已發布正式的技術報告與修復版本。

宣稱一個品牌「絕對安全」毫無價值。真正有價值的,是把「隨機性從何而來、最終連結到什麼、在失效時流向何處、以及如何在出貨產品中驗證這一切」變成一個你可以持續反覆檢查的系統性質。

References

[1] Coinkite: Technical Deep Dive into the Entropy Issue [2] Coinkite: Mk3 Security Advisory [3] Coldcard 5.6.0 / 1.5.0Q fix commit [4] Coldcard 5.6.0 release commit [5] Coldcard 1.5.0Q release commit [6] 2021 libNgU seed generation migration commit [7] Official v4.0.0 source snapshot [8] libNgU random.c [9] MicroPython commit introducing the fallback [10] Block Engineering technical analysis [11] Coldcard dice roll math [12] Coldcard passphrase documentation [13] OneKey trezorcrypto.random source branch [14] OneKey seed generation logic [15] OneKey standard Makefile [16] OneKey SCons config

使用 OneKey 保護您的加密之旅

View details for 選購 OneKey選購 OneKey

選購 OneKey

全球最先進嘅硬件錢包。

View details for 下載應用程式下載應用程式

下載應用程式

只需電郵, 即可快速開始全球資產交易。

View details for OneKey SifuOneKey Sifu

OneKey Sifu

即刻諮詢,掃除疑慮。

繼續閱讀