check mac of dehydrated key to match default 4s key mac before adopting

This commit is contained in:
Bruno Windels 2021-11-03 02:07:57 +01:00
parent 567cdd5510
commit 8a36eb4532
3 changed files with 32 additions and 14 deletions

View file

@ -437,7 +437,7 @@ 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) { if (dehydratedDevice) {
const ssssKey = await createSSSSKeyFromDehydratedDeviceKey(dehydratedDevice.key, this._storage); const ssssKey = await createSSSSKeyFromDehydratedDeviceKey(dehydratedDevice.key, this._storage, this._platform);
if (ssssKey) { if (ssssKey) {
this._writeSSSSKey(ssssKey); this._writeSSSSKey(ssssKey);
} }

View file

@ -32,23 +32,20 @@ export class KeyDescription {
return this._keyDescription?.algorithm; return this._keyDescription?.algorithm;
} }
isCompatible(d) { async isCompatible(key, platform) {
if (this.algorithm === "m.secret_storage.v1.aes-hmac-sha2") {
const kd = this._keyDescription; const kd = this._keyDescription;
const kdOther = d._keyDescription; if (kd.mac) {
if (kd.algorithm === "m.secret_storage.v1.aes-hmac-sha2") { const otherMac = await calculateKeyMac(key.binaryKey, kd.iv, platform);
if (kdOther.algorithm !== kd.algorithm) { return kd.mac === otherMac;
return false; } else if (kd.passphrase) {
} const kdOther = key.description._keyDescription;
if (kd.passphrase) {
if (!kdOther.passphrase) { if (!kdOther.passphrase) {
return false; return false;
} }
return kd.passphrase.algorithm === kdOther.passphrase.algorithm && return kd.passphrase.algorithm === kdOther.passphrase.algorithm &&
kd.passphrase.iterations === kdOther.passphrase.iterations && kd.passphrase.iterations === kdOther.passphrase.iterations &&
kd.passphrase.salt === kdOther.passphrase.salt; kd.passphrase.salt === kdOther.passphrase.salt;
} else {
return !!kd.iv && kd.iv === kdOther.iv &&
!!kd.mac && kd.mac === kdOther.mac;
} }
} }
return false; return false;
@ -81,3 +78,24 @@ export class Key {
return this._keyDescription.algorithm; return this._keyDescription.algorithm;
} }
} }
async function calculateKeyMac(key, ivStr, platform) {
const {crypto, encoding} = platform;
const {utf8, base64} = encoding;
const {derive, aes, hmac} = crypto;
const iv = base64.decode(ivStr);
// salt for HKDF, with 8 bytes of zeros
const zerosalt = new Uint8Array(8);
const ZERO_STR = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
const info = utf8.encode("");
const keybits = await derive.hkdf(key, zerosalt, info, "SHA-256", 512);
const aesKey = keybits.slice(0, 32);
const hmacKey = keybits.slice(32);
const ciphertext = await aes.encryptCTR({key: aesKey, iv, data: utf8.encode(ZERO_STR)});
const mac = await hmac.compute(hmacKey, ciphertext, "SHA-256");
return base64.encode(mac);
}

View file

@ -80,9 +80,9 @@ export async function keyFromCredentialAndDescription(type, credential, keyDescr
return key; return key;
} }
export async function keyFromDehydratedDeviceKey(key, storage) { export async function keyFromDehydratedDeviceKey(key, storage, platform) {
const keyDescription = await readDefaultKeyDescription(storage); const keyDescription = await readDefaultKeyDescription(storage);
if (key.description.isCompatible(keyDescription)) { if (await keyDescription.isCompatible(key, platform)) {
return key.withDescription(keyDescription); return key.withDescription(keyDescription);
} }
} }