1
0
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:
Richard van der Hoff
2017-06-20 12:39:36 +01:00
parent cfa871c076
commit 1f86dbd12f
4 changed files with 153 additions and 25 deletions

View File

@@ -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();
});
});
});
});

View File

@@ -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();

View File

@@ -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

View File

@@ -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);