Skip to content

Commit 7cf59d6

Browse files
authored
Element-R: support for migration of the room list from legacy crypto (#4036)
* Support for migration of the room list from legacy crypto * fix migration for empty legacy store
1 parent 5967c67 commit 7cf59d6

File tree

4 files changed

+90
-4
lines changed

4 files changed

+90
-4
lines changed

spec/integ/crypto/rust-crypto.spec.ts

+2
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ describe("MatrixClient.initRustCrypto", () => {
136136

137137
expect(await matrixClient.getCrypto()!.getActiveSessionBackupVersion()).toEqual("7");
138138

139+
expect(await matrixClient.getCrypto()!.isEncryptionEnabledInRoom("!CWLUCoEWXSFyTCOtfL:matrix.org")).toBe(true);
140+
139141
// check the progress callback
140142
expect(progressListener.mock.calls.length).toBeGreaterThan(50);
141143

src/crypto/store/base.ts

+3
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,9 @@ export enum MigrationState {
325325

326326
/** OLM_SESSIONS_MIGRATED, and in addition, we have migrated all the Megolm sessions. */
327327
MEGOLM_SESSIONS_MIGRATED,
328+
329+
/** MEGOLM_SESSIONS_MIGRATED, and in addition, we have migrated all the room settings. */
330+
ROOM_SETTINGS_MIGRATED,
328331
}
329332

330333
/**

src/rust-crypto/index.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { ServerSideSecretStorage } from "../secret-storage";
2323
import { ICryptoCallbacks } from "../crypto";
2424
import { Logger } from "../logger";
2525
import { CryptoStore } from "../crypto/store/base";
26-
import { migrateFromLegacyCrypto } from "./libolm_migration";
26+
import { migrateFromLegacyCrypto, migrateRoomSettingsFromLegacyCrypto } from "./libolm_migration";
2727

2828
/**
2929
* Create a new `RustCrypto` implementation
@@ -112,6 +112,7 @@ export async function initRustCrypto(args: {
112112
args.secretStorage,
113113
args.cryptoCallbacks,
114114
storeHandle,
115+
args.legacyCryptoStore,
115116
);
116117

117118
storeHandle.free();
@@ -128,6 +129,7 @@ async function initOlmMachine(
128129
secretStorage: ServerSideSecretStorage,
129130
cryptoCallbacks: ICryptoCallbacks,
130131
storeHandle: StoreHandle,
132+
legacyCryptoStore?: CryptoStore,
131133
): Promise<RustCrypto> {
132134
logger.debug("Init OlmMachine");
133135

@@ -137,6 +139,15 @@ async function initOlmMachine(
137139
storeHandle,
138140
);
139141

142+
// A final migration step, now that we have an OlmMachine.
143+
if (legacyCryptoStore) {
144+
await migrateRoomSettingsFromLegacyCrypto({
145+
logger,
146+
legacyStore: legacyCryptoStore,
147+
olmMachine,
148+
});
149+
}
150+
140151
// Disable room key requests, per https://github.com/vector-im/element-web/issues/26524.
141152
olmMachine.roomKeyRequestsEnabled = false;
142153

src/rust-crypto/libolm_migration.ts

+73-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2023 The Matrix.org Foundation C.I.C.
2+
Copyright 2023-2024 The Matrix.org Foundation C.I.C.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -22,10 +22,14 @@ import { IndexedDBCryptoStore } from "../crypto/store/indexeddb-crypto-store";
2222
import { decryptAES, IEncryptedPayload } from "../crypto/aes";
2323
import { IHttpOpts, MatrixHttpApi } from "../http-api";
2424
import { requestKeyBackupVersion } from "./backup";
25+
import { IRoomEncryption } from "../crypto/RoomList";
2526

2627
/**
2728
* Determine if any data needs migrating from the legacy store, and do so.
2829
*
30+
* This migrates the base account data, and olm and megolm sessions. It does *not* migrate the room list, which should
31+
* happen after an `OlmMachine` is created, via {@link migrateRoomSettingsFromLegacyCrypto}.
32+
*
2933
* @param args - Arguments object.
3034
*/
3135
export async function migrateFromLegacyCrypto(args: {
@@ -76,8 +80,8 @@ export async function migrateFromLegacyCrypto(args: {
7680
await legacyStore.startup();
7781
let migrationState = await legacyStore.getMigrationState();
7882

79-
if (migrationState === MigrationState.MEGOLM_SESSIONS_MIGRATED) {
80-
// All migration is done.
83+
if (migrationState >= MigrationState.MEGOLM_SESSIONS_MIGRATED) {
84+
// All migration is done for now. The room list comes later, once we have an OlmMachine.
8185
return;
8286
}
8387

@@ -255,6 +259,72 @@ async function migrateMegolmSessions(
255259
}
256260
}
257261

262+
/**
263+
* Determine if any room settings need migrating from the legacy store, and do so.
264+
*
265+
* @param args - Arguments object.
266+
*/
267+
export async function migrateRoomSettingsFromLegacyCrypto({
268+
logger,
269+
legacyStore,
270+
olmMachine,
271+
}: {
272+
/** A `Logger` instance that will be used for debug output. */
273+
logger: Logger;
274+
275+
/** Store to migrate data from. */
276+
legacyStore: CryptoStore;
277+
278+
/** OlmMachine to store the new data on. */
279+
olmMachine: RustSdkCryptoJs.OlmMachine;
280+
}): Promise<void> {
281+
if (!(await legacyStore.containsData())) {
282+
// This store was never used. Nothing to migrate.
283+
return;
284+
}
285+
286+
const migrationState = await legacyStore.getMigrationState();
287+
288+
if (migrationState >= MigrationState.ROOM_SETTINGS_MIGRATED) {
289+
// We've already migrated the room settings.
290+
return;
291+
}
292+
293+
let rooms: Record<string, IRoomEncryption> = {};
294+
295+
await legacyStore.doTxn("readwrite", [IndexedDBCryptoStore.STORE_ROOMS], (txn) => {
296+
legacyStore.getEndToEndRooms(txn, (result) => {
297+
rooms = result;
298+
});
299+
});
300+
301+
logger.debug(`Migrating ${Object.keys(rooms).length} sets of room settings`);
302+
for (const [roomId, legacySettings] of Object.entries(rooms)) {
303+
try {
304+
const rustSettings = new RustSdkCryptoJs.RoomSettings();
305+
306+
if (legacySettings.algorithm !== "m.megolm.v1.aes-sha2") {
307+
logger.warn(`Room ${roomId}: ignoring room with invalid algorithm ${legacySettings.algorithm}`);
308+
continue;
309+
}
310+
rustSettings.algorithm = RustSdkCryptoJs.EncryptionAlgorithm.MegolmV1AesSha2;
311+
rustSettings.sessionRotationPeriodMs = legacySettings.rotation_period_ms;
312+
rustSettings.sessionRotationPeriodMessages = legacySettings.rotation_period_msgs;
313+
await olmMachine.setRoomSettings(new RustSdkCryptoJs.RoomId(roomId), rustSettings);
314+
315+
// We don't attempt to clear out the settings from the old store, or record where we've gotten up to,
316+
// which means that if the app gets restarted while we're in the middle of this migration, we'll start
317+
// again from scratch. So be it. Given that legacy crypto loads the whole room list into memory on startup
318+
// anyway, we know it can't be that big.
319+
} catch (e) {
320+
logger.warn(`Room ${roomId}: ignoring settings ${JSON.stringify(legacySettings)} which caused error ${e}`);
321+
}
322+
}
323+
324+
logger.debug(`Completed room settings migration`);
325+
await legacyStore.setMigrationState(MigrationState.ROOM_SETTINGS_MIGRATED);
326+
}
327+
258328
async function getAndDecryptCachedSecretKey(
259329
legacyStore: CryptoStore,
260330
legacyPickleKey: Uint8Array,

0 commit comments

Comments
 (0)