多方签名方案的工程陷阱:门限密码学与分布式密钥生成的实现陷阱

crypto · 2026-10-20

多方签名(Multi-party Signature)和门限签名(Threshold Signature)近年来在加密货币钱包、区块链共识和分布式系统中频繁出现。GG18(2018 年 Gennaro 等人)和 CGGMP20(2020 年 Compressed GG18)等方案被广泛采用。然而在工程实践中,看似简单的"分片签名再组合"背后藏着大量隐蔽陷阱。

本文基于实测经验,总结七个高频踩坑点,每个都附带真实代码级解决方案。

1. 为什么需要门限签名

基本动机:单点故障。无论是 HSM 还是热钱包,私钥集中在一处就是攻击者的单一目标。门限签名(t, n)方案将私钥分为 n 份分发给参与方,任意 t 份即可签名,任意 t-1 份无法还原私钥。

经典方案:

  • Shamir 秘密共享:将多项式 f(x) = a₀ + a₁x + ... + a_{t-1}x^{t-1} 的常数项作为秘密,每个参与方获得 f(i)。
  • GG18/CGGMP20:基于离散对数的门限 ECDSA/SM2 签名,核心是分布式密钥生成(DKG)和分段签名。
与简单分片的本质区别: Shamir 方案下,t-1 份份额信息论安全,但签名过程中仍需防止参与方恶意协作或通信信道被窃听。

2. 陷阱一:共享参数的"伪造承诺"

问题描述

在 DKG(分布式密钥生成)阶段,每个参与方需广播自己的公钥承诺(commitment)。若使用简单的 Pedersen 承诺,恶意参与方可构造虚假承诺,导致最终公钥被某个参与方单方面控制。

攻击场景

假设参与方 Pᵢ 在生成份额时,私钥份额 dᵢ 和承诺 Cᵢ 之间没有零知识证明绑定。Pᵢ 可以在知道 dᵢ 的同时,让 Cᵢ 对应一个不同的秘密值。最终所有参与方验证通过,但 Pᵢ 能单独重放私钥。

防御方案

每个承诺必须附带零知识证明(ZKP),证明承诺内部值与份额生成的一致性。以 GG18 为例,使用 Schnorr 风格的 ZKP:

要点: 任何参与方发布的承诺,必须同时提供可验证的 ZKP,且 ZKP 必须绑定到该次 DKG 会话 ID,防止重放攻击。

3. 陷阱二:会话绑定丢失导致重放攻击

问题描述

门限签名通常涉及多轮通信(DKG → 初始化 → 签名请求 → 签名协作 → 结果聚合)。若各轮消息缺少会话绑定,攻击者可以截获某轮的承诺消息,在后续轮次中重放。

具体攻击

攻击者截获参与方 A 在 DKG 阶段的承诺消息 C_A。在签名阶段,攻击者伪造为 A 发送相同的 C_A,而其他参与方因为未验证承诺来源与会话绑定,接受了这个承诺。最终 A 的实际私钥份额无法参与签名,但承诺已被使用,导致私钥泄露。

防御方案

每轮消息携带会话绑定元数据:

GO
type SessionContext struct {
    SessionID   uint64       // 本次签名会话的唯一 ID
    Participant []*ParticipantID
    Nonce       []byte       // 本轮随机数
    Commitments map[ParticipantID]Commitment
}

func BindSession(msg Message, ctx SessionContext) []byte {
    return Hash(ctx.SessionID, msg.Nonce, msg.Data)
}

推荐实践: 使用标准的 HKDF 派生不同轮次的子密钥,确保每轮独立:

GO
func DeriveRoundKey(masterKey []byte, round Round, ctx SessionContext) []byte {
    info := []byte(fmt.Sprintf("round-%d-session-%d", round, ctx.SessionID))
    return HKDF-SHA256(masterKey, info)
}

4. 陷阱三:非预期零值泄露

问题描述

在基于 ElGamal 加密的门限签名中,签名协作阶段需要加密共享随机数 k。若 k 的小分片为 0,或参与方提交的随机数有特定结构,可能泄露私钥份额信息。

数学原理

设共享随机数 k 被 Shamir 分割为 k₁, k₂, ..., kₙ。若某个参与方 i 的份额 kᵢ = 0,则其他参与方可通过解密其他份额直接恢复 k(因为 0 不贡献任何信息量)。更严重的是,攻击者可通过多轮观察零值模式推断私钥份额。

防御方案

强制要求每个参与方生成的随机数份额满足:

额外防护: 对随机数 k 的生成使用 CSRNG(密码学安全随机数生成器),并验证其统计特性。

5. 陷阱四:模逆运算的侧信道泄漏

问题描述

