libsignal - Signal Protocol for Dart
A Dart FFI wrapper 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 | |
|---|---|---|---|---|---|
| Support | SDK 21+ | 12.0+ | 10.14+ | arm64, x64 | x64 |
| Arch | arm64, armv7, x64 | arm64 | arm64, x64 | arm64, x64 | x64 |
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
- Zero Configuration: Pre-built native libraries included via Build Hooks
- High Performance: Direct FFI bindings with minimal overhead
- Automated Updates: Native libraries auto-rebuild when new libsignal versions are released
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 (lib/src/keys/)
| Class | Key Methods | Native Functions |
|---|---|---|
PrivateKey |
generate, sign, agree, serialize | signal_privatekey_* |
PublicKey |
verify, serialize, compare | signal_publickey_* |
IdentityKeyPair |
generate, serialize, signAlternateIdentity | signal_identitykeypair_* |
PreKeyPair |
generate, serialize | signal_pre_key_record_* |
SignedPreKeyPair |
generate, serialize | signal_signed_pre_key_record_* |
KyberPreKeyPair |
generate, serialize | signal_kyber_* |
Protocol (lib/src/protocol/)
| Class | Key Methods | Native Functions |
|---|---|---|
SessionCipher |
encrypt, decryptSignalMessage, decryptPreKeySignalMessage | signal_encrypt_message, signal_decrypt_* |
SessionBuilder |
processPreKeyBundle | signal_process_prekey_bundle |
SessionRecord |
serialize, deserialize | signal_session_record_* |
ProtocolAddress |
new, name, deviceId | signal_address_* |
SignalMessage |
serialize, body, counter, verifyMac | signal_message_* |
PreKeySignalMessage |
serialize, preKeyId, signedPreKeyId | signal_pre_key_signal_message_* |
Groups (lib/src/groups/)
| Class | Key Methods | Native Functions |
|---|---|---|
GroupSession |
createDistributionMessage, encrypt, decrypt | signal_group_encrypt_message, signal_group_decrypt_message |
SenderKeyRecord |
serialize, deserialize | signal_sender_key_record_* |
SenderKeyMessage |
serialize, getDistributionId | signal_sender_key_message_* |
SenderKeyDistributionMessage |
create, serialize | signal_sender_key_distribution_message_* |
Sealed Sender (lib/src/sealed_sender/)
| Class | Key Methods | Native Functions |
|---|---|---|
SealedSessionCipher |
encrypt, decrypt | signal_sealed_session_cipher_* |
SenderCertificate |
create, validate, serialize | signal_sender_certificate_* |
ServerCertificate |
create, serialize | signal_server_certificate_* |
UnidentifiedSenderMessageContent |
create, serialize | signal_unidentified_sender_message_content_* |
Crypto (lib/src/crypto/)
| Class | Key Methods | Native Functions |
|---|---|---|
Hkdf |
deriveSecrets | signal_hkdf_derive |
Aes256GcmSiv |
encrypt, decrypt | signal_aes_gcm_siv_* |
Fingerprint |
displayString, scannableEncoding, compare | signal_fingerprint_* |
Stores (lib/src/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 | Native Functions | Reason |
|---|---|---|
| zkgroup | signal_group_secret_params_*, signal_profile_key_* |
Server-side verification, not needed for basic messaging |
| Registration | signal_registration_* |
Account registration service |
| Backup | signal_backup_*, signal_message_backup_* |
Message backup and restore |
| SVR | signal_svr_* |
Secure Value Recovery for PIN-based backup |
| Call Links | signal_call_link_* |
Call link credentials |
| Connection Manager | signal_connection_manager_* |
Network connection handling |
| HSM Enclave | signal_hsm_enclave_* |
Hardware security module communication |
| CDSI | signal_cdsi_* |
Contact Discovery Service |
Installation
Add to your pubspec.yaml:
dependencies:
libsignal: ^x.x.x
Native libraries are downloaded automatically during the build process.
Quick Start
import 'package:libsignal/libsignal.dart';
void main() {
// Initialize the library (optional but recommended for performance)
LibSignal.init();
// Generate identity key pair
final identity = IdentityKeyPair.generate();
print('Identity public key: ${identity.publicKey.serialize().length} bytes');
// Clean up when done
identity.dispose();
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.serialize().length}');
print('Private key length: ${identity.privateKey.serialize().length}');
// Pre-Keys (one-time keys for X3DH)
final preKey = PreKeyPair.generate(preKeyId: 1);
final signedPreKey = SignedPreKeyPair.generate(
signedPreKeyId: 1,
identityKeyPair: identity,
);
// Kyber Pre-Keys (post-quantum key exchange)
final kyberPreKey = KyberPreKeyPair.generate(
kyberPreKeyId: 1,
identityKeyPair: identity,
);
// Clean up
identity.dispose();
preKey.dispose();
signedPreKey.dispose();
kyberPreKey.dispose();
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...
identity.dispose(); // Clean up when done
Performance Optimization
For better performance, initialize once at app start:
void main() {
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
Best Practices:
- Always call
dispose()on key pairs and sessions to free native resources - Call
clearSecrets()on sensitive data when done for immediate memory zeroing - Secrets are also auto-zeroed via Finalizers on GC (defense-in-depth), but don't rely solely on this
- Use
LibSignalUtils.constantTimeEquals()for comparing secrets (prevents timing attacks) - Keep the library updated to the latest version
- Use UTC timestamps for certificate validation to avoid timezone issues
// Secure usage example
final identity = IdentityKeyPair.generate();
// ... use identity for encryption ...
// Clean up sensitive data
identity.clearSecrets();
identity.dispose();
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 |
Recommended storage options:
flutter_secure_storage- for identity keys and other critical secretsdrift/sqflitewith SQLCipher - for session recordshivewith encryption - lightweight alternative
Building from Source
Prerequisites
- Flutter 3.38+
- FVM (optional, for version management)
- Rust toolchain (for building native libraries):
- rustup - Rust toolchain installer and version manager
cargo- Rust package manager (installed automatically with rustup)cbindgen- C header generator (installed automatically during build viacargo install)
- protoc - Protocol Buffers compiler (required by libsignal's spqr dependency):
- 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
# Full setup (FVM + Rust + protoc)
make setup
# Or install components separately:
make setup-fvm # FVM, Flutter, dependencies, git hooks
make setup-build # Rust toolchain, protoc
# Build native libraries for your platform
make build ARGS="macos" # or linux, windows, ios, android
Available Commands
# Setup
make setup # Full setup (FVM + build dependencies)
make setup-fvm # Install FVM and Flutter only
make setup-build # Install native build dependencies (Rust, protoc)
# Build
make build ARGS="<platform>" # Build native libraries (macos, ios, android, linux, windows)
# Development
make regen # Regenerate FFI bindings from libsignal headers
make check # Check for libsignal updates
# Quality Assurance
make test # Run tests
make coverage # Run tests with coverage report
make analyze # Run static analysis
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 version # Show current libsignal version
make help # Show all commands
Architecture
┌─────────────────────────────────────────────┐
│ libsignal (Rust) │ ← Core implementation
├─────────────────────────────────────────────┤
│ libsignal-ffi (Rust) │ ← C FFI layer
├─────────────────────────────────────────────┤
│ signal_ffi.h (C header) │ ← cbindgen output
├─────────────────────────────────────────────┤
│ libsignal_bindings.dart (Dart FFI) │ ← ffigen output
├─────────────────────────────────────────────┤
│ libsignal (Dart API) │ ← High-level API
└─────────────────────────────────────────────┘
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.