WIP for enabling session backup from dehydration key

This commit is contained in:
Bruno Windels 2021-10-29 19:17:31 +02:00
parent 6d9d8797fe
commit 567cdd5510
5 changed files with 59 additions and 14 deletions

View file

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

View file

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

View file

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

View file

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

View file

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