rename session backup to key backup to be consistent with RoomKey

This commit is contained in:
Bruno Windels 2022-01-26 09:51:48 +01:00
parent 933a1b4636
commit 86caa5f9b1
13 changed files with 82 additions and 82 deletions

View file

@ -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();

View file

@ -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() {

View file

@ -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() {

View file

@ -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() {

View file

@ -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();
}
}

View file

@ -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);

View file

@ -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<KeyBackup | undefined> {
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}`);
}

View file

@ -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);
});
}

View file

@ -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"});
}

View file

@ -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!`);

View file

@ -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()}))),
]);
}

View file

@ -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) {

View file

@ -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(