From f71d86f0054fc2c96ac008fde67f46a419f85add Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Tue, 11 Apr 2023 15:42:19 +0200 Subject: [PATCH] Element-R: pass device list change notifications into rust crypto-sdk (#3254) --- spec/integ/sliding-sync-sdk.spec.ts | 6 +++++- src/common-crypto/CryptoBackend.ts | 10 +++++++++- src/crypto/index.ts | 19 +++++-------------- src/rust-crypto/rust-crypto.ts | 17 +++++++++++++++-- src/sliding-sync-sdk.ts | 9 ++------- src/sync-accumulator.ts | 2 +- src/sync.ts | 4 ++-- 7 files changed, 39 insertions(+), 28 deletions(-) diff --git a/spec/integ/sliding-sync-sdk.spec.ts b/spec/integ/sliding-sync-sdk.spec.ts index 37fcd879f..6fdaeb119 100644 --- a/spec/integ/sliding-sync-sdk.spec.ts +++ b/spec/integ/sliding-sync-sdk.spec.ts @@ -662,13 +662,17 @@ 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({ + changed: ["@alice:localhost"], + left: ["@bob:localhost"], + }); }); it("can update OTK counts and unused fallback keys", () => { diff --git a/src/common-crypto/CryptoBackend.ts b/src/common-crypto/CryptoBackend.ts index afe77d177..ddce0a622 100644 --- a/src/common-crypto/CryptoBackend.ts +++ b/src/common-crypto/CryptoBackend.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import type { IToDeviceEvent } from "../sync-accumulator"; +import type { IDeviceLists, IToDeviceEvent } from "../sync-accumulator"; import { MatrixEvent } from "../models/event"; import { Room } from "../models/room"; import { CryptoApi } from "../crypto-api"; @@ -113,6 +113,14 @@ export interface SyncCryptoCallbacks { */ processKeyCounts(oneTimeKeysCounts?: Record, unusedFallbackKeys?: string[]): Promise; + /** + * Handle the notification from /sync that device lists have + * been changed. + * + * @param deviceLists - device_lists field from /sync + */ + processDeviceLists(deviceLists: IDeviceLists): Promise; + /** * Called by the /sync loop whenever an m.room.encryption event is received. * diff --git a/src/crypto/index.ts b/src/crypto/index.ts index 3c787a0c2..ca0a48c6d 100644 --- a/src/crypto/index.ts +++ b/src/crypto/index.ts @@ -71,7 +71,7 @@ import { CryptoStore } from "./store/base"; import { IVerificationChannel } from "./verification/request/Channel"; import { TypedEventEmitter } from "../models/typed-event-emitter"; import { IContent } from "../models/event"; -import { ISyncResponse, IToDeviceEvent } from "../sync-accumulator"; +import { IDeviceLists, ISyncResponse, IToDeviceEvent } from "../sync-accumulator"; import { ISignatures } from "../@types/signed"; import { IMessage } from "./algorithms/olm"; import { CryptoBackend, OnSyncCompletedData } from "../common-crypto/CryptoBackend"; @@ -2893,21 +2893,12 @@ export class Crypto extends TypedEventEmitter["device_lists"], - ): Promise { - // 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; - + public async processDeviceLists(deviceLists: IDeviceLists): Promise { // 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 // list store is at least as fresh as the sync token from the sync store, ie. @@ -2916,7 +2907,7 @@ export class Crypto extends TypedEventEmitter(), unusedFallbackKeys = new Set(), + devices = new RustSdkCryptoJs.DeviceLists(), }: { events?: IToDeviceEvent[]; oneTimeKeysCounts?: Map; unusedFallbackKeys?: Set; + devices?: RustSdkCryptoJs.DeviceLists; }): Promise { const result = await this.olmMachine.receiveSyncChanges( events ? JSON.stringify(events) : "[]", - new RustSdkCryptoJs.DeviceLists(), + devices, oneTimeKeysCounts, unusedFallbackKeys, ); @@ -229,6 +232,16 @@ export class RustCrypto implements CryptoBackend { } } + /** called by the sync loop to process the notification that device lists have + * been changed. + * + * @param deviceLists - device_lists field from /sync + */ + public async processDeviceLists(deviceLists: IDeviceLists): Promise { + 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 diff --git a/src/sliding-sync-sdk.ts b/src/sliding-sync-sdk.ts index de2365f38..e0aa627e2 100644 --- a/src/sliding-sync-sdk.ts +++ b/src/sliding-sync-sdk.ts @@ -85,13 +85,8 @@ class ExtensionE2EE implements Extension { // 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"], - ); + if (data.device_lists) { + await this.crypto.processDeviceLists(data.device_lists); } // Handle one_time_keys_count and unused_fallback_key_types diff --git a/src/sync-accumulator.ts b/src/sync-accumulator.ts index fef03d74c..570bc8b1a 100644 --- a/src/sync-accumulator.ts +++ b/src/sync-accumulator.ts @@ -132,7 +132,7 @@ interface IToDevice { events: IToDeviceEvent[]; } -interface IDeviceLists { +export interface IDeviceLists { changed?: string[]; left?: string[]; } diff --git a/src/sync.ts b/src/sync.ts index ec7bd5244..9e400f62b 100644 --- a/src/sync.ts +++ b/src/sync.ts @@ -1515,8 +1515,8 @@ export class SyncApi { // Handle device list updates if (data.device_lists) { - if (this.syncOpts.crypto) { - await this.syncOpts.crypto.handleDeviceListChanges(syncEventData, data.device_lists); + if (this.syncOpts.cryptoCallbacks) { + await this.syncOpts.cryptoCallbacks.processDeviceLists(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