Class OlmMachine
End-to-end encryption state machine of one device: keys, Olm sessions with other devices and Megolm sessions for rooms.
Implements
Inherited Members
Namespace: Uniform.Matrix.Protocol.Crypto
Assembly: Uniform.Matrix.Protocol.Crypto.dll
Syntax
public sealed class OlmMachine : IDisposable
Remarks
Feed every sync to ReceiveSyncAsync(SyncResponse, CancellationToken), decrypt m.room.encrypted timeline events with DecryptRoomEventAsync(string, ClientEvent, CancellationToken),
and encrypt outgoing events for encrypted rooms with EncryptRoomEventAsync(string, string, object, IReadOnlyCollection<string>, EncryptionContent?, CancellationToken). Calls are serialized, so it can be shared between threads.
Split into partial files:
- OlmMachine.cs: account, key upload and device lists.
- OlmMachine.Olm.cs: Olm sessions and to-device messages, including received room keys.
- OlmMachine.Megolm.cs: room message encryption, key sharing and decryption.
- OlmMachine.CrossSigning.cs: cross-signing keys, trust and recovery from secret storage.
- OlmMachine.Backup.cs: server-side key backup.
- OlmMachine.KeyRequests.cs: room key and secret requests between the user's own devices.
- OlmMachine.Verification.cs: SAS emoji verification of devices.
Examples
OlmMachine machine = await OlmMachine.CreateAsync(new MatrixKeyTransport(client), store, client.UserId!, client.DeviceId!, pickleKey);
await foreach (SyncResponse sync in client.SyncLoopAsync())
{
await machine.ReceiveSyncAsync(sync);
// decrypt timeline events with machine.DecryptRoomEventAsync(roomId, ev)
}
Fields
| Edit this page View SourceWedgedSessionRetry
How long before a device whose message could not be decrypted gets another new session.
Declaration
public static readonly TimeSpan WedgedSessionRetry
Field Value
| Type | Description |
|---|---|
| TimeSpan |
Properties
| Edit this page View SourceDeviceId
This device.
Declaration
public string DeviceId { get; }
Property Value
| Type | Description |
|---|---|
| string |
IdentityKeys
This device's public identity keys.
Declaration
public IdentityKeys IdentityKeys { get; }
Property Value
| Type | Description |
|---|---|
| IdentityKeys |
RoomEventSender
Send a room event, encrypted in encrypted rooms: the room, the event type and content; set by the client, which owns the room's encryption. Without it, in-room verification requests are not answered.
Declaration
public Func<string, string, JsonObject, CancellationToken, Task>? RoomEventSender { get; set; }
Property Value
| Type | Description |
|---|---|
| Func<string, string, JsonObject, CancellationToken, Task> |
SasEmojiTable
The SAS emoji table, by index.
Declaration
public static IReadOnlyList<SasEmoji> SasEmojiTable { get; }
Property Value
| Type | Description |
|---|---|
| IReadOnlyList<SasEmoji> |
UserId
Logged-in user.
Declaration
public string UserId { get; }
Property Value
| Type | Description |
|---|---|
| string |
Verifications
Verifications in progress, ours and incoming; safe to read from VerificationChanged.
Declaration
public IReadOnlyList<Verification> Verifications { get; }
Property Value
| Type | Description |
|---|---|
| IReadOnlyList<Verification> |
Methods
| Edit this page View SourceAcceptVerificationAsync(Verification, CancellationToken)
Accept an incoming request; the requester then starts the emoji exchange.
Declaration
public Task AcceptVerificationAsync(Verification verification, CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| Verification | verification | |
| CancellationToken | cancellationToken |
Returns
| Type | Description |
|---|---|
| Task |
BackUpRoomKeysAsync(CancellationToken)
Upload room keys not yet in the backup; return how many. Does nothing without a backup whose private key this device holds.
Declaration
public Task<int> BackUpRoomKeysAsync(CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| CancellationToken | cancellationToken |
Returns
| Type | Description |
|---|---|
| Task<int> |
CancelVerificationAsync(Verification, string, CancellationToken)
Cancel a verification, e.g. when the emoji do not match.
Declaration
public Task CancelVerificationAsync(Verification verification, string code = "m.user", CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| Verification | verification | Verification to cancel. |
| string | code | Cancel code; |
| CancellationToken | cancellationToken | Cancellation token. |
Returns
| Type | Description |
|---|---|
| Task |
ConfirmVerificationAsync(Verification, CancellationToken)
Confirm that the emoji match: sends our MACs, and finishes when theirs check out.
Declaration
public Task ConfirmVerificationAsync(Verification verification, CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| Verification | verification | |
| CancellationToken | cancellationToken |
Returns
| Type | Description |
|---|---|
| Task |
CreateAsync(IKeyTransport, ICryptoStore, string, string, byte[], TimeProvider?, CancellationToken)
Load the device's account from store, or create one on first use.
Declaration
public static Task<OlmMachine> CreateAsync(IKeyTransport transport, ICryptoStore store, string userId, string deviceId, byte[] pickleKey, TimeProvider? time = null, CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| IKeyTransport | transport | Network calls, usually |
| ICryptoStore | store | Persistent state. |
| string | userId | Logged-in user. |
| string | deviceId | This device. |
| byte[] | pickleKey | 32-byte key encrypting stored pickles; keep it secret and stable. |
| TimeProvider | time | Clock for session rotation; system clock when null. |
| CancellationToken | cancellationToken | Cancellation token. |
Returns
| Type | Description |
|---|---|
| Task<OlmMachine> |
DecryptRoomEventAsync(string, ClientEvent, CancellationToken)
Decrypt a Megolm m.room.encrypted event of roomId; other events are returned unchanged.
Declaration
public Task<RoomEventDecryption> DecryptRoomEventAsync(string roomId, ClientEvent clientEvent, CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| string | roomId | |
| ClientEvent | clientEvent | |
| CancellationToken | cancellationToken |
Returns
| Type | Description |
|---|---|
| Task<RoomEventDecryption> |
DiscardOutboundSessionAsync(string, CancellationToken)
Forget the room's outbound session, so the next message starts a new one; e.g. after a member left.
Declaration
public Task DiscardOutboundSessionAsync(string roomId, CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| string | roomId | |
| CancellationToken | cancellationToken |
Returns
| Type | Description |
|---|---|
| Task |
Dispose()
Wipe key material in memory.
Declaration
public void Dispose()
EncryptRoomEventAsync(string, string, object, IReadOnlyCollection<string>, EncryptionContent?, CancellationToken)
Encrypt an event for roomId, first sharing the room key with devices of memberIds that lack it; return the m.room.encrypted content to send.
Declaration
public Task<EncryptedContent> EncryptRoomEventAsync(string roomId, string eventType, object content, IReadOnlyCollection<string> memberIds, EncryptionContent? settings = null, CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| string | roomId | Encrypted room. |
| string | eventType | Type of the event to encrypt, e.g. RoomMessage. |
| object | content | Event content; serialized with Options. |
| IReadOnlyCollection<string> | memberIds | Joined and invited members, whose devices get the key. |
| EncryptionContent | settings | Room's |
| CancellationToken | cancellationToken | Cancellation token. |
Returns
| Type | Description |
|---|---|
| Task<EncryptedContent> |
GetDeviceTrust(DeviceInfo)
Return how much device is trusted.
Declaration
public DeviceTrust GetDeviceTrust(DeviceInfo device)
Parameters
| Type | Name | Description |
|---|---|---|
| DeviceInfo | device |
Returns
| Type | Description |
|---|---|
| DeviceTrust |
GetDevices(string)
Return known devices of userId; call TrackUsersAsync(IEnumerable<string>, CancellationToken) first to fetch them.
Declaration
public IReadOnlyList<DeviceInfo> GetDevices(string userId)
Parameters
| Type | Name | Description |
|---|---|---|
| string | userId |
Returns
| Type | Description |
|---|---|
| IReadOnlyList<DeviceInfo> |
GetIdentity(string)
Return the known cross-signing keys of userId.
Declaration
public UserIdentity? GetIdentity(string userId)
Parameters
| Type | Name | Description |
|---|---|---|
| string | userId |
Returns
| Type | Description |
|---|---|
| UserIdentity |
GetSecurityStatusAsync(CancellationToken)
Report cross-signing, backup and secret storage state, querying the server for current keys.
Declaration
public Task<SecurityStatus> GetSecurityStatusAsync(CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| CancellationToken | cancellationToken |
Returns
| Type | Description |
|---|---|
| Task<SecurityStatus> |
IsIdentityTrusted(UserIdentity)
Return whether a user's cross-signing identity is trusted: verified directly, our own with its private key here, or signed by our trusted user-signing key.
Declaration
public bool IsIdentityTrusted(UserIdentity identity)
Parameters
| Type | Name | Description |
|---|---|---|
| UserIdentity | identity |
Returns
| Type | Description |
|---|---|
| bool |
IsSignedByIdentity(DeviceInfo)
Return whether device is signed by its owner's current self-signing key, whether or not this device trusts that identity; such a device can vouch for others of the same account.
Declaration
public bool IsSignedByIdentity(DeviceInfo device)
Parameters
| Type | Name | Description |
|---|---|---|
| DeviceInfo | device |
Returns
| Type | Description |
|---|---|
| bool |
ReceiveRoomEventAsync(string, ClientEvent, CancellationToken)
Handle an event of a room's timeline that belongs to an in-room verification: a request message to this user, or a later m.key.verification.* event that refers to one; other events, and this user's own, are skipped.
Declaration
public Task ReceiveRoomEventAsync(string roomId, ClientEvent clientEvent, CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| string | roomId | Room of the event. |
| ClientEvent | clientEvent | The event, decrypted. |
| CancellationToken | cancellationToken | Cancellation token. |
Returns
| Type | Description |
|---|---|
| Task |
ReceiveSyncAsync(SyncResponse, CancellationToken)
Process the encryption parts of a sync: device list changes, to-device messages and the one-time key count; return decrypted to-device events.
Declaration
public Task<IReadOnlyList<DecryptedToDeviceEvent>> ReceiveSyncAsync(SyncResponse sync, CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| SyncResponse | sync | |
| CancellationToken | cancellationToken |
Returns
| Type | Description |
|---|---|
| Task<IReadOnlyList<DecryptedToDeviceEvent>> |
RecoverAsync(string, CancellationToken)
Unlock secret storage with a recovery key or passphrase, restores cross-signing and the backup key, signs this device, and import backed-up room keys.
Declaration
public Task<RecoveryResult> RecoverAsync(string recoveryKeyOrPassphrase, CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| string | recoveryKeyOrPassphrase | |
| CancellationToken | cancellationToken |
Returns
| Type | Description |
|---|---|
| Task<RecoveryResult> |
Remarks
Throws CryptographicException for a wrong key, and InvalidOperationException when the account has no secret storage.
RequestVerificationAsync(string, string?, CancellationToken)
Ask userId's devices, or one of them, to verify; for our own user, our other devices.
Declaration
public Task<Verification> RequestVerificationAsync(string userId, string? deviceId = null, CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| string | userId | |
| string | deviceId | |
| CancellationToken | cancellationToken |
Returns
| Type | Description |
|---|---|
| Task<Verification> |
RequestVerificationInRoomAsync(string, string, Func<JsonObject, CancellationToken, Task<string>>, CancellationToken)
Ask userId to verify in roomId, their direct chat, as other users are verified: sendRequest sends the request message, encrypted where the room is, and returns its event ID, which identifies the verification.
Declaration
public Task<Verification> RequestVerificationInRoomAsync(string roomId, string userId, Func<JsonObject, CancellationToken, Task<string>> sendRequest, CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| string | roomId | |
| string | userId | |
| Func<JsonObject, CancellationToken, Task<string>> | sendRequest | |
| CancellationToken | cancellationToken |
Returns
| Type | Description |
|---|---|
| Task<Verification> |
RestoreBackupAsync(CancellationToken)
Download and import all keys of the current backup; return how many were new. Throws InvalidOperationException when this device lacks the backup key.
Declaration
public Task<int> RestoreBackupAsync(CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| CancellationToken | cancellationToken |
Returns
| Type | Description |
|---|---|
| Task<int> |
SendEncryptedToDevicesAsync(string, JsonObject, IReadOnlyCollection<(string UserId, string DeviceId)>, CancellationToken)
Send a to-device event of type to each of devices, encrypted over Olm, as MatrixRTC sends its media keys; return how many devices it went to. Devices without keys, or whose Olm session could not be made, are left out.
Declaration
public Task<int> SendEncryptedToDevicesAsync(string type, JsonObject content, IReadOnlyCollection<(string UserId, string DeviceId)> devices, CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| string | type | Event type, e.g. |
| JsonObject | content | Event content. |
| IReadOnlyCollection<(string UserId, string DeviceId)> | devices | Recipients, by user and device ID; this device is left out. |
| CancellationToken | cancellationToken | Cancellation token. |
Returns
| Type | Description |
|---|---|
| Task<int> |
SetUpRecoveryAsync(string?, CancellationToken)
Set up cross-signing, a key backup and secret storage on an account without cross-signing; return the recovery key to show the user once.
Declaration
public Task<string> SetUpRecoveryAsync(string? password, CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| string | password | Password, when the server asks for it; when null, |
| CancellationToken | cancellationToken | Cancellation token. |
Returns
| Type | Description |
|---|---|
| Task<string> |
Remarks
Throws InvalidOperationException when the account already has cross-signing keys: replacing them would distrust every verified device, so use RecoverAsync(string, CancellationToken) instead.
TrackUsersAsync(IEnumerable<string>, CancellationToken)
Fetch device lists of userIds that are unknown or outdated.
Declaration
public Task TrackUsersAsync(IEnumerable<string> userIds, CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| IEnumerable<string> | userIds | |
| CancellationToken | cancellationToken |
Returns
| Type | Description |
|---|---|
| Task |
UploadInitialKeysAsync(CancellationToken)
Upload keys now when they have never been uploaded, e.g. right after login.
Declaration
public Task UploadInitialKeysAsync(CancellationToken cancellationToken = default)
Parameters
| Type | Name | Description |
|---|---|---|
| CancellationToken | cancellationToken |
Returns
| Type | Description |
|---|---|
| Task |
Events
| Edit this page View SourceRoomKeyReceived
Raised with room and session ID when a room key arrives, so events that failed to decrypt can be retried.
Declaration
public event Action<string, string>? RoomKeyReceived
Event Type
| Type | Description |
|---|---|
| Action<string, string> |
SecretReceived
Raised with the secret name when another of our devices sent a secret, e.g. after verification.
Declaration
public event Action<string>? SecretReceived
Event Type
| Type | Description |
|---|---|
| Action<string> |
ToDeviceDecryptionFailed
Raised with the sender, its Curve25519 key and the reason when a to-device message could not be decrypted.
Declaration
public event Action<string, string, string>? ToDeviceDecryptionFailed
Event Type
| Type | Description |
|---|---|
| Action<string, string, string> |
VerificationChanged
Raised when a verification starts, changes state or ends; handlers run inside the machine, so marshal UI work.
Declaration
public event Action<Verification>? VerificationChanged
Event Type
| Type | Description |
|---|---|
| Action<Verification> |
VerificationIgnored
Raised when a verification message is dropped, with its sender, the sending device if known, and why; for logs, as the other side gets no answer.
Declaration
public event Action<string, string?, string>? VerificationIgnored
Event Type
| Type | Description |
|---|---|
| Action<string, string, string> |
WedgedSessionRecovered
Raised with the sender, its device and whether the message went out, after starting a new Olm session with a device whose message could not be decrypted.
Declaration
public event Action<string, string, bool>? WedgedSessionRecovered
Event Type
| Type | Description |
|---|---|
| Action<string, string, bool> |