diff --git a/src/matrix/Session.js b/src/matrix/Session.js index 18aa9d0a..036edcec 100644 --- a/src/matrix/Session.js +++ b/src/matrix/Session.js @@ -24,6 +24,7 @@ import { ObservableMap } from "../observable/index.js"; import {User} from "./User.js"; import {DeviceMessageHandler} from "./DeviceMessageHandler.js"; import {Account as E2EEAccount} from "./e2ee/Account.js"; +import {uploadAccountAsDehydratedDevice} from "./e2ee/Dehydration.js"; 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"; @@ -284,8 +285,8 @@ export class Session { pickleKey: PICKLE_KEY, userId: this._sessionInfo.userId, olmWorker: this._olmWorker, - deviceId, - storage, + deviceId: this.deviceId, + storage: this._storage, }); log.set("keys", this._e2eeAccount.identityKeys); this._setupEncryption(); diff --git a/src/matrix/SessionContainer.js b/src/matrix/SessionContainer.js index f74d2ea7..3b6d07d6 100644 --- a/src/matrix/SessionContainer.js +++ b/src/matrix/SessionContainer.js @@ -29,6 +29,7 @@ import {Session} from "./Session.js"; import {PasswordLoginMethod} from "./login/PasswordLoginMethod.js"; import {TokenLoginMethod} from "./login/TokenLoginMethod.js"; import {SSOLoginHelper} from "./login/SSOLoginHelper.js"; +import {getDehydratedDevice} from "./e2ee/Dehydration.js"; export const LoadStatus = createEnum( "NotLoading", @@ -38,7 +39,7 @@ export const LoadStatus = createEnum( "SetupAccount", // asked to restore from dehydrated device if present, call sc.accountSetup.finish() to progress to the next stage "Loading", "SessionSetup", // upload e2ee keys, ... - "Migrating", //not used atm, but would fit here + "Migrating", // not used atm, but would fit here "FirstSync", "Error", "Ready", @@ -174,7 +175,7 @@ export class SessionContainer { } return; } - const dehydratedDevice = await this._inspectAccountAfterLogin(sessionInfo); + const dehydratedDevice = await this._inspectAccountAfterLogin(sessionInfo, log); if (dehydratedDevice) { sessionInfo.deviceId = dehydratedDevice.deviceId; } @@ -240,7 +241,7 @@ export class SessionContainer { }); await this._session.load(log); if (dehydratedDevice) { - await log.wrap("dehydrateIdentity", log => await this._session.dehydrateIdentity(dehydratedDevice, log)); + await log.wrap("dehydrateIdentity", log => this._session.dehydrateIdentity(dehydratedDevice, log)); } else if (!this._session.hasIdentity) { this._status.set(LoadStatus.SessionSetup); await log.wrap("createIdentity", log => this._session.createIdentity(log)); @@ -308,25 +309,27 @@ export class SessionContainer { } } - async _inspectAccountAfterLogin(sessionInfo) { - this._status.set(LoadStatus.QueryAccount); - const hsApi = new HomeServerApi({ - homeserver: sessionInfo.homeServer, - accessToken: sessionInfo.accessToken, - request: this._platform.request, + _inspectAccountAfterLogin(sessionInfo, log) { + return log.wrap("inspectAccount", async log => { + this._status.set(LoadStatus.QueryAccount); + const hsApi = new HomeServerApi({ + homeserver: sessionInfo.homeServer, + accessToken: sessionInfo.accessToken, + request: this._platform.request, + }); + const olm = await this._olmPromise; + const encryptedDehydratedDevice = await getDehydratedDevice(hsApi, olm, log); + if (encryptedDehydratedDevice) { + let resolveStageFinish; + const promiseStageFinish = new Promise(r => resolveStageFinish = r); + this._accountSetup = new AccountSetup(encryptedDehydratedDevice, resolveStageFinish); + this._status.set(LoadStatus.SetupAccount); + await promiseStageFinish; + const dehydratedDevice = this._accountSetup?._dehydratedDevice; + this._accountSetup = null; + return dehydratedDevice; + } }); - const olm = await this._olmPromise; - const encryptedDehydratedDevice = await getDehydratedDevice(hsApi, olm); - if (encryptedDehydratedDevice) { - let resolveStageFinish; - const promiseStageFinish = new Promise(r => resolveStageFinish = r); - this._accountSetup = new AccountSetup(encryptedDehydratedDevice, resolveStageFinish); - this._status.set(LoadStatus.SetupAccount); - await promiseStageFinish; - const dehydratedDevice = this._accountSetup?._dehydratedDevice; - this._accountSetup = null; - return dehydratedDevice; - } } get accountSetup() { diff --git a/src/matrix/e2ee/Account.js b/src/matrix/e2ee/Account.js index 4a680ec6..91392232 100644 --- a/src/matrix/e2ee/Account.js +++ b/src/matrix/e2ee/Account.js @@ -52,14 +52,14 @@ export class Account { deviceId, areDeviceKeysUploaded, serverOTKCount, olm, olmWorker}); } } - + static async adoptDehydratedDevice({olm, dehydratedDevice, pickleKey, hsApi, userId, olmWorker, storage}) { const account = dehydratedDevice.adoptUnpickledOlmAccount(); - const areDeviceKeysUploaded = true; - const oneTimeKeys = JSON.parse(this._account.one_time_keys()); + const oneTimeKeys = JSON.parse(account.one_time_keys()); // only one algorithm supported by olm atm, so hardcode its name const oneTimeKeysEntries = Object.entries(oneTimeKeys.curve25519); const serverOTKCount = oneTimeKeysEntries.length; + const areDeviceKeysUploaded = true; await initiallyStoreAccount(account, pickleKey, areDeviceKeysUploaded, serverOTKCount, storage); return new Account({ pickleKey, hsApi, account, userId, @@ -76,11 +76,13 @@ export class Account { account.create(); account.generate_one_time_keys(account.max_number_of_one_time_keys()); } + const areDeviceKeysUploaded = false; + const serverOTKCount = 0; if (storage) { - await initiallyStoreAccount(account, pickleKey, false, 0, storage); + await initiallyStoreAccount(account, pickleKey, areDeviceKeysUploaded, serverOTKCount, storage); } return new Account({pickleKey, hsApi, account, userId, - deviceId, areDeviceKeysUploaded, serverOTKCount: 0, olm, olmWorker}); + deviceId, areDeviceKeysUploaded, serverOTKCount, olm, olmWorker}); } constructor({pickleKey, hsApi, account, userId, deviceId, areDeviceKeysUploaded, serverOTKCount, olm, olmWorker}) { diff --git a/src/matrix/e2ee/Dehydration.js b/src/matrix/e2ee/Dehydration.js index f01a9423..4ae7dd71 100644 --- a/src/matrix/e2ee/Dehydration.js +++ b/src/matrix/e2ee/Dehydration.js @@ -14,20 +14,23 @@ See the License for the specific language governing permissions and limitations under the License. */ -export const DEHYDRATION_LIBOLM_PICKLE_ALGORITHM = "org.matrix.msc2697.v1.olm.libolm_pickle"; +const DEHYDRATION_LIBOLM_PICKLE_ALGORITHM = "org.matrix.msc2697.v1.olm.libolm_pickle"; -async function getDehydratedDevice(hsApi, olm) { - const response = await hsApi.getDehydratedDevice().response(); - if (response.device_data.algorithm === DEHYDRATION_LIBOLM_PICKLE_ALGORITHM) { - return new DehydratedDevice(response, olm); +export async function getDehydratedDevice(hsApi, olm, log) { + try { + const response = await hsApi.getDehydratedDevice({log}).response(); + if (response.device_data.algorithm === DEHYDRATION_LIBOLM_PICKLE_ALGORITHM) { + return new EncryptedDehydratedDevice(response, olm); + } + } catch (err) { + if (err.name !== "HomeServerError") { + log.error = err; + } + return undefined; } } -async function hasRemainingDevice(ownUserId, ownDeviceId, storage) { - -} - -async function uploadAccountAsDehydratedDevice(account, hsApi, key, deviceDisplayName, log) { +export async function uploadAccountAsDehydratedDevice(account, hsApi, key, deviceDisplayName, log) { const response = await hsApi.createDehydratedDevice({ device_data: { algorithm: DEHYDRATION_LIBOLM_PICKLE_ALGORITHM, @@ -66,7 +69,7 @@ class DehydratedDevice { this._account = account; } - claim(hsApi, log) { + async claim(hsApi, log) { try { const response = await hsApi.claimDehydratedDevice(this.deviceId, {log}).response(); return response.success; @@ -83,6 +86,6 @@ class DehydratedDevice { } get deviceId() { - this._dehydratedDevice.device_id; + return this._dehydratedDevice.device_id; } } diff --git a/src/matrix/net/HomeServerApi.js b/src/matrix/net/HomeServerApi.js index d823abfd..c8950635 100644 --- a/src/matrix/net/HomeServerApi.js +++ b/src/matrix/net/HomeServerApi.js @@ -242,12 +242,12 @@ export class HomeServerApi { return this._get(`/dehydrated_device`, null, null, options); } - createDehydratedDevice(payload, options = null) { + createDehydratedDevice(payload, options = {}) { options.prefix = DEHYDRATION_PREFIX; return this._put(`/dehydrated_device`, null, payload, options); } - claimDehydratedDevice(deviceId) { + claimDehydratedDevice(deviceId, options = {}) { options.prefix = DEHYDRATION_PREFIX; return this._post(`/dehydrated_device/claim`, null, {device_id: deviceId}, options); }