diff --git a/src/domain/AccountSetupViewModel.js b/src/domain/AccountSetupViewModel.js index b2ce808a..4ad0d8d5 100644 --- a/src/domain/AccountSetupViewModel.js +++ b/src/domain/AccountSetupViewModel.js @@ -16,7 +16,7 @@ limitations under the License. import {ViewModel} from "./ViewModel.js"; import {KeyType} from "../matrix/ssss/index"; -import {Status} from "./session/settings/SessionBackupViewModel.js"; +import {Status} from "./session/settings/KeyBackupViewModel.js"; export class AccountSetupViewModel extends ViewModel { constructor(accountSetup) { @@ -50,7 +50,7 @@ export class AccountSetupViewModel extends ViewModel { } } -// this vm adopts the same shape as SessionBackupViewModel so the same view can be reused. +// this vm adopts the same shape as KeyBackupViewModel so the same view can be reused. class DecryptDehydratedDeviceViewModel extends ViewModel { constructor(accountSetupViewModel, decryptedCallback) { super(); diff --git a/src/domain/session/SessionStatusViewModel.js b/src/domain/session/SessionStatusViewModel.js index fcedb371..3f2263ac 100644 --- a/src/domain/session/SessionStatusViewModel.js +++ b/src/domain/session/SessionStatusViewModel.js @@ -36,7 +36,7 @@ export class SessionStatusViewModel extends ViewModel { this._reconnector = reconnector; this._status = this._calculateState(reconnector.connectionStatus.get(), sync.status.get()); this._session = session; - this._setupSessionBackupUrl = this.urlCreator.urlForSegment("settings"); + this._setupKeyBackupUrl = this.urlCreator.urlForSegment("settings"); this._dismissSecretStorage = false; } @@ -44,17 +44,17 @@ export class SessionStatusViewModel extends ViewModel { const update = () => this._updateStatus(); this.track(this._sync.status.subscribe(update)); this.track(this._reconnector.connectionStatus.subscribe(update)); - this.track(this._session.needsSessionBackup.subscribe(() => { + this.track(this._session.needsKeyBackup.subscribe(() => { this.emitChange(); })); } - get setupSessionBackupUrl () { - return this._setupSessionBackupUrl; + get setupKeyBackupUrl () { + return this._setupKeyBackupUrl; } get isShown() { - return (this._session.needsSessionBackup.get() && !this._dismissSecretStorage) || this._status !== SessionStatus.Syncing; + return (this._session.needsKeyBackup.get() && !this._dismissSecretStorage) || this._status !== SessionStatus.Syncing; } get statusLabel() { @@ -70,7 +70,7 @@ export class SessionStatusViewModel extends ViewModel { case SessionStatus.SyncError: return this.i18n`Sync failed because of ${this._sync.error}`; } - if (this._session.needsSessionBackup.get()) { + if (this._session.needsKeyBackup.get()) { return this.i18n`Set up session backup to decrypt older messages.`; } return ""; @@ -135,7 +135,7 @@ export class SessionStatusViewModel extends ViewModel { get isSecretStorageShown() { // TODO: we need a model here where we can have multiple messages queued up and their buttons don't bleed into each other. - return this._status === SessionStatus.Syncing && this._session.needsSessionBackup.get() && !this._dismissSecretStorage; + return this._status === SessionStatus.Syncing && this._session.needsKeyBackup.get() && !this._dismissSecretStorage; } get canDismiss() { diff --git a/src/domain/session/settings/SessionBackupViewModel.js b/src/domain/session/settings/KeyBackupViewModel.js similarity index 94% rename from src/domain/session/settings/SessionBackupViewModel.js rename to src/domain/session/settings/KeyBackupViewModel.js index 5a127904..38d37c18 100644 --- a/src/domain/session/settings/SessionBackupViewModel.js +++ b/src/domain/session/settings/KeyBackupViewModel.js @@ -20,7 +20,7 @@ import {createEnum} from "../../../utils/enum"; export const Status = createEnum("Enabled", "SetupKey", "SetupPhrase", "Pending"); -export class SessionBackupViewModel extends ViewModel { +export class KeyBackupViewModel extends ViewModel { constructor(options) { super(options); this._session = options.session; @@ -43,7 +43,7 @@ export class SessionBackupViewModel extends ViewModel { let status; const hasSecretStorageKey = this._session.hasSecretStorageKey.get(); if (hasSecretStorageKey === true) { - status = this._session.sessionBackup ? Status.Enabled : Status.SetupKey; + status = this._session.keyBackup ? Status.Enabled : Status.SetupKey; } else if (hasSecretStorageKey === false) { status = Status.SetupKey; } else { @@ -59,7 +59,7 @@ export class SessionBackupViewModel extends ViewModel { } get purpose() { - return this.i18n`set up session backup`; + return this.i18n`set up key backup`; } offerDehydratedDeviceSetup() { @@ -75,7 +75,7 @@ export class SessionBackupViewModel extends ViewModel { } get backupVersion() { - return this._session.sessionBackup?.version; + return this._session.keyBackup?.version; } get status() { diff --git a/src/domain/session/settings/SettingsViewModel.js b/src/domain/session/settings/SettingsViewModel.js index 70e507b8..0b68f168 100644 --- a/src/domain/session/settings/SettingsViewModel.js +++ b/src/domain/session/settings/SettingsViewModel.js @@ -15,7 +15,7 @@ limitations under the License. */ import {ViewModel} from "../../ViewModel.js"; -import {SessionBackupViewModel} from "./SessionBackupViewModel.js"; +import {KeyBackupViewModel} from "./KeyBackupViewModel.js"; class PushNotificationStatus { constructor() { @@ -43,7 +43,7 @@ export class SettingsViewModel extends ViewModel { this._updateService = options.updateService; const {client} = options; this._client = client; - this._sessionBackupViewModel = this.track(new SessionBackupViewModel(this.childOptions({session: this._session}))); + this._keyBackupViewModel = this.track(new KeyBackupViewModel(this.childOptions({session: this._session}))); this._closeUrl = this.urlCreator.urlUntilSegment("session"); this._estimate = null; this.sentImageSizeLimit = null; @@ -115,8 +115,8 @@ export class SettingsViewModel extends ViewModel { return !!this.platform.updateService; } - get sessionBackupViewModel() { - return this._sessionBackupViewModel; + get keyBackupViewModel() { + return this._keyBackupViewModel; } get storageQuota() { diff --git a/src/matrix/Session.js b/src/matrix/Session.js index 94b68106..9510cbfd 100644 --- a/src/matrix/Session.js +++ b/src/matrix/Session.js @@ -29,7 +29,7 @@ import {Decryption as OlmDecryption} from "./e2ee/olm/Decryption.js"; import {Encryption as OlmEncryption} from "./e2ee/olm/Encryption.js"; import {Decryption as MegOlmDecryption} from "./e2ee/megolm/Decryption"; import {KeyLoader as MegOlmKeyLoader} from "./e2ee/megolm/decryption/KeyLoader"; -import {SessionBackup} from "./e2ee/megolm/SessionBackup.js"; +import {KeyBackup} from "./e2ee/megolm/keybackup/KeyBackup"; import {Encryption as MegOlmEncryption} from "./e2ee/megolm/Encryption.js"; import {MEGOLM_ALGORITHM} from "./e2ee/common.js"; import {RoomEncryption} from "./e2ee/RoomEncryption.js"; @@ -75,7 +75,7 @@ export class Session { this._megolmDecryption = null; this._getSyncToken = () => this.syncToken; this._olmWorker = olmWorker; - this._sessionBackup = null; + this._keyBackup = null; this._hasSecretStorageKey = new ObservableValue(null); this._observedRoomStatus = new Map(); @@ -91,7 +91,7 @@ export class Session { } this._createRoomEncryption = this._createRoomEncryption.bind(this); this._forgetArchivedRoom = this._forgetArchivedRoom.bind(this); - this.needsSessionBackup = new ObservableValue(false); + this.needsKeyBackup = new ObservableValue(false); } get fingerprintKey() { @@ -170,11 +170,11 @@ export class Session { megolmEncryption: this._megolmEncryption, megolmDecryption: this._megolmDecryption, storage: this._storage, - sessionBackup: this._sessionBackup, + keyBackup: this._keyBackup, encryptionParams, notifyMissingMegolmSession: () => { - if (!this._sessionBackup) { - this.needsSessionBackup.set(true) + if (!this._keyBackup) { + this.needsKeyBackup.set(true) } }, clock: this._platform.clock @@ -183,7 +183,7 @@ export class Session { /** * Enable secret storage by providing the secret storage credential. - * This will also see if there is a megolm session backup and try to enable that if so. + * This will also see if there is a megolm key backup and try to enable that if so. * * @param {string} type either "passphrase" or "recoverykey" * @param {string} credential either the passphrase or the recovery key, depending on the type @@ -193,15 +193,15 @@ export class Session { if (!this._olm) { throw new Error("olm required"); } - if (this._sessionBackup) { + if (this._keyBackup) { return false; } const key = await ssssKeyFromCredential(type, credential, this._storage, this._platform, this._olm); - // and create session backup, which needs to read from accountData + // and create key backup, which needs to read from accountData const readTxn = await this._storage.readTxn([ this._storage.storeNames.accountData, ]); - await this._createSessionBackup(key, readTxn); + await this._createKeyBackup(key, readTxn); await this._writeSSSSKey(key); this._hasSecretStorageKey.set(true); return key; @@ -233,21 +233,21 @@ export class Session { throw err; } await writeTxn.complete(); - if (this._sessionBackup) { + if (this._keyBackup) { for (const room of this._rooms.values()) { if (room.isEncrypted) { - room.enableSessionBackup(undefined); + room.enableKeyBackup(undefined); } } - this._sessionBackup?.dispose(); - this._sessionBackup = undefined; + this._keyBackup?.dispose(); + this._keyBackup = undefined; } this._hasSecretStorageKey.set(false); } - async _createSessionBackup(ssssKey, txn) { + async _createKeyBackup(ssssKey, txn) { const secretStorage = new SecretStorage({key: ssssKey, platform: this._platform}); - this._sessionBackup = await SessionBackup.fromSecretStorage( + this._keyBackup = await KeyBackup.fromSecretStorage( this._platform, this._olm, secretStorage, @@ -256,18 +256,18 @@ export class Session { this._storage, txn ); - if (this._sessionBackup) { + if (this._keyBackup) { for (const room of this._rooms.values()) { if (room.isEncrypted) { - room.enableSessionBackup(this._sessionBackup); + room.enableKeyBackup(this._keyBackup); } } } - this.needsSessionBackup.set(false); + this.needsKeyBackup.set(false); } - get sessionBackup() { - return this._sessionBackup; + get keyBackup() { + return this._keyBackup; } get hasIdentity() { @@ -405,8 +405,8 @@ export class Session { dispose() { this._olmWorker?.dispose(); this._olmWorker = undefined; - this._sessionBackup?.dispose(); - this._sessionBackup = undefined; + this._keyBackup?.dispose(); + this._keyBackup = undefined; this._megolmDecryption?.dispose(); this._megolmDecryption = undefined; this._e2eeAccount?.dispose(); @@ -434,7 +434,7 @@ export class Session { await txn.complete(); } // enable session backup, this requests the latest backup version - if (!this._sessionBackup) { + if (!this._keyBackup) { if (dehydratedDevice) { await log.wrap("SSSSKeyFromDehydratedDeviceKey", async log => { const ssssKey = await createSSSSKeyFromDehydratedDeviceKey(dehydratedDevice.key, this._storage, this._platform); @@ -452,7 +452,7 @@ export class Session { const ssssKey = await ssssReadKey(txn); if (ssssKey) { // txn will end here as this does a network request - await this._createSessionBackup(ssssKey, txn); + await this._createKeyBackup(ssssKey, txn); } this._hasSecretStorageKey.set(!!ssssKey); } @@ -583,8 +583,8 @@ export class Session { await log.wrap("deviceMsgs", log => this._deviceMessageHandler.writeSync(preparation, txn, log)); // this should come after the deviceMessageHandler, so the room keys are already written and their // isBetter property has been checked - if (this._sessionBackup) { - this._sessionBackup.writeKeys(preparation.newRoomKeys, txn, log); + if (this._keyBackup) { + this._keyBackup.writeKeys(preparation.newRoomKeys, txn, log); } } @@ -623,8 +623,8 @@ export class Session { await log.wrap("uploadKeys", log => this._e2eeAccount.uploadKeys(this._storage, false, log)); } } - if (this._sessionBackup) { - this._sessionBackup.flush(); + if (this._keyBackup) { + this._keyBackup.flush(); } } diff --git a/src/matrix/e2ee/RoomEncryption.js b/src/matrix/e2ee/RoomEncryption.js index 5a511d6e..2e6ee254 100644 --- a/src/matrix/e2ee/RoomEncryption.js +++ b/src/matrix/e2ee/RoomEncryption.js @@ -28,7 +28,7 @@ const MIN_PRESHARE_INTERVAL = 60 * 1000; // 1min // TODO: this class is a good candidate for splitting up into encryption and decryption, there doesn't seem to be much overlap export class RoomEncryption { - constructor({room, deviceTracker, olmEncryption, megolmEncryption, megolmDecryption, encryptionParams, storage, sessionBackup, notifyMissingMegolmSession, clock}) { + constructor({room, deviceTracker, olmEncryption, megolmEncryption, megolmDecryption, encryptionParams, storage, keyBackup, notifyMissingMegolmSession, clock}) { this._room = room; this._deviceTracker = deviceTracker; this._olmEncryption = olmEncryption; @@ -39,7 +39,7 @@ export class RoomEncryption { // caches devices to verify events this._senderDeviceCache = new Map(); this._storage = storage; - this._sessionBackup = sessionBackup; + this._keyBackup = keyBackup; this._notifyMissingMegolmSession = notifyMissingMegolmSession; this._clock = clock; this._isFlushingRoomKeyShares = false; @@ -48,11 +48,11 @@ export class RoomEncryption { this._disposed = false; } - enableSessionBackup(sessionBackup) { - if (this._sessionBackup && !!sessionBackup) { + enableKeyBackup(keyBackup) { + if (this._keyBackup && !!keyBackup) { return; } - this._sessionBackup = sessionBackup; + this._keyBackup = keyBackup; } async restoreMissingSessionsFromBackup(entries, log) { @@ -130,7 +130,7 @@ export class RoomEncryption { })); } - if (!this._sessionBackup) { + if (!this._keyBackup) { return; } @@ -174,7 +174,7 @@ export class RoomEncryption { async _requestMissingSessionFromBackup(senderKey, sessionId, log) { // show prompt to enable secret storage - if (!this._sessionBackup) { + if (!this._keyBackup) { log.set("enabled", false); this._notifyMissingMegolmSession(); return; @@ -182,7 +182,7 @@ export class RoomEncryption { log.set("id", sessionId); log.set("senderKey", senderKey); try { - const roomKey = await this._sessionBackup.getRoomKey(this._room.id, sessionId, log); + const roomKey = await this._keyBackup.getRoomKey(this._room.id, sessionId, log); if (roomKey) { if (roomKey.senderKey !== senderKey) { log.set("wrong_sender_key", roomKey.senderKey); diff --git a/src/matrix/e2ee/megolm/SessionBackup.ts b/src/matrix/e2ee/megolm/keybackup/KeyBackup.ts similarity index 89% rename from src/matrix/e2ee/megolm/SessionBackup.ts rename to src/matrix/e2ee/megolm/keybackup/KeyBackup.ts index b76f3fca..98456973 100644 --- a/src/matrix/e2ee/megolm/SessionBackup.ts +++ b/src/matrix/e2ee/megolm/keybackup/KeyBackup.ts @@ -14,20 +14,20 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {StoreNames} from "../../storage/common"; -import {LRUCache} from "../../../utils/LRUCache"; -import {keyFromStorage, keyFromBackup} from "./decryption/RoomKey"; -import {MEGOLM_ALGORITHM} from "../common"; +import {StoreNames} from "../../../storage/common"; +import {LRUCache} from "../../../../utils/LRUCache"; +import {keyFromStorage, keyFromBackup} from "../decryption/RoomKey"; +import {MEGOLM_ALGORITHM} from "../../common"; -import type {HomeServerApi} from "../../net/HomeServerApi"; -import type {IncomingRoomKey, RoomKey} from "./decryption/RoomKey"; -import type {KeyLoader} from "./decryption/KeyLoader"; -import type {SecretStorage} from "../../ssss/SecretStorage"; -import type {Storage} from "../../storage/idb/Storage"; -import type {DeviceIdentity} from "../../storage/idb/stores/DeviceIdentityStore"; -import type {ILogItem} from "../../../logging/types"; -import type {Platform} from "../../../platform/web/Platform"; -import type {Transaction} from "../../storage/idb/Transaction"; +import type {HomeServerApi} from "../../../net/HomeServerApi"; +import type {IncomingRoomKey, RoomKey} from "../decryption/RoomKey"; +import type {KeyLoader} from "../decryption/KeyLoader"; +import type {SecretStorage} from "../../../ssss/SecretStorage"; +import type {Storage} from "../../../storage/idb/Storage"; +import type {DeviceIdentity} from "../../../storage/idb/stores/DeviceIdentityStore"; +import type {ILogItem} from "../../../../logging/types"; +import type {Platform} from "../../../../platform/web/Platform"; +import type {Transaction} from "../../../storage/idb/Transaction"; import type * as OlmNamespace from "@matrix-org/olm"; type Olm = typeof OlmNamespace; @@ -85,7 +85,7 @@ type MegOlmSessionKeyInfo = { type SessionKeyInfo = MegOlmSessionKeyInfo | {algorithm: string}; -export class SessionBackup { +export class KeyBackup { constructor( private readonly backupInfo: BackupInfo, private readonly algorithm: Curve25519, @@ -187,14 +187,14 @@ export class SessionBackup { this.algorithm.dispose(); } - static async fromSecretStorage(platform: Platform, olm: Olm, secretStorage: SecretStorage, hsApi: HomeServerApi, keyLoader: KeyLoader, storage: Storage, txn: Transaction) { + static async fromSecretStorage(platform: Platform, olm: Olm, secretStorage: SecretStorage, hsApi: HomeServerApi, keyLoader: KeyLoader, storage: Storage, txn: Transaction): Promise { const base64PrivateKey = await secretStorage.readSecret("m.megolm_backup.v1", txn); if (base64PrivateKey) { const privateKey = new Uint8Array(platform.encoding.base64.decode(base64PrivateKey)); const backupInfo = await hsApi.roomKeysVersion().response() as BackupInfo; if (backupInfo.algorithm === Curve25519Algorithm) { const algorithm = Curve25519.fromAuthData(backupInfo.auth_data, privateKey, olm); - return new SessionBackup(backupInfo, algorithm, hsApi, keyLoader, storage, platform); + return new KeyBackup(backupInfo, algorithm, hsApi, keyLoader, storage, platform); } else { throw new Error(`Unknown backup algorithm: ${backupInfo.algorithm}`); } diff --git a/src/matrix/room/BaseRoom.js b/src/matrix/room/BaseRoom.js index 39232ae5..ecd2d860 100644 --- a/src/matrix/room/BaseRoom.js +++ b/src/matrix/room/BaseRoom.js @@ -461,11 +461,11 @@ export class BaseRoom extends EventEmitter { return observable; } - enableSessionBackup(sessionBackup) { - this._roomEncryption?.enableSessionBackup(sessionBackup); + enableKeyBackup(keyBackup) { + this._roomEncryption?.enableKeyBackup(keyBackup); // TODO: do we really want to do this every time you open the app? - if (this._timeline && sessionBackup) { - this._platform.logger.run("enableSessionBackup", log => { + if (this._timeline && keyBackup) { + this._platform.logger.run("enableKeyBackup", log => { return this._roomEncryption.restoreMissingSessionsFromBackup(this._timeline.remoteEntries, log); }); } diff --git a/src/matrix/storage/idb/schema.ts b/src/matrix/storage/idb/schema.ts index 44a04bba..2aca60a2 100644 --- a/src/matrix/storage/idb/schema.ts +++ b/src/matrix/storage/idb/schema.ts @@ -272,7 +272,7 @@ async function clearAllStores(db: IDBDatabase, txn: IDBTransaction) { } } -// v15 adds the sessionsNeedingBackup store, for session backup +// v15 adds the sessionsNeedingBackup store, for key backup function createSessionsNeedingBackup(db: IDBDatabase): void { db.createObjectStore("sessionsNeedingBackup", {keyPath: "key"}); } diff --git a/src/platform/web/ui/login/AccountSetupView.js b/src/platform/web/ui/login/AccountSetupView.js index 32a4afb5..e0d41693 100644 --- a/src/platform/web/ui/login/AccountSetupView.js +++ b/src/platform/web/ui/login/AccountSetupView.js @@ -15,13 +15,13 @@ limitations under the License. */ import {TemplateView} from "../general/TemplateView"; -import {SessionBackupSettingsView} from "../session/settings/SessionBackupSettingsView.js"; +import {KeyBackupSettingsView} from "../session/settings/KeyBackupSettingsView.js"; export class AccountSetupView extends TemplateView { render(t, vm) { return t.div({className: "Settings" /* hack for now to get the layout right*/}, [ t.h3(vm.i18n`Restore your encrypted history?`), - t.ifView(vm => vm.decryptDehydratedDeviceViewModel, vm => new SessionBackupSettingsView(vm.decryptDehydratedDeviceViewModel)), + t.ifView(vm => vm.decryptDehydratedDeviceViewModel, vm => new KeyBackupSettingsView(vm.decryptDehydratedDeviceViewModel)), t.map(vm => vm.deviceDecrypted, (decrypted, t) => { if (decrypted) { return t.p(vm.i18n`That worked out, you're good to go!`); diff --git a/src/platform/web/ui/session/SessionStatusView.js b/src/platform/web/ui/session/SessionStatusView.js index bd8c6dbb..a629b2a1 100644 --- a/src/platform/web/ui/session/SessionStatusView.js +++ b/src/platform/web/ui/session/SessionStatusView.js @@ -26,7 +26,7 @@ export class SessionStatusView extends TemplateView { spinner(t, {hidden: vm => !vm.isWaiting}), t.p(vm => vm.statusLabel), t.if(vm => vm.isConnectNowShown, t => t.button({className: "link", onClick: () => vm.connectNow()}, "Retry now")), - t.if(vm => vm.isSecretStorageShown, t => t.a({href: vm.setupSessionBackupUrl}, "Go to settings")), + t.if(vm => vm.isSecretStorageShown, t => t.a({href: vm.setupKeyBackupUrl}, "Go to settings")), t.if(vm => vm.canDismiss, t => t.div({className: "end"}, t.button({className: "dismiss", onClick: () => vm.dismiss()}))), ]); } diff --git a/src/platform/web/ui/session/settings/SessionBackupSettingsView.js b/src/platform/web/ui/session/settings/KeyBackupSettingsView.js similarity index 98% rename from src/platform/web/ui/session/settings/SessionBackupSettingsView.js rename to src/platform/web/ui/session/settings/KeyBackupSettingsView.js index b8206c55..bf3fd296 100644 --- a/src/platform/web/ui/session/settings/SessionBackupSettingsView.js +++ b/src/platform/web/ui/session/settings/KeyBackupSettingsView.js @@ -17,7 +17,7 @@ limitations under the License. import {TemplateView, InlineTemplateView} from "../../general/TemplateView"; import {StaticView} from "../../general/StaticView.js"; -export class SessionBackupSettingsView extends TemplateView { +export class KeyBackupSettingsView extends TemplateView { render(t, vm) { return t.mapView(vm => vm.status, status => { switch (status) { diff --git a/src/platform/web/ui/session/settings/SettingsView.js b/src/platform/web/ui/session/settings/SettingsView.js index 78ca2007..93e44307 100644 --- a/src/platform/web/ui/session/settings/SettingsView.js +++ b/src/platform/web/ui/session/settings/SettingsView.js @@ -15,7 +15,7 @@ limitations under the License. */ import {TemplateView} from "../../general/TemplateView"; -import {SessionBackupSettingsView} from "./SessionBackupSettingsView.js" +import {KeyBackupSettingsView} from "./KeyBackupSettingsView.js" export class SettingsView extends TemplateView { render(t, vm) { @@ -47,8 +47,8 @@ export class SettingsView extends TemplateView { }, vm.i18n`Log out`)), ); settingNodes.push( - t.h3("Session Backup"), - t.view(new SessionBackupSettingsView(vm.sessionBackupViewModel)) + t.h3("Key backup"), + t.view(new KeyBackupSettingsView(vm.keyBackupViewModel)) ); settingNodes.push(