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 expect from 'expect';
import q from 'q';
import sdk from '../../../..'; import sdk from '../../../..';
import algorithms from '../../../../lib/crypto/algorithms'; import algorithms from '../../../../lib/crypto/algorithms';
@@ -105,5 +106,55 @@ describe("MegolmDecryption", function() {
megolmDecryption.decryptEvent(event); megolmDecryption.decryptEvent(event);
expect(event.getContent()).toEqual('testytest'); 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 * @typedef {Object} module:crypto/OlmDevice.MegolmSessionData
* @property {String} sender_key Sender's Curve25519 device key * @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 {Object<string, string>} sender_claimed_keys Other keys the sender claims.
* @property {String} room_id Room this session is used in * @property {String} room_id Room this session is used in
* @property {String} session_id Unique id for the session * @property {String} session_id Unique id for the session
@@ -587,6 +589,8 @@ OlmDevice.prototype.getOutboundGroupSessionKey = function(sessionId) {
* @property {string} room_Id * @property {string} room_Id
* @property {string} session pickled Olm.InboundGroupSession * @property {string} session pickled Olm.InboundGroupSession
* @property {Object<string, string>} keysClaimed * @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} roomId room in which this session will be used
* @param {string} senderKey base64-encoded curve25519 key of the sender * @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} sessionId session identifier
* @param {string} sessionKey base64-encoded secret key * @param {string} sessionKey base64-encoded secret key
* @param {Object<string, string>} keysClaimed Other keys the sender claims. * @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( OlmDevice.prototype.addInboundGroupSession = function(
roomId, senderKey, sessionId, sessionKey, keysClaimed, roomId, senderKey, forwardingCurve25519KeyChain,
sessionId, sessionKey, keysClaimed,
exportFormat,
) { ) {
const self = this; const self = this;
@@ -685,7 +695,11 @@ OlmDevice.prototype.addInboundGroupSession = function(
// new session. // new session.
const session = new Olm.InboundGroupSession(); const session = new Olm.InboundGroupSession();
try { try {
session.create(sessionKey); if (exportFormat) {
session.import_session(sessionKey);
} else {
session.create(sessionKey);
}
if (sessionId != session.session_id()) { if (sessionId != session.session_id()) {
throw new Error( throw new Error(
"Mismatched group session ID from senderKey: " + senderKey, "Mismatched group session ID from senderKey: " + senderKey,
@@ -696,6 +710,7 @@ OlmDevice.prototype.addInboundGroupSession = function(
room_id: roomId, room_id: roomId,
session: session.pickle(this._pickleKey), session: session.pickle(this._pickleKey),
keysClaimed: keysClaimed, keysClaimed: keysClaimed,
forwardingCurve25519KeyChain: forwardingCurve25519KeyChain,
}; };
self._saveInboundGroupSession( self._saveInboundGroupSession(
@@ -765,8 +780,9 @@ OlmDevice.prototype.importInboundGroupSession = function(data) {
* *
* @return {null} the sessionId is unknown * @return {null} the sessionId is unknown
* *
* @return {{result: string, senderKey: string, keysClaimed: * @return {{result: string, senderKey: string,
* Object<string, string>}} result * forwardingCurve25519KeyChain: [string],
* keysClaimed: Object<string, string>}}
*/ */
OlmDevice.prototype.decryptGroupMessage = function( OlmDevice.prototype.decryptGroupMessage = function(
roomId, senderKey, sessionId, body, roomId, senderKey, sessionId, body,
@@ -800,6 +816,7 @@ OlmDevice.prototype.decryptGroupMessage = function(
result: plaintext, result: plaintext,
keysClaimed: sessionData.keysClaimed || {}, keysClaimed: sessionData.keysClaimed || {},
senderKey: senderKey, 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} senderKey base64-encoded curve25519 key of the sender
* @param {string} sessionId session identifier * @param {string} sessionId session identifier
* *
* @returns {{chain_index: number, key: string}} details of the session key. The * @returns {{chain_index: number, key: string,
* key is a base64-encoded megolm key in export format. * 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) { OlmDevice.prototype.getInboundGroupSessionKey = function(roomId, senderKey, sessionId) {
function getKey(session, sessionData) { function getKey(session, sessionData) {
const messageIndex = session.first_known_index(); const messageIndex = session.first_known_index();
const claimedKeys = sessionData.keysClaimed || {};
const senderEd25519Key = claimedKeys.ed25519 || null;
return { return {
"chain_index": messageIndex, "chain_index": messageIndex,
"key": session.export_session(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, "room_id": r.room_id,
"session_id": sessionId, "session_id": sessionId,
"session_key": session.export_session(messageIndex), "session_key": session.export_session(messageIndex),
"forwarding_curve25519_key_chain":
session.forwardingCurve25519KeyChain || [],
}; };
} finally { } finally {
session.free(); session.free();

View File

@@ -250,7 +250,7 @@ MegolmEncryption.prototype._prepareNewSession = function() {
const key = this._olmDevice.getOutboundGroupSessionKey(sessionId); const key = this._olmDevice.getOutboundGroupSessionKey(sessionId);
this._olmDevice.addInboundGroupSession( this._olmDevice.addInboundGroupSession(
this._roomId, this._olmDevice.deviceCurve25519Key, sessionId, this._roomId, this._olmDevice.deviceCurve25519Key, [], sessionId,
key.key, {ed25519: this._olmDevice.deviceEd25519Key}, 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) { MegolmDecryption.prototype._requestKeysForEvent = function(event) {
@@ -645,8 +646,11 @@ MegolmDecryption.prototype._addEventToPendingList = function(event) {
*/ */
MegolmDecryption.prototype.onRoomKeyEvent = function(event) { MegolmDecryption.prototype.onRoomKeyEvent = function(event) {
const content = event.getContent(); const content = event.getContent();
const senderKey = event.getSenderKey();
const sessionId = content.session_id; const sessionId = content.session_id;
let senderKey = event.getSenderKey();
let forwardingKeyChain = [];
let exportFormat = false;
let keysClaimed;
if (!content.room_id || if (!content.room_id ||
!sessionId || !sessionId ||
@@ -655,15 +659,49 @@ MegolmDecryption.prototype.onRoomKeyEvent = function(event) {
console.error("key event is missing fields"); console.error("key event is missing fields");
return; return;
} }
if (!senderKey) { if (!senderKey) {
console.error("key event has no sender key (not encrypted?)"); console.error("key event has no sender key (not encrypted?)");
return; 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}`); console.log(`Adding key for megolm session ${senderKey}|${sessionId}`);
this._olmDevice.addInboundGroupSession( this._olmDevice.addInboundGroupSession(
content.room_id, senderKey, sessionId, content.room_id, senderKey, forwardingKeyChain, sessionId,
content.session_key, event.getKeysClaimed(), content.session_key, keysClaimed,
exportFormat,
); );
// cancel any outstanding room key requests for this session // cancel any outstanding room key requests for this session
@@ -722,22 +760,10 @@ MegolmDecryption.prototype.shareKeysWithDevice = function(keyRequest) {
+ userId + ":" + deviceId, + userId + ":" + deviceId,
); );
const key = this._olmDevice.getInboundGroupSessionKey( const payload = this._buildKeyForwardingMessage(
body.room_id, body.sender_key, body.session_id, 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 = { const encryptedContent = {
algorithm: olmlib.OLM_ALGORITHM, algorithm: olmlib.OLM_ALGORITHM,
sender_key: this._olmDevice.deviceCurve25519Key, sender_key: this._olmDevice.deviceCurve25519Key,
@@ -765,6 +791,27 @@ MegolmDecryption.prototype.shareKeysWithDevice = function(keyRequest) {
}).done(); }).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 * @inheritdoc

View File

@@ -161,7 +161,8 @@ function _registerEventHandlers(crypto, eventEmitter) {
eventEmitter.on("toDeviceEvent", function(event) { eventEmitter.on("toDeviceEvent", function(event) {
try { try {
if (event.getType() == "m.room_key") { if (event.getType() == "m.room_key"
|| event.getType() == "m.forwarded_room_key") {
crypto._onRoomKeyEvent(event); crypto._onRoomKeyEvent(event);
} else if (event.getType() == "m.new_device") { } else if (event.getType() == "m.new_device") {
crypto._onNewDeviceEvent(event); crypto._onNewDeviceEvent(event);