---
title: "MPC 钱包安全"
description: "Fystack 如何通过门限签名、分布式密钥生成和安全的多方签名协议实现多方计算（MPC）钱包安全。"
url: https://docs.fystack.io/zh/security/mpc-security
last_updated: 2026-11-13
---

> ## Documentation index
> Fetch the complete documentation index at: https://docs.fystack.io/llms.txt
> Use it to discover all available pages before exploring further.

# MPC 钱包安全

Fystack 使用 **多方计算（MPC）** 消除私钥管理中的单点故障。任何一方都不会持有完整的私钥。密钥分片分布在多个节点上，签名则通过门限密码学协同计算得出。

本文介绍 Fystack 的 MPC 实现如何通过分布式信任、安全通信和可验证计算来保护钱包密钥。

_Diagram: Fystack MPC 钱包安全架构_

### 节点职责

每个节点：

- 仅在本地存储 **自己的加密分片**。
- 通过 **Ed25519 验证** 和 **ECDH 保护的 P2P** 通信参与门限签名。
- 即使被攻破，也无法重建或导出完整密钥。

| 属性 | 说明 |
| --- | --- |
| 机密性 | 任何单个节点或运维人员都无法访问完整私钥。 |
| 完整性 | 所有节点间消息均通过 Ed25519 或 Cloud KMS 签名进行认证。 |
| 韧性 | 支持 n 个节点、门限为 t 的配置，最多可容忍 (t-1) 个节点故障。 |
| 静态加密 | 密钥分片在本地以 AES 加密存储。 |
| 传输加密 | 通过 NATS TLS 以及 P2P 通道上的 ECDH 加密层强制实施。 |

---

## 1. 概述

多方计算让多个参与方能够共同执行密码学运算，而任何一方都无法访问完整私钥。Fystack 实现了门限签名方案，必须有最少数量（门限）的参与方协作，才能生成有效签名。

| 组件 | 说明 |
| --- | --- |
| MPC 节点 | 至少由 3 个节点组成一个集群。 |
| 门限（t） | 即使最多 (t-1) 个节点被攻破，钱包依然安全。 |
| 密钥分片 | 私钥在数学上被拆分为多个分片。 |
| 分片加密 | 每个分片均使用 AES 加密（BadgerDB 默认加密）。 |
| 消息安全 | 双层保护：NATS TLS 加上用于 P2P 加密的 ECDH 密钥交换。 |
| 消息认证 | Apex 使用签名消息（Ed25519 或 Cloud KMS）与 MPC 集群通信。 |

### 主要优势

- **无单点失陷：** 单个节点被攻破不会暴露私钥
- **分布式信任：** 必须由多个独立参与方协作
- **对区块链透明：** 输出的签名是标准的 ECDSA/EdDSA 签名
- **动态门限：** 可根据交易金额调整签名要求
- **审计记录：** 每个签名会话都有日志记录且可追溯

---

## 2. 分布式密钥生成（DKG）

Fystack 使用 **分布式密钥生成** 创建钱包密钥，任何一方都不会持有完整私钥。

### DKG 流程

**阶段 1：承诺**

- 每个参与方生成一个 t-1 次的随机多项式（t 为门限）
- 各参与方广播其多项式的承诺
- 使用 Feldman VSS（可验证秘密共享）验证承诺

**阶段 2：分片分发**

- 每个参与方在特定点上计算其多项式的值
- 通过加密通道将分片安全地发送给对应的参与方
- 每个参与方都会从其他每个参与方各收到一个分片

**阶段 3：验证**

- 各参与方根据广播的承诺验证收到的分片
- 识别无效分片并提出投诉
- 如果检测到过多无效分片，协议将中止

**阶段 4：密钥构建**

- 每个参与方组合有效分片，形成自己的密钥分片
- 根据承诺计算出公钥
- 任何一方都不知道完整私钥

**DKG 配置**

```yaml
mpc_threshold: 2   # Number of MPC nodes required to sign
```

`mpc_threshold` 表示 n-of-t 方案中的门限 `t`。在有 `n` 个活跃 MPC 节点时，系统强制要求 `2 ≤ t ≤ n`，在最多 `(t - 1)` 个节点故障或离线的情况下仍可生成签名。例如，`n = 3` 且 `mpc_threshold: 2` 时构成一个 2-of-3 集群：任意两个经过认证的节点即可签名，但攻破单个节点不足以恢复私钥。

