check mac of dehydrated key to match default 4s key mac before adopting
This commit is contained in:
parent
567cdd5510
commit
8a36eb4532
3 changed files with 32 additions and 14 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -32,23 +32,20 @@ export class KeyDescription {
|
|||
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 kdOther = d._keyDescription;
|
||||
if (kd.algorithm === "m.secret_storage.v1.aes-hmac-sha2") {
|
||||
if (kdOther.algorithm !== kd.algorithm) {
|
||||
return false;
|
||||
}
|
||||
if (kd.passphrase) {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue