From 1d1309870a5319f9b70d355a232817fba9e21e56 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 7 Dec 2023 06:32:27 -0500 Subject: [PATCH] Don't back up keys that we got from backup (#3934) * don't back up keys that we got from backup * lint * lint again * remove key source struct and add function for importing from backup * apply changes from review --------- Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- spec/unit/rust-crypto/rust-crypto.spec.ts | 43 +++++++++++++++++++++++ src/client.ts | 3 +- src/common-crypto/CryptoBackend.ts | 11 +++++- src/crypto-api.ts | 5 +-- src/crypto/index.ts | 8 +++++ src/rust-crypto/rust-crypto.ts | 28 +++++++++++++-- 6 files changed, 90 insertions(+), 8 deletions(-) diff --git a/spec/unit/rust-crypto/rust-crypto.spec.ts b/spec/unit/rust-crypto/rust-crypto.spec.ts index 7685f811d..5480ffc1b 100644 --- a/spec/unit/rust-crypto/rust-crypto.spec.ts +++ b/spec/unit/rust-crypto/rust-crypto.spec.ts @@ -51,6 +51,7 @@ import * as testData from "../../test-utils/test-data"; import { defer } from "../../../src/utils"; import { logger } from "../../../src/logger"; import { OutgoingRequestsManager } from "../../../src/rust-crypto/OutgoingRequestsManager"; +import { Curve25519AuthData } from "../../../src/crypto-api/keybackup"; const TEST_USER = "@alice:example.com"; const TEST_DEVICE_ID = "TEST_DEVICE"; @@ -931,6 +932,48 @@ describe("RustCrypto", () => { await rustCrypto.onUserIdentityUpdated(new RustSdkCryptoJs.UserId(testData.TEST_USER_ID)); expect(await keyBackupStatusPromise).toBe(true); }); + + it("does not back up keys that came from backup", async () => { + const rustCrypto = await makeTestRustCrypto(); + const olmMachine: OlmMachine = rustCrypto["olmMachine"]; + + await olmMachine.enableBackupV1( + (testData.SIGNED_BACKUP_DATA.auth_data as Curve25519AuthData).public_key, + testData.SIGNED_BACKUP_DATA.version!, + ); + + // we import two keys: one "from backup", and one "from export" + const [backedUpRoomKey, exportedRoomKey] = testData.MEGOLM_SESSION_DATA_ARRAY; + await rustCrypto.importBackedUpRoomKeys([backedUpRoomKey]); + await rustCrypto.importRoomKeys([exportedRoomKey]); + + // we ask for the keys that should be backed up + const roomKeysRequest = await olmMachine.backupRoomKeys(); + expect(roomKeysRequest).toBeTruthy(); + const roomKeys = JSON.parse(roomKeysRequest!.body); + + // we expect that the key "from export" is present + expect(roomKeys).toMatchObject({ + rooms: { + [exportedRoomKey.room_id]: { + sessions: { + [exportedRoomKey.session_id]: {}, + }, + }, + }, + }); + + // we expect that the key "from backup" is not present + expect(roomKeys).not.toMatchObject({ + rooms: { + [backedUpRoomKey.room_id]: { + sessions: { + [backedUpRoomKey.session_id]: {}, + }, + }, + }, + }); + }); }); }); diff --git a/src/client.ts b/src/client.ts index 9afcddf29..563e79cfe 100644 --- a/src/client.ts +++ b/src/client.ts @@ -3984,10 +3984,9 @@ export class MatrixClient extends TypedEventEmitter): Promise; + + /** + * Import a list of room keys restored from backup + * + * @param keys - a list of session export objects + * @param opts - options object + * @returns a promise which resolves once the keys have been imported + */ + importBackedUpRoomKeys(keys: IMegolmSessionData[], opts?: ImportRoomKeysOpts): Promise; } /** The methods which crypto implementations should expose to the Sync api diff --git a/src/crypto-api.ts b/src/crypto-api.ts index 165079ca7..7a4647c21 100644 --- a/src/crypto-api.ts +++ b/src/crypto-api.ts @@ -597,9 +597,10 @@ export interface ImportRoomKeyProgressData { export interface ImportRoomKeysOpts { /** Reports ongoing progress of the import process. Can be used for feedback. */ progressCallback?: (stage: ImportRoomKeyProgressData) => void; - // TODO, the rust SDK will always such imported keys as untrusted + /** @deprecated the rust SDK will always such imported keys as untrusted */ untrusted?: boolean; - source?: String; // TODO: Enum (backup, file, ??) + /** @deprecated not useful externally */ + source?: string; } /** diff --git a/src/crypto/index.ts b/src/crypto/index.ts index 2f67dc1ba..2a3c371ac 100644 --- a/src/crypto/index.ts +++ b/src/crypto/index.ts @@ -1897,6 +1897,14 @@ export class Crypto extends TypedEventEmitter { + opts.source = "backup"; + return this.importRoomKeys(keys, opts); + } + /** * Store a set of keys as our own, trusted, cross-signing keys. * diff --git a/src/rust-crypto/rust-crypto.ts b/src/rust-crypto/rust-crypto.ts index c32662a0f..f6ce6f099 100644 --- a/src/rust-crypto/rust-crypto.ts +++ b/src/rust-crypto/rust-crypto.ts @@ -225,7 +225,7 @@ export class RustCrypto extends TypedEventEmitter { - // TODO when backup support will be added we would need to expose the `from_backup` flag in the bindings const jsonKeys = JSON.stringify(keys); - await this.olmMachine.importRoomKeys(jsonKeys, (progress: BigInt, total: BigInt) => { + await this.olmMachine.importExportedRoomKeys(jsonKeys, (progress: BigInt, total: BigInt): void => { const importOpt: ImportRoomKeyProgressData = { total: Number(total), successes: Number(progress), @@ -1265,6 +1264,29 @@ export class RustCrypto extends TypedEventEmitter { + const keysByRoom: Map> = new Map(); + for (const key of keys) { + const roomId = new RustSdkCryptoJs.RoomId(key.room_id); + if (!keysByRoom.has(roomId)) { + keysByRoom.set(roomId, new Map()); + } + keysByRoom.get(roomId)!.set(key.session_id, key); + } + await this.olmMachine.importBackedUpRoomKeys(keysByRoom, (progress: BigInt, total: BigInt): void => { + const importOpt: ImportRoomKeyProgressData = { + total: Number(total), + successes: Number(progress), + stage: "load_keys", + failures: 0, + }; + opts?.progressCallback?.(importOpt); + }); + } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SyncCryptoCallbacks implementation