From a1c66738db3d269bffc084dcee23ce2a2a6dfce2 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 29 Sep 2021 19:39:26 +0200 Subject: [PATCH] migration to initialize & restore e2ee identity backup --- src/matrix/storage/idb/QueryTarget.ts | 2 ++ src/matrix/storage/idb/schema.ts | 28 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/matrix/storage/idb/QueryTarget.ts b/src/matrix/storage/idb/QueryTarget.ts index e84c41e1..01f46e1a 100644 --- a/src/matrix/storage/idb/QueryTarget.ts +++ b/src/matrix/storage/idb/QueryTarget.ts @@ -19,6 +19,8 @@ import {StorageError} from "../common"; import {LogItem} from "../../../logging/LogItem.js"; import {IDBKey} from "./Transaction"; +// this is the part of the Transaction class API that is used here and in the Store subclass, +// to make it easier to replace it with alternative implementations in schema.ts and unit tests export interface ITransaction { idbFactory: IDBFactory; IDBKeyRange: typeof IDBKeyRange; diff --git a/src/matrix/storage/idb/schema.ts b/src/matrix/storage/idb/schema.ts index 14e0bc10..c7d41a6f 100644 --- a/src/matrix/storage/idb/schema.ts +++ b/src/matrix/storage/idb/schema.ts @@ -1,4 +1,5 @@ import {IDOMStorage} from "./types"; +import {ITransaction} from "./QueryTarget"; import {iterateCursor, NOT_DONE, reqAsPromise} from "./utils"; import {RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from "../../room/members/RoomMember.js"; import {addRoomToIdentity} from "../../e2ee/DeviceTracker.js"; @@ -7,6 +8,7 @@ import {SummaryData} from "../../room/RoomSummary"; import {RoomMemberStore, MemberData} from "./stores/RoomMemberStore"; import {RoomStateEntry} from "./stores/RoomStateStore"; import {SessionStore} from "./stores/SessionStore"; +import {Store} from "./Store"; import {encodeScopeTypeKey} from "./stores/OperationStore"; import {MAX_UNICODE} from "./stores/common"; import {LogItem} from "../../../logging/LogItem.js"; @@ -28,6 +30,7 @@ export const schema: MigrationFunc[] = [ createTimelineRelationsStore, fixMissingRoomsInUserIdentities, changeSSSSKeyPrefix, + backupAndRestoreE2EEAccountToLocalStorage ]; // TODO: how to deal with git merge conflicts of this array? @@ -215,3 +218,28 @@ async function changeSSSSKeyPrefix(db: IDBDatabase, txn: IDBTransaction) { session.put({key: `${SESSION_E2EE_KEY_PREFIX}ssssKey`, value: ssssKey}); } } +// v13 +async function backupAndRestoreE2EEAccountToLocalStorage(db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: LogItem) { + const session = txn.objectStore("session"); + // the Store object gets passed in several things through the Transaction class (a wrapper around IDBTransaction), + // the only thing we should need here is the databaseName though, so we mock it out. + // ideally we should have an easier way to go from the idb primitive layer to the specific store classes where + // we implement logic, but for now we need this. + const databaseNameHelper: ITransaction = { + databaseName: db.name, + get idbFactory(): IDBFactory { throw new Error("unused");}, + get IDBKeyRange(): typeof IDBKeyRange { throw new Error("unused");}, + addWriteError() {}, + }; + const sessionStore = new SessionStore(new Store(session, databaseNameHelper), localStorage); + // if we already have an e2ee identity, write a backup to local storage. + // further updates to e2ee keys in the session store will also write to local storage from 0.2.15 on, + // but here we make sure a backup is immediately created after installing the update and we don't wait until + // the olm account needs to change + sessionStore.writeE2EEIdentityToLocalStorage(); + // and if we already have a backup, restore it now for any missing key in idb. + // this will restore the backup every time the idb database is dropped as it will + // run through all the migration steps when recreating it. + const restored = await sessionStore.tryRestoreE2EEIdentityFromLocalStorage(log); + log.set("restored", restored); +}