libsignal - Signal Protocol for Dart
Dart bindings for libsignal, providing Signal Protocol implementation for end-to-end encryption, sealed sender, group messaging, and secure cryptographic operations.
Platform Support
| Android | iOS | macOS | Linux | Windows | Web | |
|---|---|---|---|---|---|---|
| Support | SDK 21+ | 12.0+ | 10.14+ | arm64, x64 | x64 | ✓ |
| Arch | arm64, armv7, x64 | arm64 | arm64, x64 | arm64, x64 | x64 | wasm32 |
Features
- Flutter & CLI Support: Works with Flutter apps and standalone Dart CLI applications
- Signal Protocol: End-to-end encryption with perfect forward secrecy (Double Ratchet, X3DH)
- Sealed Sender: Anonymous message sending (server won't know who sent the message)
- Group Messaging: Efficient group encryption using SenderKey distribution
- Automatic Builds: Native libraries downloaded automatically via build hooks
- High Performance: Direct Rust integration via Flutter Rust Bridge
Implementation Status
Overview of wrapped functionality from the native libsignal library:
| Category | Status | Description |
|---|---|---|
| Signal Protocol | ✓ | Double Ratchet, X3DH, session encryption/decryption |
| Key Management | ✓ | Ed25519, X25519, Kyber (post-quantum) |
| Pre-Keys | ✓ | Regular, signed, and Kyber pre-keys |
| Group Messaging | ✓ | SenderKey protocol for efficient group encryption |
| Sealed Sender | ✓ | Anonymous message sending with certificates |
| Fingerprints | ✓ | Safety numbers for identity verification |
| Crypto Utilities | ✓ | HKDF, AES-256-GCM-SIV |
| Store Interfaces | ✓ | All 6 store types with in-memory implementations |
| zkgroup | ✗ | Zero-knowledge groups, profile credentials |
| Registration | ✗ | Account registration service |
| Backup | ✗ | Message backup and restore |
| SVR | ✗ | Secure Value Recovery (PIN-based backup) |
| Call Links | ✗ | Call link credentials and authentication |
| Connection Manager | ✗ | Network connection handling |
Detailed Implementation
Implemented Features
Keys
| Class | Key Methods |
|---|---|
PrivateKey |
generate, sign, agree, serialize |
PublicKey |
verify, serialize, compare |
IdentityKeyPair |
generate, serialize, signAlternateIdentity |
Protocol
| Class | Key Methods |
|---|---|
SessionCipher |
encrypt, decryptSignalMessage, decryptPreKeySignalMessage |
SessionBuilder |
processPreKeyBundle |
SessionRecord |
serialize, deserialize |
ProtocolAddress |
new, name, deviceId |
SignalMessage |
serialize, body, counter, verifyMac |
PreKeySignalMessage |
serialize, preKeyId, signedPreKeyId |
Groups
| Class | Key Methods |
|---|---|
GroupSession |
createDistributionMessage, encrypt, decrypt |
SenderKeyRecord |
serialize, deserialize |
SenderKeyMessage |
serialize, getDistributionId |
SenderKeyDistributionMessage |
create, serialize |
Sealed Sender
| Class | Key Methods |
|---|---|
SealedSessionCipher |
encrypt, decrypt |
SenderCertificate |
create, validate, serialize |
ServerCertificate |
create, serialize |
UnidentifiedSenderMessageContent |
create, serialize |
Crypto
| Class | Key Methods |
|---|---|
Hkdf |
deriveSecrets |
Aes256GcmSiv |
encrypt, decrypt |
Fingerprint |
displayString, scannableEncoding, compare |
Stores
| Interface | In-Memory Implementation | Purpose |
|---|---|---|
SessionStore |
InMemorySessionStore |
Session state persistence |
IdentityKeyStore |
InMemoryIdentityKeyStore |
Identity key management |
PreKeyStore |
InMemoryPreKeyStore |
One-time pre-keys |
SignedPreKeyStore |
InMemorySignedPreKeyStore |
Signed pre-keys |
KyberPreKeyStore |
InMemoryKyberPreKeyStore |
Post-quantum pre-keys |
SenderKeyStore |
InMemorySenderKeyStore |
Group messaging keys |
Not Implemented
| Category | Reason |
|---|---|
| zkgroup | Server-side verification, not needed for basic messaging |
| Registration | Account registration service |
| Backup | Message backup and restore |
| SVR | Secure Value Recovery for PIN-based backup |
| Call Links | Call link credentials |
| Connection Manager | Network connection handling |
| HSM Enclave | Hardware security module communication |
| CDSI | Contact Discovery Service |
Installation
Add to your pubspec.yaml:
dependencies:
libsignal: ^x.x.x
Native libraries are downloaded automatically during build via Dart build hooks.
No Rust required for end users - precompiled binaries are downloaded from GitHub Releases. Fallback to source build if Rust is installed.
Web Support
For web builds, WASM files are automatically downloaded to web/pkg/ during the build process.
Manual setup (if automatic download fails):
# In the libsignal package directory
make build-web
Then copy rust/target/wasm32/ files to your app's web/pkg/ directory.
Quick Start
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 Reference
Key Types
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(),
);
Session Encryption (Double Ratchet)
import 'package:libsignal/libsignal.dart';
// Create stores
final sessionStore = InMemorySessionStore();
final identityStore = InMemoryIdentityKeyStore(identity, registrationId);
// Build session from pre-key bundle
final builder = SessionBuilder(
sessionStore: sessionStore,
identityKeyStore: identityStore,
);
await builder.processPreKeyBundle(recipientAddress, preKeyBundle);
// Encrypt messages
final cipher = SessionCipher(
sessionStore: sessionStore,
identityKeyStore: identityStore,
);
final encrypted = await cipher.encrypt(recipientAddress, plaintext);
// Decrypt messages
final decrypted = await cipher.decrypt(senderAddress, ciphertext);
Sealed Sender (Anonymous Messaging)
import 'package:libsignal/libsignal.dart';
// Create sealed session cipher
final sealedCipher = SealedSessionCipher(
sessionStore: sessionStore,
identityKeyStore: identityStore,
);
// Create sender certificate (issued by server)
final senderCert = SenderCertificate.create(
senderUuid: 'my-uuid',
deviceId: 1,
senderKey: identity.publicKey,
expiration: DateTime.now().toUtc().add(Duration(days: 30)),
signerCertificate: serverCert,
signerKey: serverPrivateKey,
);
// Encrypt with sealed sender (server won't know who sent it)
final sealed = await sealedCipher.encrypt(
recipientAddress,
plaintext,
senderCert,
contentHint: ContentHint.resendable,
);
// Recipient decrypts and learns sender identity
final result = await recipientCipher.decrypt(
sealed,
trustRoot: trustRootPublicKey,
timestamp: DateTime.now().toUtc(),
localUuid: 'recipient-uuid',
localDeviceId: 1,
);
print('Message from: ${result.senderUuid}');
Group Messaging (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,
);
Resource Management
Basic Usage
final identity = IdentityKeyPair.generate();
// Use identity...
// FRB handles cleanup automatically via finalizers
Performance Optimization
For better performance, initialize once at app start:
void main() async {
await LibSignal.init(); // Recommended at app startup
runApp(MyApp());
}
Security Notes
Key Features:
- Signal Protocol - Battle-tested encryption used by Signal, WhatsApp, and others
- Perfect Forward Secrecy - Past messages stay secure even if keys are compromised
- Kyber Support - Post-quantum key exchange for future-proof security
- Rust Implementation - All cryptographic operations run in Rust (libsignal-protocol) with constant-time implementations
Best Practices:
- Keep the library updated to the latest version
- Use UTC timestamps for certificate validation to avoid timezone issues
- Let the library handle cryptographic comparisons — avoid comparing secrets in Dart code
Stores
Signal Protocol requires persistent storage for session state (Double Ratchet). This library provides store interfaces and in-memory implementations.
In-Memory Stores (Testing Only)
final sessionStore = InMemorySessionStore();
final identityStore = InMemoryIdentityKeyStore(identity, registrationId);
final preKeyStore = InMemoryPreKeyStore();
final signedPreKeyStore = InMemorySignedPreKeyStore();
final kyberPreKeyStore = InMemoryKyberPreKeyStore();
final senderKeyStore = InMemorySenderKeyStore();
Warning: In-memory stores lose all data on app restart. Use only for:
- Unit tests
- Development/debugging
- Demo applications
Production Requirements
For production apps, implement the store interfaces with secure storage:
| Store | Purpose | Security Level |
|---|---|---|
SessionStore |
Encrypted session state | High (contains key material) |
IdentityKeyStore |
Identity keys | Critical (long-term secrets) |
PreKeyStore |
One-time pre-keys | High |
SignedPreKeyStore |
Signed pre-keys | High |
KyberPreKeyStore |
Post-quantum pre-keys | High |
SenderKeyStore |
Group messaging keys | High |
Building from Source
For End Users
No setup required! Precompiled native libraries are downloaded automatically from GitHub Releases during flutter build.
For Contributors / Source Builds
If you want to build from source (or precompiled binaries are not available):
- Flutter 3.38+
- FVM (optional, for version management)
- Rust toolchain:
- rustup - Rust toolchain installer
cargo- Rust package manager (installed with rustup)
- protoc - Protocol Buffers compiler:
- macOS:
brew install protobuf - Ubuntu/Debian:
apt-get install protobuf-compiler - Windows: Download from GitHub
- macOS:
Setup
# Clone the repository
git clone https://github.com/djx-y-z/libsignal_dart.git
cd libsignal_dart
# Install FVM and dependencies
make setup
# Run tests
make test
Available Commands
# Setup
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)
# Development
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)
# Quality Assurance
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
# Utilities
make get # Get dependencies
make clean # Clean build artifacts
make help # Show all commands
Architecture
┌─────────────────────────────────────────────┐
│ 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
└─────────────────────────────────────────────┘
Acknowledgements
This library would not be possible without libsignal by Signal, which provides the underlying Rust implementation of the Signal Protocol.
License
This project is licensed under the AGPL-3.0 License - see the LICENSE file for details.
The bundled libsignal library is also licensed under AGPL-3.0 - see LICENSE.libsignal for the Signal license.
Related Projects
- libsignal - The underlying Rust library
- Signal - The Signal project
- Signal Protocol Specification - Protocol documentation
Contributing
Contributions are welcome! Please read our Contributing Guidelines before submitting issues or pull requests.
For major changes, please open an issue first to discuss what you would like to change.
Libraries
- libsignal
- Dart FFI bindings for libsignal - Signal Protocol implementation.