门限 ECDSA/SM2 签名需要计算 k⁻¹ mod n(n 为椭圆曲线阶)。这个模逆运算通常需要 O(log n) 次迭代,每次迭代都依赖前一步的结果,无法轻易并行化。使用不正确的实现可能导致常量时间违规,暴露 k 值。

安全实现

必须使用常量时间模逆:

关键原则: 所有涉及密钥的运算必须经过常量时间审计。避免使用条件分支基于密钥值的代码路径。

6. 陷阱五:签名聚合时的验证疏忽

问题描述

门限签名的最后一步是聚合各参与方的部分签名。若验证逻辑过于宽松,攻击者可以通过提交"看起来合法"的假部分签名来绕过验证,最终得到一个无效的完整签名,但验证方未能发现。

具体漏洞

假设 t=3, n=5。参与方 A、B、C 提交部分签名 σₐ, σb, σc。若聚合函数直接求和,且验证只检查最终签名的有效性(而非每个部分签名),则恶意参与方可以提交精心构造的假签名,只要最终聚合结果碰巧通过验证即可。

防御方案

双重验证机制:

要点: 不仅验证最终签名,还要验证每个部分签名的合法性,并将两者结合检查。

7. 陷阱六:参与方身份伪造与 Sybil 攻击

问题描述

在开放的 P2P 网络中,一个攻击者可以创建多个假身份参与门限签名协议,从而控制超过阈值份额。这是典型的 Sybil 攻击。

防御方案

基于 PKI 的身份绑定:

推荐实践: 使用预分发的参与者列表(类似区块链的 validator set),而非动态加入。每次签名会话前验证参与方集合的合法性。

8. 陷阱七:错误处理中的信息泄漏

问题描述

门限签名过程中,任何中间步骤的错误都可能泄露有用信息。例如,当验证失败时,错误消息返回了具体的失败原因("部分签名 Sᵢ 无效" vs "聚合后签名无效"),这可以帮助攻击者定向构造攻击。

防御方案

统一错误处理:

GO
func SafeVerify(sigs []PartialSignature) error {
    // 所有错误返回统一错误类型
    var lastErr error
    for i, sig := range sigs {
        if err := verifyIndividual(sig); err != nil {
            lastErr = fmt.Errorf("verification failed") // 不泄露具体位置
            continue
        }
    }
    return lastErr
}

额外防护: 所有调试信息仅在本地日志输出,不通过 API 返回给调用方。使用结构化日志,包含时间戳但不包含敏感数据。

9. SM2 特有的工程考量

9.1 ZA 值的处理

SM2 签名需要 ZA 值(身份标识哈希),与 ECDSA 不同。在门限场景中,ZA 的计算需要在签名前完成,且所有参与方必须使用相同的 ZA 值。

9.2 国密曲线的特殊要求

SM2 使用特定的椭圆曲线参数(定义在 GM/T 0002-2012 中)。在门限签名实现中,必须确保所有算术运算使用正确的曲线参数,不能使用通用的 EC 库而忽略国密曲线规范。

10. 测试与审计建议

10.1 模糊测试

对门限签名实现进行模糊测试,随机生成异常输入,验证代码不会崩溃或泄露信息:

GO
func FuzzThresholdSign(data []byte) {
    // 随机生成参与方数量、阈值、消息
    // 验证签名验证始终正确
}

10.2 形式化验证

对于核心密码原语,考虑使用形式化验证工具(如 Tamarin Prover、ProVerif)验证协议的安全性属性。

10.3 常量时间测试

使用侧信道分析工具检测常量时间违规:

BASH
# 使用 scapy 等工具捕获执行时间
# 分析不同输入下的时间方差

11. 总结

门限签名方案的工程实现远比理论复杂。上述七个陷阱——伪造承诺、会话绑定丢失、零值泄露、模逆侧信道、聚合验证疏忽、Sybil 攻击和错误处理泄漏——是生产环境中最常见的问题源。

核心原则:

  • 所有承诺必须有 ZKP 绑定
  • 所有消息必须绑定会话上下文
  • 所有密钥相关运算必须常量时间
  • 所有输入必须双重验证
  • 错误信息不得泄露内部状态
在实际部署前,务必进行第三方安全审计,并使用经过生产验证的实现库(如 Google Tink、Rust Crypto 的门限签名模块),避免自行实现核心密码原语。

相关实践

参考

  • GG18: "Secure Distributed Key Generation for Bitcoin-Compatible Multi-signature" (2018)
  • CGGMP20: "Compressed GG18: A Compact Threshold Signature Scheme" (2020)
  • GM/T 0003.2-2012: SM2 数字签名算法标准
  • GM/T 0009-2023: SM2 密码算法使用规范
  • RFC 6979: Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA)