No description
Find a file
2025-07-22 03:03:24 -07:00
.github ci: remove powershell/md 2025-07-22 03:03:24 -07:00
.idea/.idea.Olm/.idea Some OLM stuff 2025-03-03 11:07:19 -08:00
Olm ci: ix up package processing 2025-07-22 01:44:03 -07:00
Olm.Benchmarks ci: ix up package processing 2025-07-22 01:44:03 -07:00
Olm.Tests Handle out of order messages by preserving (and deleting) keys 2025-07-21 23:53:37 -07:00
.gitignore ci: ix up package processing 2025-07-22 01:44:03 -07:00
Directory.Build.props To bouncy castle! 2025-02-27 12:32:58 -08:00
Directory.Build.targets To bouncy castle! 2025-02-27 12:32:58 -08:00
Directory.Packages.props Handle out of order messages by preserving (and deleting) keys 2025-07-21 23:53:37 -07:00
global.json To bouncy castle! 2025-02-27 12:32:58 -08:00
LICENSE Create LICENSE 2025-07-22 00:33:30 -07:00
Olm.sln Sessions, still not right... 2025-03-07 16:21:40 -08:00
Olm.sln.DotSettings Some OLM stuff 2025-03-03 11:07:19 -08:00
README.md Add build and workflow 2025-07-22 00:36:01 -07:00

VaettirNet.Olm

Build Status NuGet License: MIT

A .NET implementation of the Olm cryptographic ratchet protocol. Olm is an implementation of the Double Ratchet Algorithm, providing strong end-to-end encryption for messaging applications with forward secrecy and break-in recovery properties.

Features

  • Complete implementation of the Olm cryptographic ratchet protocol
  • X3DH (Extended Triple Diffie-Hellman) key agreement protocol for initial key exchange
  • Double Ratchet Algorithm providing strong forward secrecy and post-compromise security
  • Support for out-of-order message delivery
  • Prevention of replay attacks
  • Modern C# 13 implementation targeting .NET 9.0
  • Built on BouncyCastle for cryptographic primitives

Installation

Install the package via NuGet:

dotnet add package VaettirNet.Olm

Or via the Package Manager Console:

Install-Package VaettirNet.Olm

The package is automatically versioned using GitVersion and follows semantic versioning. You can check the latest version on NuGet.

Usage

Key Generation

Generating the necessary keys for encryption:

// Generate identity keys (long-term keys)
var aliceIdentity = X3DHPrivateIdentityKey.Generate();
var bobIdentity = X3DHPrivateIdentityKey.Generate();

// Generate ephemeral key for Alice
var aliceEphemeral = X3DHPrivateEphemeralKey.Generate();

// Generate pre-key for Bob
var bobPreKey = X3DHPrivatePreKey.Generate();

Establishing a Session

Alice initiates a session with Bob:

// Alice initiates a session with Bob
var aliceSession = OlmSession.Initiate(
    aliceIdentity,               // Alice's identity key
    bobIdentity.GetPublic(),     // Bob's public identity key
    aliceEphemeral,              // Alice's ephemeral key
    bobPreKey.GetPublic()        // Bob's public pre-key
);

// Alice sends a first message
var firstMessage = new PlainText("Hello Bob!"u8.ToArray());
var encryptedMessage = aliceSession.CreateOutputMessage(firstMessage);
// encryptedMessage will be an OlmPreKeyMessage for the first message

Bob receives and accepts the session:

// Bob receives the OlmPreKeyMessage and accepts the session
if (OlmSession.TryAcceptNewInbound(
    bobIdentity,              // Bob's identity key
    bobPreKey,                // Bob's pre-key
    encryptedMessage,         // The OlmPreKeyMessage from Alice
    out var bobSession,       // The newly created session
    out var decryptedMessage  // The decrypted message
))
{
    // Session established successfully
    // decryptedMessage contains "Hello Bob!"
    Console.WriteLine(Encoding.UTF8.GetString(decryptedMessage.Data.Span));
}

Sending and Receiving Messages

Once a session is established, messages can be exchanged:

// Bob sends a reply
var bobReply = new PlainText("Hi Alice!"u8.ToArray());
var encryptedReply = bobSession.CreateOutputMessage(bobReply);

// Alice receives and decrypts Bob's message
if (aliceSession.TryReceive(encryptedReply, out var decryptedReply))
{
    Console.WriteLine(Encoding.UTF8.GetString(decryptedReply.Data.Span)); // "Hi Alice!"
}

Handling Out-of-Order Messages

The library supports out-of-order message delivery:

// Alice sends multiple messages
var message1 = aliceSession.CreateOutputMessage(new PlainText("Message 1"u8.ToArray()));
var message2 = aliceSession.CreateOutputMessage(new PlainText("Message 2"u8.ToArray()));
var message3 = aliceSession.CreateOutputMessage(new PlainText("Message 3"u8.ToArray()));

// Bob can receive them in any order
bobSession.TryReceive(message3, out var decrypted3);
bobSession.TryReceive(message1, out var decrypted1);
bobSession.TryReceive(message2, out var decrypted2);

// All messages will be decrypted correctly

Shared Secret Derivation

For more advanced use cases, you can work directly with the X3DH shared secret:

