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, keyFromCredential as ssssKeyFromCredential,
readKey as ssssReadKey, readKey as ssssReadKey,
writeKey as ssssWriteKey, writeKey as ssssWriteKey,
removeKey as ssssRemoveKey removeKey as ssssRemoveKey,
keyFromDehydratedDeviceKey as createSSSSKeyFromDehydratedDeviceKey
} from "./ssss/index.js"; } from "./ssss/index.js";
import {SecretStorage} from "./ssss/SecretStorage.js"; import {SecretStorage} from "./ssss/SecretStorage.js";
import {ObservableValue, RetainedObservableValue} from "../observable/ObservableValue"; import {ObservableValue, RetainedObservableValue} from "../observable/ObservableValue";
@ -205,6 +206,12 @@ export class Session {
this._storage.storeNames.accountData, this._storage.storeNames.accountData,
]); ]);
await this._createSessionBackup(key, readTxn); 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 // only after having read a secret, write the key
// as we only find out if it was good if the MAC verification succeeds // as we only find out if it was good if the MAC verification succeeds
const writeTxn = await this._storage.readWriteTxn([ const writeTxn = await this._storage.readWriteTxn([
@ -217,8 +224,6 @@ export class Session {
throw err; throw err;
} }
await writeTxn.complete(); await writeTxn.complete();
this._hasSecretStorageKey.set(true);
return key;
} }
async disableSecretStorage() { async disableSecretStorage() {
@ -419,7 +424,7 @@ export class Session {
* and useful to store so we can later tell what capabilities * and useful to store so we can later tell what capabilities
* our homeserver has. * our homeserver has.
*/ */
async start(lastVersionResponse, log) { async start(lastVersionResponse, dehydratedDevice, log) {
if (lastVersionResponse) { if (lastVersionResponse) {
// store /versions response // store /versions response
const txn = await this._storage.readWriteTxn([ const txn = await this._storage.readWriteTxn([
@ -431,6 +436,12 @@ export class Session {
} }
// enable session backup, this requests the latest backup version // enable session backup, this requests the latest backup version
if (!this._sessionBackup) { if (!this._sessionBackup) {
if (dehydratedDevice) {
const ssssKey = await createSSSSKeyFromDehydratedDeviceKey(dehydratedDevice.key, this._storage);
if (ssssKey) {
this._writeSSSSKey(ssssKey);
}
}
const txn = await this._storage.readTxn([ const txn = await this._storage.readTxn([
this._storage.storeNames.session, this._storage.storeNames.session,
this._storage.storeNames.accountData, this._storage.storeNames.accountData,

View file

@ -259,7 +259,9 @@ export class SessionContainer {
this._requestScheduler.start(); this._requestScheduler.start();
this._sync.start(); this._sync.start();
this._sessionStartedByReconnector = true; 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) { if (this._isDisposed) {
return; return;
} }
const d = dehydratedDevice;
dehydratedDevice = undefined;
// log as ref as we don't want to await it // 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 { try {
const pickledAccount = this._dehydratedDevice.device_data.account; const pickledAccount = this._dehydratedDevice.device_data.account;
account.unpickle(key.binaryKey, pickledAccount); account.unpickle(key.binaryKey, pickledAccount);
return new DehydratedDevice(this._dehydratedDevice, account, keyType, key); return new DehydratedDevice(this._dehydratedDevice, account, key);
} catch (err) { } catch (err) {
account.free(); account.free();
if (err.message === "OLM.BAD_ACCOUNT_KEY") { if (err.message === "OLM.BAD_ACCOUNT_KEY") {
@ -78,10 +78,9 @@ class EncryptedDehydratedDevice {
} }
class DehydratedDevice { class DehydratedDevice {
constructor(dehydratedDevice, account, keyType, key) { constructor(dehydratedDevice, account, key) {
this._dehydratedDevice = dehydratedDevice; this._dehydratedDevice = dehydratedDevice;
this._account = account; this._account = account;
this._keyType = keyType;
this._key = key; this._key = key;
} }
@ -109,10 +108,6 @@ class DehydratedDevice {
return this._key; return this._key;
} }
get keyType() {
return this._keyType;
}
dispose() { dispose() {
this._account?.free(); this._account?.free();
this._account = undefined; this._account = undefined;

View file

@ -31,6 +31,28 @@ export class KeyDescription {
get algorithm() { get algorithm() {
return this._keyDescription?.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 { export class Key {
@ -39,6 +61,10 @@ export class Key {
this._binaryKey = binaryKey; this._binaryKey = binaryKey;
} }
withDescription(description) {
return new Key(description, this._binaryKey);
}
get description() { get description() {
return this._keyDescription; return this._keyDescription;
} }

View file

@ -50,7 +50,9 @@ export async function readKey(txn) {
return; return;
} }
const keyAccountData = await txn.accountData.get(`m.secret_storage.key.${keyData.id}`); const keyAccountData = await txn.accountData.get(`m.secret_storage.key.${keyData.id}`);
if (keyAccountData) {
return new Key(new KeyDescription(keyData.id, keyAccountData.content), keyData.binaryKey); return new Key(new KeyDescription(keyData.id, keyAccountData.content), keyData.binaryKey);
}
} }
@ -77,3 +79,10 @@ export async function keyFromCredentialAndDescription(type, credential, keyDescr
} }
return key; return key;
} }
export async function keyFromDehydratedDeviceKey(key, storage) {
const keyDescription = await readDefaultKeyDescription(storage);
if (key.description.isCompatible(keyDescription)) {
return key.withDescription(keyDescription);
}
}