You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-26 17:03:12 +03:00
WIP e2e key backup support
Continues from uhoreg's branch
This commit is contained in:
@@ -774,9 +774,27 @@ MatrixClient.prototype.getKeyBackupVersion = function(callback) {
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}).catch(e => {
|
||||
if (e.errcode === 'M_NOT_FOUND') {
|
||||
if (callback) callback(null);
|
||||
return null;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {bool} true if the client is configured to back up keys to
|
||||
* the server, otherwise false.
|
||||
*/
|
||||
MatrixClient.prototype.getKeyBackupEnabled = function() {
|
||||
if (this._crypto === null) {
|
||||
throw new Error("End-to-end encryption disabled");
|
||||
}
|
||||
return Boolean(this._crypto.backupKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable backing up of keys, using data previously returned from
|
||||
* getKeyBackupVersion.
|
||||
@@ -786,6 +804,7 @@ MatrixClient.prototype.enableKeyBackup = function(info) {
|
||||
throw new Error("End-to-end encryption disabled");
|
||||
}
|
||||
|
||||
this._crypto.backupInfo = info;
|
||||
this._crypto.backupKey = new global.Olm.PkEncryption();
|
||||
this._crypto.backupKey.set_recipient_key(info.auth_data.public_key);
|
||||
}
|
||||
@@ -798,7 +817,8 @@ MatrixClient.prototype.disableKeyBackup = function() {
|
||||
throw new Error("End-to-end encryption disabled");
|
||||
}
|
||||
|
||||
this._crypto.backupKey = undefined;
|
||||
this._crypto.backupInfo = null;
|
||||
this._crypto.backupKey = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -836,11 +856,16 @@ MatrixClient.prototype.createKeyBackupVersion = function(info, callback) {
|
||||
algorithm: info.algorithm,
|
||||
auth_data: info.auth_data, // FIXME: should this be cloned?
|
||||
}
|
||||
this._crypto._signObject(data.auth_data);
|
||||
return this._crypto._signObject(data.auth_data).then(() => {
|
||||
return this._http.authedRequest(
|
||||
undefined, "POST", "/room_keys/version", undefined, data,
|
||||
).then((res) => {
|
||||
this.enableKeyBackup(info);
|
||||
);
|
||||
}).then((res) => {
|
||||
this.enableKeyBackup({
|
||||
algorithm: info.algorithm,
|
||||
auth_data: info.auth_data,
|
||||
version: res.version,
|
||||
});
|
||||
if (callback) {
|
||||
callback(null, res);
|
||||
}
|
||||
@@ -848,6 +873,27 @@ MatrixClient.prototype.createKeyBackupVersion = function(info, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
MatrixClient.prototype.deleteKeyBackupVersion = function(version) {
|
||||
if (this._crypto === null) {
|
||||
throw new Error("End-to-end encryption disabled");
|
||||
}
|
||||
|
||||
// If we're currently backing up to this backup... stop.
|
||||
// (We start using it automatically in createKeyBackupVersion
|
||||
// so this is symmetrical).
|
||||
if (this._crypto.backupInfo && this._crypto.backupInfo.version === version) {
|
||||
this.disableKeyBackup();
|
||||
}
|
||||
|
||||
const path = utils.encodeUri("/room_keys/version/$version", {
|
||||
$version: version,
|
||||
});
|
||||
|
||||
return this._http.authedRequest(
|
||||
undefined, "DELETE", path, undefined, undefined,
|
||||
);
|
||||
};
|
||||
|
||||
MatrixClient.prototype._makeKeyBackupPath = function(roomId, sessionId, version) {
|
||||
let path;
|
||||
if (sessionId !== undefined) {
|
||||
@@ -890,6 +936,14 @@ MatrixClient.prototype.sendKeyBackup = function(roomId, sessionId, version, data
|
||||
);
|
||||
};
|
||||
|
||||
MatrixClient.prototype.backupAllGroupSessions = function(version) {
|
||||
if (this._crypto === null) {
|
||||
throw new Error("End-to-end encryption disabled");
|
||||
}
|
||||
|
||||
return this._crypto.backupAllGroupSessions(version);
|
||||
};
|
||||
|
||||
MatrixClient.prototype.restoreKeyBackups = function(decryptionKey, roomId, sessionId, version, callback) {
|
||||
if (this._crypto === null) {
|
||||
throw new Error("End-to-end encryption disabled");
|
||||
@@ -924,7 +978,7 @@ MatrixClient.prototype.restoreKeyBackups = function(decryptionKey, roomId, sessi
|
||||
})
|
||||
};
|
||||
|
||||
MatrixClient.prototype.deleteKeyBackups = function(roomId, sessionId, version, callback) {
|
||||
MatrixClient.prototype.deleteKeysFromBackup = function(roomId, sessionId, version, callback) {
|
||||
if (this._crypto === null) {
|
||||
throw new Error("End-to-end encryption disabled");
|
||||
}
|
||||
|
||||
@@ -263,6 +263,14 @@ MegolmEncryption.prototype._prepareNewSession = async function() {
|
||||
key.key, {ed25519: this._olmDevice.deviceEd25519Key},
|
||||
);
|
||||
|
||||
if (this._crypto.backupInfo) {
|
||||
// Not strictly necessary to wait for this
|
||||
await this._crypto.backupGroupSession(
|
||||
this._roomId, this._olmDevice.deviceCurve25519Key, [],
|
||||
sessionId, key.key,
|
||||
);
|
||||
}
|
||||
|
||||
return new OutboundSessionInfo(sessionId);
|
||||
};
|
||||
|
||||
@@ -840,11 +848,13 @@ MegolmDecryption.prototype.onRoomKeyEvent = function(event) {
|
||||
// have another go at decrypting events sent with this session.
|
||||
this._retryDecryption(senderKey, sessionId);
|
||||
}).then(() => {
|
||||
return this.backupGroupSession(
|
||||
if (this._crypto.backupInfo) {
|
||||
return this._crypto.backupGroupSession(
|
||||
content.room_id, senderKey, forwardingKeyChain,
|
||||
content.session_id, content.session_key, keysClaimed,
|
||||
exportFormat,
|
||||
);
|
||||
}
|
||||
}).catch((e) => {
|
||||
console.error(`Error handling m.room_key_event: ${e}`);
|
||||
});
|
||||
@@ -967,54 +977,6 @@ MegolmDecryption.prototype.importRoomKey = function(session) {
|
||||
});
|
||||
};
|
||||
|
||||
MegolmDecryption.prototype.backupGroupSession = async function(
|
||||
roomId, senderKey, forwardingCurve25519KeyChain,
|
||||
sessionId, sessionKey, keysClaimed,
|
||||
exportFormat,
|
||||
) {
|
||||
// new session.
|
||||
const session = new Olm.InboundGroupSession();
|
||||
let first_known_index;
|
||||
try {
|
||||
if (exportFormat) {
|
||||
session.import_session(sessionKey);
|
||||
} else {
|
||||
session.create(sessionKey);
|
||||
}
|
||||
if (sessionId != session.session_id()) {
|
||||
throw new Error(
|
||||
"Mismatched group session ID from senderKey: " +
|
||||
senderKey,
|
||||
);
|
||||
}
|
||||
|
||||
if (!exportFormat) {
|
||||
sessionKey = session.export_session();
|
||||
}
|
||||
const first_known_index = session.first_known_index();
|
||||
|
||||
const sessionData = {
|
||||
algorithm: olmlib.MEGOLM_ALGORITHM,
|
||||
sender_key: senderKey,
|
||||
sender_claimed_keys: keysClaimed,
|
||||
forwardingCurve25519KeyChain: forwardingCurve25519KeyChain,
|
||||
session_key: sessionKey
|
||||
};
|
||||
const encrypted = this._crypto.backupKey.encrypt(JSON.stringify(sessionData));
|
||||
const data = {
|
||||
first_message_index: first_known_index,
|
||||
forwarded_count: forwardingCurve25519KeyChain.length,
|
||||
is_verified: false, // FIXME: how do we determine this?
|
||||
session_data: encrypted
|
||||
};
|
||||
return this._baseApis.sendKeyBackup(roomId, sessionId, data);
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
} finally {
|
||||
session.free();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Have another go at decrypting events after we receive a key
|
||||
*
|
||||
|
||||
@@ -75,7 +75,8 @@ function Crypto(baseApis, sessionStore, userId, deviceId,
|
||||
// track whether this device's megolm keys are being backed up incrementally
|
||||
// to the server or not.
|
||||
// XXX: this should probably have a single source of truth from OlmAccount
|
||||
this.backupKey = null;
|
||||
this.backupInfo = null; // The info dict from /room_keys/version
|
||||
this.backupKey = null; // The encryption key object
|
||||
|
||||
this._olmDevice = new OlmDevice(sessionStore, cryptoStore);
|
||||
this._deviceList = new DeviceList(
|
||||
@@ -848,6 +849,83 @@ Crypto.prototype.importRoomKeys = function(keys) {
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
Crypto.prototype._backupPayloadForSession = function(
|
||||
senderKey, forwardingCurve25519KeyChain,
|
||||
sessionId, sessionKey, keysClaimed,
|
||||
exportFormat,
|
||||
) {
|
||||
// new session.
|
||||
const session = new Olm.InboundGroupSession();
|
||||
let first_known_index;
|
||||
try {
|
||||
if (exportFormat) {
|
||||
session.import_session(sessionKey);
|
||||
} else {
|
||||
session.create(sessionKey);
|
||||
}
|
||||
if (sessionId != session.session_id()) {
|
||||
throw new Error(
|
||||
"Mismatched group session ID from senderKey: " +
|
||||
senderKey,
|
||||
);
|
||||
}
|
||||
|
||||
if (!exportFormat) {
|
||||
sessionKey = session.export_session();
|
||||
}
|
||||
const first_known_index = session.first_known_index();
|
||||
|
||||
const sessionData = {
|
||||
algorithm: olmlib.MEGOLM_ALGORITHM,
|
||||
sender_key: senderKey,
|
||||
sender_claimed_keys: keysClaimed,
|
||||
session_key: sessionKey,
|
||||
forwarding_curve25519_key_chain: forwardingCurve25519KeyChain,
|
||||
};
|
||||
const encrypted = this.backupKey.encrypt(JSON.stringify(sessionData));
|
||||
return {
|
||||
first_message_index: first_known_index,
|
||||
forwarded_count: forwardingCurve25519KeyChain.length,
|
||||
is_verified: false, // FIXME: how do we determine this?
|
||||
session_data: encrypted,
|
||||
};
|
||||
} finally {
|
||||
session.free();
|
||||
}
|
||||
};
|
||||
|
||||
Crypto.prototype.backupGroupSession = function(
|
||||
roomId, senderKey, forwardingCurve25519KeyChain,
|
||||
sessionId, sessionKey, keysClaimed,
|
||||
exportFormat,
|
||||
) {
|
||||
if (!this.backupInfo) {
|
||||
throw new Error("Key backups are not enabled");
|
||||
}
|
||||
|
||||
const data = this._backupPayloadForSession(
|
||||
senderKey, forwardingCurve25519KeyChain,
|
||||
sessionId, sessionKey, keysClaimed,
|
||||
exportFormat,
|
||||
);
|
||||
return this._baseApis.sendKeyBackup(roomId, sessionId, this.backupInfo.version, data);
|
||||
};
|
||||
|
||||
Crypto.prototype.backupAllGroupSessions = async function(version) {
|
||||
const keys = await this.exportRoomKeys();
|
||||
const data = {};
|
||||
for (const key of keys) {
|
||||
if (data[key.room_id] === undefined) data[key.room_id] = {sessions: {}};
|
||||
|
||||
data[key.room_id]['sessions'][key.session_id] = this._backupPayloadForSession(
|
||||
key.sender_key, key.forwarding_curve25519_key_chain,
|
||||
key.session_id, key.session_key, key.sender_claimed_keys, true,
|
||||
);
|
||||
}
|
||||
return this._baseApis.sendKeyBackup(undefined, undefined, version, {rooms: data});
|
||||
};
|
||||
|
||||
/* eslint-disable valid-jsdoc */ //https://github.com/eslint/eslint/issues/7307
|
||||
/**
|
||||
* Encrypt an event according to the configuration of the room.
|
||||
|
||||
Reference in New Issue
Block a user