MPC 签名服务部署与密钥管理指南
数据来源:MSG Chain 代码库核实
主网状态: No-Go — 当前 MSGChain 主网裁决为 No-Go,以下内容反映代码实际状态,不代表生产可用。
技术栈:MSG Chain + Dilithium-5 PQC + GG18/GG20/CMP 阈值协议 + agent_mpc_v1 合约
域:msgchain.org | 链 ID:msg-chain-1 | Bech32 前缀:msg
定位:本文档聚焦 MPC 签名服务基础设施(节点部署、密钥管理、协议原理),不涉及 AI Agent MPC 钱包集成(参见 04-AI-Agent/AI Agent MPC多重签名钱包接入指南.md)
目录
- MPC 理论基础
- MPC vs 多签
- MSG Chain MPC 组件架构
- MPC 节点部署
- 密钥分片管理
- 密钥生命周期
- 密钥隔离与 HSM
- 监控与审计
- 与 MSG Chain 的集成
- 灾难恢复
- 实践:部署 3/5 阈值 MPC 签名集群
- 安全边界与限制
一、MPC 理论基础
1.1 多方计算概述
多方计算(Multi-Party Computation,MPC)是一种密码学协议,允许多个参与方在不泄露各自私密输入的情况下,共同计算一个函数并得到结果。在数字签名场景下,MPC 的目标是:
多个参与方各自持有私钥的一个分片,协作生成一个有效的签名,但任何一方(甚至少于阈值的多方合谋)都无法获知完整的私钥。
MPC 签名的核心数学基础包括:
- 秘密共享(Secret Sharing):将秘密拆分为多个分片
- 安全多方计算协议:各方在加密状态下执行协议轮次
- 阈值密码学(Threshold Cryptography):t-of-n 签名策略
1.2 GG18 协议
GG18(Garay-Gennaro 2018)是首个实用的两轮 ECDSA 阈值签名协议,由 Rosario Gennaro 和 Steven Goldfeder 在 2018 年提出。
1.2.1 协议特性
| 属性 | 描述 |
|---|---|
| 签名算法 | ECDSA(Secp256k1) |
| 阈值 | t-of-n,任意 t <= n |
| 轮次 | DKG:2 轮,签名:2 轮 |
| 密钥生成 | 分布式密钥生成(DKG),无可信中心 |
| 假设 | 诚实多数 + 同步网络 |
| 安全性 | 静态安全(Static security) |
1.2.2 GG18 DKG 流程
Phase 1: 各方生成 Paillier 密钥对 (pk_i, sk_i)
用于加密同态运算
Phase 2: 各方生成秘密多项式
方 i 生成: f_i(x) = a_{i0} + a_{i1}*x + ... + a_{i(t-1)}*x^{t-1}
其中 a_{i0} 是方 i 的秘密贡献
Phase 3: 分片分发
方 i 计算 s_{ij} = f_i(j),加密后发送给方 j
方 j 收到所有 s_{ij},求和得到自己的完整分片: sk_j = sum_i s_{ij}
Phase 4: 验证
各方广播承诺 (a_{ik}*G),用于验证 s_{ij} 的正确性
不一致 → 投诉 → 恶意方被识别
Phase 5: 公钥聚合
各方计算: PK = (sum_i a_{i0})*G
得到共享公钥 PK
1.2.3 GG18 签名流程
输入: 消息 m,各参与方持有分片 sk_i,群公钥 PK
Round 1(预处理/Offline):
- 各方生成随机数 k_i, gamma_i
- 各方计算 R_i = k_i*G 并广播
- 各方计算 delta_i = k_i*gamma_i(加密形式)
Round 2(签名/Online):
- 计算 R = sum R_i
- 计算 r = R.x
- 使用 Paillier 同态计算 s
- 输出签名 (r, s)
1.3 GG20 协议
GG20 是 Gennaro-Goldfeder 在 2020 年对 GG18 的改进版本。
1.3.1 GG18 已知问题
| 问题 | 描述 | 影响 |
|---|---|---|
| Paillier 密钥长度攻击 | 如果 Paillier 模数长度不足,攻击者可恢复私钥 | 私钥泄露 |
| 重放攻击 | 签名随机数重用可导致私钥恢复 | 私钥泄露 |
| 非自适应安全 | 无法处理动态腐化(adaptive corruption) | 安全模型有限 |
| 零知识证明缺失 | 某些中间值缺少 ZKP 验证 | 恶意方可以作弊 |
1.3.2 GG20 改进
GG20 针对性改进:
1. Paillier 模数检查
- 增加 Paillier 模数的零知识证明
- 要求 N_Paillier 至少有 2048 位
2. 范围证明(Range Proof)
- 对加密值增加范围证明
- 防止 Overflow Attack
3. MtA(Multiparty-to-Aggregator)改进
- 安全的乘法-加法转换
- 每步附带 ZKP 验证
4. 确定性 nonce 生成
- RFC 6979 风格的确定性 nonce
- 消除可重用攻击面
5. 可识别性(Identifiable Abort)
- 协议中止时可定位恶意方
- 支持问责机制
1.3.3 GG20 签名轮次
Round 1 - 承诺阶段:
─ 各方生成 Paillier 密钥对
─ 计算承诺值 commitment = H(nonce || pk)
─ 广播 commitment_i
Round 2 - 密钥分享阶段:
─ 各方打开承诺(揭示 nonce_i 和 pk_i)
─ 交换秘密分片(加密)
─ 验证 ZKP
Round 3 - 签名预处理:
─ 生成随机数 k_i
─ 计算 R_i = k_i*G
─ 广播 R_i 并附带 ZKP
Round 4 - 在线签名:
─ 计算 R = sum R_i
─ 计算消息哈希 e = H(m)
─ 通过 MtA 计算 s
─ 输出签名 (r, s)
1.4 CMP 协议
CMP(Canetti-Makriyannis-Peli)是一种支持 EdDSA(Ed25519)的阈值签名协议。
1.4.1 CMP 核心特性
| 属性 | CMP 协议 |
|---|---|
| 签名算法 | EdDSA (Ed25519, Ed448) |
| 阈值 | t-of-n |
| 轮次 | DKG: 4 轮,签名: 2 轮 |
| 签名聚合 | 支持批量验证 |
| 安全性 | 自适应安全(Adaptive security) |
| 假设 | 异步网络模型 |
1.4.2 EdDSA 与 ECDSA 的协议差异
ECDSA (GG18/GG20) EdDSA (CMP)
---------------- ----------
签名等式 s = k^{-1}(e + r*sk) s = r + h*sk
随机数需求 强:两次使用同一 k 泄露私钥 一次性私钥(Hash 派生)
同态加密 Paillier + ZKP 不需要
MtA 子协议 必需 不需要
签名大小 ~70 B ~64 B
验证速度 ~1.0ms ~0.6ms
CMP 不需要 Paillier 同态加密和 MtA 子协议,
因此计算开销显著低于 GG18/GG20。
1.4.3 CMP DKG 流程
CMP DKG(4 轮):
Round 1: 各方生成临时密钥并广播承诺
ephemeral_sk_i = random()
ephemeral_pk_i = ephemeral_sk_i*G
comm_i = H(ephemeral_pk_i || salt_i)
广播 comm_i
Round 2: 打开承诺并广播公开密钥
广播 (ephemeral_pk_i, salt_i)
各方验证同步性
Round 3: 多方 DH 密钥交换
每对 (i, j) 计算共享密钥: K_{ij}
使用 K_{ij} 加密发送秘密分片 s_{ij}
Round 4: 分片验证与公钥聚合
验证接收到的分片
聚合: sk_i = sum_j s_{ji}
计算群公钥: PK = sum_i pk_i
1.5 阈值 ECDSA vs 阈值 EdDSA
| 对比维度 | 阈值 ECDSA (GG18/GG20) | 阈值 EdDSA (CMP) |
|---|---|---|
| 数学基础 | Secp256k1 椭圆曲线 | Curve25519 椭圆曲线 |
| 协议复杂度 | 高(需 Paillier + MtA + ZKP) | 低(无需同态加密) |
| 签名轮次 | Offline + Online(2 轮签名) | 2 轮 |
| 计算开销 | 高 | 低 |
| 通信开销 | 中等 | 低 |
| 量子抗性 | 否(需 Dilithium 扩展) | 否(需 Dilithium 扩展) |
| 实现成熟度 | 高 | 中等 |
| MSG Chain 策略 | Dilithium-5 阈值扩展 | 未来考虑 |
1.6 MSG Chain 的改进:阈值 Dilithium-5
MSG Chain 采用的阈值签名方案是将 GG20 框架扩展至 Dilithium-5。
1.6.1 阈值 Dilithium-5 的挑战
Dilithium-5 签名结构:
私钥: (s1, s2)
公钥: (t1 = A * s1)
签名: sigma = (z, h)
其中:
z = y + c * s1
c = H(m || w)
w = HighBits(A * y)
挑战 1: Dilithium-5 使用拒绝采样(Rejection Sampling)
阈值版本需要在多方间协调拒绝逻辑
挑战 2: Dilithium-5 使用掩码向量 y 而非随机标量 k
分片需要扩展到向量运算
挑战 3: 签名过程中 w = HighBits(A*y) 的非线性
多方同态计算困难
1.6.2 当前实现策略
阈值 Dilithium-5 策略
策略 A: 秘密共享 + 本地签名(当前)
- 使用 Shamir 秘密共享拆分 Dilithium-5 私钥
- 各方独立生成部分签名
- 聚合器组合部分签名为完整签名
- 部分实现,适合本地沙箱
策略 B: 完整 GG20 风格阈值签名(未来)
- 实现 DKG 生成 Dilithium-5 密钥分片
- 完整的 MtA 风格签名轮次
- 需要 Dilithium-5 的 Paillier-like 同态方案
- 路线图中
二、MPC vs 多签
2.1 阈值签名 vs 多签
| 维度 | 阈值签名(MPC) | 传统多签(Multisig) |
|---|---|---|
| 链上签名体积 | 单个签名(固定大小) | 多个签名(线性增长) |
| Gas 开销 | 单次验证 | 多次验证 |
| 签名者数量对链的影响 | 无影响 | 每增加一个签名者,签名体积增加 |
| 隐私性 | 聚合签名不暴露参与方 | 链上可见所有签名者 |
| 密钥存储 | 分片存储,私钥从未完整出现 | 完整私钥各自存储 |
| 恢复机制 | 分片丢失可重新分发 | 私钥丢失不可恢复 |
| 适配现有合约 | 无需修改(签名格式一致) | 需修改合约验证逻辑 |
2.2 签名体积对比
传统多签体积 (2-of-3, Ed25519):
─ 3 个公钥: 3 x 32 B = 96 B
─ 2 个签名: 2 x 64 B = 128 B
─ 总计: ~224 B
传统多签体积 (3-of-5, Ed25519):
─ 5 个公钥: 5 x 32 B = 160 B
─ 3 个签名: 3 x 64 B = 192 B
─ 总计: ~352 B
MPC 阈值签名 (2-of-3, Dilithium-5):
─ 单个签名: ~4,595 B
─ 不暴露参与方: 1 个签名包含所有信息
2.3 安全模型对比
传统多签安全主要依赖于:
─ 各参与方独立保管完整私钥
─ 链上合约验证签名数量是否达到阈值
安全弱点:
1. 私钥单点泄露
2. 侧信道攻击(完整私钥在签名过程中全程在内存中)
3. 量子威胁(Ed25519/Secp256k1 均无量子抗性)
MPC 安全模型的核心差异:
1. 私钥从不存在(分片形式,从未完整重构)
2. 签名过程安全(私钥分片不离开本地)
3. 前向安全性(长期密钥泄露不影响历史签名)
4. 可识别中止(可定位恶意参与方)
5. 量子安全(Dilithium-5 分片继承量子抗性)
2.4 适用场景对比
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| DAO 金库管理 | MPC 阈值签名 | 单签名 Gas 低,不暴露治理成员 |
| 小额多签钱包 | 传统多签 | 实现简单,Gas 差异不大 |
| 高价值资产管理 | MPC + HSM | 最高安全等级,分片 + 硬件隔离 |
| AI Agent 自主签名 | MPC | Agent 不能持有完整私钥 |
| 跨链资产桥 | MPC + 门限预言机 | 签名者分布在多条链 |
| 企业财务管理 | MPC | 审批流配置灵活,审计合规 |
三、MSG Chain MPC 组件架构
3.1 架构总览
MSG Chain MPC 服务架构
外部调用方 (AI Agent / dApp / DAO / 企业应用)
|
v
Agent API 网关层
POST /agent/v1/mpc/wallet [Stub]
POST /agent/v1/mpc/wallet/{id}/sign [Stub]
GET /agent/v1/mpc/wallet/{id} [Implemented]
GET /agent/v1/mpc/wallet/{id}/sign/{session} [Impl]
|
v
MPC Orchestrator(协调器)
[会话管理] [节点发现] [状态机] [审计日志] [限流器]
|
v
MPC P2P 网络层
节点 A --- P2P --- 节点 B --- P2P --- 节点 C
libp2p / Noise / Kademlia DHT
|
v
密码学协议层
[GG20 框架 (Paillier + MtA + ZKP)]
[Dilithium-5 签名引擎]
[Shamir 秘密共享]
|
v
链集成层
Cosmos SDK Tx Builder --> agent_mpc_v1 合约
|
v
msg-chain-1 (RPC: https://rpc.msgchain.org)
3.2.2 MPC 会话状态机
MPC 会话状态转换图
CREATED
|
v
PENDING_SIGNATURES
/ \
v v
COLLECTING TIME_LIMIT_EXCEEDED
| |
v v
FINALIZING TIMEOUT
|
/------+------\
v v
COMPLETED FAILED
状态定义:
CREATED: 会话刚创建,等待签名者加入
PENDING_SIGNATURES: 签名者正在生成部分签名
COLLECTING: 部分签名正在收集中
FINALIZING: 已收集足够部分签名,正在聚合计算
COMPLETED: 签名完成,可获取完整签名
FAILED: 签名失败(阈值不足/验证失败/协议错误)
TIMEOUT: 超时未完成
会话超时机制:
- session_timeout: 可配置(默认 300 秒)
- 预处理超时: 60 秒
- 每轮通信超时: 30 秒
- 聚合超时: 15 秒
3.3 当前实现状态
重要声明:MSG Chain 的 MPC 签名服务目前处于部分实现 / Stub 状态。以下组件的状态分类帮助开发者准确评估生产可用性。
| 组件 | 子系统 | 状态 | 生产可用 | 备注 |
|---|---|---|---|---|
| Agent API | GET 查询路径 | 已实现 | 是 | 公开端点,无认证要求 |
| Agent API | POST/DELETE 写路径 | Stub | 否 | 返回 X-MSG-Stub=true,仅沙箱可用 |
| agent_mpc_v1 合约 | 合约骨架 | 部分实现 | 否 | 基本状态存储实现,阈值签名逻辑未完成 |
| Dilithium-5 签名 | 标准签名 | 已实现 | 是 | 生产中用于区块/交易签名 |
| 阈值 Dilithium-5 | 秘密共享分片 | 部分实现 | 否 | Shamir 分片可用,完整 GG20 未实现 |
| MPC P2P 网络 | 节点发现和通信 | 原型 | 否 | 基础 libp2p 框架搭建中 |
| MPC Orchestrator | 会话管理 | 原型 | 否 | 本地单节点可模拟 |
| 密钥存储 | 分片加密存储 | 部分实现 | 否 | 本地文件存储可用,HSM 集成未完成 |
| 监控指标 | Prometheus 暴露 | 已实现 | 是 | 节点基础指标可用 |
| 灾难恢复 | 分片备份恢复 | 设计阶段 | 否 | 恢复协议设计未完成 |
3.4 组件通信图
组件间通信序列(创建钱包 + 签名):
Agent API Orchestrator MPC 节点群组
| | |
|-- POST /wallet (Stub) ------->| |
| |-- DKGInit ------------------->|
| |<-- Ack -----------------------|
| |-- DKGCommit ----------------->|
| |<-- Commitments ---------------|
| |-- DKGShare ------------------>|
| |<-- EncryptedShares -----------|
| |-- DKGVerify ----------------->|
| |<-- VerificationResult --------|
| |-- DKGComplete --------------->|
| |<-- Share + PK ----------------|
|<-- wallet_created ------------| |
| | |
|-- POST /sign (Stub) -------->| |
| |-- SignPrepare (e, signers) -->|
| |-- Node_A: SignRound1 -------->|
| |-- Node_B: SignRound1 -------->|
| |-- Node_C: SignRound1 -------->|
| |<-- R_i, Gamma_i, proofs -----|
| |-- Node_A: SignRound2 -------->|
| |<-- partial_sig_A ------------|
| |-- Node_B: SignRound2 -------->|
| |<-- partial_sig_B ------------|
| |-- Node_C: SignRound2 -------->|
| |<-- partial_sig_C ------------|
| |-- Aggregate verification --->|
|<-- full_signature ------------| |
3.5 Stub 端点行为
当调用 Stub 端点时,Agent API 返回如下特征:
{
"wallet_id": "mpc-wallet-stub-xxxx",
"status": "simulated",
"simulation": {
"note": "This endpoint is a stub for local development only.",
"production_readiness": false
}
}
HTTP 响应头中包含 X-MSG-Stub: true 标识。客户端应始终检查此头。
四、MPC 节点部署
4.1 节点内部架构
MPC Sign Node
[协议引擎] DKG 状态机 / 签名状态机 / 消息序列化
[密码学引擎] Dilithium-5 / Shamir 秘密共享 / Paillier / ZKP
[密钥存储] 节点标识密钥 / MPC 分片存储 / HSM 接口 (PKCS#11)
[P2P 通信层] libp2p / Noise 加密 / Kademlia DHT
[API 接口层] gRPC / HTTP/REST / Prometheus 指标
4.2 节点发现
三层发现机制:
Layer 1: 静态种子节点
- 配置文件指定种子节点 multiaddress
- 启动时连接种子节点
- 种子节点提供对等列表
Layer 2: Kademlia DHT
- 使用 libp2p Kademlia 协议
- 网络内自动发现对等节点
- 支持 NAT 穿透
Layer 3: 链上注册
- 节点在 agent_mpc_v1 合约注册
- 合约存储节点 multiaddress
- 新节点可通过合约事件发现
4.3 P2P 通信
传输层安全栈: 应用层 (/mpc/dkg/1.0.0) -> libp2p Stream -> Noise XX 握手 -> Yamux mux -> TCP/QUIC
所有 MPC 协议消息使用 Protocol Buffers 序列化,包含两层加密保护:
Layer 1: 传输层加密(Noise XX)- 端到端加密和身份认证
Layer 2: 应用层加密 - DKG 分片值使用接收方公钥加密
4.4 DKG 流程
DKG 流程(以 3/5 阈值为例):
Step 1: 参数协商
- 协调器发送 DKGInit 消息给所有签名者
- 各方确认参与 -> 返回 ACK
Step 2: 各方生成密钥材料
- 每个节点 i: 生成 Paillier 密钥对、多项式、承诺、ZKP
Step 3: 广播承诺
- 广播 (C_i, proof_i, comm_i)
- 等待所有参与方到达
Step 4: 广播 Paillier 公钥
- 各节点打开承诺,验证一致性
Step 5: 交换分片
- 使用接收方 Paillier 公钥加密发送分片
Step 6: 分片验证
- 解密后验证分片正确性,不匹配则投诉
Step 7: 投诉处理
- 被投诉节点自证,如果证实恶意则移除
Step 8: 分片聚合
- 节点 j 计算: sk_j = sum_i s_{ij}
- 计算: PK = sum_i a_{i0}*G
4.5 签名轮次
参与签名: Node_A, Node_B, Node_C, 阈值: 3
Pre-signing Phase (预处理,可离线):
- 各方生成随机数 k_i, chi_i
- 计算 R_i = k_i*G, 广播 R_i
- 计算 R = sum R_i, r = R.x mod q
Online Signing Phase (需在线通信):
- 计算消息哈希 e = H(m)
- 各方通过 MtA 子协议交换乘积值
- 各方计算部分签名
- 发送部分签名给聚合器
Aggregation Phase:
- Coordinator 聚合所有部分签名
- 验证完整签名的有效性
- 返回完整 Dilithium-5 签名
4.6 节点配置
# mpc-node-config.yaml
node:
identity:
key_path: "/etc/mpc-node/keys/node-identity.dilithium"
peer_id_file: "/etc/mpc-node/keys/peer_id"
network:
listen_addresses:
- "/ip4/0.0.0.0/tcp/9001"
- "/ip4/0.0.0.0/udp/9001/quic-v1"
bootstrap_peers:
- "/ip4/seed1.msgchain.org/tcp/9001/p2p/12D3KooW..."
protocol:
dkg_timeout_seconds: 300
sign_timeout_seconds: 60
max_concurrent_sessions: 10
max_parties: 50
min_threshold: 2
crypto:
paillier_key_bits: 2048
dilithium_security_level: 5
use_tee: false
storage:
type: "filesystem"
path: "/var/lib/mpc-node/shares"
encryption_key: ""
backup_path: "/var/backups/mpc-node"
api:
grpc_port: 9002
http_port: 9003
metrics_port: 9004
logging:
level: "info"
format: "json"
monitoring:
prometheus_enabled: true
health_check_interval: 10
heartbeat_interval: 30
五、密钥分片管理
5.1 分片生成
5.1.1 Shamir 秘密共享原理
给定: 秘密 s(Dilithium-5 私钥的标量分量)
阈值: t
总参与方: n
构造多项式: f(x) = s + a_1*x + a_2*x^2 + ... + a_{t-1}*x^{t-1}
分片计算: share_i = f(i) for i = 1, 2, ..., n
重构(拉格朗日插值):
s = f(0) = sum_{i in S} share_i * L_i(0)
L_i(0) = prod_{j in S, j != i} (j / (j - i))
5.1.2 DKG 分片数据结构
share_metadata:
scheme: GG20-Dilithium5
threshold: 3
total_parties: 5
party_index: 1
session_id: dkg-session-uuid-xxx
share_data:
sk_share: { s1: 0x..., s2: 0x... }
chain_code: 0x...
public_key: dilithium5:...
polynomial_commitments: [0x..., 0x..., 0x...]
5.2 分片存储
5.2.1 四层存储层级
Level 0: 内存 - 使用 mlock() 防止交换到磁盘,签名完成后立即擦除
Level 1: 本地加密存储 - AES-256-GCM 加密,文件系统或数据库
Level 2: HSM 硬件安全模块 - PKCS#11 接口,分片永不离开 HSM
Level 3: 离线备份 - 加密导出到冷存储,地理分布式
5.2.2 加密存储实现
import os
import hashlib
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
class SecureShareStorage:
def __init__(self, master_key, storage_path):
assert len(master_key) == 32
self.master_key = master_key
self.storage_path = storage_path
def _derive_share_key(self, wallet_id, party_index):
context = f"mpc-share-{wallet_id}-party-{party_index}"
return hashlib.sha256(self.master_key + context.encode()).digest()
def encrypt_share(self, share_data, wallet_id, party_index):
key = self._derive_share_key(wallet_id, party_index)
aesgcm = AESGCM(key)
nonce = os.urandom(12)
ad = f"{wallet_id}:{party_index}".encode()
ct = aesgcm.encrypt(nonce, share_data, ad)
return nonce + ct
def decrypt_share(self, encrypted_data, wallet_id, party_index):
key = self._derive_share_key(wallet_id, party_index)
aesgcm = AESGCM(key)
nonce, ct = encrypted_data[:12], encrypted_data[12:]
ad = f"{wallet_id}:{party_index}".encode()
return aesgcm.decrypt(nonce, ct, ad)
def store_share(self, share_data, wallet_id, party_index):
enc = self.encrypt_share(share_data, wallet_id, party_index)
path = os.path.join(self.storage_path, f"{wallet_id}_{party_index}.enc")
with open(path, "wb") as f:
f.write(enc)
def load_share(self, wallet_id, party_index):
path = os.path.join(self.storage_path, f"{wallet_id}_{party_index}.enc")
with open(path, "rb") as f:
return self.decrypt_share(f.read(), wallet_id, party_index)
5.3 分片备份
5.3.1 三层次备份策略
Layer A: 热备份 - 加密分区副本,延迟 < 5 分钟
Layer B: 温备份 - S3 兼容存储 + KMS 加密密钥,保留 30 天
Layer C: 冷备份 - 硬件安全模块 + 纸质二维码,地理分散
备份加密要求:
- 所有备份使用独立于节点主密钥的备份密钥加密
- 备份密钥分开存储(密钥管理服务 + 阈值保护)
- 每次备份包含完整性校验和
5.4 分片恢复
5.4.1 单节点分片恢复
触发条件: 节点宕机超过 24h / 存储损坏 / 需要替换节点
恢复步骤:
Phase 1: 验证恢复资格(检查活节点 >= t)
Phase 2: 分片重新分发(协作刷新协议,保持公钥不变)
Phase 3: 新节点初始化(生成身份密钥,加入 P2P 网络)
Phase 4: 验证(测试签名,更新分片版本号)
5.4.2 协作刷新协议
当节点 C 的存储损坏时,剩余节点生成零和偏移多项式
(常数项为 0,因此公钥不变),分发偏移分片给节点 C
新分片 = 旧分片 + 收到的偏移总和
六、密钥生命周期
6.1 生命周期模型
Phase 1: 创建 (Create) - DKG 分布式密钥生成,分片分发,公钥验证
Phase 2: 活跃 (Active) - 阈值签名服务,分片版本管理,监控审计
Phase 3a: 轮换 (Rotate) - 生成新密钥,递增版本号
Phase 3b: 归档 (Archive) - 锁定密钥,只读状态,停止签名
Phase 3c: 销毁 (Destroy) - 永久删除分片,符合 NIST SP 800-88
6.2 密钥创建验证
def verify_new_mpc_key(wallet_id, public_key, threshold, total_parties):
"""验证新创建的 MPC 密钥对"""
# 1. 验证聚合公钥格式
if len(public_key) != 2592: # Dilithium-5 公钥大小
return False
# 2. 执行测试签名
test_msg = b"MPC_KEY_VERIFICATION_" + wallet_id.encode()
signature = perform_test_sign(test_msg)
if signature is None:
return False
# 3. 验证签名
if not verify_dilithium5_signature(public_key, test_msg, signature):
return False
# 4. 检查分片数量
if load_share_count(wallet_id) != total_parties:
return False
# 5. 验证所有参与方的承诺一致性
commitments = load_dkg_commitments(wallet_id)
if not verify_polynomial_commitments(commitments, public_key):
return False
return True
6.3 密钥轮换
定期轮换: 每个季度(90 天)轮换一次,高安全要求每月轮换
事件驱动轮换: 签名者变更、安全事件、节点被攻破
零停机轮换协议:
Phase 0: 准备工作(通知、健康检查、记录活跃会话)
Phase 1: 分片刷新(执行协作刷新协议)
Phase 2: 并行运行(旧/新分片同时可用)
Phase 3: 旧分片销毁(等待进行中会话完成)
Phase 4: 验证与切换(测试签名,更新版本号)
轮换状态机:
```python
from enum import Enum
class RotationPhase(Enum):
INITIATED = "initiated"
PREPARING = "preparing"
REFRESHING = "refreshing"
PARALLEL_RUN = "parallel_run"
DESTROYING_OLD = "destroying_old"
VERIFYING = "verifying"
COMPLETED = "completed"
FAILED = "failed"
ROLLED_BACK = "rolled_back"
class KeyRotationManager:
def __init__(self, node_registry):
self.node_registry = node_registry
self.active_rotations = {}
def initiate_rotation(self, wallet_id):
session = {
"rotation_id": f"rot-{wallet_id}-{int(time.time())}",
"phase": RotationPhase.INITIATED,
"old_version": self._get_current_version(wallet_id),
"new_version": self._get_current_version(wallet_id) + 1,
"participant_status": {nid: "pending" for nid in self.node_registry},
}
self.active_rotations[session["rotation_id"]] = session
return session
def advance_phase(self, rotation_id):
session = self.active_rotations.get(rotation_id)
if session["phase"] == RotationPhase.INITIATED:
self._prepare_rotation(session)
session["phase"] = RotationPhase.PREPARING
elif session["phase"] == RotationPhase.PREPARING:
self._execute_refresh(session)
session["phase"] = RotationPhase.REFRESHING
elif session["phase"] == RotationPhase.REFRESHING:
session["phase"] = RotationPhase.PARALLEL_RUN
elif session["phase"] == RotationPhase.PARALLEL_RUN:
self._destroy_old_shares(session)
session["phase"] = RotationPhase.DESTROYING_OLD
elif session["phase"] == RotationPhase.DESTROYING_OLD:
if self._verify_new_shares(session):
session["phase"] = RotationPhase.COMPLETED
else:
session["phase"] = RotationPhase.ROLLED_BACK
def _destroy_old_shares(self, session):
for node_id in self.node_registry:
self._secure_erase(node_id, session["wallet_id"], session["old_version"])
logger.info(f"Old shares destroyed for {session['wallet_id']}")
def _secure_erase(self, node_id, wallet_id, version):
share_path = f"/var/lib/mpc-node/shares/{wallet_id}_v{version}_*.enc"
# 使用 NIST SP 800-88 标准覆写 3 次
for pattern in [b"\\x00", b"\\xff", os.urandom(4096)]:
self._overwrite(share_path, pattern)
os.remove(share_path)
6.3 密钥归档
自动归档: 密钥连续 180 天未使用 / 钱包余额为 0 超 30 天 / 签名者低于阈值超 90 天
手动归档: 合规要求 / 调查隔离 / 迁移后
归档操作: 活性密钥 -> archived,加密打包,停止签名请求,保留审计日志
6.4 密钥销毁
合规销毁: 保留期限到期(7 年),监管批准
安全销毁: 密钥被攻破,节点被物理控制
Type A: 软销毁 - 删除加密分片,标记为 destroyed,保留元数据
Type B: 硬销毁 - 物理销毁介质,NIST SP 800-88 安全擦除
6.5 合规要求
密钥生成: DKG 无需可信中心,CSPRNG,完整审计
密钥存储: AES-256-GCM 加密,最小权限,<= 90 天轮换
密钥使用: >= t 个签名者,审计日志,速率限制
密钥备份: 地理分布式,密钥分离,年度演练
密钥归档: 7 年保留,加密防篡改,不可用于签名
密钥销毁: 书面批准,NIST SP 800-88,销毁证书
七、密钥隔离与 HSM
7.1 分片存储威胁模型
Threat 1: 操作系统级入侵 - 缓解: 全盘加密 + HSM + 最小权限
Threat 2: 内存转储 - 缓解: mlock() + 内存加密 + 使用后立即擦除
Threat 3: 供应链攻击 - 缓解: 依赖审计 + 签名验证 + TEE 隔离
Threat 4: 侧信道攻击 - 缓解: 恒定时间实现 + 噪声注入 + HSM
Threat 5: 物理访问 - 缓解: 全盘加密 + TPM 绑定 + 物理安全
7.2 TEE 架构
TEE(可信执行环境)集成:
操作系统 (不可信)
[TEE Enclave (可信环境)]
MPC Protocol Engine (GG20)
Crypto Engine (Dilithium-5)
Share Store (加密分片)
远程证明 (Remote Attestation)
[P2P 通信层 libp2p (不可信)]
支持的 TEE 后端: Intel SGX/TDX, AMD SEV-SNP, ARM TrustZone
远程证明流程:
Verifier -> Challenge -> Enclave 生成 Quote -> 返回 Quote+公钥
-> IAS/DCAP 验证签名 -> 确认可信代码 -> 信任公钥用于加密通信
7.3 HSM 集成
HSM 部署模式:
模式 A: 每节点独立 HSM - 最高安全,成本高,适用高价值资产
模式 B: 共享云 HSM - 成本低,弹性扩展,适用标准部署
模式 C: 软件 HSM + TEE - 部署灵活,适用开发测试
模式 D: 混合模式 - 平衡安全与成本,适用大多数生产场景
支持的 HSM: YubiHSM 2, Nitrokey HSM, AWS/Azure/GCP CloudHSM, Ledger
八、监控与审计
8.1 监控架构
MPC 节点监控架构:
Prometheus <--scrape-- MPC Node (port 9004)
|
v
Alertmanager --> 告警通知 (Slack/PagerDuty/Email)
|
v
Grafana --> 可视化仪表盘
每节点暴露的监控维度:
系统层: CPU/内存/磁盘/网络 IO
协议层: DKG/签名延迟/成功率
网络层: P2P 连接数/消息吞吐量
安全层: 异常检测/验证失败/投诉
密钥层: 分片数量/版本/备份状态
8.2 Prometheus 指标
mpc_node_info - 节点元数据
mpc_node_uptime_seconds - 节点运行时间
mpc_node_peers_connected - 连接对等节点数
mpc_dkg_total - DKG 会话计数(按状态分类)
mpc_sign_total - 签名会话计数(按状态分类)
mpc_sign_duration_seconds - 签名延迟直方图
mpc_node_health - 节点健康状态
mpc_node_shares_count - 本地分片数量
mpc_p2p_messages_sent - P2P 消息计数(按类型分类)
mpc_share_verification_failures - 分片验证失败计数
mpc_complaints_filed - DKG 投诉计数
8.3 告警规则
# mpc-alert-rules.yml
groups:
- name: mpc-node
rules:
- alert: NodeDown
expr: mpc_node_health == 0
for: 5m
labels: { severity: critical }
annotations:
summary: "MPC node {{ $labels.node_id }} is down"
- alert: HighSignFailureRate
expr: rate(mpc_sign_total{status="failed"}[5m]) / rate(mpc_sign_total{status="completed"}[5m]) > 0.05
for: 10m
labels: { severity: warning }
annotations:
summary: "Signature failure rate > 5%"
- alert: LowPeerCount
expr: mpc_node_peers_connected < 3
for: 2m
labels: { severity: warning }
annotations:
summary: "Node has fewer than 3 connected peers"
- alert: DkgFailure
expr: rate(mpc_dkg_total{status="failed"}[30m]) > 0
for: 1m
labels: { severity: critical }
annotations:
summary: "DKG failure detected"
- alert: ShareVerificationFailure
expr: rate(mpc_share_verification_failures[15m]) > 0
for: 5m
labels: { severity: critical }
annotations:
summary: "Share verification failures detected"
8.4 Grafana 仪表盘建议
Row 1: 集群概览 - 健康状态 / 活跃会话 / 成功率 / 延迟 P50/P95/P99
Row 2: 节点详情 - 连接数 / 分片数 / CPU内存 / 网络 IO
Row 3: 协议指标 - DKG 成功率 / 签名轮次延迟 / MtA 时间 / ZKP 时间
Row 4: P2P 网络 - 连接状态 / 消息吞吐量 / 消息延迟
Row 5: 安全 - 异常检测 / 失败尝试 / 投诉频率 / 轮换状态
8.5 防篡改审计日志
import json, hashlib, os
from datetime import datetime
class MPCAuditLogger:
"""哈希链审计日志系统"""
def __init__(self, log_path):
self.log_path = log_path
self._chain_head = self._load_chain_head()
def _load_chain_head(self):
try:
with open(f"{self.log_path}.chain", "r") as f:
return f.read().strip()
except FileNotFoundError:
return None
def _update_chain_head(self, entry_hash):
with open(f"{self.log_path}.chain", "w") as f:
f.write(entry_hash)
def log_event(self, event_type, severity, context, result=None):
entry = {
"timestamp": datetime.utcnow().isoformat() + "Z",
"event_type": event_type,
"severity": severity,
"source": {"node_id": os.uname().nodename, "pid": os.getpid()},
"context": context,
"result": result or {"status": "success"},
"previous_hash": self._chain_head or "",
}
entry_json = json.dumps(entry, sort_keys=True, default=str)
entry_hash = hashlib.sha256(entry_json.encode()).hexdigest()
entry["entry_hash"] = entry_hash
with open(self.log_path, "a") as f:
f.write(json.dumps(entry, default=str) + "\\n")
self._update_chain_head(entry_hash)
return entry_hash
def verify_chain_integrity(self):
prev = ""
with open(self.log_path, "r") as f:
for line in f:
entry = json.loads(line)
entry_copy = dict(entry)
del entry_copy["entry_hash"]
computed = hashlib.sha256(
json.dumps(entry_copy, sort_keys=True, default=str).encode()
).hexdigest()
if computed != entry["entry_hash"]:
return False
if entry["previous_hash"] != prev:
return False
prev = entry["entry_hash"]
return True
8.6 审计事件分类
KEY_MGMT: KEY_CREATED / KEY_ROTATED / KEY_ARCHIVED / KEY_DESTROYED / KEY_BACKUP / KEY_RESTORED
SIGN: SIGN_REQUESTED / SIGN_PARTIAL / SIGN_COMPLETED / SIGN_FAILED / SIGN_TIMEOUT
NODE: NODE_JOINED / NODE_LEFT / NODE_SUSPENDED / NODE_BLACKLISTED
PROTOCOL: DKG_PHASE_STARTED / DKG_COMPLAINT / DKG_ABORTED
ACCESS: API_KEY_CREATED / UNAUTHORIZED_ACCESS / RATE_LIMIT_HIT
8.7 异常签名检测引擎
from collections import defaultdict
from dataclasses import dataclass
import time
@dataclass
class AnomalyAlert:
alert_id: str
severity: str
rule_name: str
description: str
affected_nodes: list
timestamp: float
class AnomalyDetector:
def __init__(self):
self.sign_rate_tracker = defaultdict(list)
self.alert_handlers = []
def register_alert_handler(self, handler):
self.alert_handlers.append(handler)
def _emit_alert(self, alert):
for handler in self.alert_handlers:
handler(alert)
def check_sign_rate(self, wallet_id, node_id):
now = time.time()
window = 60
key = f"{wallet_id}:{node_id}"
self.sign_rate_tracker[key] = [
t for t in self.sign_rate_tracker[key] if now - t < window
]
self.sign_rate_tracker[key].append(now)
rate = len(self.sign_rate_tracker[key])
if rate > 60:
return AnomalyAlert(
alert_id=f"anom-{int(now)}",
severity="critical",
rule_name="sign_rate_exceeded",
description=f"Node {node_id} signing rate: {rate}/min",
affected_nodes=[node_id],
timestamp=now,
)
return None
def check_signer_combo(self, wallet_id, signers, history):
combo = frozenset(signers)
if combo not in history.get(wallet_id, set()):
return AnomalyAlert(
alert_id=f"anom-combo-{int(time.time())}",
severity="warning",
rule_name="unusual_signer_combo",
description=f"New signer combination: {signers}",
affected_nodes=signers,
timestamp=time.time(),
)
return None
def check_transaction_value(self, wallet_id, amount, historical_stats):
stats = historical_stats.get(wallet_id)
if not stats:
return None
if amount > stats["mean"] + 5 * stats["std"]:
return AnomalyAlert(
alert_id=f"anom-value-{int(time.time())}",
severity="critical",
rule_name="unusual_value",
description=f"Amount {amount} exceeds 5sigma threshold",
affected_nodes=["all"],
timestamp=time.time(),
)
return None
九、与 MSG Chain 的集成
9.1 集成架构
MPC 签名服务 -> Agent API 网关 -> MSG Chain 区块链
[签名会话] [交易构建器] [状态机管理器] -> [POST/GET MPC 端点] -> [agent_mpc_v1 合约]
Chain ID: msg-chain-1
Bech32: msg
RPC: https://rpc.msgchain.org
REST: https://api.msgchain.org
9.2 MPC Gateway 客户端
import httpx
class MPCGatewayClient:
def __init__(self, gateway_url="https://api.msgchain.org",
api_key=None, chain_id="msg-chain-1"):
self.gateway_url = gateway_url
headers = {"Content-Type": "application/json"}
if api_key:
headers["X-API-Key"] = api_key
self._client = httpx.Client(base_url=gateway_url, headers=headers, timeout=60)
def request_signature(self, wallet_id, tx_body, signer_order, timeout=300, memo=""):
payload = {"tx_body": tx_body, "signer_order": signer_order,
"session_timeout": timeout, "memo": memo, "chain_id": "msg-chain-1"}
resp = self._client.post(f"/agent/v1/mpc/wallet/{wallet_id}/sign", json=payload)
if resp.headers.get("X-MSG-Stub") == "true":
print("[WARN] MPC sign endpoint is Stub")
resp.raise_for_status()
return resp.json()
def query_session(self, wallet_id, session_id):
resp = self._client.get(f"/agent/v1/mpc/wallet/{wallet_id}/sign/{session_id}")
resp.raise_for_status()
return resp.json()
def poll_signature(self, wallet_id, session_id, interval=2.0, timeout=300.0):
import time
start = time.time()
while True:
if time.time() - start > timeout:
raise TimeoutError(f"Session {session_id} timed out")
state = self.query_session(wallet_id, session_id)
if state["status"] == "COMPLETED":
return state
elif state["status"] in ("FAILED", "TIMEOUT"):
raise RuntimeError(state.get("error", {}).get("message", "unknown"))
time.sleep(interval)
9.3 交易构建与广播
def build_cosmos_tx(messages, signature, pubkey, tx_config, memo="", fee_gas=350_000):
import base64, json
gas_price_num = int(float(tx_config["gas_price"]) * 10**18)
fee_amt = str(gas_price_num * fee_gas)
tx = {
"body": {"messages": messages, "memo": memo},
"auth_info": {
"signer_infos": [{
"public_key": {"@type": "/cosmos.crypto.dilithium.PubKey",
"key": base64.b64encode(pubkey).decode()},
"mode_info": {"single": {"mode": "SIGN_MODE_DIRECT"}},
"sequence": str(tx_config["sequence"]),
}],
"fee": {"amount": [{"denom": "umsg", "amount": fee_amt}],
"gas_limit": str(fee_gas)},
},
"signatures": [base64.b64encode(signature).decode()],
}
return json.dumps(tx).encode("utf-8")
def broadcast_tx(rpc_endpoint, signed_tx_bytes):
import base64, httpx
tx_b64 = base64.b64encode(signed_tx_bytes).decode("ascii")
resp = httpx.post(rpc_endpoint, json={
"jsonrpc": "2.0", "id": 1, "method": "broadcast_tx_sync",
"params": {"tx": tx_b64},
}, timeout=30)
resp.raise_for_status()
return resp.json().get("result", {})
def wait_for_tx(rpc_endpoint, tx_hash, timeout=60):
import time, httpx
start = time.time()
while time.time() - start < timeout:
resp = httpx.post(rpc_endpoint, json={
"jsonrpc": "2.0", "id": 1, "method": "tx",
"params": {"hash": f"0x{tx_hash}"},
})
if resp.status_code == 200:
tx = resp.json().get("result", {})
if tx.get("height") and int(tx["height"]) > 0:
return {"height": int(tx["height"]), "tx_hash": tx_hash}
time.sleep(2)
raise TimeoutError(f"Tx {tx_hash} not confirmed")
9.4 agent_mpc_v1 合约接口状态
ExecuteMsg:
RegisterWallet { wallet_id, threshold, signers, public_key } - 已实现
UpdateSigners { wallet_id, signers } - 部分实现
VerifySignature { wallet_id, message, signature } - 已实现 (Dilithium-5 验证)
ArchiveWallet { wallet_id } - 部分实现
QueryMsg:
GetWallet { wallet_id } -> WalletInfo - 已实现
ListWallets { owner } -> Vec<WalletInfo> - 已实现
GetSigners { wallet_id } -> Vec<SignerInfo> - 已实现
未实现: 阈值签名逻辑(链上聚合/阈值检查)
9.5 完整集成时序
1. 构建 Unsigned Tx - 创建 Cosmos SDK 交易消息
2. 发起 MPC 签名会话 - POST /agent/v1/mpc/wallet/{id}/sign
3. 收集部分签名 - 各签名者提交部分签名,协调器聚合
4. 组装完整交易 - 将签名和公钥嵌入交易
5. 广播至 MSG Chain - broadcast_tx_sync
6. 等待确认 - 轮询 tx 查询端点
预计耗时: MPC 签名 1-5s + 广播上链 5-10s = 总计约 6-15s
十、灾难恢复
10.1 灾难场景分类
Level 1: 单节点故障 - 宕机/网络分区/进程崩溃 - 自动重启恢复
Level 2: 多节点故障 - 2+ 节点宕机/数据中心故障 - 紧急恢复协议
Level 3: 密钥分片丢失 - 存储损坏/备份不可用 - 备份还原/社交恢复
Level 4: 全网级灾难 - 所有节点故障 - 冷备份启动/新链部署
10.2 单节点恢复
自动恢复流程:
Step 1: 健康检查(进程/磁盘/网络/分片完整性)
Step 2: 状态恢复(加载分片,连接 P2P,同步状态)
Step 3: 节点验证(其他节点验证身份和分片完整性)
Step 4: 恢复签名服务(OFFLINE -> ACTIVE)
10.3 多节点故障恢复
条件: 丢失节点数量 >= t,签名能力丧失
紧急恢复:
Phase 1: 灾难评估(剩余节点、数据完整性)
Phase 2: 选择策略(剩余节点/备份恢复/社交恢复/钱包迁移)
Phase 3: 执行恢复(启动备用节点,分配分片,运行恢复协议)
Phase 4: 验证切换(测试签名,更新注册,通知依赖方)
10.4 恢复演练计划
Q1: 单节点故障恢复演练 - 目标 < 5 分钟恢复签名能力
Q2: 多节点故障恢复演练 - 目标 < 30 分钟恢复签名能力
Q3: 备份恢复演练 - 目标 100% 备份可用性
Q4: 全网级灾难演练 - 目标 < 4 小时恢复完整服务
十一、实践:部署 3/5 阈值 MPC 签名集群
11.1 环境准备
硬件要求(每个节点):
CPU: 4 核以上(推荐 8 核)
内存: 8 GB 以上(推荐 16 GB)
磁盘: 50 GB SSD(加密分区)
网络: 100 Mbps 以上,低延迟
软件要求: Ubuntu 22.04 / Debian 12, Docker 24.0+, Docker Compose 2.20+
网络要求:
TCP 9001: P2P 节点通信
TCP 9002: gRPC 内部通信
TCP 9003: HTTP API
TCP 9004: Prometheus 指标
11.2 初始化节点身份
# 初始化 5 个节点的身份密钥
for node in bootstrap node-a node-b node-c node-d node-e; do
mkdir -p "./keys/$node"
msg-chain-devkit keys generate --algorithm dilithium5 --output "./keys/$node/node-identity.dilithium"
msg-chain-devkit p2p keygen --output "./keys/$node/peer_id"
done
11.3 Docker Compose 部署
version: "3.9"
networks:
mpc-net:
driver: bridge
ipam:
config:
- subnet: "172.20.0.0/16"
services:
bootstrap:
image: msgchain/mpc-node:latest
container_name: mpc-bootstrap
ports: ["9001:9001", "9004:9004"]
networks: {mpc-net: {ipv4_address: "172.20.0.100"}}
node-a:
image: msgchain/mpc-node:latest
container_name: mpc-node-a
depends_on: [bootstrap]
networks: {mpc-net: {ipv4_address: "172.20.0.1"}}
volumes:
- ./keys/node-a:/etc/mpc-node/keys
- ./data/node-a:/var/lib/mpc-node
node-b:
image: msgchain/mpc-node:latest
container_name: mpc-node-b
depends_on: [bootstrap]
networks: {mpc-net: {ipv4_address: "172.20.0.2"}}
volumes:
- ./keys/node-b:/etc/mpc-node/keys
- ./data/node-b:/var/lib/mpc-node
node-c:
image: msgchain/mpc-node:latest
container_name: mpc-node-c
depends_on: [bootstrap]
networks: {mpc-net: {ipv4_address: "172.20.0.3"}}
volumes:
- ./keys/node-c:/etc/mpc-node/keys
- ./data/node-c:/var/lib/mpc-node
node-d:
image: msgchain/mpc-node:latest
container_name: mpc-node-d
depends_on: [bootstrap]
networks: {mpc-net: {ipv4_address: "172.20.0.4"}}
volumes:
- ./keys/node-d:/etc/mpc-node/keys
- ./data/node-d:/var/lib/mpc-node
node-e:
image: msgchain/mpc-node:latest
container_name: mpc-node-e
depends_on: [bootstrap]
networks: {mpc-net: {ipv4_address: "172.20.0.5"}}
volumes:
- ./keys/node-e:/etc/mpc-node/keys
- ./data/node-e:/var/lib/mpc-node
orchestrator:
image: msgchain/mpc-orchestrator:latest
container_name: mpc-orchestrator
depends_on: [node-a, node-b, node-c, node-d, node-e]
ports: ["8080:8080", "9090:9090"]
environment:
- ORCHESTRATOR_CHAIN_ID=msg-chain-1
- ORCHESTRATOR_RPC_ENDPOINT=https://rpc.msgchain.org
networks: {mpc-net: {ipv4_address: "172.20.0.200"}}
11.4 启动与管理
# 启动集群
docker compose up -d
# 查看节点状态
curl -s http://localhost:9004/health | jq .
# 查看 P2P 对等节点
curl -s http://localhost:9003/api/v1/peers | jq .
# 查看本地分片
curl -s http://localhost:9003/api/v1/shares | jq .
# 导出备份
curl -X POST http://localhost:9003/api/v1/shares/export -H "Content-Type: application/json" -d '{"backup_key_id": "backup-key-v2", "output_dir": "/backup"}'
# Prometheus 指标
curl -s http://localhost:9004/metrics | grep mpc_
11.5 执行 DKG 和测试签名
import httpx, time, json
def run_dkg(orchestrator_url, wallet_name, threshold, signer_nodes):
"""通过 Orchestrator 发起 DKG"""
client = httpx.Client(base_url=orchestrator_url, timeout=300)
payload = {
"name": wallet_name,
"threshold": threshold,
"total_signers": len(signer_nodes),
"signers": [{"node_id": n["node_id"], "pubkey": n["pubkey"]} for n in signer_nodes],
"chain_id": "msg-chain-1",
}
resp = client.post("/agent/v1/mpc/wallet", json=payload)
is_stub = resp.headers.get("X-MSG-Stub") == "true"
if is_stub:
print("[Stub] Wallet creation is simulated - sandbox only")
wallet = resp.json()
print(f"Wallet created: {wallet['wallet_id']}")
print(f"Address: {wallet.get('address', 'N/A')}")
print(f"Threshold: {threshold}/{len(signer_nodes)}")
return wallet["wallet_id"]
def test_mpc_signature(orchestrator_url, wallet_id, signer_order):
"""执行测试签名验证 DKG 成功"""
client = httpx.Client(base_url=orchestrator_url, timeout=60)
test_payload = {"test_message": "MPC_CLUSTER_VERIFICATION"}
resp = client.post(
f"/agent/v1/mpc/wallet/{wallet_id}/sign",
json={"payload": test_payload, "signer_order": signer_order, "chain_id": "msg-chain-1"},
)
session = resp.json()
session_id = session["session_id"]
print(f"Sign session: {session_id}")
# 轮询直到完成
for i in range(30):
state = client.get(
f"/agent/v1/mpc/wallet/{wallet_id}/sign/{session_id}"
).json()
status = state["status"]
if status == "COMPLETED":
sig = state.get("full_signature", "")
print(f"Signature obtained: {sig[:64]}...")
print(f"Size: {state.get('signature_size_bytes', 'N/A')} bytes")
return True
elif status in ("FAILED", "TIMEOUT"):
print(f"Signing failed: {state.get('error', {})}")
return False
print(f" Status: {status} (attempt {i+1})")
time.sleep(2)
return False
# 使用示例
nodes = [
{"node_id": "node-a", "pubkey": "dilithium5:pubkey_a_here..."},
{"node_id": "node-b", "pubkey": "dilithium5:pubkey_b_here..."},
{"node_id": "node-c", "pubkey": "dilithium5:pubkey_c_here..."},
{"node_id": "node-d", "pubkey": "dilithium5:pubkey_d_here..."},
{"node_id": "node-e", "pubkey": "dilithium5:pubkey_e_here..."},
]
wallet_id = run_dkg("http://localhost:8080", "my-3-5-wallet", 3, nodes)
test_mpc_signature("http://localhost:8080", wallet_id, ["node-a", "node-b", "node-c"])
11.6 节点管理操作
# 查看节点健康状态
curl -s http://localhost:9004/health | jq .
# 查看 P2P 对等节点列表
curl -s http://localhost:9003/api/v1/peers | jq .
# 查看本地存储的分片列表
curl -s http://localhost:9003/api/v1/shares | jq .
# 查看活跃签名会话
curl -s http://localhost:8080/api/v1/sessions/active | jq .
# 查看节点日志 (错误级别)
docker logs mpc-node-a --tail 100 2>&1 | grep -i error
# 导出加密分片备份
curl -X POST http://localhost:9003/api/v1/shares/export \
-H "Content-Type: application/json" \
-d '{"backup_key_id": "backup-key-v2", "output_dir": "/backup"}'
# 暂停节点签名 (优雅维护)
curl -X POST http://localhost:9003/api/v1/pause
# 恢复节点签名
curl -X POST http://localhost:9003/api/v1/resume
# 触发密钥轮换
curl -X POST http://localhost:8080/api/v1/keys/rotate \
-H "Content-Type: application/json" \
-d '{"wallet_id": "mpc-wallet-uuid-xxx"}'
# 查看 Prometheus 指标
curl -s http://localhost:9004/metrics | grep mpc_
11.7 集群健康检查
#!/bin/bash
NODES=("node-a" "node-b" "node-c" "node-d" "node-e")
ALL_HEALTHY=true
for node in "${NODES[@]}"; do
PORT=9004 # In production, each node has a different port
STATUS=$(curl -sf "http://localhost:$PORT/health" 2>/dev/null | jq -r '.status' || echo "unreachable")
if [ "$STATUS" = "healthy" ]; then echo "[PASS] $node"; else echo "[FAIL] $node"; ALL_HEALTHY=false; fi
done
if [ "$ALL_HEALTHY" = true ]; then echo "Cluster HEALTHY"; else echo "Cluster UNHEALTHY"; fi
十二、安全边界与限制
12.1 当前实现限制
诚实陈述:MSG Chain 的 MPC 签名服务当前处于 partial/Stub 状态。以下限制必须在评估生产使用前仔细审查。
| 限制领域 | 具体问题 | 影响程度 | 预计解决 |
|---|---|---|---|
| API 写路径 | 所有 POST/DELETE 端点返回 Stub 响应 | 不可生产 | Q4 2026 |
| agent_mpc_v1 合约 | 阈值签名逻辑未实现 | 链上无法验证 | 待定 |
| 阈值 Dilithium-5 | 仅 Shamir 分片可用,完整 GG20 未实现 | 安全性不足 | 路线图中 |
| P2P 网络 | 基础框架搭建中 | 无法部署多节点 | Q4 2026 |
| HSM 集成 | PKCS#11 接口实现中 | 无法硬件存储 | Q1 2027 |
| 灾难恢复 | 恢复协议设计阶段 | 无开发参考级恢复 | Q4 2026 |
12.2 风险缓解策略
短期(当前 - 2026 Q4):
- 仅在沙箱/测试环境使用 MPC 写路径
- 使用标准 Dilithium-5 签名(非阈值)进行生产签名
- 通过传统 cw3 multisig 合约实现多签需求
中期(2026 Q4 - 2027 Q2):
- MPC 写路径解除 Stub 后升级到阈值签名
- 实施分片备份策略,开始 HSM 集成评估
长期(2027 Q2+):
- 完整 GG20-Dilithium5 协议上线
- TEE + HSM 双保险架构
- 开发参考级灾难恢复能力
12.3 未来路线图
v0.1 (当前 - 原型)
Shamir 秘密共享 + 基本存储
Dilithium-5 标准签名(已实现)
Agent API GET 端点(已实现)
Agent API POST/DELETE 端点(Stub)
v0.2 (2026 Q4 - 早期可用)
完整 GG20 框架集成
agent_mpc_v1 合约阈值逻辑
MPC P2P 网络 alpha 版本
测试网可用
v0.3 (2027 Q1 - Beta)
阈值 Dilithium-5 协议完整
HSM PKCS#11 集成
灾难恢复协议
监控仪表盘
v1.0 (2027 Q2+ - GA)
TEE 集成(SGX/TDX)
完整审计和合规
地理冗余部署
性能优化(~500 TPS 签名)
12.4 总结
优势:
- 后量子安全(Dilithium-5)
- 单签名大小固定
- 与 Cosmos SDK 交易格式兼容
- Agent API 架构支持无缝升级
- 支持分层安全(MPC + 多签 + 社交恢复)
当前限制:
- 写路径端点为 Stub,不可用于生产
- agent_mpc_v1 阈值逻辑未实现
- 完整 GG20 框架未集成
- HSM/TEE 部署尚在路线图中
建议:
- 生产部署使用标准 Dilithium-5 签名 + cw3 多签
- 沙箱环境中探索 MPC 阈值签名能力
- 关注路线图时间节点,规划升级迁移
附录一:术语表
| 术语 | 英文 | 定义 |
|---|---|---|
| MPC | Multi-Party Computation | 多方计算,多个参与方协同计算但不泄露各自私密输入 |
| DKG | Distributed Key Generation | 分布式密钥生成,无可信中心的密钥对生成协议 |
| GG18 | Garay-Gennaro 2018 | 首个实用两轮 ECDSA 阈值签名协议 |
| GG20 | Garay-Gennaro 2020 | 改进版阈值签名协议 |
| CMP | Canetti-Makriyannis-Peli | 支持 EdDSA 的阈值签名协议 |
| MtA | Multiplication-to-Addition | 乘法转加法子协议 |
| ZKP | Zero-Knowledge Proof | 零知识证明 |
| Paillier | Paillier Cryptosystem | 加法同态加密方案 |
| Threshold Signature | 阈值签名 | t-of-n 签名方案 |
| Shamir SSS | Shamir's Secret Sharing | 基于拉格朗日插值的秘密共享方案 |
| HSM | Hardware Security Module | 硬件安全模块 |
| TEE | Trusted Execution Environment | 可信执行环境 |
| Dilithium-5 | ML-DSA | NIST FIPS 204 后量子签名方案 |
| Enclave | - | TEE 中的安全执行区域 |
| PKCS#11 | Cryptoki | 密码学令牌接口标准 |
| AEAD | Authenticated Encryption with AD | 认证加密 |
| libp2p | - | 模块化 P2P 网络协议栈 |
| Refreshing | 分片刷新 | 保持公钥不变但更新分片 |
| Stub | 桩 | 端点已定义但返回模拟响应 |
附录二:参考协议与标准
参考协议:
GG18: "Fast Multiparty Threshold ECDSA with Fast Trustless Setup" (2018)
GG20: "One Round Threshold ECDSA with Identifiable Abort" (2020)
CMP: "Threshold Signatures in the Asynchronous Setting" (Canetti et al.)
FIPS 204: "Module-Lattice-Based Digital Signature Standard" (NIST, 2024)
参考标准:
NIST SP 800-88: "Guidelines for Media Sanitization"
PKCS#11: "Cryptographic Token Interface Standard"
FIPS 140-2/3: "Security Requirements for Cryptographic Modules"
RFC 6979: "Deterministic Usage of DSA and ECDSA"
附录三:配置参考
# orchestrator.yaml - MPC 协调器配置
orchestrator:
chain:
chain_id: "msg-chain-1"
rpc_endpoint: "https://rpc.msgchain.org"
rest_endpoint: "https://api.msgchain.org"
bech32_prefix: "msg"
api:
listen: ":8080"
rate_limit: 100
rate_burst: 200
session:
default_timeout: 300
max_sessions: 100
mpc:
supported_protocols: ["GG20-Dilithium5"]
default_threshold: 3
min_threshold: 2
max_parties: 50
monitoring:
metrics_enabled: true
metrics_port: 9090
health_check_interval: 10
MSG Chain Whitepaper | https://msgchain.org/whitepaper | 代码库实际状态,不代表生产可用
