1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-29 16:43:09 +03:00

Trust on decrypt

Trust backups that we've restored by saving the matching pubkey
locally.

NB. Contains technically breaking API changes to the backup restore
(takes backupInfo rather than version).
This commit is contained in:
David Baker
2019-02-07 14:37:25 +00:00
parent 9e12fc4d7d
commit 5e4f10a80c
2 changed files with 41 additions and 18 deletions

View File

@@ -1093,28 +1093,28 @@ MatrixClient.prototype.isValidRecoveryKey = function(recoveryKey) {
}
};
MatrixClient.prototype.restoreKeyBackupWithPassword = async function(
password, targetRoomId, targetSessionId, version,
) {
const backupInfo = await this.getKeyBackupVersion();
MatrixClient.prototype.RESTORE_BACKUP_ERROR_BAD_KEY = 'RESTORE_BACKUP_ERROR_BAD_KEY';
MatrixClient.prototype.restoreKeyBackupWithPassword = async function(
password, targetRoomId, targetSessionId, backupInfo,
) {
const privKey = await keyForExistingBackup(backupInfo, password);
return this._restoreKeyBackup(
privKey, targetRoomId, targetSessionId, version,
privKey, targetRoomId, targetSessionId, backupInfo,
);
};
MatrixClient.prototype.restoreKeyBackupWithRecoveryKey = function(
recoveryKey, targetRoomId, targetSessionId, version,
recoveryKey, targetRoomId, targetSessionId, backupInfo,
) {
const privKey = decodeRecoveryKey(recoveryKey);
return this._restoreKeyBackup(
privKey, targetRoomId, targetSessionId, version,
privKey, targetRoomId, targetSessionId, backupInfo,
);
};
MatrixClient.prototype._restoreKeyBackup = function(
privKey, targetRoomId, targetSessionId, version,
privKey, targetRoomId, targetSessionId, backupInfo,
) {
if (this._crypto === null) {
throw new Error("End-to-end encryption disabled");
@@ -1122,16 +1122,26 @@ MatrixClient.prototype._restoreKeyBackup = function(
let totalKeyCount = 0;
let keys = [];
const path = this._makeKeyBackupPath(targetRoomId, targetSessionId, version);
const path = this._makeKeyBackupPath(
targetRoomId, targetSessionId, backupInfo.version,
);
const decryption = new global.Olm.PkDecryption();
let backupPubKey;
try {
decryption.init_with_private_key(privKey);
backupPubKey = decryption.init_with_private_key(privKey);
} catch(e) {
decryption.free();
throw e;
}
// If the pubkey computed from the private data we've been given
// doesn't match the one in the auth_data, the user has enetered
// a different recovery key / the wrong passphrase.
if (backupPubKey !== backupInfo.auth_data.public_key) {
return Promise.reject({errcode: this.RESTORE_BACKUP_ERROR_BAD_KEY});
}
return this._http.authedRequest(
undefined, "GET", path.path, path.queryData,
).then((res) => {
@@ -1166,6 +1176,8 @@ MatrixClient.prototype._restoreKeyBackup = function(
}
return this.importRoomKeys(keys);
}).then(() => {
return this._crypto.setTrustedBackupPubKey(backupPubKey);
}).then(() => {
return {total: totalKeyCount, imported: keys.length};
}).finally(() => {

View File

@@ -292,6 +292,13 @@ Crypto.prototype._checkAndStartKeyBackup = async function() {
}
};
Crypto.prototype.setTrustedBackupPubKey = async function(trustedPubKey) {
// This should be redundant post cross-signing is a thing, so just
// plonk it in localStorage for now.
global.localStorage.setItem("mx_trusted_backup_pubkey", trustedPubKey);
await this.checkKeyBackup();
};
/**
* Forces a re-check of the key backup and enables/disables it
* as appropriate.
@@ -315,6 +322,7 @@ Crypto.prototype.checkKeyBackup = async function() {
Crypto.prototype.isKeyBackupTrusted = async function(backupInfo) {
const ret = {
usable: false,
trusted_locally: false,
sigs: [],
};
@@ -325,16 +333,19 @@ Crypto.prototype.isKeyBackupTrusted = async function(backupInfo) {
!backupInfo.auth_data.public_key ||
!backupInfo.auth_data.signatures
) {
console.log("Key backup is absent or missing required data");
logger.info("Key backup is absent or missing required data");
return ret;
}
const mySigs = backupInfo.auth_data.signatures[this._userId];
if (!mySigs || mySigs.length === 0) {
console.log("Ignoring key backup because it lacks any signatures from this user");
return ret;
const trustedPubkey = global.localStorage.getItem("mx_trusted_backup_pubkey");
if (backupInfo.auth_data.public_key === trustedPubkey) {
logger.info("Backup public key " + trustedPubkey + " is trusted locally");
ret.trusted_locally = true;
}
const mySigs = backupInfo.auth_data.signatures[this._userId] || [];
for (const keyId of Object.keys(mySigs)) {
const sigInfo = { deviceId: keyId.split(':')[1] }; // XXX: is this how we're supposed to get the device ID?
const device = this._deviceList.getStoredDevice(
@@ -352,17 +363,17 @@ Crypto.prototype.isKeyBackupTrusted = async function(backupInfo) {
);
sigInfo.valid = true;
} catch (e) {
console.log("Bad signature from device " + device.deviceId, e);
logger.info("Bad signature from device " + device.deviceId, e);
sigInfo.valid = false;
}
} else {
sigInfo.valid = null; // Can't determine validity because we don't have the signing device
console.log("Ignoring signature from unknown key " + keyId);
logger.info("Ignoring signature from unknown key " + keyId);
}
ret.sigs.push(sigInfo);
}
ret.usable = ret.sigs.some((s) => s.valid && s.device.isVerified());
ret.usable = ret.sigs.some((s) => s.valid && s.device.isVerified()) || ret.trusted_locally;
return ret;
};