diff --git a/src/matrix/Session.js b/src/matrix/Session.js index bbeb6d1e..9ee458bd 100644 --- a/src/matrix/Session.js +++ b/src/matrix/Session.js @@ -437,7 +437,7 @@ 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); + const ssssKey = await createSSSSKeyFromDehydratedDeviceKey(dehydratedDevice.key, this._storage, this._platform); if (ssssKey) { this._writeSSSSKey(ssssKey); } diff --git a/src/matrix/ssss/common.js b/src/matrix/ssss/common.js index 45bdfc2b..406e8558 100644 --- a/src/matrix/ssss/common.js +++ b/src/matrix/ssss/common.js @@ -32,23 +32,20 @@ export class KeyDescription { 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) { + async isCompatible(key, platform) { + if (this.algorithm === "m.secret_storage.v1.aes-hmac-sha2") { + const kd = this._keyDescription; + if (kd.mac) { + const otherMac = await calculateKeyMac(key.binaryKey, kd.iv, platform); + return kd.mac === otherMac; + } else if (kd.passphrase) { + const kdOther = key.description._keyDescription; 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; @@ -81,3 +78,24 @@ export class Key { 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); +} diff --git a/src/matrix/ssss/index.js b/src/matrix/ssss/index.js index efa2cb0a..cb795766 100644 --- a/src/matrix/ssss/index.js +++ b/src/matrix/ssss/index.js @@ -80,9 +80,9 @@ export async function keyFromCredentialAndDescription(type, credential, keyDescr return key; } -export async function keyFromDehydratedDeviceKey(key, storage) { +export async function keyFromDehydratedDeviceKey(key, storage, platform) { const keyDescription = await readDefaultKeyDescription(storage); - if (key.description.isCompatible(keyDescription)) { + if (await keyDescription.isCompatible(key, platform)) { return key.withDescription(keyDescription); } }