feat(e2e): 端对端加密完全对齐老项目 — cipher_guard_sdk 修正 + EncryptionManager 集成
Some checks failed
CI / Lint (push) Has been cancelled

修正 cipher_guard_sdk 4 个关键密码学差异使其与老 Flutter 项目 (im-client-im-dev) 和 iOS
EncryptionManager 完全互操作:

1. AES: 显式 SIC/CTR 模式 + 16 zero-byte IV(原 SDK 用随机 IV + KDF 派生密钥)
2. RSA: bare RSAEngine 无 PKCS1 padding(原 SDK 用 PKCS1Encoding)
3. Session key: 32-char alphanumeric ASCII(原 SDK 用 base64 random bytes)
4. Wire format: base64(ciphertext) 无 IV 前缀

新增 EncryptionManager:
- Per-chat round-based key chain(最多 10 rounds/chat,FIFO 淘汰)
- 登录后自动 setup:RSA 密钥对生成/存储 + 公钥上传 + chat 密钥拉取解密
- API 集成:cipher/v2/key/my, key/set, chat/my
- 消息加密返回 JSON envelope {"round":N,"data":"<base64>"}
- 消息解密兼容 JSON envelope + legacy raw base64

集成到消息流:
- SendMessageUseCase: 发送前加密 content → wireContent
- WsMessageService: 收到消息后解密 content + lastMsg
- 无密钥时 fallback 到明文(对齐 iOS 行为)

注意:/app/api/cipher/v2/key/set 仍为预发布接口,仅测试阶段使用

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
pp-bot
2026-04-14 21:44:00 +09:00
parent e8f58212e6
commit 52a3f0f45c
18 changed files with 1307 additions and 367 deletions

View File

@@ -1,8 +1,10 @@
import 'package:cipher_guard_sdk/cipher_guard_sdk.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:im_app/core/foundation/device_info.dart';
import 'package:im_app/core/services/app_initializer.dart';
import 'package:im_app/core/services/encryption_manager.dart';
import 'package:im_app/app/di/network_provider.dart';
// ── 认证 ──────────────────────────────────────────────────────────────────────
@@ -26,32 +28,60 @@ class AuthNotifier extends ChangeNotifier {
/// 登录用户的 UID登录成功后由 LoginViewModel 写入
int? get currentUid => _currentUid;
/// E2E EncryptionManager — 由外部注入(见 LoginViewModel
EncryptionManager? _encryptionManager;
NetworksSdkApi? _api;
void setEncryptionDeps(EncryptionManager encMgr, NetworksSdkApi api) {
_encryptionManager = encMgr;
_api = api;
}
void login({required int uid}) {
_isLoggedIn = true;
_currentUid = uid;
// TODO: 接入 cipher_guard_sdk 后,在此处完成 RSA 密钥注入:
// 1. 从安全存储keychain / secure storage读取公私钥对只读一次
// 2. cipherSdk.setActiveKeyPair(publicKey: pubPem, privateKey: privPem)
// 须在 notifyListeners() 之前完成,确保路由跳转后 onEncryptRequest 回调触发时密钥已就绪。
notifyListeners();
// E2E setup: 对齐 iOS AppCoordinator.onLogin → EncryptionManager.setup()
if (_encryptionManager != null && _api != null) {
_encryptionManager!.setup(_api!);
}
}
void logout() {
_isLoggedIn = false;
_currentUid = null;
// TODO: 接入 cipher_guard_sdk 后,退出登录时清除内存密钥
// cipherSdk.clearActiveKeyPair()
// cipherSdk.clearDerivedKeyCache()
// E2E teardown: 清除所有加密密钥
_encryptionManager?.clearKeys();
notifyListeners();
}
}
/// 登录状态 Provider
///
/// 使用 [Provider] 持有 [AuthNotifier] 单例
/// go_router 通过 [GoRouter.refreshListenable] 直接监听 [AuthNotifier]ChangeNotifier
/// Riverpod 侧不需要响应式更新(导航由 go_router 接管)。
final authNotifierProvider = Provider<AuthNotifier>((ref) => AuthNotifier());
/// 自动注入 EncryptionManager + API clientlogin() 后自动触发 E2E setup
final authNotifierProvider = Provider<AuthNotifier>((ref) {
final auth = AuthNotifier();
auth.setEncryptionDeps(
ref.read(encryptionManagerProvider),
ref.read(networkSdkApiProvider),
);
return auth;
});
// ── E2E 加密 ────────────────────────────────────────────────────────────────
/// CipherGuardSdkApi 单例 — 对齐老项目加密引擎
final cipherSdkProvider = Provider<CipherGuardSdkApi>((ref) {
return CipherGuardSdkApi();
});
/// EncryptionManager 单例 — per-chat key chain + API integration
///
/// 登录后调用 `encMgr.setup(api)` 启动 E2E退出时调用 `encMgr.clearKeys()`。
/// 对齐 iOS EncryptionManager + 老项目 EncryptionMgr。
final encryptionManagerProvider = Provider<EncryptionManager>((ref) {
return EncryptionManager(cipherSdk: ref.read(cipherSdkProvider));
});
// ── 主题 ──────────────────────────────────────────────────────────────────────