对于 GG18/GG20 风格的鲁棒 MPC 协议，我们还建议 `t > n / 2`，以保证拜占庭容错：即使 `f = n - t` 个节点存在恶意、离线或合谋，诚实节点仍占多数，可以阻止伪造签名或私钥重建。

### 安全特性

- **鲁棒性：** 最多可容忍 n-t 个恶意参与方
- **可验证性：** 所有参与方都可以验证分片的正确性
- **隐私性：** 不会泄露任何关于私钥的信息
- **非交互性：** 完成初始设置后无需再次执行 DKG

---

## 3. 门限签名

门限签名允许（满足门限的）部分参与方协同生成有效签名，而无需重建私钥。

### 签名协议（ECDSA）

**第 1 轮：承诺阶段**

**生成承诺**

```text
// Each signer generates commitment
const k_i = randomScalar()
const gamma_i = randomScalar()
const commitment_i = {
  K_i: k_i * G,           // Ephemeral public key
  Gamma_i: gamma_i * G,   // Blinding factor
}
broadcastCommitment(commitment_i)
```

**第 2 轮：生成部分签名**

**部分签名**

```text
// After receiving all commitments
const R = combineCommitments(allCommitments)
const r = R.x  // x-coordinate of combined point

// Each party computes partial signature
const s_i = k_i + r * keyShare_i * hash(message)
broadcastPartialSignature(s_i)
```

**第 3 轮：签名聚合**

**组合签名**

```text
// Combine partial signatures
const s = lagrangeInterpolation(partialSignatures)
const signature = { r, s }

// Verify signature
const valid = ecdsaVerify(signature, publicKey, message)
```

### 签名工作流程

| 步骤 | 操作 | 安全检查 |
| --- | --- | --- |
| 1. 会话初始化 | 使用交易详情创建签名会话 | 验证会话 ID 的唯一性 |
| 2. 选择参与方 | 选择满足门限数量的参与方 | 验证参与方在线且已通过认证 |
| 3. 承诺轮次 | 交换临时密钥的承诺 | 校验承诺的格式和新鲜度 |
| 4. 部分签名 | 每个参与方计算并分享部分签名 | 验证部分签名的有效性 |
| 5. 聚合 | 将部分签名组合成最终签名 | 使用公钥验证最终签名 |
| 6. 广播 | 将已签名的交易提交到区块链 | 确认交易已被接受 |

---

## 4. 网络安全

### NATS 传输（TLS）

MPC 节点通过 `config.yaml` 中声明的共享 NATS 总线交换流量。生产部署需要 TLS endpoint 以及锁定的客户端证书：

**NATS TLS 配置**

```yaml
nats:
  url: tls://nats.example.com:4222
  username: ""
  password: ""
  tls:
    client_cert: "/etc/mpcium/certs/client-cert.pem"
    client_key: "/etc/mpcium/certs/client-key.pem"
    ca_cert: "/etc/mpcium/certs/rootCA.pem"
```

如果没有上述 `tls://` URL 和 `nats.tls.*` 路径，`deployments/systemd/setup-config.sh` 脚本会拒绝在生产环境中运行，从而确保每个节点都通过经过认证的 TLS 隧道与 NATS 通信。

### 签名消息总线

在发布 TSS 载荷之前，节点会使用其 Ed25519 身份对其进行签名（参见 `pkg/identity/fileStore`）：

**签署 TSS 消息**

```go
func (s *fileStore) SignMessage(msg *types.TssMessage) ([]byte, error) {
	msgBytes, err := msg.MarshalForSigning()
	if err != nil {
		return nil, fmt.Errorf("failed to marshal message for signing: %w", err)
	}
	signature := ed25519.Sign(s.privateKey, msgBytes)
	return signature, nil
}
```

每个节点在接受消息前都会验证签名，以防止伪造的对等方或重放数据：

**验证 TSS 消息**

