VeilSwap
← 全部文章
科普2026-10-299 分钟阅读

硬件钱包固件:验证签名,而不是看域名

实测:下载 Trezor 2.9.1(1,614,848 字节),逐字节复算出官方指纹——文件 SHA-256 与指纹不同,而两者都对。附完整验签步骤与 2-of-3 签名复核结果。

2026 年 10 月 29 日,我从 data.trezor.io 拉下 Trezor T2T1 的 2.9.1 固件:1,614,848 字节,SHA-256 是 6a84c200f13b564ffc2f16c2a6806272b0e7561d04d434f55b495d15c5a0c027。而厂商 releases.json 里同一版本写的指纹是 aaa616c14904cf6eb940497d7fd5da8735ab0d0ecdebd3f6ced49fff0ce278a0。一个文件两个不同的值,而且两个都对。如果你曾经对固件跑 sha256sum、拿去和官网数字比、发现对不上、于是以为自己下到了被篡改的包——原因就在这里。

两个哈希,两件事

SHA-256 盖的是磁盘上的字节,属于传输校验:它告诉你下载没被截断、没在途中损坏。指纹则是完全另一回事——它是设备 bootloader 对固件头算出来的值,也是厂商签名所签署的值。具体说,2.9.1 这个文件由三段组成:4,608 字节的厂商头(magic 为 TRZV)、1,024 字节的固件头(magic 为 TRZF,起始于偏移 4,608)、以及 1,609,216 字节的代码。固件头里写着版本 2.9.1.0、fix_version 2.9.0.0、hw_model T2T1、单调计数器 2、过期字段 0、16 个 32 字节的代码哈希槽(其中 13 个有值:12 个完整的 128 KiB 分块加最后一个 36,352 字节的分块)、这一代机型上留空的 3 个 ECDSA 签名槽,以及 1 字节 sigmask 和 64 字节签名。

五步自己复算

1)先找字节串 TRZF,这里在偏移 4,608。2)从随后 12 个字节读 header_len(1,024)和 code_length(1,609,216)。3)把代码切成 128 KiB 分块,但边界要对齐到整个文件的起点,不是代码的起点——所以第 0 块是 131,072 − 5,632 = 125,440 字节;按直觉从代码起点切,算出来的 13 个哈希一个都对不上。4)重建表头:把这 13 个哈希写进偏移 32 开始的 512 字节槽区,把 544–739(空着的 ECDSA 区)和最后 65 字节(sigmask 加签名)清零。5)对 1,024 字节表头做 blake2s。得到 aaa616c14904cf6eb940497d7fd5da8735ab0d0ecdebd3f6ced49fff0ce278a0,与 releases.json 里那串一模一样。对 2.8.9、2.8.10、2.9.0 重复同一套流程,也各自命中它们的公开值——这才说明方法是正确的,而不是碰巧。

签名才是锁,而且是 2-of-3

表头里的 sigmask 字节是 3,也就是选中了 0x20、0x40、0x60 三个厂商公钥里的第 0、1 把。Trezor 用的是 CoSi:一种把各签名方公钥当成 Edwards 点相加的阈值方案,所以那 64 字节签名在合并公钥 52ef8f8496c888a45aa526cd980105f1dd6ce7b435f0be8fde86152c244a7805 下,就是一条普通的 Ed25519 签名。我先实现曲线运算、用 RFC 8032 的测试向量校准,再拿摘要去验:true。换成单独用第 0 把或单独用第 1 把去验,都是 false——这是 2-of-3,不是「三把里任意一把」。4,608 字节的厂商头同样被签名,用编译进工具链的三把 bootloader 公钥中的两把验证通过,摘要 abb773af7aa4c475…。所以链条是「厂商头 → 固件」,锚在设备上的密钥里,不在服务器上。

改一个字节,看它怎么失败

为了验证这套校验本身,我在偏移 100,000 翻转一位,完全按一个控制了下载源的人的做法重算 13 个分块哈希、重建表头、再跑一次验证。摘要从 aaa616… 变成 249da0562e5ce9c15f3537cec046894c46c488cd6017850c79ed66053a4e2218,签名验证返回 false。这正是这套设计的意义:重算哈希不要钱,伪造签名要命。防降级由第二套机制负责——表头里的单调计数器是 2,装载值更低的镜像会被拒,这才挡住了「回滚到有已知漏洞的旧版」这类攻击。还有一个细节值得点出,因为它说明字段表不是编的:过期字段是 0,厂商自己的解析代码里写着注释「expiry is not used now」。

「官方渠道」到底买到了什么

