rwadurian/frontend/mobile-app/docs/mpc_share_backup.md

19 KiB
Raw Blame History

MPC Share 备份与恢复方案

概述

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

架构背景

MPC 2-of-3 阈值签名

RWA Durian 使用基于 Binance 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 包含:

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 域分离

备份流程代码

/// 创建 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,             // 设备存储
  );
}

密钥派生

/// 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 密钥
}

加密实现

/// 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]                                                 │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

恢复流程代码

/// 从助记词恢复 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

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 轮迭代增加计算成本

常量时间比较

/// 防止时序攻击的比较函数
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 签名

验证助记词

/// 验证助记词是否能解密存储的 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 个测试用例)

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');
  });
});

运行测试

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

依赖

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

相关文件

版本历史

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

参考资料