From 993a86ddb269f884ff1dcaf6492731a3d318cebb Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 20 Jan 2022 11:16:08 +0100 Subject: [PATCH] convert SessionBackup to typescript and pass in keyloader --- src/matrix/Session.js | 17 ++-- src/matrix/e2ee/megolm/SessionBackup.js | 62 -------------- src/matrix/e2ee/megolm/SessionBackup.ts | 107 ++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 69 deletions(-) delete mode 100644 src/matrix/e2ee/megolm/SessionBackup.js create mode 100644 src/matrix/e2ee/megolm/SessionBackup.ts diff --git a/src/matrix/Session.js b/src/matrix/Session.js index 8ccc71cc..0d7d057d 100644 --- a/src/matrix/Session.js +++ b/src/matrix/Session.js @@ -70,6 +70,7 @@ export class Session { this._e2eeAccount = null; this._deviceTracker = null; this._olmEncryption = null; + this._keyLoader = null; this._megolmEncryption = null; this._megolmDecryption = null; this._getSyncToken = () => this.syncToken; @@ -141,8 +142,8 @@ export class Session { now: this._platform.clock.now, ownDeviceId: this._sessionInfo.deviceId, }); - const keyLoader = new MegOlmKeyLoader(this._olm, PICKLE_KEY, 20); - this._megolmDecryption = new MegOlmDecryption(keyLoader, this._olmWorker); + this._keyLoader = new MegOlmKeyLoader(this._olm, PICKLE_KEY, 20); + this._megolmDecryption = new MegOlmDecryption(this._keyLoader, this._olmWorker); this._deviceMessageHandler.enableEncryption({olmDecryption, megolmDecryption: this._megolmDecryption}); } @@ -246,12 +247,14 @@ export class Session { async _createSessionBackup(ssssKey, txn) { const secretStorage = new SecretStorage({key: ssssKey, platform: this._platform}); - this._sessionBackup = await SessionBackup.fromSecretStorage({ - platform: this._platform, - olm: this._olm, secretStorage, - hsApi: this._hsApi, + this._sessionBackup = await SessionBackup.fromSecretStorage( + this._platform, + this._olm, + secretStorage, + this._hsApi, + this._keyLoader, txn - }); + ); if (this._sessionBackup) { for (const room of this._rooms.values()) { if (room.isEncrypted) { diff --git a/src/matrix/e2ee/megolm/SessionBackup.js b/src/matrix/e2ee/megolm/SessionBackup.js deleted file mode 100644 index daba9961..00000000 --- a/src/matrix/e2ee/megolm/SessionBackup.js +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright 2020 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -export class SessionBackup { - constructor({backupInfo, decryption, hsApi}) { - this._backupInfo = backupInfo; - this._decryption = decryption; - this._hsApi = hsApi; - } - - async getSession(roomId, sessionId, log) { - const sessionResponse = await this._hsApi.roomKeyForRoomAndSession(this._backupInfo.version, roomId, sessionId, {log}).response(); - const sessionInfo = this._decryption.decrypt( - sessionResponse.session_data.ephemeral, - sessionResponse.session_data.mac, - sessionResponse.session_data.ciphertext, - ); - return JSON.parse(sessionInfo); - } - - get version() { - return this._backupInfo.version; - } - - dispose() { - this._decryption.free(); - } - - static async fromSecretStorage({platform, olm, secretStorage, hsApi, txn}) { - 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(); - const expectedPubKey = backupInfo.auth_data.public_key; - const decryption = new olm.PkDecryption(); - try { - const pubKey = decryption.init_with_private_key(privateKey); - if (pubKey !== expectedPubKey) { - throw new Error(`Bad backup key, public key does not match. Calculated ${pubKey} but expected ${expectedPubKey}`); - } - } catch(err) { - decryption.free(); - throw err; - } - return new SessionBackup({backupInfo, decryption, hsApi}); - } - } -} - diff --git a/src/matrix/e2ee/megolm/SessionBackup.ts b/src/matrix/e2ee/megolm/SessionBackup.ts new file mode 100644 index 00000000..9e875185 --- /dev/null +++ b/src/matrix/e2ee/megolm/SessionBackup.ts @@ -0,0 +1,107 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import type {HomeServerApi} from "../../net/HomeServerApi"; +import type {RoomKey} from "./decryption/RoomKey"; +import type {KeyLoader} from "./decryption/KeyLoader"; +import type {SecretStorage} from "../../ssss/SecretStorage"; +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; + +type SignatureMap = { + [userId: string]: {[deviceIdAndAlgorithm: string]: string} +} + +interface BaseBackupInfo { + version: string, + etag: string, + count: number, +} + +const Curve25519Algorithm = "m.megolm_backup.v1.curve25519-aes-sha2"; + +interface Curve25519BackupInfo extends BaseBackupInfo { + algorithm: typeof Curve25519Algorithm, + auth_data: Curve25519AuthData, +} + +interface OtherBackupInfo extends BaseBackupInfo { + algorithm: "other" +}; + +type BackupInfo = Curve25519BackupInfo | OtherBackupInfo; + +interface Curve25519AuthData { + public_key: string, + signatures: SignatureMap +} + +type AuthData = Curve25519AuthData; + +export class SessionBackup { + constructor( + private readonly backupInfo: BackupInfo, + private readonly decryption: Olm.PkDecryption, + private readonly hsApi: HomeServerApi, + private readonly keyLoader: KeyLoader + ) {} + + async getSession(roomId: string, sessionId: string, log: ILogItem) { + const sessionResponse = await this.hsApi.roomKeyForRoomAndSession(this.backupInfo.version, roomId, sessionId, {log}).response(); + const sessionInfo = this.decryption.decrypt( + sessionResponse.session_data.ephemeral, + sessionResponse.session_data.mac, + sessionResponse.session_data.ciphertext, + ); + return JSON.parse(sessionInfo); + } + + get version() { + return this.backupInfo.version; + } + + dispose() { + this.decryption.free(); + } + + static async fromSecretStorage(platform: Platform, olm: Olm, secretStorage: SecretStorage, hsApi: HomeServerApi, keyLoader: KeyLoader, txn: Transaction) { + 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 expectedPubKey = backupInfo.auth_data.public_key; + const decryption = new olm.PkDecryption(); + try { + const pubKey = decryption.init_with_private_key(privateKey); + if (pubKey !== expectedPubKey) { + throw new Error(`Bad backup key, public key does not match. Calculated ${pubKey} but expected ${expectedPubKey}`); + } + } catch(err) { + decryption.free(); + throw err; + } + return new SessionBackup(backupInfo, decryption, hsApi, keyLoader); + } else { + throw new Error(`Unknown backup algorithm: ${backupInfo.algorithm}`); + } + } + } +} +