让人不安的地方在这里:厂商的密钥根本不在下载包里。那 96 字节的厂商公钥在 2.8.9、2.8.10、2.9.0、2.9.1 里逐字节相同(它们自己的 SHA-256 是 b56e59ef7eaf8217…),因为厂商头在出厂时就固定进设备,不是每次发版重新下发。所以一个恶意镜像站能给你的只是:一个真的已签镜像、一个旧的已签镜像、或者一个坏包——三者都偷不走钱,因为 bootloader 要么接受合法签名,要么拒绝。恶意页面真正能偷的是你的助记词:伪装成「固件更新器」、要你输入 24 个词的页面,才是现实中的攻击,而它没有任何密码学环节可供拦截。这就是为什么下载页只关乎钓鱼卫生,不关乎信任。三个真实风险是:助记词钓鱼式「更新向导」、钱包与 dApp 背后的桌面/npm 供应链(2023 年 12 月的 Ledger Connect Kit、2024 年 12 月的 @solana/web3.js)、以及带着别人厂商头的翻新机或仿冒机。另外值得记一笔:2.9.1 另有一个 bitcoin-only 构建,指纹是 77b53ba1e381fcf8…,比对指纹同时也避免你刷错变体。

Ledger 把信任锚放在了别处

Ledger 的模型不是这套的「更差版本」,而是另一种取舍。私钥放在安全元件里,固件由安全元件校验,配套应用做基于认证的「正品检查」,应用层开源而元件内的 OS 不开源。你放弃的恰好是我上面做的那件事:没有一个可以自己重算的第三方可复现摘要,也没有一个能拿公开密钥去核的阈值签名,验证被委托给了设备和厂商。你换到的是:密钥材料从不待在通用微控制器里。两种模型都挡不住那个真正掏空钱包的攻击——你自己把助记词输进了设备以外的东西。

兑换本身碰不到这些——直到你要签名

一笔兑换单只收五个字段:fromAsset、toAsset、fromAmount、toAddress、refundAddress。不连钱包、不签名、不要助记词——所以固件风险落在你从钱包往外转币的那一刻,而不是在兑换的那一刻。给个量级:今天早上目录里有 516 个资产、74 条网络、415 个币种代号、61 个稳定币,以及 6 个需要额外备注(tag/memo)的资产。10:07 UTC 的报价:100 USDT-TRC20 换 0.14687052 XMR(合 680.87 美元/XMR,比 597.08 的现货高 14.03%,几乎全是每单固定下限),1 XMR 换 578.800581 USDT(出金侧比现货低 3.06%),0.05 BTC 换 6.61007527 XMR,即 132.20151 XMR/BTC,而现货是 80,072 美元/BTC。这些数字只是写作时的快照,实时价以页面为准。 (XMR 换 USDT 退出路线)

刷固件前的六项检查

1)只从厂商自己的域名下载;任何要你输入助记词的「固件更新器」都按攻击处理。2)算出指纹并与 releases.json 比对——`trezorctl firmware verify trezor-t2t1-2.9.1.bin --fingerprint aaa616c1…` 一条命令同时做签名和指纹。3)确认阈值:sigmask 为 3、三把密钥,意味着两个协签方,不是一个。4)确认版本不是降级——单调计数器不能倒退。5)用官方应用经 USB 刷入,并在设备屏幕上确认版本号,不是笔记本屏幕。6)重启后再从设备读一次版本和指纹;如果设备报出的东西和你刷进去的不一致,停下来,把这台硬件当成可疑对象。

威胁模型可以压成一句话:固件攻击要么需要厂商私钥,要么需要你的助记词,而这两样都在浏览器之外。所以域名、锁标、下载页都不能成为你依赖的控制点——它们只决定你在路上会不会被钓鱼。用上面的算法把文件验一次,把助记词留在设备里,硬件钱包最吓人的那一块就不再是谜,而是一个可以打勾的检查项。

常见问题

官方公布的指纹就是文件 SHA-256 吗?

不是。Trezor T2T1 2.9.1 的文件 SHA-256 是 6a84c200f13b…,指纹是 aaa616c14904…——后者是把 16 个分块哈希重算、所有签名字段清零后,对 1,024 字节固件头做 blake2s 得到的摘要。签名盖的是这个摘要,所以只能拿指纹对比指纹;SHA-256 只用来判断下载有没有被截断(文件必须正好 1,614,848 字节)。

改过的固件能通过签名校验吗?

不能。我在偏移 100,000 处翻转一位,按攻击者的做法重算了全部 13 个分块哈希并重建表头:摘要从 aaa616… 变成 249da056…,签名验证返回 false。要过关必须拿到厂商三把私钥中的两把,而它们不出签名机。

把助记词输进软件里,硬件钱包还能保护我吗?

不能。助记词本身才是钱包,设备只在它不离开设备时提供保护。凡是要求你输入 24 个词来「完成固件更新」的页面,都是不含任何密码学环节的钓鱼,签名校验抓不到它。真正的更新是签名的、从官方应用经 USB 刷入、并在设备屏幕上确认——全程不需要任何单词。

准备好换币了吗?

无账户 · 无 KYC · 无邮箱

立即兑换
硬件钱包固件:验证签名,而不是看域名 — VeilSwap