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
[](https://pub.dev/packages/libsignal)
[](https://github.com/djx-y-z/libsignal_dart/actions/workflows/test.yml)
[](https://gist.github.com/djx-y-z/246880c242ae85c452f4de0e6e91838c)
[](LICENSE)
[](https://dart.dev)
[](https://flutter.dev)
[](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 的备份) |
| 通话链接 | ✗ | 通话链接凭证与身份验证 |
| 连接管理器 | ✗ | 网络连接处理 |
## 安装
将其添加到你的 `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 进行讨论,说明你希望进行的改动。
详细实现
### 已实现的功能 #### 密钥 | 类 | 主要方法 | |-------|-------------| | `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 | 联系人发现服务 |标签:Dart, Flutter, Signal协议, 可视化界面, 密码学库, 端到端加密