1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-29 16:43:09 +03:00

Add getKeysProved and getKeysClaimed methods to MatrixEvent.

These list the keys that sender of the event must have ownership
of and the keys of that the sender claims ownership of.

All olm and megolm messages prove ownership of a curve25519 key.
All new olm and megolm message will now claim ownership of a
ed25519 key.

This allows us to detect if an attacker claims ownership of a curve25519
key they don't own when advertising their device keys, because when we
receive an event from the original user it will have a different ed25519 key
to the attackers.
This commit is contained in:
Mark Haines
2016-09-15 16:26:43 +01:00
parent 49a74755a8
commit bde6a171f6
5 changed files with 57 additions and 14 deletions

View File

@@ -514,21 +514,23 @@ OlmDevice.prototype.getOutboundGroupSessionKey = function(sessionId) {
* store an InboundGroupSession in the session store * store an InboundGroupSession in the session store
* *
* @param {string} roomId * @param {string} roomId
* @param {string} senderKey * @param {string} senderCurve25519Key
* @param {string} sessionId * @param {string} sessionId
* @param {Olm.InboundGroupSession} session * @param {Olm.InboundGroupSession} session
* @param {object} keysClaimed Other keys the sender claims.
* @private * @private
*/ */
OlmDevice.prototype._saveInboundGroupSession = function( OlmDevice.prototype._saveInboundGroupSession = function(
roomId, senderKey, sessionId, session roomId, senderCurve25519Key, sessionId, session, keysClaimed
) { ) {
var r = { var r = {
room_id: roomId, room_id: roomId,
session: session.pickle(this._pickleKey), session: session.pickle(this._pickleKey),
keysClaimed: keysClaimed,
}; };
this._sessionStore.storeEndToEndInboundGroupSession( this._sessionStore.storeEndToEndInboundGroupSession(
senderKey, sessionId, JSON.stringify(r) senderKey, senderCurve25519Key, sessionId, JSON.stringify(r)
); );
}; };
@@ -569,7 +571,12 @@ OlmDevice.prototype._getInboundGroupSession = function(
var session = new Olm.InboundGroupSession(); var session = new Olm.InboundGroupSession();
try { try {
session.unpickle(this._pickleKey, r.session); session.unpickle(this._pickleKey, r.session);
return {sessionExists: true, result: func(session)}; return {
sessionExists: true,
result: func(session),
keysProved: {curve25519: senderKey},
keysClaimed: r.keysClaimed || {},
};
} finally { } finally {
session.free(); session.free();
} }

View File

@@ -360,7 +360,7 @@ utils.inherits(MegolmDecryption, base.DecryptionAlgorithm);
* @param {object} event raw event * @param {object} event raw event
* *
* @return {object} object with 'result' key with decrypted payload (with * @return {object} object with 'result' key with decrypted payload (with
* properties 'type', 'content') and a 'sessionKey' key. * properties 'type', 'content') and a 'sessionExists' key.
* *
* @throws {module:crypto/algorithms/base.DecryptionError} if there is a * @throws {module:crypto/algorithms/base.DecryptionError} if there is a
* problem decrypting the event * problem decrypting the event
@@ -382,7 +382,8 @@ MegolmDecryption.prototype.decryptEvent = function(event) {
event.room_id, content.sender_key, content.session_id, content.ciphertext event.room_id, content.sender_key, content.session_id, content.ciphertext
); );
if (res.sessionExists) { if (res.sessionExists) {
return {result: JSON.parse(res.result), sessionExists: true}; res.result = JSON.parse(res.result);
return res;
} else { } else {
return {sessionExists: false}; return {sessionExists: false};
} }
@@ -411,7 +412,7 @@ MegolmDecryption.prototype.onRoomKeyEvent = function(event) {
this._olmDevice.addInboundGroupSession( this._olmDevice.addInboundGroupSession(
content.room_id, event.getSenderKey(), content.session_id, content.room_id, event.getSenderKey(), content.session_id,
content.session_key, content.chain_index content.session_key, content.chain_index, event.getKeysClaimed()
); );
}; };

View File

@@ -119,6 +119,17 @@ OlmEncryption.prototype.encryptMessage = function(room, eventType, content) {
room_id: room.roomId, room_id: room.roomId,
type: eventType, type: eventType,
content: content, content: content,
// Include the ED25519 key so that the recipient knows what
// device this message came from.
// We don't need to include the curve25519 key since the
// recipient will already know this from the olm headers.
// When combined with the device keys retrieved from the
// homeserver signed by the ed25519 key this proves that
// the curve25519 key and the ed25519 key are owned by
// the same device.
keys: {
"ed25519": self._olmDevice.deviceEd25519Key
},
} }
); );
}); });
@@ -200,7 +211,13 @@ OlmDecryption.prototype.decryptEvent = function(event) {
// TODO: Check the sender user id matches the sender key. // TODO: Check the sender user id matches the sender key.
// TODO: check the room_id and fingerprint // TODO: check the room_id and fingerprint
if (payloadString !== null) { if (payloadString !== null) {
return {result: JSON.parse(payloadString), sessionExists: true}; var payload = JSON.parse(payloadString);
return {
result: payload,
sessionExists: true,
keysProved: {curve25519: deviceKey},
keysClaimed: payload.keys || {}
};
} else { } else {
throw new base.DecryptionError("Bad Encrypted Message"); throw new base.DecryptionError("Bad Encrypted Message");
} }

View File

@@ -823,8 +823,11 @@ Crypto.prototype.decryptEvent = function(event) {
olmDevice: this._olmDevice, olmDevice: this._olmDevice,
}); });
var r = alg.decryptEvent(event); var r = alg.decryptEvent(event);
var payload = r.result;
payload.keysClaimed = r.keysClaimed;
payload.keysProved = r.keysProved;
if (r.sessionExists) { if (r.sessionExists) {
return r.result; return payload;
} else { } else {
// We've got a message for a session we don't have. // We've got a message for a session we don't have.
// Maybe the sender forgot to tell us about the session. // Maybe the sender forgot to tell us about the session.

View File

@@ -233,12 +233,27 @@ module.exports.MatrixEvent.prototype = {
return Boolean(this._clearEvent.type); return Boolean(this._clearEvent.type);
}, },
/**
* Returns the curve25519 key that sent this event
*/
getSenderKey: function() { getSenderKey: function() {
if (!this.isEncrypted()) { return this.getKeysProved().curve25519 || null;
return null; },
}
var c = this.getWireContent(); /**
return c.sender_key; * The keys that must have been owned by the sender of this encrypted event.
* @return {object}
*/
getKeysProved: function() {
return this._clearEvent.keysProved || {};
},
/**
* The additional keys the sender of this encrypted event claims to possess
* @return {object}
*/
getKeysClaimed: function() {
return this._clearEvent.keysClaimed || {};
}, },
getUnsigned: function() { getUnsigned: function() {