You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-12-10 07:22:27 +03:00
Sign & trust the key backup from the SSK
This commit is contained in:
@@ -1035,7 +1035,7 @@ MatrixClient.prototype.prepareKeyBackupVersion = async function(password) {
|
|||||||
* @param {object} info Info object from prepareKeyBackupVersion
|
* @param {object} info Info object from prepareKeyBackupVersion
|
||||||
* @returns {Promise<object>} Object with 'version' param indicating the version created
|
* @returns {Promise<object>} Object with 'version' param indicating the version created
|
||||||
*/
|
*/
|
||||||
MatrixClient.prototype.createKeyBackupVersion = function(info, auth, replacesSsk) {
|
MatrixClient.prototype.createKeyBackupVersion = async function(info, auth, replacesSsk) {
|
||||||
if (this._crypto === null) {
|
if (this._crypto === null) {
|
||||||
throw new Error("End-to-end encryption disabled");
|
throw new Error("End-to-end encryption disabled");
|
||||||
}
|
}
|
||||||
@@ -1056,6 +1056,14 @@ MatrixClient.prototype.createKeyBackupVersion = function(info, auth, replacesSsk
|
|||||||
// sign the USK with the SSK
|
// sign the USK with the SSK
|
||||||
pkSign(uskInfo, Buffer.from(info.accountKeys.self_signing_key_seed, 'base64'), this.credentials.userId);
|
pkSign(uskInfo, Buffer.from(info.accountKeys.self_signing_key_seed, 'base64'), this.credentials.userId);
|
||||||
|
|
||||||
|
// Now sig the backup auth data. Do it as this device first because crypto._signObject
|
||||||
|
// is dumb and bluntly replaces the whole signatures block...
|
||||||
|
// this can probably go away very soon in favour of just signing with the SSK.
|
||||||
|
await this._crypto._signObject(data.auth_data);
|
||||||
|
|
||||||
|
// now also sign the auth data with the SSK
|
||||||
|
pkSign(data.auth_data, Buffer.from(info.accountKeys.self_signing_key_seed, 'base64'), this.credentials.userId);
|
||||||
|
|
||||||
const keys = {
|
const keys = {
|
||||||
self_signing_key: {
|
self_signing_key: {
|
||||||
user_id: this.credentials.userId,
|
user_id: this.credentials.userId,
|
||||||
@@ -1072,11 +1080,12 @@ MatrixClient.prototype.createKeyBackupVersion = function(info, auth, replacesSsk
|
|||||||
return this._cryptoStore.doTxn('readwrite', [IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => {
|
return this._cryptoStore.doTxn('readwrite', [IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => {
|
||||||
// store the newly generated account keys
|
// store the newly generated account keys
|
||||||
this._cryptoStore.storeAccountKeys(txn, info.accountKeys);
|
this._cryptoStore.storeAccountKeys(txn, info.accountKeys);
|
||||||
|
}).then(() => {
|
||||||
|
// re-check the SSK in the device store if necessary
|
||||||
|
return this._crypto.checkOwnSskTrust();
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
// upload the public part of the account keys
|
// upload the public part of the account keys
|
||||||
return this.uploadDeviceSigningKeys(keys);
|
return this.uploadDeviceSigningKeys(keys);
|
||||||
}).then(() => {
|
|
||||||
return this._crypto._signObject(data.auth_data);
|
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
return this._http.authedRequest(
|
return this._http.authedRequest(
|
||||||
undefined, "POST", "/room_keys/version", undefined, data,
|
undefined, "POST", "/room_keys/version", undefined, data,
|
||||||
@@ -1235,6 +1244,8 @@ MatrixClient.prototype._restoreKeyBackup = async function(
|
|||||||
await this._cryptoStore.doTxn('readwrite', [IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => {
|
await this._cryptoStore.doTxn('readwrite', [IndexedDBCryptoStore.STORE_ACCOUNT], (txn) => {
|
||||||
this._cryptoStore.storeAccountKeys(txn, accountKeys);
|
this._cryptoStore.storeAccountKeys(txn, accountKeys);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await this._crypto.checkOwnSskTrust();
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
decryption.free();
|
decryption.free();
|
||||||
throw e;
|
throw e;
|
||||||
|
|||||||
@@ -266,6 +266,17 @@ Crypto.prototype.init = async function() {
|
|||||||
*/
|
*/
|
||||||
Crypto.prototype._onDeviceListUserSskUpdated = async function(userId) {
|
Crypto.prototype._onDeviceListUserSskUpdated = async function(userId) {
|
||||||
if (userId === this._userId) {
|
if (userId === this._userId) {
|
||||||
|
this.checkOwnSskTrust();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check the copy of our SSK that we have in the device list and see if it
|
||||||
|
* matches our private part. If it does, mark it as trusted.
|
||||||
|
*/
|
||||||
|
Crypto.prototype.checkOwnSskTrust = async function() {
|
||||||
|
const userId = this._userId;
|
||||||
|
|
||||||
// If we see an update to our own SSK, check it against the SSK we have and,
|
// If we see an update to our own SSK, check it against the SSK we have and,
|
||||||
// if it matches, mark it as verified
|
// if it matches, mark it as verified
|
||||||
|
|
||||||
@@ -305,13 +316,14 @@ Crypto.prototype._onDeviceListUserSskUpdated = async function(userId) {
|
|||||||
if (seenPubkey === localPubkey) {
|
if (seenPubkey === localPubkey) {
|
||||||
logger.info("Published self-signing key matches local copy: marking as verified");
|
logger.info("Published self-signing key matches local copy: marking as verified");
|
||||||
this.setSskVerification(userId, SskInfo.SskVerification.VERIFIED);
|
this.setSskVerification(userId, SskInfo.SskVerification.VERIFIED);
|
||||||
|
// Now we may be able to trust our key backup
|
||||||
|
await this.checkKeyBackup();
|
||||||
} else {
|
} else {
|
||||||
logger.info(
|
logger.info(
|
||||||
"Published self-signing key DOES NOT match local copy! Local: " +
|
"Published self-signing key DOES NOT match local copy! Local: " +
|
||||||
localPubkey + ", published: " + seenPubkey,
|
localPubkey + ", published: " + seenPubkey,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -397,7 +409,34 @@ Crypto.prototype.isKeyBackupTrusted = async function(backupInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const keyId of Object.keys(mySigs)) {
|
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 sigInfo = {};
|
||||||
|
// Could be an SSK but just say this is the device ID for backwards compat
|
||||||
|
sigInfo.deviceId = keyId.split(':')[1];
|
||||||
|
|
||||||
|
// first check to see if it's from our SSK
|
||||||
|
const ssk = this._deviceList.getStoredSskForUser(this._userId);
|
||||||
|
if (ssk && ssk.getKeyId() === keyId) {
|
||||||
|
sigInfo.self_signing_key = ssk;
|
||||||
|
try {
|
||||||
|
await olmlib.verifySignature(
|
||||||
|
this._olmDevice,
|
||||||
|
backupInfo.auth_data,
|
||||||
|
this._userId,
|
||||||
|
sigInfo.deviceId,
|
||||||
|
ssk.getFingerprint(),
|
||||||
|
);
|
||||||
|
sigInfo.valid = true;
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Bad signature from ssk " + ssk.getKeyId(), e);
|
||||||
|
sigInfo.valid = false;
|
||||||
|
}
|
||||||
|
ret.sigs.push(sigInfo);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now look for a sig from a device
|
||||||
|
// At some point this can probably go away and we'll just support
|
||||||
|
// it being signed by the SSK
|
||||||
const device = this._deviceList.getStoredDevice(
|
const device = this._deviceList.getStoredDevice(
|
||||||
this._userId, sigInfo.deviceId,
|
this._userId, sigInfo.deviceId,
|
||||||
);
|
);
|
||||||
@@ -423,7 +462,13 @@ Crypto.prototype.isKeyBackupTrusted = async function(backupInfo) {
|
|||||||
ret.sigs.push(sigInfo);
|
ret.sigs.push(sigInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.usable = ret.sigs.some((s) => s.valid && s.device.isVerified());
|
ret.usable = ret.sigs.some((s) => {
|
||||||
|
return (
|
||||||
|
s.valid && (
|
||||||
|
(s.device && s.device.isVerified()) || (s.self_signing_key && s.self_signing_key.isVerified())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
});
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2340,11 +2385,17 @@ Crypto.prototype._getRoomDecryptor = function(roomId, algorithm) {
|
|||||||
* @param {Object} obj Object to which we will add a 'signatures' property
|
* @param {Object} obj Object to which we will add a 'signatures' property
|
||||||
*/
|
*/
|
||||||
Crypto.prototype._signObject = async function(obj) {
|
Crypto.prototype._signObject = async function(obj) {
|
||||||
const sigs = {};
|
const sigs = obj.signatures || {};
|
||||||
sigs[this._userId] = {};
|
const unsigned = obj.unsigned;
|
||||||
|
|
||||||
|
delete obj.signatures;
|
||||||
|
delete obj.unsigned;
|
||||||
|
|
||||||
|
sigs[this._userId] = sigs[this._userId] || {};
|
||||||
sigs[this._userId]["ed25519:" + this._deviceId] =
|
sigs[this._userId]["ed25519:" + this._deviceId] =
|
||||||
await this._olmDevice.sign(anotherjson.stringify(obj));
|
await this._olmDevice.sign(anotherjson.stringify(obj));
|
||||||
obj.signatures = sigs;
|
obj.signatures = sigs;
|
||||||
|
if (unsigned !== undefined) obj.unsigned = unsigned;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -283,9 +283,10 @@ const _verifySignature = module.exports.verifySignature = async function(
|
|||||||
|
|
||||||
// prepare the canonical json: remove unsigned and signatures, and stringify with
|
// prepare the canonical json: remove unsigned and signatures, and stringify with
|
||||||
// anotherjson
|
// anotherjson
|
||||||
delete obj.unsigned;
|
const mangledObj = Object.assign({}, obj);
|
||||||
delete obj.signatures;
|
delete mangledObj.unsigned;
|
||||||
const json = anotherjson.stringify(obj);
|
delete mangledObj.signatures;
|
||||||
|
const json = anotherjson.stringify(mangledObj);
|
||||||
|
|
||||||
olmDevice.verifySignature(
|
olmDevice.verifySignature(
|
||||||
signingKey, json, signature,
|
signingKey, json, signature,
|
||||||
|
|||||||
@@ -62,6 +62,10 @@ export default class SskInfo {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getKeyId() {
|
||||||
|
return Object.keys(this.keys)[0];
|
||||||
|
}
|
||||||
|
|
||||||
getFingerprint() {
|
getFingerprint() {
|
||||||
return Object.values(this.keys)[0];
|
return Object.values(this.keys)[0];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user