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
Add support for forwarding room keys to megolm
when we receive a m.forwarded_room_key, add it to the crypto store, but remember who forwarded it to us, so we can decide whether to trust them separately.
This commit is contained in:
@@ -5,6 +5,7 @@ try {
|
||||
}
|
||||
|
||||
import expect from 'expect';
|
||||
import q from 'q';
|
||||
|
||||
import sdk from '../../../..';
|
||||
import algorithms from '../../../../lib/crypto/algorithms';
|
||||
@@ -105,5 +106,55 @@ describe("MegolmDecryption", function() {
|
||||
megolmDecryption.decryptEvent(event);
|
||||
expect(event.getContent()).toEqual('testytest');
|
||||
});
|
||||
|
||||
it('can respond to a key request event', function() {
|
||||
const keyRequest = {
|
||||
userId: '@alice:foo',
|
||||
deviceId: 'alidevice',
|
||||
requestBody: {
|
||||
room_id: ROOM_ID,
|
||||
sender_key: "SENDER_CURVE25519",
|
||||
session_id: groupSession.session_id(),
|
||||
},
|
||||
};
|
||||
|
||||
expect(megolmDecryption.hasKeysForKeyRequest(keyRequest))
|
||||
.toBe(true);
|
||||
|
||||
// set up some pre-conditions for the share call
|
||||
const deviceInfo = {};
|
||||
mockCrypto.getStoredDevice.andReturn(deviceInfo);
|
||||
mockOlmLib.ensureOlmSessionsForDevices.andReturn(
|
||||
q({'@alice:foo': {'alidevice': {
|
||||
sessionId: 'alisession',
|
||||
}}}),
|
||||
);
|
||||
mockBaseApis.sendToDevice = expect.createSpy();
|
||||
|
||||
|
||||
// do the share
|
||||
megolmDecryption.shareKeysWithDevice(keyRequest);
|
||||
|
||||
// it's asynchronous, so we have to wait a bit
|
||||
return q.delay(1).then(() => {
|
||||
// check that it called encryptMessageForDevice with
|
||||
// appropriate args.
|
||||
expect(mockOlmLib.encryptMessageForDevice.calls.length)
|
||||
.toEqual(1);
|
||||
|
||||
const call = mockOlmLib.encryptMessageForDevice.calls[0];
|
||||
const payload = call.arguments[6];
|
||||
|
||||
expect(payload.type).toEqual("m.forwarded_room_key");
|
||||
expect(payload.content).toInclude({
|
||||
sender_key: "SENDER_CURVE25519",
|
||||
sender_claimed_ed25519_key: "SENDER_ED25519",
|
||||
session_id: groupSession.session_id(),
|
||||
chain_index: 0,
|
||||
forwarding_curve25519_key_chain: [],
|
||||
});
|
||||
expect(payload.content.session_key).toExist();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -55,6 +55,8 @@ function checkPayloadLength(payloadString) {
|
||||
*
|
||||
* @typedef {Object} module:crypto/OlmDevice.MegolmSessionData
|
||||
* @property {String} sender_key Sender's Curve25519 device key
|
||||
* @property {String[]} forwarding_curve25519_key_chain Devices which forwarded
|
||||
* this session to us (normally empty).
|
||||
* @property {Object<string, string>} sender_claimed_keys Other keys the sender claims.
|
||||
* @property {String} room_id Room this session is used in
|
||||
* @property {String} session_id Unique id for the session
|
||||
@@ -587,6 +589,8 @@ OlmDevice.prototype.getOutboundGroupSessionKey = function(sessionId) {
|
||||
* @property {string} room_Id
|
||||
* @property {string} session pickled Olm.InboundGroupSession
|
||||
* @property {Object<string, string>} keysClaimed
|
||||
* @property {[string]} forwardingCurve25519KeyChain Devices involved in forwarding
|
||||
* this session to us (normally empty).
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -657,12 +661,18 @@ OlmDevice.prototype._getInboundGroupSession = function(
|
||||
*
|
||||
* @param {string} roomId room in which this session will be used
|
||||
* @param {string} senderKey base64-encoded curve25519 key of the sender
|
||||
* @param {string[]} forwardingCurve25519KeyChain Devices involved in forwarding
|
||||
* this session to us.
|
||||
* @param {string} sessionId session identifier
|
||||
* @param {string} sessionKey base64-encoded secret key
|
||||
* @param {Object<string, string>} keysClaimed Other keys the sender claims.
|
||||
* @param {boolean} exportFormat true if the megolm keys are in export format
|
||||
* (ie, they lack an ed25519 signature)
|
||||
*/
|
||||
OlmDevice.prototype.addInboundGroupSession = function(
|
||||
roomId, senderKey, sessionId, sessionKey, keysClaimed,
|
||||
roomId, senderKey, forwardingCurve25519KeyChain,
|
||||
sessionId, sessionKey, keysClaimed,
|
||||
exportFormat,
|
||||
) {
|
||||
const self = this;
|
||||
|
||||
@@ -685,7 +695,11 @@ OlmDevice.prototype.addInboundGroupSession = function(
|
||||
// new session.
|
||||
const session = new Olm.InboundGroupSession();
|
||||
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,
|
||||
@@ -696,6 +710,7 @@ OlmDevice.prototype.addInboundGroupSession = function(
|
||||
room_id: roomId,
|
||||
session: session.pickle(this._pickleKey),
|
||||
keysClaimed: keysClaimed,
|
||||
forwardingCurve25519KeyChain: forwardingCurve25519KeyChain,
|
||||
};
|
||||
|
||||
self._saveInboundGroupSession(
|
||||
@@ -765,8 +780,9 @@ OlmDevice.prototype.importInboundGroupSession = function(data) {
|
||||
*
|
||||
* @return {null} the sessionId is unknown
|
||||
*
|
||||
* @return {{result: string, senderKey: string, keysClaimed:
|
||||
* Object<string, string>}} result
|
||||
* @return {{result: string, senderKey: string,
|
||||
* forwardingCurve25519KeyChain: [string],
|
||||
* keysClaimed: Object<string, string>}}
|
||||
*/
|
||||
OlmDevice.prototype.decryptGroupMessage = function(
|
||||
roomId, senderKey, sessionId, body,
|
||||
@@ -800,6 +816,7 @@ OlmDevice.prototype.decryptGroupMessage = function(
|
||||
result: plaintext,
|
||||
keysClaimed: sessionData.keysClaimed || {},
|
||||
senderKey: senderKey,
|
||||
forwardingCurve25519KeyChain: sessionData.forwardingCurve25519KeyChain || [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -846,16 +863,26 @@ OlmDevice.prototype.hasInboundSessionKeys = function(roomId, senderKey, sessionI
|
||||
* @param {string} senderKey base64-encoded curve25519 key of the sender
|
||||
* @param {string} sessionId session identifier
|
||||
*
|
||||
* @returns {{chain_index: number, key: string}} details of the session key. The
|
||||
* key is a base64-encoded megolm key in export format.
|
||||
* @returns {{chain_index: number, key: string,
|
||||
* forwarding_curve25519_key_chain: [string],
|
||||
* sender_claimed_ed25519_key: string,
|
||||
* }}
|
||||
* details of the session key. The key is a base64-encoded megolm key in
|
||||
* export format.
|
||||
*/
|
||||
OlmDevice.prototype.getInboundGroupSessionKey = function(roomId, senderKey, sessionId) {
|
||||
function getKey(session, sessionData) {
|
||||
const messageIndex = session.first_known_index();
|
||||
|
||||
const claimedKeys = sessionData.keysClaimed || {};
|
||||
const senderEd25519Key = claimedKeys.ed25519 || null;
|
||||
|
||||
return {
|
||||
"chain_index": messageIndex,
|
||||
"key": session.export_session(messageIndex),
|
||||
"forwarding_curve25519_key_chain":
|
||||
sessionData.forwardingCurve25519KeyChain || [],
|
||||
"sender_claimed_ed25519_key": senderEd25519Key,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -894,6 +921,8 @@ OlmDevice.prototype.exportInboundGroupSession = function(senderKey, sessionId) {
|
||||
"room_id": r.room_id,
|
||||
"session_id": sessionId,
|
||||
"session_key": session.export_session(messageIndex),
|
||||
"forwarding_curve25519_key_chain":
|
||||
session.forwardingCurve25519KeyChain || [],
|
||||
};
|
||||
} finally {
|
||||
session.free();
|
||||
|
||||
@@ -250,7 +250,7 @@ MegolmEncryption.prototype._prepareNewSession = function() {
|
||||
const key = this._olmDevice.getOutboundGroupSessionKey(sessionId);
|
||||
|
||||
this._olmDevice.addInboundGroupSession(
|
||||
this._roomId, this._olmDevice.deviceCurve25519Key, sessionId,
|
||||
this._roomId, this._olmDevice.deviceCurve25519Key, [], sessionId,
|
||||
key.key, {ed25519: this._olmDevice.deviceEd25519Key},
|
||||
);
|
||||
|
||||
@@ -595,7 +595,8 @@ MegolmDecryption.prototype._decryptEvent = function(event, requestKeysOnFail) {
|
||||
);
|
||||
}
|
||||
|
||||
event.setClearData(payload, res.senderKey, res.keysClaimed.ed25519);
|
||||
event.setClearData(payload, res.senderKey, res.keysClaimed.ed25519,
|
||||
res.forwardingCurve25519KeyChain);
|
||||
};
|
||||
|
||||
MegolmDecryption.prototype._requestKeysForEvent = function(event) {
|
||||
@@ -645,8 +646,11 @@ MegolmDecryption.prototype._addEventToPendingList = function(event) {
|
||||
*/
|
||||
MegolmDecryption.prototype.onRoomKeyEvent = function(event) {
|
||||
const content = event.getContent();
|
||||
const senderKey = event.getSenderKey();
|
||||
const sessionId = content.session_id;
|
||||
let senderKey = event.getSenderKey();
|
||||
let forwardingKeyChain = [];
|
||||
let exportFormat = false;
|
||||
let keysClaimed;
|
||||
|
||||
if (!content.room_id ||
|
||||
!sessionId ||
|
||||
@@ -655,15 +659,49 @@ MegolmDecryption.prototype.onRoomKeyEvent = function(event) {
|
||||
console.error("key event is missing fields");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!senderKey) {
|
||||
console.error("key event has no sender key (not encrypted?)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getType() == "m.forwarded_room_key") {
|
||||
exportFormat = true;
|
||||
forwardingKeyChain = content.forwarding_curve25519_key_chain;
|
||||
if (!utils.isArray(forwardingKeyChain)) {
|
||||
forwardingKeyChain = [];
|
||||
}
|
||||
|
||||
// copy content before we modify it
|
||||
forwardingKeyChain = forwardingKeyChain.slice();
|
||||
forwardingKeyChain.push(senderKey);
|
||||
|
||||
senderKey = content.sender_key;
|
||||
if (!senderKey) {
|
||||
console.error("forwarded_room_key event is missing sender_key field");
|
||||
return;
|
||||
}
|
||||
|
||||
const ed25519Key = content.sender_claimed_ed25519_key;
|
||||
if (!ed25519Key) {
|
||||
console.error(
|
||||
`forwarded_room_key_event is missing sender_claimed_ed25519_key field`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
keysClaimed = {
|
||||
ed25519: ed25519Key,
|
||||
};
|
||||
} else {
|
||||
keysClaimed = event.getKeysClaimed();
|
||||
}
|
||||
|
||||
console.log(`Adding key for megolm session ${senderKey}|${sessionId}`);
|
||||
this._olmDevice.addInboundGroupSession(
|
||||
content.room_id, senderKey, sessionId,
|
||||
content.session_key, event.getKeysClaimed(),
|
||||
content.room_id, senderKey, forwardingKeyChain, sessionId,
|
||||
content.session_key, keysClaimed,
|
||||
exportFormat,
|
||||
);
|
||||
|
||||
// cancel any outstanding room key requests for this session
|
||||
@@ -722,22 +760,10 @@ MegolmDecryption.prototype.shareKeysWithDevice = function(keyRequest) {
|
||||
+ userId + ":" + deviceId,
|
||||
);
|
||||
|
||||
const key = this._olmDevice.getInboundGroupSessionKey(
|
||||
const payload = this._buildKeyForwardingMessage(
|
||||
body.room_id, body.sender_key, body.session_id,
|
||||
);
|
||||
|
||||
const payload = {
|
||||
type: "m.forwarded_room_key",
|
||||
content: {
|
||||
algorithm: olmlib.MEGOLM_ALGORITHM,
|
||||
room_id: body.room_id,
|
||||
sender_key: body.sender_key,
|
||||
session_id: body.session_id,
|
||||
session_key: key.key,
|
||||
chain_index: key.chain_index,
|
||||
},
|
||||
};
|
||||
|
||||
const encryptedContent = {
|
||||
algorithm: olmlib.OLM_ALGORITHM,
|
||||
sender_key: this._olmDevice.deviceCurve25519Key,
|
||||
@@ -765,6 +791,27 @@ MegolmDecryption.prototype.shareKeysWithDevice = function(keyRequest) {
|
||||
}).done();
|
||||
};
|
||||
|
||||
MegolmDecryption.prototype._buildKeyForwardingMessage = function(
|
||||
roomId, senderKey, sessionId,
|
||||
) {
|
||||
const key = this._olmDevice.getInboundGroupSessionKey(
|
||||
roomId, senderKey, sessionId,
|
||||
);
|
||||
|
||||
return {
|
||||
type: "m.forwarded_room_key",
|
||||
content: {
|
||||
algorithm: olmlib.MEGOLM_ALGORITHM,
|
||||
room_id: roomId,
|
||||
sender_key: senderKey,
|
||||
sender_claimed_ed25519_key: key.sender_claimed_ed25519_key,
|
||||
session_id: sessionId,
|
||||
session_key: key.key,
|
||||
chain_index: key.chain_index,
|
||||
forwarding_curve25519_key_chain: key.forwarding_curve25519_key_chain,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
||||
@@ -161,7 +161,8 @@ function _registerEventHandlers(crypto, eventEmitter) {
|
||||
|
||||
eventEmitter.on("toDeviceEvent", function(event) {
|
||||
try {
|
||||
if (event.getType() == "m.room_key") {
|
||||
if (event.getType() == "m.room_key"
|
||||
|| event.getType() == "m.forwarded_room_key") {
|
||||
crypto._onRoomKeyEvent(event);
|
||||
} else if (event.getType() == "m.new_device") {
|
||||
crypto._onNewDeviceEvent(event);
|
||||
|
||||
Reference in New Issue
Block a user