一次签名即致命:SecondFi 钱包的 Ed25519 实现缺陷导致私钥泄露
2026-07-0110:11
Beosin
2026-07-01 10:11
Beosin
2026-07-01 10:11
收藏文章
订阅专栏

6 月 21 日至 23 日,Cardano 主网上数以千计的钱包遭遇攻击,约 1600 万枚 ADA 和大量 NFT 被转移,总价值约 250 万美元。此外,约 1.29 亿枚 ADA(价值约 2000 万美元)被 SecondFi 以安全原因自行转移到第三方托管机构。

 

漏洞分析

 

此次的核心问题在 Ed25519 的实现。Ed25519 签名算法在设计之初就通过确定性 nonce 生成彻底杜绝了随机数故障。按 RFC 8032,Ed25519 签名时应从私钥派生出两部分:签名标量 s 和秘密前缀 prefix。签名 nonce r 的应计算为:

 

r = SHA512(prefix || M) mod L

 

其中 prefix 是密钥扩展(SHA-512(seed))的前 32 字节,这正是 Ed25519 安全的关键。而 SecondFi 的实现恰恰遗漏了这个 prefix。在密钥从 BIP32-Ed25519 派生层传递到签名适配层的过程中,nonce r 的秘密前缀被彻底遗漏,只有签名标量被传递给了底层签名器。最终签名器实际执行的运算是:

r = SHA512(M) mod L 

这使得 nonce r 从秘密值变成公开可计算值。而在 Ed25519 中,还有以下标准对象:

B:基点,也叫 base point / generator。它是 Ed25519 曲线上预定义的固定点。私钥标量乘以 B 会得到公钥点。

A:公钥点。

如果私钥签名标量是 s,那么:

A = s * B

L:基点 B 所在子群的阶,也就是标量运算的模数。Ed25519 的所有标量计算都在 mod L 下进行。

M:被签名的消息。在普通 Ed25519 里,M 就是调用签名函数时传入的消息字节串。

所以签名流程可以简化理解为:

s = private key signing scalarA = s * B   # public keyM = message to be signedr = SHA512(prefix || M) mod LR = r * Bk = SHA512(R || A || M) mod LS = r + k * s mod L

最终签名通常包含 R 与 S,如果 r 公开可计算,攻击者只要拿到链上一笔由受影响钱包签出的交易,就能得到公开的 M、R、A、S,并可以从公开的 M 算出 r,再用:

 

s = (S - r) * inverse(k) mod L

 

直接恢复私钥签名标量 s。也就是说,某个地址只要曾经用受影响实现签过一次交易,它对应的私钥标量就已经泄露。

下面 PoC 用一个小素数阶的 EdDSA 代数模型复现漏洞原理,并不支持解析真实的 Cardano 交易和真实的链上签名:

import hashlibL = 2**127 - 1  # not Ed25519's real group order, just for demonstrationdef H(*parts):    h = hashlib.sha512()    for p in parts:        h.update(p if isinstance(p, bytes) else str(p).encode())    return int.from_bytes(h.digest(), "little") % Ldef inv(x):    return pow(x, -1, L)# Victim private/public scalar in a toy additive group.secret_s = 98765432123456789public_A = secret_s  # In real Ed25519: A = s * BM = b"cardano tx body hash, toy example"# Buggy signer: prefix was dropped, so r is public and message-only.r = H(M)R = r              # In real Ed25519: R = r * Bk = H(R, public_A, M)S = (r + k * secret_s) % Lprint("[victim signature]")print("A =", public_A)print("R =", R)print("S =", S)# Attacker only needs public A, R, S, and M.r_public = H(M)fingerprint = (R == r_public)recovered_s = ((S - r_public) * inv(k)) % Lprint("\n[attacker]")print("bug fingerprint:", fingerprint)print("recovered secret:", recovered_s)print("success:", recovered_s == secret_s)


结语

本次 SecondFi 钱包安全事件的根本原因为 Ed25519 规范实现层面的错误,这一疏忽导致了灾难性的后果:任何使用 SecondFi 钱包在 Cardano 主网上签名过的地址,其私钥均已公开可推导。其开发团队应当使用测试向量对签名逻辑的实现进行严格验证,确保签名器接收到的内容与派生阶段产生的内容完全一致。

 

Beosin 作为全球最早一批从事形式化验证的区块链安全公司,主打”安全 + 合规“全生态业务,在全球 10 多个国家和地区设立了分部,业务涵盖项目上线前的代码安全审计、项目运行时的安全风险监控与阻断、被盗追回、虚拟资产反洗钱(AML)以及符合各地监管要求的合规评估等“一站式”区块链合规产品 + 安全服务。欢迎点击公众号留言框,与我们联系。

【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。

专栏文章
查看更多
数据请求中

推荐专栏

数据请求中

一起「遇见」未来

DOWNLOAD FORESIGHT NEWS APP

Download QR Code