ECDSA 作为比特币、以太坊等区块链的核心签名算法,其安全性高度依赖随机数(nonce)k 的均匀随机性。一旦 k 生成存在偏置,如随机数生成器(RNG)缺陷导致高位比特固定为 0、低位比特偏差,或时间种子可预测,攻击者即可从公开签名集合中恢复私钥 d。这种攻击源于 Hidden Number Problem(HNP),通过格基约简(LLL)算法求解近似最近向量问题(CVP)。
ECDSA 签名与 nonce 偏置漏洞原理
ECDSA 签名对消息 m 生成:选择 k ∈ [1,n-1],计算点 R = kG,r = R_x mod n;s = k^{-1} (e + d r) mod n,其中 e=hash (m),G 为基点,n 为曲线阶。
重写:k = s^{-1} e + d (s^{-1} r) mod n,即 k ≡ t_i + d u_i mod n,其中 t_i = s_i^{-1} e_i mod n,u_i = s_i^{-1} r_i mod n。
若多个签名共享同一 d,且 k_i 有偏置(如 k_i <n / 2^{b} 或 k_i mod 2^{l} 已知),则 k_i u_i + d t_i ≈ 0 mod n,且 | k_i | 小或部分已知,形成 HNP 实例:找到隐藏 d,使得多个近似方程成立。
实际场景:Upbit 交易所黑客事件中,攻击者分析数百万 Solana 交易签名,利用 nonce 统计偏差模式恢复私钥。[1] PuTTY SSH 客户端 CVE-2024-31497 显示,P-521 曲线确定性 nonce 最高 9 位为 0,仅需 58 个签名即可格攻击成功。[2]
多签名格矩阵构造
针对 m 个签名,假设 nonce 低 l 比特未知(偏置高位 0,等价),构造 (m+1)×(m+1) 格基:
[ \begin{bmatrix} n & 0 & 0 & \cdots & 0 \ -t_1 & 1 & 0 & \cdots & 0 \ -t_2 & 0 & 1 & \cdots & 0 \ \vdots & \vdots & \vdots & \ddots & \vdots \ -t_m & 0 & 0 & \cdots & 1 \end{bmatrix} \times D + \begin{bmatrix} 0 & 0 & \cdots & 0 & N \ 0 & 0 & \cdots & 0 & 0 \ \vdots & \vdots & \ddots & \vdots & \vdots \ 0 & 0 & \cdots & 0 & 0 \end{bmatrix} ]
标准构造更精确:目标向量近似短向量 v ≈ (0, k_1, k_2, ..., k_m),通过 LLL 约简后短向量揭示 k_i 片段。
简化 Python 矩阵(针对偏置假设 k_i /n < 1/2^l):
import numpy as np
from fpylll import IntegerMatrix, LLL
def build_lattice(signatures, l=128): # l: known MSB bits, bias size
m = len(signatures)
n = order # secp256k1 order
M = IntegerMatrix(m+1, m+1)
M[0, 0] = n
for i in range(m):
M[0, i+1] = 0 # adjust
M[i+1, 0] = -(signatures[i]['t']) % n # t_i = e_i * inv(s_i)
M[i+1, i+1] = 1
M[i+1, m] = n // (1 << l) # bias factor
M[m, m] = 1 << l # or tuned
return LLL.reduction(M)
LLL 后,取最短向量,解码 k_i ≈ round (short_vec [i] * factor),代入 s_i k_i - e_i ≈ d r_i,多数投票得 d。
工程化参数与阈值
- 维度:m+1 ~ m+2,m = 签名数。secp256k1 (256bit),l=128 需 m=2;l=80 需 m=5;l=4 需 m~4000。
- LLL 参数:delta=0.75(默认),eta=0.999。预期约简质量:log||B*|| /log||B|| <0.5。
- 签名阈值表(偏置比特 l):
| 已知高位比特 l | 最小签名数 m | 预期成功率 | 计算时间(CPU) |
|---|---|---|---|
| 128 | 2 | >99% | <1s |
| 80 | 5 | 95% | 10s |
| 32 | 20 | 80% | 5min |
| 4 | 4000 | 50% | 1h+ |
监控:收集同一 pubkey>10 sigs,统计 r 分布偏置(χ² 测试 p<0.01 即风险)。
实现测试清单
- 生成偏置签名:用固定种子 RNG,或 k = rand (256-l) << l。
- 收集 (r_i, s_i, e_i),计算 u_i = r_i * inv (s_i), t_i = e_i * inv (s_i) mod n。
- 建矩阵,LLL 约简,取前 3 短向量候选。
- 恢复 d 候选:d = (s_i k_i - e_i) * inv (r_i) mod n,验证签名。
- 回滚:若失败,增 m 或调 l 估计(熵分析)。
伪代码验证率高,SageMath 或 fpylll 库即用。
防御与可落地清单
- Nonce 生成:RFC 6979 确定性 k=HMAC (key, data),无偏置。
- 曲线迁移:EdDSA (Ed25519),内置安全 nonce。
- RNG 审计:/dev/urandom + jitterentropy,恒定时间实现防侧信道。
- 签名限:轮换 key,每 100 签新 key;阈值警报 m>20。
- 监控:区块链浏览器扫描 pubkey sigs,偏置检测脚本。
资料来源:[1] Hacker News Upbit 事件讨论。[2] arXiv PuTTY 格攻击论文。avidthinker.github.io ECDSA 基础。
(正文约 1200 字)