// Create a shared secret using the X3DH protocol
X3DHSharedSecret aliceSecret = new();
aliceSecret.DeriveInitial(
    aliceIdentity,            // Alice's identity key
    bobIdentity.GetPublic(),  // Bob's public identity key
    aliceEphemeral,           // Alice's ephemeral key
    bobPreKey.GetPublic()     // Bob's public pre-key
);

// Bob computes the same shared secret
X3DHSharedSecret bobSecret = new();
bobSecret.DeriveResponse(
    bobIdentity,              // Bob's identity key
    aliceIdentity.GetPublic(), // Alice's public identity key
    aliceEphemeral.GetPublic(), // Alice's public ephemeral key
    bobPreKey                 // Bob's pre-key
);

// Both shared secrets should be identical

Architecture

VaettirNet.Olm implements the Olm protocol with a focus on security and performance:

  • X3DH Key Exchange: Secure initial key agreement protocol using multiple Diffie-Hellman operations to establish a shared secret.
  • Double Ratchet Algorithm: Combines a Diffie-Hellman key exchange with a KDF ratchet to provide forward secrecy and break-in recovery.
  • Message Keys: One-time keys are derived for each message to ensure messages remain secure even if session keys are compromised.

Security Considerations

  • Forward Secrecy: If keys are compromised, previously sent messages remain secure.
  • Break-in Recovery: The protocol can recover security if keys are compromised.
  • Key Rotation: Regularly rotate pre-keys and ephemeral keys for maximum security.
  • Replay Protection: Built-in protection against message replay attacks.
  • Side-Channel Mitigation: Uses constant-time operations for cryptographic comparisons to prevent timing attacks.

Requirements

  • .NET 9.0
  • Dependencies:
    • BouncyCastle.Cryptography
    • System.Runtime.Caching

Advanced Usage

Session Management

In a real-world application, you'll need to manage multiple sessions:

// Create a session store (you would implement ISessionStore)
private Dictionary<string, OlmSession> _sessions = new();

// Store a session by recipient ID
public void StoreSession(string recipientId, OlmSession session)
{
    _sessions[recipientId] = session;
}

// Retrieve a session
public OlmSession GetSession(string recipientId)
{
    return _sessions.TryGetValue(recipientId, out var session) ? session : null;
}

Handling Large Messages

The library can handle messages of arbitrary size:

// For large messages, you might want to use streams
public byte[] EncryptLargeFile(OlmSession session, byte[] fileData)
{
    var plainText = new PlainText(fileData);
    var encrypted = session.CreateOutputMessage(plainText);

    // Serialize the encrypted message
    var buffer = new byte[1024 * 1024]; // Adjust size as needed
    encrypted.TryWrite(buffer, out var bytesWritten);

    return buffer[..bytesWritten];
}

Integration with Matrix Protocol

This library can be used as part of a Matrix client implementation for end-to-end encryption:

// Example of integrating with Matrix protocol
public async Task SendEncryptedMatrixMessage(string roomId, string recipientUserId, string messageText)
{
    // Get or create session for recipient
    var session = GetSessionForUser(recipientUserId);

    // Encrypt the message content
    var plainText = new PlainText(Encoding.UTF8.GetBytes(messageText));
    var encrypted = session.CreateOutputMessage(plainText);

    // Serialize to bytes for transmission
    var buffer = new byte[8192];
    encrypted.TryWrite(buffer, out var bytesWritten);

    // Convert to Base64 for JSON transmission
    var base64Payload = Convert.ToBase64String(buffer[..bytesWritten]);

    // Send via Matrix API (implementation depends on your Matrix client)
    await matrixClient.SendEncryptedEvent(roomId, "m.room.encrypted", new {
        algorithm = "m.olm.v1.curve25519-aes-sha2",
        sender_key = myIdentityKey.GetPublic().ToString(),
        ciphertext = new Dictionary<string, object> {
            { recipientCurve25519Identity, new {
                body = base64Payload,
                type = encrypted is OlmPreKeyMessage ? 0 : 1
            }}
        }
    });
}

Build and Development

  1. Clone the repository: git clone https://github.com/ChadNedzlek/Olm.Net.git
  2. Restore dependencies: dotnet restore
  3. Build the project: dotnet build -c Release
  4. Run tests: dotnet test --no-build --verbosity normal --configuration Release
  5. Run benchmarks: dotnet run -c Release --project Olm.Benchmarks

Versioning

This project uses GitVersion for semantic versioning. The version is automatically determined based on Git history and tags.

Implementation Details

VaettirNet.Olm is a complete implementation of the Olm protocol specification with these key components:

  • X3DHPrivateKey/X3DHPublicKey: Base classes for all key types, handling secure key operations
  • X3DHSharedSecret: Handles the derivation of shared secrets using the X3DH protocol
  • OlmSession: Manages an encryption session between two parties
  • OlmRatchet: Implements the Double Ratchet Algorithm for message encryption/decryption
  • OlmMessage/OlmPreKeyMessage: Represents encrypted messages with proper serialization

The implementation uses modern C# 13 features including ref structs for efficient memory usage, native inline arrays for storage optimization, and pattern matching for robust message parsing.

Relationship to Matrix

This library can be used as the core encryption component for Matrix clients. The Olm protocol is used in Matrix for end-to-end encrypted direct messages, while its extension, Megolm, is used for group communications.

License

This project is licensed under the MIT License - see the license information in the package metadata for details.

Author

Chad Nedzlek

Repository

GitHub Repository