```go
func (s *fileStore) VerifyMessage(msg *types.TssMessage) error {
	if msg.Signature == nil {
		return fmt.Errorf("message has no signature")
	}
	publicKey, err := s.GetPublicKey(partyIDToNodeID(msg.From))
	if err != nil {
		return fmt.Errorf("failed to get sender's public key: %w", err)
	}
	msgBytes, err := msg.MarshalForSigning()
	if err != nil {
		return fmt.Errorf("failed to marshal message for verification: %w", err)
	}
	if !ed25519.Verify(publicKey, msgBytes, msg.Signature) {
		return fmt.Errorf("invalid signature")
	}
	return nil
}
```

### 点对点加密

节点通过专用的 NATS 主题（`pkg/mpc/key_exchange_session.go`）执行 ECDH 交换，为每个对等方派生对称密钥。这些密钥用于对每个载荷进行 AES-GCM 加密：

**加密 TSS 载荷**

```go
func (s *fileStore) EncryptMessage(plaintext []byte, peerID string) ([]byte, error) {
	key, err := s.GetSymmetricKey(peerID)
	if err != nil {
		return nil, err
	}
	if key == nil {
		return nil, fmt.Errorf("no symmetric key for peer %s", peerID)
	}
	return encryption.EncryptAESGCMWithNonceEmbed(plaintext, key)
}
```

### ECDH 握手流程

ECDH 会话（`pkg/mpc/key_exchange_session.go`）构建在 NATS 总线之上：

1. 每个节点生成一个临时 X25519 密钥对，并订阅共享的 `ecdh:exchange` 主题。
2. 节点广播其公钥和时间戳，并使用其 Ed25519 身份进行签名（`SignEcdhMessage`）。
3. 监听方验证签名，使用自己的私钥执行 `ECDH()`，并通过 HKDF-SHA256 派生对称密钥。
4. 派生出的 32 字节密钥通过 `identityStore.SetSymmetricKey` 按对等方缓存，并由上述 AES-GCM 辅助函数复用。

**HKDF 密钥派生**

```go
func (e *ecdhSession) deriveSymmetricKey(secret []byte, peerID string) []byte {
	info := deriveConsistentInfo(e.nodeID, peerID) // stable ordering of IDs
	hkdfReader := hkdf.New(sha256.New, secret, nil, info)
	key := make([]byte, 32) // AES-256 key
	if _, err := hkdfReader.Read(key); err != nil {
		e.errCh <- err
		return nil
	}
	return key
}
```

如果对等方断开连接，`RemovePeer` 会丢弃缓存的对称密钥，因此重新连接时需要重新握手，从而确保通道密钥的临时性。

### 网络隔离

| 控制措施 | 实现方式 |
| --- | --- |
| 私有网络 | MPC 节点通过私有 VPC/VPN 通信 |
| 防火墙规则 | 仅开放 MPC 端口，并限制为已知节点 IP |
| 速率限制 | 限制每个节点的最大消息速率，以防范 DoS |
| 连接监控 | 记录所有连接并监控异常 |

---

## 5. 存储安全

密钥分片持久化存储在 BadgerDB 中。除非提供加密密钥（`badger_password`），否则 MPC 服务会拒绝打开数据库，该密钥会通过 `WithEncryptionKey(...)`（`pkg/kvstore/badger.go`）传入 Badger 的 AES-256 加密。备份沿用相同的加密格式，因此没有密码时，无论是运行中的数据库文件还是快照归档都无法读取。

**Badger 加密**

```go
opts := badger.DefaultOptions(config.DBPath).
  WithEncryptionKey(config.EncryptionKey).
  WithCompression(options.ZSTD)

db, err := badger.Open(opts)
```

---

## 6. 总结

Fystack 的 MPC 实现通过分布式信任、门限密码学和安全的多方协议消除了单点故障。通过将私钥拆分为分片并要求协同签名，我们确保任何单一环节被攻破都不会导致资产损失。

**关键安全特性：**

- **分布式密钥生成**，无单一信任点
- **门限签名**，需要多个参与方协作
- **加密通信**，采用 TLS 以及基于 ECDH 派生密钥的 AES-GCM 载荷加密
- **BadgerDB 静态加密**，使用 AES-256 及运维人员提供的密码
- **可验证计算**，用于检测恶意参与方
