Skip to content

Commit

Permalink
Add device list to new crypto
Browse files Browse the repository at this point in the history
  • Loading branch information
florianduros committed Apr 5, 2023
1 parent 2daa429 commit 5812842
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 29 deletions.
18 changes: 11 additions & 7 deletions spec/integ/sliding-sync-sdk.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -662,24 +662,28 @@ describe("SlidingSyncSdk", () => {
});

it("can update device lists", () => {
client!.crypto!.processDeviceLists = jest.fn();
ext.onResponse({
device_lists: {
changed: ["@alice:localhost"],
left: ["@bob:localhost"],
},
});
// TODO: more assertions?
expect(client!.crypto!.processDeviceLists).toHaveBeenCalledWith(
{ oldSyncToken: "yep" },
{ changed: ["@alice:localhost"], left: ["@bob:localhost"] },
);
});

it("can update OTK counts", () => {
it("can update OTK counts", async () => {
client!.crypto!.updateOneTimeKeyCount = jest.fn();
ext.onResponse({
await ext.onResponse({
device_one_time_keys_count: {
signed_curve25519: 42,
},
});
expect(client!.crypto!.updateOneTimeKeyCount).toHaveBeenCalledWith(42);
ext.onResponse({
await ext.onResponse({
device_one_time_keys_count: {
not_signed_curve25519: 42,
// missing field -> default to 0
Expand All @@ -688,12 +692,12 @@ describe("SlidingSyncSdk", () => {
expect(client!.crypto!.updateOneTimeKeyCount).toHaveBeenCalledWith(0);
});

it("can update fallback keys", () => {
ext.onResponse({
it("can update fallback keys", async () => {
await ext.onResponse({
device_unused_fallback_key_types: ["signed_curve25519"],
});
expect(client!.crypto!.getNeedsNewFallback()).toEqual(false);
ext.onResponse({
await ext.onResponse({
device_unused_fallback_key_types: ["not_signed_curve25519"],
});
expect(client!.crypto!.getNeedsNewFallback()).toEqual(true);
Expand Down
12 changes: 12 additions & 0 deletions src/common-crypto/CryptoBackend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { CryptoApi } from "../crypto-api";
import { DeviceTrustLevel, UserTrustLevel } from "../crypto/CrossSigning";
import { IEncryptedEventInfo } from "../crypto/api";
import { IEventDecryptionResult } from "../@types/crypto";
import { ISyncStateData } from "../sync";
import { ISyncResponse } from "../sync-accumulator";

/**
* Common interface for the crypto implementations
Expand Down Expand Up @@ -113,6 +115,16 @@ export interface SyncCryptoCallbacks {
*/
processKeyCounts(oneTimeKeysCounts?: Record<string, number>, unusedFallbackKeys?: string[]): Promise<void>;

/**
* Handle the notification from /sync or /keys/changes that device lists have
* been changed.
*
* @param syncData - Object containing sync tokens associated with this sync
* @param deviceLists - device_lists field from /sync, or response from
* /keys/changes
*/
processDeviceLists(syncData: ISyncStateData, deviceLists?: Required<ISyncResponse>["device_lists"]): Promise<void>;

/**
* Called by the /sync loop whenever an m.room.encryption event is received.
*
Expand Down
10 changes: 5 additions & 5 deletions src/crypto/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2922,16 +2922,16 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
* been changed.
*
* @param syncData - Object containing sync tokens associated with this sync
* @param syncDeviceLists - device_lists field from /sync, or response from
* @param deviceLists - device_lists field from /sync, or response from
* /keys/changes
*/
public async handleDeviceListChanges(
public async processDeviceLists(
syncData: ISyncStateData,
syncDeviceLists: Required<ISyncResponse>["device_lists"],
deviceLists?: Required<ISyncResponse>["device_lists"],
): Promise<void> {
// Initial syncs don't have device change lists. We'll either get the complete list
// of changes for the interval or will have invalidated everything in willProcessSync
if (!syncData.oldSyncToken) return;
if (!syncData.oldSyncToken || !deviceLists) return;

// Here, we're relying on the fact that we only ever save the sync data after
// sucessfully saving the device list data, so we're guaranteed that the device
Expand All @@ -2941,7 +2941,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
// If we didn't make this assumption, we'd have to use the /keys/changes API
// to get key changes between the sync token in the device list and the 'old'
// sync token used here to make sure we didn't miss any.
await this.evalDeviceListChanges(syncDeviceLists);
await this.evalDeviceListChanges(deviceLists);
}

/**
Expand Down
24 changes: 23 additions & 1 deletion src/rust-crypto/rust-crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import { DeviceTrustLevel, UserTrustLevel } from "../crypto/CrossSigning";
import { RoomEncryptor } from "./RoomEncryptor";
import { OutgoingRequest, OutgoingRequestProcessor } from "./OutgoingRequestProcessor";
import { KeyClaimManager } from "./KeyClaimManager";
import { ISyncResponse } from "../sync-accumulator";
import { ISyncStateData } from "../sync";
import { MapWithDefault } from "../utils";

/**
Expand Down Expand Up @@ -176,20 +178,23 @@ export class RustCrypto implements CryptoBackend {
* @param events - the received to-device messages
* @param oneTimeKeysCounts - the received one time key counts
* @param unusedFallbackKeys - the received unused fallback keys
* @param devices - the received devices
* @returns A list of preprocessed to-device messages.
*/
private async receiveSyncChanges({
events,
oneTimeKeysCounts = new Map<string, number>(),
unusedFallbackKeys = new Set<string>(),
devices = new RustSdkCryptoJs.DeviceLists(),
}: {
events?: IToDeviceEvent[];
oneTimeKeysCounts?: Map<string, number>;
unusedFallbackKeys?: Set<string>;
devices?: RustSdkCryptoJs.DeviceLists;
}): Promise<IToDeviceEvent[]> {
const result = await this.olmMachine.receiveSyncChanges(
events ? JSON.stringify(events) : "[]",
new RustSdkCryptoJs.DeviceLists(),
devices,
oneTimeKeysCounts,
unusedFallbackKeys,
);
Expand Down Expand Up @@ -229,6 +234,23 @@ export class RustCrypto implements CryptoBackend {
}
}

/** called by the sync loop to process
*
* @param syncData - Object containing sync tokens associated with this sync
* @param deviceLists - device_lists field from /sync, or response from
*/
public async processDeviceLists(
syncData: ISyncStateData,
deviceLists: Required<ISyncResponse>["device_lists"],
): Promise<void> {
// Initial syncs don't have device change lists. We'll either get the complete list
// of changes for the interval or will have invalidated everything in willProcessSync
if (!syncData.oldSyncToken || !deviceLists) return;

const devices = new RustSdkCryptoJs.DeviceLists(deviceLists.changed, deviceLists.left);
await this.receiveSyncChanges({ devices });
}

/** called by the sync loop on m.room.encrypted events
*
* @param room - in which the event was received
Expand Down
14 changes: 6 additions & 8 deletions src/sliding-sync-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,12 @@ class ExtensionE2EE implements Extension<ExtensionE2EERequest, ExtensionE2EEResp

public async onResponse(data: ExtensionE2EEResponse): Promise<void> {
// Handle device list updates
if (data["device_lists"]) {
await this.crypto.handleDeviceListChanges(
{
oldSyncToken: "yep", // XXX need to do this so the device list changes get processed :(
},
data["device_lists"],
);
}
await this.crypto.processDeviceLists(
{
oldSyncToken: "yep", // XXX need to do this so the device list changes get processed :(
},
data["device_lists"],
);

// Handle one_time_keys_count
if (data["device_one_time_keys_count"]) {
Expand Down
14 changes: 6 additions & 8 deletions src/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1514,14 +1514,12 @@ export class SyncApi {
}

// Handle device list updates
if (data.device_lists) {
if (this.syncOpts.crypto) {
await this.syncOpts.crypto.handleDeviceListChanges(syncEventData, data.device_lists);
} else {
// FIXME if we *don't* have a crypto module, we still need to
// invalidate the device lists. But that would require a
// substantial bit of rework :/.
}
if (this.syncOpts.cryptoCallbacks) {
await this.syncOpts.cryptoCallbacks.processDeviceLists(syncEventData, data.device_lists);
} else {
// FIXME if we *don't* have a crypto module, we still need to
// invalidate the device lists. But that would require a
// substantial bit of rework :/.
}

// Handle one_time_keys_count and unused fallback keys
Expand Down

0 comments on commit 5812842

Please sign in to comment.