From f98b3dd5fab313ee7a9f3cabe51924a8eae55273 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 27 Aug 2020 19:12:06 +0200 Subject: [PATCH] create/load olm account before first sync --- src/matrix/Session.js | 41 ++++++++++++++ src/matrix/SessionContainer.js | 7 +-- src/matrix/e2ee/Account.js | 54 +++++++++++++++++++ src/matrix/storage/idb/stores/SessionStore.js | 4 ++ 4 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 src/matrix/e2ee/Account.js diff --git a/src/matrix/Session.js b/src/matrix/Session.js index 90516c5f..907a4c4b 100644 --- a/src/matrix/Session.js +++ b/src/matrix/Session.js @@ -18,6 +18,8 @@ import {Room} from "./room/Room.js"; import { ObservableMap } from "../observable/index.js"; import { SendScheduler, RateLimitingBackoff } from "./SendScheduler.js"; import {User} from "./User.js"; +import {Account as E2EEAccount} from "./e2ee/Account.js"; +const PICKLE_KEY = "DEFAULT_KEY"; export class Session { // sessionInfo contains deviceId, userId and homeServer @@ -31,6 +33,34 @@ export class Session { this._roomUpdateCallback = (room, params) => this._rooms.update(room.id, params); this._user = new User(sessionInfo.userId); this._olm = olm; + this._e2eeAccount = null; + } + + async beforeFirstSync(isNewLogin) { + if (this._olm) { + if (isNewLogin && this._e2eeAccount) { + throw new Error("there should not be an e2ee account already on a fresh login"); + } + if (!this._e2eeAccount) { + const txn = await this._storage.readWriteTxn([ + this._storage.storeNames.session + ]); + try { + this._e2eeAccount = await E2EEAccount.create({ + hsApi: this._hsApi, + olm: this._olm, + pickleKey: PICKLE_KEY, + userId: this._sessionInfo.userId, + deviceId: this._sessionInfo.deviceId, + txn + }); + } catch (err) { + txn.abort(); + throw err; + } + await txn.complete(); + } + } } async load() { @@ -44,6 +74,17 @@ export class Session { ]); // restore session object this._syncInfo = await txn.session.get("sync"); + // restore e2ee account, if any + if (this._olm) { + this._e2eeAccount = await E2EEAccount.load({ + hsApi: this._hsApi, + olm: this._olm, + pickleKey: PICKLE_KEY, + userId: this._sessionInfo.userId, + deviceId: this._sessionInfo.deviceId, + txn + }); + } const pendingEventsByRoomId = await this._getPendingEventsByRoom(txn); // load rooms const rooms = await txn.roomSummary.getAll(); diff --git a/src/matrix/SessionContainer.js b/src/matrix/SessionContainer.js index eb025ec6..ae9572a9 100644 --- a/src/matrix/SessionContainer.js +++ b/src/matrix/SessionContainer.js @@ -74,7 +74,7 @@ export class SessionContainer { if (!sessionInfo) { throw new Error("Invalid session id: " + sessionId); } - await this._loadSessionInfo(sessionInfo); + await this._loadSessionInfo(sessionInfo, false); } catch (err) { this._error = err; this._status.set(LoadStatus.Error); @@ -121,14 +121,14 @@ export class SessionContainer { // LoadStatus.Error in case of an error, // so separate try/catch try { - await this._loadSessionInfo(sessionInfo); + await this._loadSessionInfo(sessionInfo, true); } catch (err) { this._error = err; this._status.set(LoadStatus.Error); } } - async _loadSessionInfo(sessionInfo) { + async _loadSessionInfo(sessionInfo, isNewLogin) { this._status.set(LoadStatus.Loading); this._reconnector = new Reconnector({ onlineStatus: this._onlineStatus, @@ -153,6 +153,7 @@ export class SessionContainer { const olm = await this._olmPromise; this._session = new Session({storage: this._storage, sessionInfo: filteredSessionInfo, hsApi, olm}); await this._session.load(); + await this._session.beforeFirstSync(isNewLogin); this._sync = new Sync({hsApi, storage: this._storage, session: this._session}); // notify sync and session when back online diff --git a/src/matrix/e2ee/Account.js b/src/matrix/e2ee/Account.js new file mode 100644 index 00000000..a159fa64 --- /dev/null +++ b/src/matrix/e2ee/Account.js @@ -0,0 +1,54 @@ +/* +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 anotherjson from "../../../lib/another-json/index.js"; + +const ACCOUNT_SESSION_KEY = "olmAccount"; +const DEVICE_KEY_FLAG_SESSION_KEY = "areDeviceKeysUploaded"; + +export class Account { + static async load({olm, pickleKey, hsApi, userId, deviceId, txn}) { + const pickledAccount = await txn.session.get(ACCOUNT_SESSION_KEY); + if (pickledAccount) { + const account = new olm.Account(); + const areDeviceKeysUploaded = await txn.session.get(DEVICE_KEY_FLAG_SESSION_KEY); + account.unpickle(pickleKey, pickledAccount); + return new Account({pickleKey, hsApi, account, userId, deviceId, areDeviceKeysUploaded}); + } + } + + static async create({olm, pickleKey, hsApi, userId, deviceId, txn}) { + const account = new olm.Account(); + account.create(); + account.generate_one_time_keys(account.max_number_of_one_time_keys()); + const pickledAccount = account.pickle(pickleKey); + // add will throw if the key already exists + // we would not want to overwrite olmAccount here + const areDeviceKeysUploaded = false; + await txn.session.add(ACCOUNT_SESSION_KEY, pickledAccount); + await txn.session.add(DEVICE_KEY_FLAG_SESSION_KEY, areDeviceKeysUploaded); + return new Account({pickleKey, hsApi, account, userId, deviceId, areDeviceKeysUploaded}); + } + + constructor({pickleKey, hsApi, account, userId, deviceId, areDeviceKeysUploaded}) { + this._pickleKey = pickleKey; + this._hsApi = hsApi; + this._account = account; + this._userId = userId; + this._deviceId = deviceId; + this._areDeviceKeysUploaded = areDeviceKeysUploaded; + } +} diff --git a/src/matrix/storage/idb/stores/SessionStore.js b/src/matrix/storage/idb/stores/SessionStore.js index 2e74b9df..f64a8299 100644 --- a/src/matrix/storage/idb/stores/SessionStore.js +++ b/src/matrix/storage/idb/stores/SessionStore.js @@ -45,4 +45,8 @@ export class SessionStore { set(key, value) { return this._sessionStore.put({key, value}); } + + add(key, value) { + return this._sessionStore.put({key, value}); + } }