Secure end-to-end encryption for .NET — modern cryptographic protocols, production-ready
🔒 End-to-End Encrypted • 🔑 X3DH + Double Ratchet • 📱 Multi-Device • 🏢 Enterprise-Ready
A comprehensive, production-ready end-to-end encryption library for .NET applications implementing modern cryptographic protocols with a focus on security, privacy, and usability. Now with advanced features including post-quantum cryptography preparation, WebRTC transport, message batching, and enterprise-grade monitoring capabilities.
// Install via NuGet// dotnet add package LibEmiddle --version 2.7.0usingLibEmiddle.API;usingLibEmiddle.Domain.Enums;// Configure the clientvaroptions=newLibEmiddleClientOptions{TransportType=TransportType.Http,ServerEndpoint="https://your-server.com",EnableMultiDevice=true,EnableMessageHistory=true};// Create and initialize the clientusingvarclient=newLibEmiddleClient(options);awaitclient.InitializeAsync();// Start a secure conversationvarchatSession=awaitclient.CreateChatSessionAsync(recipientPublicKey,"user123");varencryptedMessage=awaitchatSession.EncryptAsync("Hello, secure world!");- X3DH Key Exchange - Extended Triple Diffie-Hellman for secure initial key agreement
- Double Ratchet Algorithm - Continuous key rotation for forward secrecy
- AES-GCM Encryption - Authenticated encryption with strong integrity guarantees
- Ed25519 & X25519 - Modern elliptic curve cryptography for digital signatures and key exchange
- Post-Quantum Cryptography - API scaffolding only; see Future Roadmap before use
- Advanced Key Rotation - Sophisticated key rotation policies and monitoring
- Replay Attack Protection - Per-sender message ID deduplication in both chat and group sessions
- Post-Removal Forward Secrecy - Group chain key rotated immediately on member removal
- Out-of-Order Delivery - Signal
PNheader field lets messages that arrive across a ratchet step still decrypt (v2.7)
- One-to-One Messaging - Secure private conversations with forward secrecy
- Group Messaging - Efficient encrypted group chats with advanced member management
- Multi-Device Support - Seamless synchronization with encrypted persistent device list
- Asynchronous Communication - Robust mailbox system with delivery and read receipts
- Message Batching - Efficient bulk messaging with compression support (v2.5)
- Flexible Transport Layer - HTTP and InMemory transports (WebRTC planned for v3.0)
- Out-of-Order Tolerance - Skipped message keys are retained across DH ratchet steps, so delayed or reordered messages still decrypt
- Unified Client API - Single
LibEmiddleClientfor all operations (IAsyncDisposablesupport) - Modular Design - Pluggable transport, storage, and crypto providers
- Session Management - Automatic session persistence and recovery with backup capabilities
- Structured Exceptions -
LibEmiddleExceptionwith typedLibEmiddleErrorCodefor precise error handling - Event-Driven - Real-time message handling with comprehensive events
- Feature Flags - Gradual rollout and configuration of new capabilities (v2.5)
- Enterprise Monitoring - Built-in diagnostics, plus retry / circuit-breaker / timeout via
ResilienceManager
Note: connection pooling, session backup, advanced key-rotation scheduling, WebRTC transport, and post-quantum crypto are present as API stubs only — they satisfy their interfaces but perform no real work. See Feature Maturity before depending on them.
// First contact: supply the recipient's full X3DHPublicBundle as UTF-8 JSON bytes.// The library caches the bundle for future lookups by identity key.byte[]bundleBytes=Encoding.UTF8.GetBytes(JsonSerializer.Serialize(recipientBundle));varchatSession=awaitclient.CreateChatSessionAsync(bundleBytes,recipientUserId:"alice@example.com",options:newChatSessionOptions{RotationStrategy=KeyRotationStrategy.Aggressive,EnableMessageHistory=true});// Subsequent contacts: a bare 32-byte identity key is sufficient// (the bundle is looked up from the local cache).varchatSession=awaitclient.CreateChatSessionAsync(recipientPublicKey,// 32-byte Ed25519 identity keyrecipientUserId:"alice@example.com",options:newChatSessionOptions{RotationStrategy=KeyRotationStrategy.Standard});// Send encrypted messagesvarencryptedMessage=awaitchatSession.EncryptAsync("Hello Alice!");// Receive and decrypt messagesvardecryptedMessage=awaitchatSession.DecryptAsync(incomingEncryptedMessage);Console.WriteLine($"Received: {decryptedMessage}");// Send message directly by recipient key (creates session if needed)vardirectMessage=awaitclient.SendChatMessageAsync(recipientPublicKey,"Direct message without explicit session creation");// Handle incoming messages with eventschatSession.MessageReceived+=(sender,args)=>{Console.WriteLine($"New message: {args.DecryptedContent}");};// Enable multi-device support in client optionsvaroptions=newLibEmiddleClientOptions{EnableMultiDevice=true,MaxLinkedDevices=5};usingvarclient=newLibEmiddleClient(options);awaitclient.InitializeAsync();// Link a new devicevarlinkMessage=client.CreateDeviceLinkMessage(newDevicePublicKey);// Process device link on the new devicevarsuccess=awaitclient.ProcessDeviceLinkMessageAsync(linkMessage);// Synchronize data across devicesvarsyncData=Encoding.UTF8.GetBytes("Session data to sync");varsyncMessages=client.CreateSyncMessages(syncData);// Send sync messages to all linked devicesforeach(var(deviceId,message)insyncMessages){awaitclient.SendToDeviceAsync(deviceId,message);}// Revoke a compromised deviceawaitclient.RevokeDeviceAsync(compromisedDevicePublicKey,"Device lost");// Create a new groupvargroupSession=awaitclient.CreateGroupAsync(groupId:"team-secure-chat",groupName:"Development Team",options:newGroupSessionOptions{RotationStrategy=KeyRotationStrategy.Standard,MaxMembers=50});// Add members to the groupawaitgroupSession.AddMemberAsync(member1PublicKey,MemberRole.Admin);awaitgroupSession.AddMemberAsync(member2PublicKey,MemberRole.Member);// Send encrypted group messagesvarencryptedGroupMessage=awaitclient.SendGroupMessageAsync("team-secure-chat","Confidential team discussion");// Join an existing group using distribution messagevarjoinedGroup=awaitclient.JoinGroupAsync(distributionMessage);// Handle group eventsgroupSession.MemberAdded+=(sender,args)=>{Console.WriteLine($"Member {args.MemberPublicKey} joined the group");};groupSession.MessageReceived+=(sender,args)=>{Console.WriteLine($"Group message: {args.DecryptedContent}");};// Rotate group keys (admin only)awaitgroupSession.RotateKeysAsync();// Leave the groupawaitclient.LeaveGroupAsync("team-secure-chat");LibEmiddle uses a flexible mailbox transport system for asynchronous encrypted message delivery. The transport layer handles message routing while encryption is managed by the Double Ratchet protocol.
// Perfect for testing and local developmentvaroptions=newLibEmiddleClientOptions{TransportType=TransportType.InMemory};usingvarclient=newLibEmiddleClient(options);awaitclient.InitializeAsync();// Production-ready HTTP REST API transportvaroptions=newLibEmiddleClientOptions{TransportType=TransportType.Http,ServerEndpoint="https://messaging-server.example.com/api",NetworkTimeoutMs=30000,EnableStrictCertificateValidation=true,CustomHeaders=newDictionary<string,string>{["Authorization"]="Bearer your-jwt-token"}};usingvarclient=newLibEmiddleClient(options);awaitclient.InitializeAsync();// Listen for incoming messagesclient.MessageReceived+=async(sender,args)=>{Console.WriteLine($"From: {Convert.ToBase64String(args.Message.SenderKey)[..8]}...");Console.WriteLine($"Message: {args.DecryptedContent}");// Optionally mark as readawaitclient.MarkMessageAsReadAsync(args.Message.Id);};// Start listening (default 5 second polling interval)awaitclient.StartListeningAsync();// For low-latency applications, use shorter pollingawaitclient.StartListeningAsync(pollingInterval:1000);// 1 second// Stop listening when doneawaitclient.StopListeningAsync();// Send a message (automatically encrypted with Double Ratchet)awaitclient.SendChatMessageAsync(recipientPublicKey,"Hello!");// Or use ChatSession for more control (requires bundle cached or pass bundleBytes on first use)varsession=awaitclient.CreateChatSessionAsync(recipientPublicKey,"user@example.com");awaitclient.SendChatMessageAsync(recipientPublicKey,"Secure message");Application → LibEmiddleClient → ChatSession → MailboxManager (encrypts)
→ IMailboxTransport → HttpMailboxTransport → Server
Key Point: Only MailboxManager handles encryption/decryption. Transport layers work with already-encrypted messages.
For a complete guide, see Mailbox Transport Guide and Message Flow Diagram.
// Comprehensive client configurationvaroptions=newLibEmiddleClientOptions{// Storage configurationIdentityKeyPath="keys/identity.key",SessionStoragePath="data/sessions",KeyStoragePath="data/keys",// Transport settingsTransportType=TransportType.Http,ServerEndpoint="https://api.example.com",NetworkTimeoutMs=30000,// Security policiesSecurityPolicy=newSecurityPolicyOptions{RequirePerfectForwardSecrecy=true,RequireMessageAuthentication=true,MinimumProtocolVersion="2.0",AllowInsecureConnections=false},// Key managementDefaultRotationStrategy=KeyRotationStrategy.Aggressive,MaxOneTimePreKeys=100,MaxSkippedMessageKeys=1000,EnableAutomaticKeyRotation=true,// Multi-device supportEnableMultiDevice=true,MaxLinkedDevices=10,// Performance and reliabilityEnableMessageHistory=true,MaxMessageHistoryPerSession=1000,EnableSecureMemory=true,EnableSessionPersistence=true,// Retry configurationRetryOptions=newRetryOptions{MaxRetries=3,BaseDelayMs=1000,MaxDelayMs=30000,BackoffMultiplier=2.0}};usingvarclient=newLibEmiddleClient(options);// Get all active sessionsvaractiveSessions=awaitclient.GetActiveSessionsAsync();// Get specific session by IDvarsession=awaitclient.GetSessionAsync(sessionId);// Get chat sessions with a specific uservaruserSessions=awaitclient.GetChatSessionsAsync(userPublicKey);// Session lifecycle managementawaitsession.ActivateAsync();awaitsession.SuspendAsync("Temporary suspension");awaitsession.ResumeAsync();awaitsession.TerminateAsync();// Session persistence and recoveryawaitclient.SaveSessionAsync(session);varrecoveredSession=awaitclient.LoadSessionAsync(sessionId);// Session metadata and historysession.Metadata["custom_field"]="value";varmessageHistory=session.GetMessageHistory();// Session eventssession.StateChanged+=(sender,args)=>{Console.WriteLine($"Session {args.SessionId} state changed to {args.NewState}");};// Enable message batching for improved throughputvaroptions=newLibEmiddleClientOptions{FeatureFlags=newFeatureFlags{EnableMessageBatching=true},BatchingOptions=newBatchingOptions{MaxBatchSize=50,BatchTimeoutMs=1000,CompressionLevel=CompressionLevel.Balanced}};// Messages are automatically batched and compressedawaitclient.SendChatMessageAsync(recipientKey,"Message 1");awaitclient.SendChatMessageAsync(recipientKey,"Message 2");awaitclient.SendChatMessageAsync(recipientKey,"Message 3");// All three messages sent in a single compressed batch// Configure post-quantum crypto algorithms (preparation for future)varoptions=newLibEmiddleClientOptions{PostQuantumOptions=newPostQuantumOptions{Algorithm=PostQuantumAlgorithm.Kyber1024,EnableHybridMode=true,// Classical + Post-quantumKeyExchangeMode=KeyExchangeMode.Hybrid}};// The system will use hybrid classical+PQ when availableusingvarclient=newLibEmiddleClient(options);// Enable comprehensive monitoring and diagnosticsvaroptions=newLibEmiddleClientOptions{FeatureFlags=newFeatureFlags{EnableDiagnostics=true,EnableResilienceManager=true},ResilienceOptions=newResilienceOptions{RetryPolicy=RetryPolicy.ExponentialBackoff,HealthCheckIntervalMs=30000,EnableFailover=true}};usingvarclient=newLibEmiddleClient(options);// Access diagnostics informationvardiagnostics=client.GetDiagnostics();Console.WriteLine($"Active Sessions: {diagnostics.ActiveSessions}");Console.WriteLine($"Messages Sent: {diagnostics.MessagesSent}");Console.WriteLine($"Network Quality: {diagnostics.NetworkQuality}");// Monitor resilience eventsclient.ResilienceManager.ConnectionRestored+=(sender,args)=>{Console.WriteLine($"Connection restored after {args.DowntimeMs}ms");};// Configure connection pooling for high-throughput scenariosvaroptions=newLibEmiddleClientOptions{ConnectionPoolOptions=newConnectionPoolOptions{MinPoolSize=5,MaxPoolSize=20,ConnectionTimeoutMs=30000,IdleTimeoutMs=300000,EnableLoadBalancing=true}};// Connections are automatically managed and reusedusingvarclient=newLibEmiddleClient(options);// Configure sophisticated key rotation policiesvarrotationPolicy=newKeyRotationPolicy{Strategy=KeyRotationStrategy.Adaptive,TimeBasedRotationIntervalHours=24,MessageCountThreshold=10000,RiskBasedRotation=true,BackupKeyCount=3};awaitclient.SetAdvancedKeyRotationPolicyAsync(sessionId,rotationPolicy);// Monitor key rotation eventsclient.KeyRotated+=(sender,args)=>{Console.WriteLine($"Keys rotated for session {args.SessionId}: {args.RotationReason}");};- Symmetric Ratchet - Every message key is derived, used once, then zeroed; used keys are never persisted
- DH Ratchet - A new Diffie-Hellman ratchet step runs on receipt of a peer's new ratchet key, giving post-compromise recovery
- Perfect Forward Secrecy - Past messages remain secure even if current keys are compromised
- Post-Removal Forward Secrecy - Group chain keys rotate immediately when a member is removed
On
KeyRotationStrategy.Standard(changed in v2.7): the DH ratchet advances only in response to a peer's new ratchet key, per the Signal specification. Earlier versions also rotated the sender's ratchet key every 20 messages; because a DH ratchet step is only valid once the peer holds the new public key, that could diverge both sides' DH inputs and permanently break a session where both parties were actively sending. Forward secrecy within a chain comes from the symmetric ratchet and is unaffected.KeyRotationStrategy.AfterEveryMessageis unchanged.
- Message Authentication - AES-256-GCM authenticates every message body
- Replay Protection - 500-entry FIFO deduplication in chat sessions, per-sender message IDs in groups
- Tampering Detection - Immediate detection of ciphertext modification
- Key Validation - X25519 public keys are checked against the full libsodium small-order point blacklist
- Secure Memory Handling - Key material is pinned and zeroed via
sodium_memzero, including superseded root keys, chain keys, retired ratchet private keys, and evicted skipped-message keys - Key Derivation - HKDF for protocol keys; Argon2id (memory-hard, 64 MB, per-key random salt) for password-based keys
- Constant-Time Operations - Protection against timing attacks
Not every type in the public surface is backed by a real implementation. This table is the authoritative list — treat anything marked Stub as unavailable.
| Area | Status | Notes |
|---|---|---|
| X3DH + Double Ratchet | ✅ Production | Signal-compliant key agreement and ratcheting |
| AES-256-GCM / Ed25519 / X25519 | ✅ Production | All via libsodium |
| Chat & group sessions | ✅ Production | Replay protection, forward secrecy, member management |
| Multi-device linking & sync | ✅ Production | Encrypted persistent device list |
| Session persistence | ✅ Production | Argon2id-derived keys, atomic writes |
| HTTP / InMemory transport | ✅ Production | |
| Resilience (retry, circuit breaker, timeout) | ✅ Production | ResilienceManager |
| Post-quantum crypto | PostQuantumCryptoStub returns random bytes for all Kyber/Dilithium/Falcon operations and its VerifyAsync returns true for any correctly-sized signature. Do not use for any security decision. | |
| WebRTC transport | Echoes sent bytes back as received | |
| Session backup manager | Returns hardcoded checksums; writes nothing to disk | |
| Connection pool | Returns a connection that echoes sends as receives | |
| Advanced key rotation scheduling | Task.Delay then a synthetic result; no keys are rotated |
All stubs are targeted for real implementations in v3.0.
Breaking — password-protected key storage.StoreKeyAsync() / RetrieveKeyAsync() now derive
their encryption key with a fresh random Argon2id salt per stored key instead of a fixed
application-wide salt. The stored blob layout changed from nonce‖ciphertext to
salt‖nonce‖ciphertext, so password-protected keys written by 2.6.x and earlier cannot be read by
2.7.0. Re-store any affected keys before upgrading, or keep a 2.6.x reader available to migrate
them. Keys stored without a password are unaffected, as are session files and device lists.
Behavioral — KeyRotationStrategy.Standard. No longer performs a send-count-based DH ratchet
rotation; see Forward Secrecy & Post-Compromise Security.
No API change and no action required.
Wire format — additive and backward compatible.EncryptedMessage gained
PreviousChainLength (the Signal PN header field). It defaults to 0 when absent, so 2.7.0 reads
messages from older peers. Older peers ignore the field. Out-of-order delivery across a ratchet step
only recovers when both sides run 2.7.0+.
dotnet add package LibEmiddle --version 2.7.0Install-Package LibEmiddle -Version 2.7.0- .NET 8.0 or .NET 10.0 (both LTS)
- Windows, Linux, or macOS
- libsodium native library (included in package)
Direct peer-to-peer encrypted communication using WebRTC data channels:
- Low-latency P2P messaging without server intermediaries
- NAT traversal with ICE/STUN/TURN support
- Adaptive bitrate based on network conditions
- Network quality monitoring and automatic fallback
Status: API stub only — WebRTCTransportStub echoes sent bytes back as received bytes. Not functional.
Target: v3.0 release
// Future API (not yet functional)varwebRtcOptions=newLibEmiddleClientOptions{TransportType=TransportType.WebRTC,WebRTCOptions=newWebRTCOptions{ICEServers=new[]{"stun:stun.l.google.com:19302"},EnableAdaptiveBitrate=true}};- Real-time presence indicators
- Message search and indexing
- Advanced group permissions and roles
- Cross-platform push notifications
- Server-side message filtering
- WebSocket transport with server push (alternative to polling)
This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
See LICENSE file for details or visit https://www.gnu.org/licenses/agpl-3.0.html
We welcome contributions! Please see our contributing guidelines and:
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Submit a pull request
The Documentation/ folder contains comprehensive technical documentation including:
- Mailbox Transport Guide: Complete guide to the mailbox transport system (Mailbox-Transport-Guide.md)
- Message Flow Diagrams: Mermaid sequence diagrams showing complete message flow (Message-Flow-Sequence.md)
- Architecture Diagrams: Full system architecture and component interactions
- Sequence Diagrams: Detailed protocol flows for all major operations
- 1-to-1 Chat establishment and messaging
- Group Chat creation and management
- Device linking and revocation processes
- Advanced key rotation workflows
- Post-quantum key exchange preparation
- Message batching and compression flows
- Technical Specifications: In-depth coverage of cryptographic protocols and security features
Built by Russell Benzing. Reach out at me@russellbenzing.com.
- Issues: GitHub Issues
- Security: For security concerns, email Russell Benzing at me@russellbenzing.com
- Documentation: See Documentation/ folder for detailed technical documentation and sequence diagrams
If LibEmiddle is useful to you, you can support the work: