安全審查
發布之前,BitTool Signer 1.0.0 的原始碼在每個平台上都接受了兩次 AI 輔助的安全審查,各次審查分別獨立進行。四份報告均在下方全文公開。沒有一次發現嚴重或高風險問題。iPhone 版的兩次審查各發現一項低風險問題;Android 版的審查發現了中風險問題,其中有四項中風險等級的發現事項在已發布的 Android 1.0.0 中尚未解決,而且其中三項位於兩款 App 共用的簽名引擎中。
- 這些都不是資安公司進行的稽核,也都沒有人類稽核員參與。
- Opus 5 與 Opus 5.5 審查出自同一模型家族,受審程式碼中相當一部分也由它撰寫或修改。報告指出,不得將其作為第三方證明或認證。
- GPT-6 Astra 的審查說明,它們是為專案擁有者撰寫的 AI 輔助工程審查,而非稽核機構出具的認證,也不是人工簽核。
- 兩份 Android 報告都建議,在用這款 App 保護大額資產之前,先修正尚未解決的中風險發現事項。 BitTool Signer Android 1.0.0 發布時這些問題仍未解決;請參閱所有發現事項。
- 獨立的第三方稽核尚未進行。 報告將它與實機測試、使用真實第三方錢包的往返測試一起,列為存放大額資金之前仍需完成的工作。
BitTool Signer iPhone 版
兩份報告
| Opus 5 | GPT-6 Astra | |
|---|---|---|
| 日期 | 2026 年 10 月 19 日 | 2026 年 10 月 18 日(報告 BTS-SEC-2026-1018-R3) |
| 審查的程式碼 | 標籤 v1.0.0,commit f621372 | 10 月 18 日的工作目錄:commit 3d706b7,加上 59 個已變更的檔案與一個新測試 |
審查的引擎(signer.js,SHA-256) | 6933d0d8…f21077 | 6933d0d8…f21077 |
| 方法 | 逐行審查原始碼(3,546 行 JavaScript、11,858 行 Swift)、11 項攻擊探測、以 4,000 個案例對 QR 輸入進行變異測試、測試套件、在乾淨環境中重新建置 | 依子系統審查原始碼、與上一輪比較、獨立製作的 Bitcoin 與 Solana 測試資料、1,509 個案例的變異測試、測試套件、可重現建置、相依套件安全公告 |
| 執行的測試 | 210 項引擎測試、168 項原生測試 | 210 項引擎測試、177 項原生與 UI 測試 |
| 結果 | 嚴重 0 · 高 0 · 中 0 · 低 1 · 資訊 3 | 嚴重 0 · 高 0 · 中 0 · 低 1 · 觀察 4 |
| 獨立性 | 自行審查,審查者也是大量程式碼的作者 | AI 輔助,為專案擁有者準備 |
兩次審查檢查的是 BitTool Signer 1.0.0 所發布的同一個引擎檔案:所有交易解析器、風險規則與簽名計畫都在其中。App 的其餘部分(介面、鑰匙圈、儲存)則是透過閱讀 Swift 原始碼來審查。
所有發現事項
| ID | 嚴重度 | 發現事項 | 在 1.0.0 中的狀態 |
|---|---|---|---|
| BTS-OP5-01 | 低 | 高風險同意只由介面強制執行。 引擎要求部分解碼與不透明的請求必須經過明確確認,但對完全解碼的高風險請求(無上限授權、setApprovalForAll、Safe 權限變更、代幣銷毀)則沒有這項要求。App 會在勾選核取方塊之前停用簽名按鈕,因此攻擊者在 1.0.0 中無法觸及這個問題;它仍然重要,是因為引擎是使用者可以透過雜湊值驗證的部分。 | 未解決。報告建議在 1.0.1 中將這項檢查加入引擎。 |
| R3-01 | 低 | 含找零的銷毀交易之 Bitcoin 摘要。 對於含有金額不為零的 OP_RETURN 輸出且有經驗證找零的交易,結果畫面與活動紀錄中的簡短摘要會顯示「Every output returns to your wallet」(所有輸出都回到你的錢包)。簽名畫面本身則會正確顯示銷毀、其金額與高風險。 | 未解決(1.0.0 所發布的引擎就是受審查的版本)。 |
| BTS-OP5-02 | 資訊 | 活動紀錄中有一個在地化鍵(localization key)是由引擎輸出組成的,而 SwiftUI 會將它以 Markdown 呈現。目前它可能出現的每個值都是固定標籤,因此這只是潛在問題。 | 未解決,潛在問題。 |
| BTS-OP5-03 | 資訊 | 保險庫的密碼 KDF(PBKDF2-HMAC-SHA256,600,000 次迭代)不具記憶體困難特性。這由第二層加密加以緩解:該層使用綁定裝置的鑰匙圈金鑰,因此單憑一份複製出去的保險庫檔案,根本無法發動攻擊。 | 已接受;建議在未來的保險庫格式中採用 Argon2id。 |
| BTS-OP5-04 | 資訊 | Solana 衍生過程中的 Ed25519 中間金鑰材料不會被明確抹除;真正起作用的控制措施是捨棄 JavaScript 環境。 | 未解決,固有限制。 |
| O-01 | 資訊 | 匯入期間,一項較慢的「這個錢包是否已存在?」查詢可能在助記詞被編輯之後才完成,並為前一個錢包提供密碼重設。重設本身會重新衍生錢包身分並拒絕錯誤的錢包;任何保險庫都不會改變。 | 報告撰寫時尚未解決。 |
| O-02 | 資訊 | Safe 與 Solana 的摘要來自範圍受限的解碼器,而不是實際執行交易的結果。離線時無法得知即時的合約狀態。 | 設計如此;已在審核畫面中說明。 |
| O-03 | 資訊 | 在 Swift 與 JavaScript 中無法保證完全清零記憶體;實體裝置、作業系統的亂數產生器與所發布的二進位檔都未經測試。 | 限制。 |
| O-04 | 資訊 | 六項僅限開發環境的相依套件安全公告(3 項中度、3 項低度)。受影響的套件不在所發布的引擎中。 | 維護事項。 |
兩份報告也重新測試了前兩輪審查的發現事項,包括一項高等級問題(含有 DELEGATECALL 與巢狀 MultiSend 的 Safe 多重簽名交易摘要),以及 Bitcoin 手續費與 Solana 銷毀摘要中的數項低等級問題,並確認它們都已修正。Opus 5 另外指出,TRON 簽名不帶有網路識別碼;這是 TRON 協定的特性而非缺陷,App 也會說明這一點。
之後在共用引擎中發現的問題
BitTool Signer iPhone 1.0.0 隨附的引擎 SHA-256 為 6933d0d8…f21077。GPT-6 Astra 對 Android 版的審查正是針對這個檔案重現了引擎方面的發現事項,而 Opus 5.5 發現事項所涉及的 Bitcoin、EVM 與 TRON 程式碼在該檔案中也完全相同。因此,這些引擎方面的發現事項同樣適用於 iPhone 1.0.0:
| ID | 嚴重度 | 對 iPhone 1.0.0 的影響 |
|---|---|---|
| BTS-02、BTS-07 | 中 | 與一筆極小轉帳一起送出的 Solana 銷毀、權限變更或授權委託,會被摘要顯示為那筆轉帳。詳情中的警告與風險確認仍然保留。 |
| BTS-08 | 中 | 錢包單簽設定下任意位址索引的比特幣找零都會被接受,包括觀察錢包永遠不會掃描到的索引。 |
| BTS-09 | 低至中 | 對於未知的 EVM 或 TRON 合約呼叫,保存的摘要不包含隨呼叫送出的原生幣。審核卡片會顯示它。 |
上文的 R3-01 與 Android 的 BTS-04 是同一缺陷,Android 版引擎已將其修正。Opus 5.5 報告建議在共用引擎中修正其餘問題,修正隨後會同時進入兩款 App。
審查確認了什麼
| 領域 | 確認內容 | 詳情 |
|---|---|---|
| 簽名 | 在 Release 建置中,唯一的簽名方式是透過固定的計畫。六項彼此獨立的檢查將簽名綁定到審核過的請求、計畫中的錢包與獲准的金鑰,而且引擎在簽名前會從解密後的種子重新衍生一切。已透過實際執行確認。 | 簽名前的檢查 |
| 隨機性 | 助記詞來自 SecRandomCopyBytes;發生錯誤時會中止建立,且沒有任何備援。引擎的 crypto.getRandomValues 與 Math.random 都橋接到同一個來源。 | 種子產生 |
| 金鑰儲存 | 兩層帶有經驗證標頭的 AES-GCM、僅存在於本裝置的鑰匙圈金鑰,以及重新啟動後依然有效的失敗嘗試延遲機制。 | 金鑰儲存 |
| 拒絕 | 本文件所描述的每一項拒絕都成立:SIGHASH_NONE/SINGLE/ANYONECANPAY、缺少前一筆交易的 SegWit v0 輸入、Solana 查找表、EIP-7702、blob 交易、對原始雜湊簽名的 eth_sign,以及不支援的 chain ID。 | 限制與錯誤 |
| 不受信任的輸入 | 兩次審查共使用 5,509 個經變異的 QR、CBOR、PSBT 與 JSON 輸入:無效輸入一律未被接受,也沒有任何未受控的例外。 | UR 與 CBOR |
| 離線 | 既未連結 CFNetwork,也未連結 Network.framework,且 JavaScript 橋接層不提供任何網路 API。 | 安全模型 |
| 供應鏈 | 引擎可從標籤逐位元組重建;執行階段相依套件都鎖定確切版本;針對正式環境相依套件的安全檢查(dependency audit)未回報任何漏洞。 | 見下文 |
審查未涵蓋的範圍
實體裝置上的測試(故障注入、記憶體傾印、旁路通道)、作業系統的亂數產生器與 Secure Enclave 的內部運作、App Store 二進位檔中引擎檔案以外部分的來源證明、在實際運作的鏈上進行交易、與第三方錢包的往返測試、密碼學相依套件的完整稽核、21 種介面語言的語言審閱,以及形式化驗證。通過的測試套件只涵蓋它所測試的案例,而非所有交易或裝置。
自行驗證引擎
App Store 上的執行檔無法逐位元組重現(Apple 會重新簽署並加密它),但引擎是未加密的資源檔,可以直接比對。從標籤建置引擎:
git clone https://github.com/bitbill/bittool-signer-ios
cd bittool-signer-ios && git checkout v1.0.0
cd engine && npm ci --ignore-scripts && npm run build
shasum -a 256 ../BitToolSigner/Resources/signer.js
# 6933d0d8a6f60faa8222506ac0bd7eef80eb884c9f73e31e9f354b4740f21077 (1,181,026 bytes)
接著將它與你所安裝 App 內的檔案比對:以 Apple Configurator 將 App 下載為 .ipa,解壓縮後計算 Payload/BitToolSigner.app/signer.js 的雜湊值。完整步驟(包括驗證能做到哪裡為止)請參閱儲存庫中的 Reproducible-Build.md。
BitTool Signer Android 版
兩份報告
| Opus 5.5 | GPT-6 Astra | |
|---|---|---|
| 日期 | 2026 年 10 月 25 日 | 2026 年 10 月 24 日 |
| 審查的程式碼 | 正式版 1.0.0(版本代碼 1000001):程式碼提交 71af693,發布紀錄 62996b8 | 發布之前的提交 e7e71fb:針對上一基準 bb1e656 之後變更的複審 |
審查的引擎(signer.js,SHA-256) | 4b371759…00fa19 | 6933d0d8…f21077,即 iPhone 1.0.0 隨附的引擎 |
| 方法 | 分五個領域進行原始碼審查,每個領域都端到端追蹤;每項發現事項都對照程式碼複核;以合成資料重現引擎問題;檢查相依套件與建置產物 | 審查變更的程式碼及其呼叫端,驗證三項修正,針對已提交的引擎套件重現問題,執行自動化回歸檢查 |
| 執行的測試 | 2,075 項 App 測試、217 項引擎測試,發布驗證腳本 25 項全部通過 | 1,616 項 Android 測試;210 項引擎測試,其中一項失敗、一項未實際執行就顯示通過(BTS-06) |
| 結果 | 嚴重 0 · 高 0 · 中 4(其中一項評為低至中)· 低 9 · 若干資訊性說明 | 嚴重 0 · 高 0 · 未解決:中 1、低 1、驗證類 1 · 已解決 3 |
| 建議 | 在面向保護大額錢包的正式發布之前,修正 BTS-07、BTS-08 與 BTS-10 | 在該版本下,暫緩面向大額錢包的正式發布 |
| 獨立性 | 受審程式碼中很大一部分出自同一模型家族 | AI 輔助;未修改正式環境程式碼 |
兩次審查都涵蓋了整個 Android App:Kotlin 模組、原生宿主(基於 QuickJS 的 JNI 層及原生 PBKDF2),以及與 iPhone 版共用的簽名引擎。
所有發現事項
Opus 5.5 審查的就是正式發布的程式碼,因此以下是 BitTool Signer Android 1.0.0 的發現事項。
| ID | 嚴重度 | 發現事項 | 所在位置 |
|---|---|---|---|
| BTS-07 | 中 | Solana 權限變更可以藏在一筆極小轉帳之後。 一個請求若在變更帳戶擁有者或權限、授權委託人或包含不透明指令的同時,附帶一筆 1 lamport 的轉帳,標題與保存的摘要都只會顯示這筆轉帳。詳情中的高風險警告仍然保留,也仍需進行風險確認。 | 共用引擎,iPhone 版同樣存在 |
| BTS-08 | 中 | 比特幣找零在任意索引上都會被接受。 只要某個輸出的腳本與錢包任一單簽設定相符,無論在分支 0 或 1、索引最高到 2³¹−1,都會被視為已驗證的找零。送往這類位址的資金在審核時顯示為「所有輸出都回到你的錢包」,但只掃描有限位址範圍的錢包可能永遠找不到它們。 | 共用引擎,iPhone 版同樣存在 |
| BTS-09 | 低至中 | 對於合約呼叫,保存的摘要不包含原生幣金額。 一個同時送出原生幣的未知 EVM 或 TRON 合約呼叫,會被保存為「未知合約呼叫」。審核卡片及其警告會顯示這筆金額。 | 共用引擎,iPhone 版同樣存在 |
| BTS-10 | 中 | 發布簽署金鑰存放在開發機上。 金鑰及其密碼以明文保存在工作目錄中(已排除在 Git 之外)。 | 發布流程 |
| BTS-11 | 低 | 在 App 鎖畫面後方,QR 碼相機可能仍持續掃描。簽名仍然需要錢包密碼。 | Android App |
| BTS-12 | 低 | 對於完全解碼的高風險請求,風險確認只由介面強制執行。 | Android App(iPhone 版:BTS-OP5-01) |
| BTS-13 | 低 | 在 Android 8–11 上發生 Keystore 錯誤後,用來驗證公開錢包資料的金鑰可能被重新產生,錢包隨後需要「修復」。不會遺失任何機密或資金。 | Android App |
| BTS-14 | 低 | 兩把輔助金鑰沒有檢查螢幕鎖定,部分文件也誇大了保護程度。保險庫本身仍然要求設定螢幕鎖定。 | Android App |
| BTS-15 | 低 | 預設的工作親和性(task affinity)使舊版 Android 上可能出現工作劫持。 | Android App |
| BTS-16 | 低 | 備份檢查可以透過反覆嘗試而通過。 | Android App(依報告,iPhone 版相同) |
| BTS-17 | 低 | 12 個字助記詞的一張復原卡只剩約 40 位元熵。 | Android App |
| BTS-18 | 低 | 取消錢包比對後,審核畫面可能停在「正在尋找相符的錢包…」。 | Android App |
| BTS-19 | 低 | Android 8.0–9 無法偵測只覆蓋確認控制項一部分的覆蓋層。簽名仍然需要密碼。 | Android App |
報告還列出了一些資訊性說明,例如:引擎執行緒沒有 CPU 時間上限(只影響可用性);在設定獨立的上傳金鑰之前,Google Play 版使用 APK 金鑰簽署;重試延遲以系統時鐘為準;以及 JVM 無法抹除的短暫助記詞副本。
GPT-6 Astra 審查的是較早的提交。它的發現事項以及在正式版中的狀態:
| ID | 嚴重度 | 發現事項 | 在 e7e71fb 中 | 在 Android 1.0.0 中 |
|---|---|---|---|---|
| BTS-01 | 中 | 部分被遮住的「簽名」與風險確認控制項可能透過覆蓋層被點按 | 已解決 | 已解決 |
| BTS-02 | 中 | 與極小轉帳混合的 Solana 銷毀或權限變更被摘要顯示為那筆轉帳 | 未解決 | 銷毀問題已修正;權限變更問題仍存在,即 BTS-07 |
| BTS-03 | 低 | 操作取消後,機密緩衝區可能殘留 | 已解決 | 已解決 |
| BTS-04 | 低 | 含找零的比特幣銷毀被摘要為「所有輸出都回到你的錢包」(即 iPhone 的 R3-01) | 未解決 | 已修正 |
| BTS-05 | 功能 | 中斷的 QR 碼掃描無法恢復 | 已解決 | 已解決 |
| BTS-06 | 驗證 | 過時的檔案路徑導致一項引擎測試失敗、另一項未實際執行就通過 | 未解決 | 已修正(217 項引擎測試全部通過) |
審查確認了什麼
| 領域 | 確認內容 |
|---|---|
| 簽名 | 沒有繞過密碼的方法,除了已審核且已比對的請求之外無法簽署任何內容。簽名計畫與請求雜湊值、計畫雜湊值和待簽位元組摘要綁定;引擎會對照解密後的種子重新解析計畫,並拒絕對未簽名比特幣交易的任何更動。取消、App 鎖或新請求之後遲到的結果會被捨棄。 |
| 金鑰儲存 | 原生程式碼中的 PBKDF2-HMAC-SHA256(600,000 次迭代)、每次加密都使用新隨機數與鹽值的 AES-256-GCM,再加上每個錢包一把不可匯出的 Android Keystore 金鑰(手機支援時使用 StrongBox)。每次密碼嘗試都在金鑰衍生之前計數,計數器損壞時一律拒絕。 |
| 記憶體中的機密 | 解鎖後的機密保存在用完即關閉的容器中,即使操作被取消也會被抹除。助記詞透過 App 自帶的鍵盤輸入,任何輸入法都看不到。 |
| 引擎宿主 | 引擎套件依 SHA-256 與長度固定;QuickJS 在沒有 eval 與 Function 的情況下執行,每個涉及機密或不可信輸入的操作都使用全新的執行環境,並設有記憶體與堆疊上限。 |
| 離線與平台 | 沒有網路權限,也沒有連網程式碼。只匯出主畫面,沒有深層連結;備份與裝置轉移被排除在外;所有視窗都不會出現在截圖、錄影與最近使用的應用程式中;在 Android 12 以上版本會隱藏其他 App 的覆蓋層。App 鎖永遠不能授權簽名,發布版不輸出任何日誌。 |
| 供應鏈 | 在乾淨的 npm ci --ignore-scripts 之後重新建置的引擎,與已提交的檔案以及 APK 中的副本逐位元組一致。正式環境相依套件稽核沒有回報任何安全公告。 |
審查未涵蓋的範圍
未涵蓋的範圍包括:實機測試(不同製造商的 Keystore 與 StrongBox 行為、生物辨識、覆蓋層、螢幕擷取)、惡意覆蓋層攻擊演練、旁通道、原生程式碼在 ARM 上的執行、真實區塊鏈上的交易、Maven 與原生相依套件的 CVE 掃描,以及 Google Play 建置的副本。兩份報告都要求在 Android 8 到最新版本的代表性裝置上檢驗發布版,Opus 5.5 也建議在這款 App 保護大額資金之前進行獨立的人工稽核。
驗證 APK 與引擎
在 bittool.com/signer 發布的 APK 可以完整核驗:檔案雜湊值、簽署憑證、其中的引擎,以及沒有網路權限(aapt2 dump badging 列出的權限中沒有 android.permission.INTERNET)。
# 1. The APK from bittool.com/signer
shasum -a 256 BitToolSigner-1.0.0-1000001.apk
# 109dbf5c622ee77672904c34a10229df3c9b0eac45b07096c510b1e1e169c2a4
apksigner verify --print-certs BitToolSigner-1.0.0-1000001.apk
# Signer #1 certificate DN: CN=BITTOOL PTE. LTD., O=BITTOOL PTE. LTD., C=SG
# Signer #1 certificate SHA-256 digest: af41ed101765393ae89038eb4c6fc7fa96d930f6fcd76b881437a39b5a9e4631
# 2. The engine inside it
unzip -p BitToolSigner-1.0.0-1000001.apk assets/engine/signer.js | shasum -a 256
# 4b3717599e8ee3ed9d2480e640e7203731543cc8ab0f2c3cec3b78e6f400fa19 (1,181,432 bytes)
# 3. The same engine, rebuilt from source
git clone https://github.com/bitbill/bittool-signer-android
cd bittool-signer-android && git checkout 71af693
cd engine && npm ci --ignore-scripts && npm run build
shasum -a 256 ../core/engine/src/main/assets/engine/signer.js
憑證在各個版本之間永不改變,Android 只會安裝帶有相同憑證的更新,App 也會在「設定 › 完全開源 › 本次建置」中顯示它。從 Google Play 安裝的副本是另一個檔案,因為 Google Play 會依上傳的套件重新建置,所以無法比對它的雜湊值;但其中的 assets/engine/signer.js 不會改變,可以在 adb pull 之後以同樣方式比對。完整步驟見程式碼儲存庫中的 Reproducible-Build.md。我們已於 2026 年 10 月 25 日對照發布的 APK 核對了上述數值。
下載報告
| 報告 | 頁數 | SHA-256 |
|---|---|---|
| iPhone · Opus 5,2026 年 10 月 19 日(PDF,118 KB) | 17 | ff7ddd17e5dffccd99db6aaa43b8f0041167b58bd9b2bd7cc11d185e779bca1a |
| iPhone · GPT-6 Astra,報告 R3,2026 年 10 月 18 日(PDF,46 KB) | 16 | 02d7a358f8f35b2978eab7db1292add4365a94b736ff79e158dee3afb842b64a |
| Android · Opus 5.5,2026 年 10 月 25 日(PDF,252 KB) | 8 | 8242d58aeec1aa88823eb0a938c9461b0681abfab3f37805e2f77a41b3bba34c |
| Android · GPT-6 Astra,複審,2026 年 10 月 24 日(PDF,22 KB) | 7 | b37aaefd40d0d522384a86255accadeb976067175d76e38466a1b4c90b036f53 |
本頁的摘要由我們撰寫;若措辭與報告有出入,以報告為準。