diff --git a/src/matrix/Session.js b/src/matrix/Session.js index d6810969..bbeb6d1e 100644 --- a/src/matrix/Session.js +++ b/src/matrix/Session.js @@ -40,7 +40,8 @@ import { keyFromCredential as ssssKeyFromCredential, readKey as ssssReadKey, writeKey as ssssWriteKey, - removeKey as ssssRemoveKey + removeKey as ssssRemoveKey, + keyFromDehydratedDeviceKey as createSSSSKeyFromDehydratedDeviceKey } from "./ssss/index.js"; import {SecretStorage} from "./ssss/SecretStorage.js"; import {ObservableValue, RetainedObservableValue} from "../observable/ObservableValue"; @@ -205,6 +206,12 @@ export class Session { this._storage.storeNames.accountData, ]); await this._createSessionBackup(key, readTxn); + this._writeSSSSKey(key); + this._hasSecretStorageKey.set(true); + return key; + } + + async _writeSSSSKey(key) { // only after having read a secret, write the key // as we only find out if it was good if the MAC verification succeeds const writeTxn = await this._storage.readWriteTxn([ @@ -217,8 +224,6 @@ export class Session { throw err; } await writeTxn.complete(); - this._hasSecretStorageKey.set(true); - return key; } async disableSecretStorage() { @@ -419,7 +424,7 @@ export class Session { * and useful to store so we can later tell what capabilities * our homeserver has. */ - async start(lastVersionResponse, log) { + async start(lastVersionResponse, dehydratedDevice, log) { if (lastVersionResponse) { // store /versions response const txn = await this._storage.readWriteTxn([ @@ -431,6 +436,12 @@ export class Session { } // enable session backup, this requests the latest backup version if (!this._sessionBackup) { + if (dehydratedDevice) { + const ssssKey = await createSSSSKeyFromDehydratedDeviceKey(dehydratedDevice.key, this._storage); + if (ssssKey) { + this._writeSSSSKey(ssssKey); + } + } const txn = await this._storage.readTxn([ this._storage.storeNames.session, this._storage.storeNames.accountData, diff --git a/src/matrix/SessionContainer.js b/src/matrix/SessionContainer.js index ae2ddece..10e5acfc 100644 --- a/src/matrix/SessionContainer.js +++ b/src/matrix/SessionContainer.js @@ -259,7 +259,9 @@ export class SessionContainer { this._requestScheduler.start(); this._sync.start(); this._sessionStartedByReconnector = true; - await log.wrap("session start", log => this._session.start(this._reconnector.lastVersionsResponse, log)); + const d = dehydratedDevice; + dehydratedDevice = undefined; + await log.wrap("session start", log => this._session.start(this._reconnector.lastVersionsResponse, d, log)); }); } }); @@ -278,8 +280,10 @@ export class SessionContainer { if (this._isDisposed) { return; } + const d = dehydratedDevice; + dehydratedDevice = undefined; // log as ref as we don't want to await it - await log.wrap("session start", log => this._session.start(lastVersionsResponse, log)); + await log.wrap("session start", log => this._session.start(lastVersionsResponse, d, log)); } } diff --git a/src/matrix/e2ee/Dehydration.js b/src/matrix/e2ee/Dehydration.js index ac2d275d..87cf0121 100644 --- a/src/matrix/e2ee/Dehydration.js +++ b/src/matrix/e2ee/Dehydration.js @@ -61,7 +61,7 @@ class EncryptedDehydratedDevice { try { const pickledAccount = this._dehydratedDevice.device_data.account; account.unpickle(key.binaryKey, pickledAccount); - return new DehydratedDevice(this._dehydratedDevice, account, keyType, key); + return new DehydratedDevice(this._dehydratedDevice, account, key); } catch (err) { account.free(); if (err.message === "OLM.BAD_ACCOUNT_KEY") { @@ -78,10 +78,9 @@ class EncryptedDehydratedDevice { } class DehydratedDevice { - constructor(dehydratedDevice, account, keyType, key) { + constructor(dehydratedDevice, account, key) { this._dehydratedDevice = dehydratedDevice; this._account = account; - this._keyType = keyType; this._key = key; } @@ -109,10 +108,6 @@ class DehydratedDevice { return this._key; } - get keyType() { - return this._keyType; - } - dispose() { this._account?.free(); this._account = undefined; diff --git a/src/matrix/ssss/common.js b/src/matrix/ssss/common.js index 579e38b3..45bdfc2b 100644 --- a/src/matrix/ssss/common.js +++ b/src/matrix/ssss/common.js @@ -31,6 +31,28 @@ export class KeyDescription { get algorithm() { return this._keyDescription?.algorithm; } + + isCompatible(d) { + const kd = this._keyDescription; + const kdOther = d._keyDescription; + if (kd.algorithm === "m.secret_storage.v1.aes-hmac-sha2") { + if (kdOther.algorithm !== kd.algorithm) { + return false; + } + if (kd.passphrase) { + if (!kdOther.passphrase) { + return false; + } + return kd.passphrase.algorithm === kdOther.passphrase.algorithm && + kd.passphrase.iterations === kdOther.passphrase.iterations && + kd.passphrase.salt === kdOther.passphrase.salt; + } else { + return !!kd.iv && kd.iv === kdOther.iv && + !!kd.mac && kd.mac === kdOther.mac; + } + } + return false; + } } export class Key { @@ -39,6 +61,10 @@ export class Key { this._binaryKey = binaryKey; } + withDescription(description) { + return new Key(description, this._binaryKey); + } + get description() { return this._keyDescription; } diff --git a/src/matrix/ssss/index.js b/src/matrix/ssss/index.js index c104cac2..efa2cb0a 100644 --- a/src/matrix/ssss/index.js +++ b/src/matrix/ssss/index.js @@ -50,7 +50,9 @@ export async function readKey(txn) { return; } const keyAccountData = await txn.accountData.get(`m.secret_storage.key.${keyData.id}`); - return new Key(new KeyDescription(keyData.id, keyAccountData.content), keyData.binaryKey); + if (keyAccountData) { + return new Key(new KeyDescription(keyData.id, keyAccountData.content), keyData.binaryKey); + } } @@ -77,3 +79,10 @@ export async function keyFromCredentialAndDescription(type, credential, keyDescr } return key; } + +export async function keyFromDehydratedDeviceKey(key, storage) { + const keyDescription = await readDefaultKeyDescription(storage); + if (key.description.isCompatible(keyDescription)) { + return key.withDescription(keyDescription); + } +}