djx-y-z/libsignal_dart

GitHub: djx-y-z/libsignal_dart

该项目是 libsignal 的 Dart 绑定库,为 Flutter 与跨平台应用提供包含 Double Ratchet、X3DH、Sealed Sender 和后量子密钥交换的 Signal Protocol 端到端加密实现。

Stars: 8 | Forks: 2

# libsignal - 适用于 Dart 的 Signal Protocol [![pub package](https://img.shields.io/pub/v/libsignal.svg)](https://pub.dev/packages/libsignal) [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/073654a99a155241.svg)](https://github.com/djx-y-z/libsignal_dart/actions/workflows/test.yml) [![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/djx-y-z/246880c242ae85c452f4de0e6e91838c/raw/coverage.json)](https://gist.github.com/djx-y-z/246880c242ae85c452f4de0e6e91838c) [![License](https://img.shields.io/badge/license-AGPL--3.0-blue.svg)](LICENSE) [![Dart](https://img.shields.io/badge/dart-%3E%3D3.10.0-brightgreen.svg)](https://dart.dev) [![Flutter](https://img.shields.io/badge/flutter-%3E%3D3.38.0-blue.svg)](https://flutter.dev) [![libsignal](https://img.shields.io/badge/libsignal-v0.96.2-orange.svg)](https://github.com/signalapp/libsignal) [libsignal](https://github.com/signalapp/libsignal) 的 Dart 绑定,提供用于端到端加密、密封发送者 (Sealed Sender)、群组消息传递和安全加密操作的 Signal Protocol 实现。 ## 平台支持 | | Android | iOS | macOS | Linux | Windows | Web | |-------------|---------|-------|--------|------------|---------|-----| | **支持情况** | SDK 24+ | 13.0+ | 10.15+ | arm64, x64 | x64 | WASM | | **架构** | arm64, armv7, x64 | arm64 | arm64, x64 | arm64, x64 | x64 | wasm32 | ## 特性 - **Flutter 与 CLI 支持**:适用于 Flutter 应用和独立的 Dart CLI 应用 - **Signal Protocol**:具备前向保密性的端到端加密(Double Ratchet, X3DH) - **密封发送者**:匿名消息发送(服务器将不知道消息的发送者是谁) - **群组消息传递**:使用 SenderKey 分发实现高效的群组加密 - **自动构建**:通过构建钩子自动下载原生库 - **高性能**:通过 Flutter Rust Bridge 直接集成 Rust ## 实现状态 原生 [libsignal](https://github.com/signalapp/libsignal) 库中已封装功能概述: | 类别 | 状态 | 描述 | |----------|:------:|-------------| | Signal Protocol | ✓ | Double Ratchet, X3DH,会话加密/解密 | | 密钥管理 | ✓ | Ed25519, X25519, Kyber (后量子) | | Pre-Keys | ✓ | 常规、已签名和 Kyber pre-keys | | 群组消息传递 | ✓ | 用于高效群组加密的 SenderKey 协议 | | 密封发送者 | ✓ | 带有证书的匿名消息发送 | | 指纹 | ✓ | 用于身份验证的安全码 | | 加密工具 | ✓ | HKDF, AES-256-GCM-SIV | | 存储接口 | ✓ | 全部 6 种存储类型及内存实现 | | zkgroup | ✗ | 零知识群组,个人资料凭证 | | 注册 | ✗ | 账户注册服务 | | 备份 | ✗ | 消息备份与还原 | | SVR | ✗ | 安全值恢复(基于 PIN 的备份) | | 通话链接 | ✗ | 通话链接凭证与身份验证 | | 连接管理器 | ✗ | 网络连接处理 |
详细实现 ### 已实现的功能 #### 密钥 | 类 | 主要方法 | |-------|-------------| | `PrivateKey` | generate, sign, agree, serialize | | `PublicKey` | verify, serialize, compare | | `IdentityKeyPair` | generate, serialize, signAlternateIdentity | #### 协议 | 类 | 主要方法 | |-------|-------------| | `SessionCipher` | encrypt, decrypt, decryptSignalMessage, decryptPreKeyMessage | | `SessionBuilder` | processPreKeyBundle | | `SessionRecord` | serialize, deserialize | | `ProtocolAddress` | new, name, deviceId | | `SignalMessage` | serialize, body, counter, verifyMac | | `PreKeySignalMessage` | serialize, preKeyId, signedPreKeyId | #### 群组 | 类 | 主要方法 | |-------|-------------| | `GroupSession` | createDistributionMessage, encrypt, decrypt | | `SenderKeyRecord` | serialize, deserialize | | `SenderKeyMessage` | serialize, getDistributionId | | `SenderKeyDistributionMessage` | create, serialize | #### 密封发送者 | 类 | 主要方法 | |-------|-------------| | `SealedSenderCipher` | encrypt, decrypt | | `SenderCertificate` | create, validate, serialize | | `ServerCertificate` | create, serialize | | `UnidentifiedSenderMessageContent` | create, serialize | #### 加密 | 类 | 主要方法 | |-------|-------------| | `Hkdf` | deriveSecrets | | `Aes256GcmSiv` | encrypt, decrypt | | `Fingerprint` | displayString, scannableEncoding, compare | #### 存储 | 接口 | 内存实现 | 用途 | |-----------|-------------------------|---------| | `SessionStore` | `InMemorySessionStore` | 会话状态持久化 | | `IdentityKeyStore` | `InMemoryIdentityKeyStore` | 身份密钥管理 | | `PreKeyStore` | `InMemoryPreKeyStore` | 一次性 pre-keys | | `SignedPreKeyStore` | `InMemorySignedPreKeyStore` | 已签名的 pre-keys | | `KyberPreKeyStore` | `InMemoryKyberPreKeyStore` | 后量子 pre-keys | | `SenderKeyStore` | `InMemorySenderKeyStore` | 群组消息密钥 | ### 未实现 | 类别 | 原因 | |----------|--------| | zkgroup | 服务器端验证,基础消息传递不需要 | | 注册 | 账户注册服务 | | 备份 | 消息备份与还原 | | SVR | 用于基于 PIN 备份的安全值恢复 | | 通话链接 | 通话链接凭证 | | 连接管理器 | 网络连接处理 | | HSM Enclave | 硬件安全模块通信 | | CDSI | 联系人发现服务 |
## 安装 将其添加到你的 `pubspec.yaml`: ``` dependencies: libsignal: ^x.x.x ``` 在构建期间,原生库会通过 Dart 构建钩子自动下载。 最终用户**无需 Rust** - 预编译的二进制文件会直接从 GitHub Releases 下载。如果安装了 Rust,则可回退到源码构建。 ### Web 支持 对于 Web 构建,WASM 文件会在构建过程中自动下载到 `web/pkg/`。 **手动设置**(如果自动下载失败): ``` # 在 libsignal package 目录中 make build-web ``` 然后将 `rust/target/wasm32/` 文件复制到你的应用的 `web/pkg/` 目录中。 ## 快速开始 ``` import 'package:libsignal/libsignal.dart'; void main() async { // Initialize the library await LibSignal.init(); // Generate identity key pair final identity = IdentityKeyPair.generate(); print('Identity public key: ${identity.publicKey.length} bytes'); // Clean up when done LibSignal.cleanup(); } ``` ## API 参考 ### 密钥类型 ``` import 'package:libsignal/libsignal.dart'; // Identity Key Pair (long-term identity) final identity = IdentityKeyPair.generate(); print('Public key length: ${identity.publicKey.length}'); // Pre-Key (one-time key for X3DH) final preKeyPrivate = PrivateKey.generate(); final preKeyPublic = preKeyPrivate.getPublicKey(); final preKey = PreKeyRecord( id: 1, publicKey: preKeyPublic, privateKey: preKeyPrivate, ); // Signed Pre-Key final signedPreKeyPrivate = PrivateKey.generate(); final signedPreKeyPublic = signedPreKeyPrivate.getPublicKey(); final identityPrivate = PrivateKey.deserialize(bytes: identity.privateKey.toList()); final signature = identityPrivate.sign(message: signedPreKeyPublic.serialize().toList()); final signedPreKey = SignedPreKeyRecord( id: 1, timestamp: BigInt.from(DateTime.now().millisecondsSinceEpoch), publicKey: signedPreKeyPublic, privateKey: signedPreKeyPrivate, signature: signature.toList(), ); // Kyber Pre-Key (post-quantum key exchange) final kyberKeyPair = KyberKeyPair.generate(); final kyberSignature = identityPrivate.sign( message: kyberKeyPair.getPublicKey().serialize().toList(), ); final kyberPreKey = KyberPreKeyRecord.create( id: 1, timestamp: BigInt.from(DateTime.now().millisecondsSinceEpoch), keyPair: kyberKeyPair, signature: kyberSignature.toList(), ); ``` ### 会话加密 (Double Ratchet) ``` import 'package:libsignal/libsignal.dart'; // Create stores final sessionStore = InMemorySessionStore(); final identityStore = InMemoryIdentityKeyStore(identity, registrationId); final preKeyStore = InMemoryPreKeyStore(); final signedPreKeyStore = InMemorySignedPreKeyStore(); final kyberPreKeyStore = InMemoryKyberPreKeyStore(); // Build session from pre-key bundle final builder = SessionBuilder( localAddress: myAddress, sessionStore: sessionStore, identityKeyStore: identityStore, ); await builder.processPreKeyBundle(recipientAddress, preKeyBundle); // Encrypt messages final cipher = SessionCipher( localAddress: myAddress, sessionStore: sessionStore, identityKeyStore: identityStore, preKeyStore: preKeyStore, signedPreKeyStore: signedPreKeyStore, kyberPreKeyStore: kyberPreKeyStore, ); final encrypted = await cipher.encrypt(recipientAddress, plaintext); // Decrypt messages final decrypted = await cipher.decrypt(senderAddress, ciphertext); ``` ### 密封发送者(匿名消息传递) ``` import 'package:libsignal/libsignal.dart'; // Create sealed sender cipher final sealedCipher = SealedSenderCipher( localAddress: myAddress, sessionStore: sessionStore, identityKeyStore: identityStore, preKeyStore: preKeyStore, signedPreKeyStore: signedPreKeyStore, kyberPreKeyStore: kyberPreKeyStore, ); // Encrypt with sealed sender (server won't know who sent it) final sealed = await sealedCipher.encrypt( recipientAddress: recipientAddress, plaintext: plaintext, senderCertificate: senderCertBytes, ); // Recipient decrypts and learns sender identity final result = await recipientCipher.decrypt( ciphertext: sealed, trustRoot: trustRootBytes, timestamp: DateTime.now().millisecondsSinceEpoch, ); print('Message from: ${result.senderAddress.name()}'); ``` ### 群组消息传递 (SenderKey) ``` import 'package:libsignal/libsignal.dart'; // Create group session final groupSession = GroupSession( senderKeyStore: InMemorySenderKeyStore(), ); // Create distribution message (send to all group members) final distributionMessage = await groupSession.createDistributionMessage( sender: myAddress, distributionId: groupId, ); // Encrypt for group final groupCiphertext = await groupSession.encrypt( sender: myAddress, distributionId: groupId, plaintext: message, ); // Decrypt group message final plaintext = await groupSession.decrypt( sender: senderAddress, distributionId: groupId, ciphertext: groupCiphertext, ); ``` ## 资源管理 ### 基本用法 ``` final identity = IdentityKeyPair.generate(); // Use identity... // FRB handles cleanup automatically via finalizers ``` ### 性能优化 为了获得更好的性能,请在应用启动时初始化一次: ``` void main() async { await LibSignal.init(); // Recommended at app startup runApp(MyApp()); } ``` ## 安全说明 **核心特性:** - **Signal Protocol** - 经过实战检验的加密技术,被 Signal、WhatsApp 等广泛使用 - **完美前向保密** - 即使密钥被泄露,过往消息依然安全 - **Kyber 支持** - 后量子密钥交换,提供面向未来的安全保障 - **Rust 实现** - 所有加密操作均在 Rust (libsignal-protocol) 中运行,采用恒定时间实现 **最佳实践:** - 保持库更新到最新版本 - 证书验证时使用 UTC 时间戳,以避免时区问题 - 让库来处理加密比较 —— 避免在 Dart 代码中比较敏感信息 - 对敏感数据(序列化的密钥、共享密钥)使用 `SecureBytes.wrap()` 或 `zeroize()` —— 参见 [SECURITY.md](SECURITY.md) ## 存储 Signal Protocol 需要持久化存储来保存会话状态 (Double Ratchet)。本库提供了存储接口和内存实现。 ### 内存存储(仅用于测试) ``` final sessionStore = InMemorySessionStore(); final identityStore = InMemoryIdentityKeyStore(identity, registrationId); final preKeyStore = InMemoryPreKeyStore(); final signedPreKeyStore = InMemorySignedPreKeyStore(); final kyberPreKeyStore = InMemoryKyberPreKeyStore(); final senderKeyStore = InMemorySenderKeyStore(); ``` ### 生产环境要求 对于生产级应用,请使用安全存储来实现存储接口: | 存储 | 用途 | 安全等级 | |-------|---------|----------------| | `SessionStore` | 加密的会话状态 | 高(包含密钥材料) | | `IdentityKeyStore` | 身份密钥 | 极高(长期密钥) | | `PreKeyStore` | 一次性 pre-keys | 高 | | `SignedPreKeyStore` | 已签名的 pre-keys | 高 | | `KyberPreKeyStore` | 后量子 pre-keys | 高 | | `SenderKeyStore` | 群组消息密钥 | 高 | ## 已知限制 ### Web: 不支持 `flutter build web --wasm` (dart2wasm) 此包适用于标准的 `flutter build web` (dart2js) 目标。当宿主应用使用 `flutter build web --wasm` / `flutter run -d chrome --wasm` (dart2wasm) 编译时,目前**无法**正常工作。调用 Rust 端会失败并提示: ``` Type 'JSValue' is not a subtype of type 'List' in type cast ``` 这是 [`flutter_rust_bridge`](https://github.com/fzyzcjy/flutter_rust_bridge) 的上游限制 —— 其生成的 Dart 解码器依赖于隐式的 JS 数组转换,这在 dart2js 上能正常工作,但在 dart2wasm 下会失败。这种模式被硬编码在 FRB 的代码生成模板中,因此它会影响所有基于 FRB 的 Dart 包,而不仅仅是这个包。上游追踪:[flutter_rust_bridge#2575](https://github.com/fzyzcjy/flutter_rust_bridge/issues/2575)。 | 命令 | 状态 | |---------|--------| | `flutter run -d chrome` | 可用 (dart2js) | | `flutter build web` | 可用 (dart2js) | | `flutter run -d chrome --wasm` | 不支持 (dart2wasm) | | `flutter build web --wasm` | 不支持 (dart2wasm) | 在这两种模式下,libsignal 的 Rust 核心都是作为 `.wasm` 模块提供的 —— `--wasm` 仅会改变 *Dart* 代码编译的目标。加密性能和功能是完全相同的。 ## 从源码构建 ### 对于最终用户 **无需设置!** 在 `flutter build` 期间,会自动从 GitHub Releases 下载预编译的原生库。 ### 对于贡献者 / 源码构建者 如果你想从源码构建(或者预编译的二进制文件不可用): - [Flutter](https://flutter.dev/) 3.38+ - [FVM](https://fvm.app/)(可选,用于版本管理) - **Rust 工具链**: - [rustup](https://rustup.rs/) - Rust 工具链安装程序 - `cargo` - Rust 包管理器(随 rustup 安装) - **protoc** - Protocol Buffers 编译器: - macOS: `brew install protobuf` - Ubuntu/Debian: `apt-get install protobuf-compiler` - Windows: [从 GitHub 下载](https://github.com/protocolbuffers/protobuf/releases) ### 设置 ``` # Clone 代码仓库 git clone https://github.com/djx-y-z/libsignal_dart.git cd libsignal_dart # 安装 FVM 及依赖项 make setup # 运行测试 make test ``` ### 可用命令 ``` # 设置 make setup # Install all required tools (Rust check, FVM, protoc, cargo-audit) make setup-fvm # Install FVM and project Flutter version only make setup-protoc # Install protoc (Protocol Buffers compiler) make setup-rust-tools # Install Rust tools (cargo-audit, flutter_rust_bridge_codegen) make setup-web # Install wasm-pack for web builds (optional) make setup-android # Install cargo-ndk for Android builds (optional) # 开发 make codegen # Regenerate Flutter Rust Bridge bindings make build # Build Rust library locally (native) make build-android # Build for Android (requires cargo-ndk + NDK) make build-web # Build WASM for web (requires wasm-pack) # CI / 版本管理 make check-new-libsignal-version # Check for new upstream libsignal version make check-template-updates # Check for new copier template version make rust-update # Update rust/Cargo.lock (cargo update) make update-changelog # Update CHANGELOG.md with AI (requires GITHUB_TOKEN) # 质量保证 make test # Run tests make coverage # Run tests with coverage report make analyze # Run static analysis make rust-audit # Check Rust dependencies for vulnerabilities make rust-check # Quick Rust type check (updates Cargo.lock) make format # Format Dart code make format-check # Check Dart code formatting make doc # Generate API documentation # 实用工具 make get # Get dependencies make clean # Clean build artifacts make help # Show all commands ``` 它还会每天检查 copier 模板更新,并创建带有更新日志和更新说明的通知 PR。 ## 架构 ``` ┌─────────────────────────────────────────────┐ │ libsignal-protocol (Rust crate) │ ← Core implementation ├─────────────────────────────────────────────┤ │ rust/src/api/*.rs (Rust wrappers) │ ← FRB-annotated functions ├─────────────────────────────────────────────┤ │ lib/src/rust/*.dart (FRB generated) │ ← Auto-generated Dart API ├─────────────────────────────────────────────┤ │ lib/src/stores/*.dart │ ← Store interfaces └─────────────────────────────────────────────┘ ``` ## 许可证 本项目基于 AGPL-3.0 许可证授权 - 有关详情请参见 [LICENSE](LICENSE) 文件。 内置的 libsignal 库同样基于 AGPL-3.0 授权 - 有关 Signal 的许可证请参见 [LICENSE.libsignal](LICENSE.libsignal)。 ## 相关项目 - [libsignal](https://github.com/signalapp/libsignal) - 底层的 Rust 库 - [Signal](https://signal.org/) - Signal 项目 - [Signal Protocol 规范](https://signal.org/docs/) - 协议文档 ## 贡献 欢迎任何形式的贡献!在提交 issue 或 pull request 之前,请先阅读我们的[贡献指南](CONTRIBUTING.md)。 对于重大更改,请先开启一个 issue 进行讨论,说明你希望进行的改动。
标签:Dart, Flutter, Signal协议, 可视化界面, 密码学库, 端到端加密