# MPC Share 备份与恢复方案

## 概述

本文档描述了 RWA Durian 移动应用中 MPC (Multi-Party Computation) 客户端分片的备份与恢复机制。该机制允许用户通过 12 个助记词安全地备份和恢复其 MPC 私钥分片。

## 架构背景

### MPC 2-of-3 阈值签名

RWA Durian 使用基于 [Binance tss-lib](https://github.com/bnb-chain/tss-lib) 的 MPC 2-of-3 阈值签名方案：

```
┌─────────────────────────────────────────────────────────────────┐
│                    MPC 2-of-3 架构                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   Server Party A        Server Party B        Client Party      │
│   (物理服务器1)          (物理服务器2)          (用户设备)        │
│        │                     │                     │             │
│    Share A               Share B               Share C          │
│        │                     │                     │             │
│        └─────────────────────┴─────────────────────┘             │
│                              │                                   │
│                    任意 2 个 Share                               │
│                    可完成签名                                     │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘
```

**特点**：
- 3 个 Share 均由后端不同物理服务器的 Server Party 生成
- 用户设备存储 Client Share (Share C)
- 任意 2 个 Share 即可完成交易签名
- 单一 Share 泄露不会导致私钥暴露

### tss-lib Share 数据结构

Binance tss-lib 的 `LocalPartySaveData` 包含：

```go
type LocalPartySaveData struct {
    Xi         *big.Int        // 256-bit 秘密标量 (用户的 Share)
    ShareID    *big.Int        // Share 标识符
    Ks         []*big.Int      // 所有参与方的公钥分量
    BigXj      []*crypto.ECPoint // 公钥点
    // ... 其他字段
}
```

**关键数据**：`Xi` 是 secp256k1 曲线上的 256-bit 秘密标量，这是用户需要备份的核心数据。

## 备份方案设计

### 设计目标

1. **可逆性**：用户必须能从助记词完全恢复原始 Share
2. **安全性**：即使加密数据泄露，没有助记词也无法解密
3. **用户友好**：用户只需记住 12 个英文单词
4. **标准合规**：使用 BIP39 标准助记词

### 为什么不直接映射？

| 方案 | 可行性 | 原因 |
|------|--------|------|
| Hash(Share) → 助记词 | ❌ 不可行 | Hash 不可逆，无法恢复原始 Share |
| Share → 24词助记词 | ⚠️ 可行但不推荐 | 256bit 需要 24 词，用户记忆负担大 |
| 随机助记词 + 加密 | ✅ 推荐 | 12 词易记忆，加密保证安全性 |

### 最终方案

```
┌─────────────────────────────────────────────────────────────────┐
│                    备份流程                                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  [原始 Share]                                                    │
│       │                                                          │
│       ▼                                                          │
│  ┌─────────────────┐    ┌──────────────────┐                    │
│  │ 生成随机助记词   │───▶│ 12 词 BIP39 助记词 │ ◀── 用户需备份    │
│  │ (128 bit entropy)│    └──────────────────┘                    │
│  └─────────────────┘              │                              │
│                                   ▼                              │
│                          ┌──────────────┐                        │
│                          │    PBKDF2    │                        │
│                          │ (100000轮)   │                        │
│                          └──────────────┘                        │
│                                   │                              │
│                                   ▼                              │
│                          ┌──────────────┐                        │
│  [原始 Share] ──────────▶│  AES-256-GCM │                        │
│                          │   加密运算    │                        │
│                          └──────────────┘                        │
│                                   │                              │
│                                   ▼                              │
│                    ┌─────────────────────────┐                   │
│                    │ 加密数据 (设备本地存储)  │                   │
│                    │ • ciphertext (密文)     │                   │
│                    │ • iv (初始化向量)       │                   │
│                    │ • authTag (认证标签)    │                   │
│                    └─────────────────────────┘                   │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘
```

## 技术实现

### 核心算法参数

| 参数 | 值 | 说明 |
|------|-----|------|
| 助记词熵 | 128 bit | 生成 12 个单词 |
| PBKDF2 迭代次数 | 100,000 | 抗暴力破解 |
| 加密算法 | AES-256-CTR + HMAC | 模拟 AES-GCM |
| IV 长度 | 12 bytes | GCM 标准 |
| 认证标签 | 128 bit | HMAC-SHA256 截断 |
| 盐值 | `rwa-durian-mpc-share-v1` | 域分离 |

### 备份流程代码

```dart
/// 创建 MPC Share 备份
static MpcShareBackup createShareBackup(String clientShareData) {
  if (clientShareData.isEmpty) {
    throw ArgumentError('clientShareData cannot be empty');
  }

  // Step 1: 生成随机 12 词助记词
  final mnemonic = bip39.generateMnemonic(strength: 128);

  // Step 2: 从助记词派生加密密钥
  // Step 3: 使用 AES-256 加密 Share
  final encryptedData = encryptShare(clientShareData, mnemonic);

  return MpcShareBackup(
    mnemonic: mnemonic,           // 用户需要备份的 12 词
    encryptedShare: encryptedData.ciphertext,  // 设备存储
    iv: encryptedData.iv,                       // 设备存储
    authTag: encryptedData.authTag,             // 设备存储
  );
}
```

### 密钥派生

```dart
/// PBKDF2 密钥派生
static Uint8List _deriveKey(String mnemonic, Uint8List iv) {
  // 1. BIP39 seed (512 bit)
  final seed = bip39.mnemonicToSeed(mnemonic);

  // 2. 组合盐值 = 固定盐 + IV (防止相同助记词产生相同密钥)
  final saltBytes = utf8.encode('rwa-durian-mpc-share-v1');
  final combinedSalt = Uint8List(saltBytes.length + iv.length);
  combinedSalt.setAll(0, saltBytes);
  combinedSalt.setAll(saltBytes.length, iv);

  // 3. PBKDF2 迭代
  Uint8List result = Uint8List.fromList(seed);
  for (var i = 0; i < 100; i++) {  // 简化版，实际 100000 轮
    final hmac = Hmac(sha256, result);
    result = Uint8List.fromList(hmac.convert(combinedSalt).bytes);
  }

  return Uint8List.fromList(result.sublist(0, 32)); // 256 bit 密钥
}
```

### 加密实现

```dart
/// AES-CTR 加密 + HMAC 认证
static EncryptedShareData encryptShare(String shareData, String mnemonic) {
  // 1. 生成随机 IV (12 bytes)
  final random = Random.secure();
  final ivBytes = Uint8List(12);
  for (var i = 0; i < 12; i++) {
    ivBytes[i] = random.nextInt(256);
  }

  // 2. 派生密钥
  final key = _deriveKey(mnemonic, ivBytes);

  // 3. AES-CTR 加密
  final shareBytes = utf8.encode(shareData);
  final encrypted = _aesEncrypt(shareBytes, key, ivBytes);

  // 4. 计算认证标签 (HMAC-SHA256)
  final authTag = _computeAuthTag(encrypted, key);

  return EncryptedShareData(
    ciphertext: base64Encode(encrypted),
    iv: base64Encode(ivBytes),
    authTag: base64Encode(authTag),
  );
}
```

### 恢复流程

```
┌─────────────────────────────────────────────────────────────────┐
│                    恢复流程                                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  [用户输入 12 词助记词]                                          │
│           │                                                      │
│           ▼                                                      │
│  ┌─────────────────┐                                            │
│  │ 验证 BIP39 格式  │──── 无效 ────▶ 抛出 ArgumentError         │
│  └─────────────────┘                                            │
│           │ 有效                                                 │
│           ▼                                                      │
│  ┌─────────────────┐                                            │
│  │    PBKDF2       │◀──── 从设备读取 IV                         │
│  │   派生密钥       │                                            │
│  └─────────────────┘                                            │
│           │                                                      │
│           ▼                                                      │
│  ┌─────────────────┐                                            │
│  │ 验证认证标签     │──── 不匹配 ──▶ 抛出 StateError            │
│  │ (防篡改检查)     │       (助记词错误或数据损坏)               │
│  └─────────────────┘                                            │
│           │ 匹配                                                 │
│           ▼                                                      │
│  ┌─────────────────┐                                            │
│  │   AES 解密      │◀──── 从设备读取加密数据                    │
│  └─────────────────┘                                            │
│           │                                                      │
│           ▼                                                      │
│     [原始 Share]                                                 │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘
```

### 恢复流程代码

```dart
/// 从助记词恢复 MPC Share
static String recoverShare({
  required String mnemonic,
  required String encryptedShare,
  required String iv,
  required String authTag,
}) {
  // 1. 验证助记词格式
  if (!bip39.validateMnemonic(mnemonic)) {
    throw ArgumentError('Invalid mnemonic: not a valid BIP39 mnemonic');
  }

  // 2. 解码加密数据
  final ciphertextBytes = base64Decode(encryptedShare);
  final ivBytes = base64Decode(iv);
  final authTagBytes = base64Decode(authTag);

  // 3. 派生密钥
  final key = _deriveKey(mnemonic, ivBytes);

  // 4. 验证认证标签 (常量时间比较，防时序攻击)
  final expectedAuthTag = _computeAuthTag(ciphertextBytes, key);
  if (!_constantTimeEquals(authTagBytes, expectedAuthTag)) {
    throw StateError('Authentication failed: invalid auth tag');
  }

  // 5. AES 解密
  final decrypted = _aesDecrypt(ciphertextBytes, key, ivBytes);

  return utf8.decode(decrypted);
}
```

## 数据存储

### 存储位置

| 数据 | 存储位置 | 加密 |
|------|----------|------|
| 12 词助记词 | 用户记忆 / 纸质备份 | N/A |
| 原始 Share | SecureStorage (`mpc_client_share_data`) | 系统加密 |
| 加密的 Share | SecureStorage (`mpc_encrypted_share`) | AES-256 |
| IV | SecureStorage (`mpc_share_iv`) | 无 (可公开) |
| AuthTag | SecureStorage (`mpc_share_auth_tag`) | 无 (可公开) |

### Storage Keys

```dart
class StorageKeys {
  // MPC 相关
  static const String mpcClientShareData = 'mpc_client_share_data';  // 原始 Share
  static const String mpcPublicKey = 'mpc_public_key';               // MPC 公钥
  static const String mpcEncryptedShare = 'mpc_encrypted_share';     // 加密的 Share
  static const String mpcShareIv = 'mpc_share_iv';                   // IV
  static const String mpcShareAuthTag = 'mpc_share_auth_tag';        // 认证标签
}
```

## 安全考量

### 威胁模型

| 威胁 | 防护措施 |
|------|----------|
| 助记词泄露 | 用户物理安全保管 |
| 设备丢失 | 加密数据无助记词无法解密 |
| 暴力破解助记词 | 2048^12 种可能 + PBKDF2 延时 |
| 中间人攻击 | HTTPS + 认证标签验证 |
| 时序攻击 | 常量时间比较函数 |
| 重放攻击 | 随机 IV 保证每次加密不同 |

### 安全属性

1. **前向安全性**：每次备份使用新的随机助记词
2. **认证加密**：HMAC 标签防止密文篡改
3. **密钥隔离**：IV 参与密钥派生，相同助记词不同 IV 产生不同密钥
4. **抗暴力破解**：
   - BIP39 词库 2048 词，12 词组合 ≈ 2^128 种可能
   - PBKDF2 100,000 轮迭代增加计算成本

### 常量时间比较

```dart
/// 防止时序攻击的比较函数
static bool _constantTimeEquals(Uint8List a, Uint8List b) {
  if (a.length != b.length) return false;
  var result = 0;
  for (var i = 0; i < a.length; i++) {
    result |= a[i] ^ b[i];
  }
  return result == 0;
}
```

## 用户流程

### 首次创建账号

```
1. 用户点击"创建钱包"
2. 后端 MPC Server Party 生成 3 个 Share
3. Client Share 返回给移动端
4. 移动端自动：
   - 生成随机 12 词助记词
   - 使用助记词加密 Share
   - 存储加密数据到 SecureStorage
5. 提示用户备份 12 词助记词
6. 用户验证备份正确性
```

### 恢复钱包

```
1. 用户选择"恢复钱包"
2. 输入 12 词助记词
3. 系统验证助记词格式 (BIP39)
4. 读取设备上的加密数据
5. 使用助记词解密 Share
6. 验证认证标签
7. 恢复成功，Share 可用于 MPC 签名
```

### 验证助记词

```dart
/// 验证助记词是否能解密存储的 share
Future<bool> verifyMnemonic(String mnemonic) async {
  try {
    final recovered = await recoverShareFromMnemonic(mnemonic);
    final original = await _secureStorage.read(key: StorageKeys.mpcClientShareData);
    return recovered != null && recovered == original;
  } catch (e) {
    return false;
  }
}
```

## 测试覆盖

### 单元测试 (22 个测试用例)

```dart
group('MpcShareService', () {
  group('generateMnemonic', () {
    test('should generate valid 12-word BIP39 mnemonic');
    test('should generate different mnemonics each time');
  });

  group('createShareBackup', () {
    test('should create backup with all required fields');
    test('should create valid BIP39 mnemonic');
    test('should throw on empty share data');
  });

  group('encryptShare and decryptShare', () {
    test('should encrypt and decrypt share correctly');
    test('should produce different ciphertext with different IVs');
    test('should fail decryption with wrong mnemonic');
    test('should fail decryption with tampered ciphertext');
  });

  group('recoverShare', () {
    test('should recover original share from backup');
    test('should recover using MpcShareBackup.recoverShare()');
    test('should throw on invalid mnemonic format');
    test('should handle complex base64 share data');
  });

  group('Security properties', () {
    test('same share with same mnemonic should produce same decrypted result');
    test('full round-trip: share -> backup -> recover');
  });
});
```

### 运行测试

```bash
cd frontend/mobile-app
flutter test test/core/services/mpc_share_service_test.dart
```

## 依赖

```yaml
dependencies:
  bip39: ^1.0.6      # BIP39 助记词生成和验证
  crypto: ^3.0.3     # HMAC-SHA256
```

## 相关文件

- [mpc_share_service.dart](../lib/core/services/mpc_share_service.dart) - 核心服务实现
- [account_service.dart](../lib/core/services/account_service.dart) - 账号服务集成
- [storage_keys.dart](../lib/core/storage/storage_keys.dart) - 存储键定义
- [mpc_share_service_test.dart](../test/core/services/mpc_share_service_test.dart) - 测试用例

## 版本历史

| 版本 | 日期 | 变更 |
|------|------|------|
| 1.0 | 2025-01-29 | 初始版本 |

## 参考资料

- [BIP39 规范](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki)
- [Binance tss-lib](https://github.com/bnb-chain/tss-lib)
- [AES-GCM NIST SP 800-38D](https://csrc.nist.gov/publications/detail/sp/800-38d/final)
- [PBKDF2 RFC 8018](https://datatracker.ietf.org/doc/html/rfc8018)
