diff --git a/spec/unit/crypto.spec.js b/spec/unit/crypto.spec.js index 18cc930b0..9039d77d9 100644 --- a/spec/unit/crypto.spec.js +++ b/spec/unit/crypto.spec.js @@ -10,6 +10,7 @@ import * as olmlib from "../../src/crypto/olmlib"; import {sleep} from "../../src/utils"; import {EventEmitter} from "events"; import {CRYPTO_ENABLED} from "../../src/client"; +import {DeviceInfo} from "../../src/crypto/deviceinfo"; const Olm = global.Olm; @@ -26,6 +27,62 @@ describe("Crypto", function() { expect(Crypto.getOlmVersion()[0]).toEqual(3); }); + describe("encrypted events", function() { + it("provides encryption information", async function() { + const client = (new TestClient( + "@alice:example.com", "deviceid", + )).client; + await client.initCrypto(); + + // unencrypted event + const event = { + getId: () => "$event_id", + getSenderKey: () => null, + getWireContent: () => {return {};}, + }; + + let encryptionInfo = client.getEventEncryptionInfo(event); + expect(encryptionInfo.encrypted).toBeFalsy(); + + // unknown sender (e.g. deleted device), forwarded megolm key (untrusted) + event.getSenderKey = () => 'YmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmI'; + event.getWireContent = () => {return {algorithm: olmlib.MEGOLM_ALGORITHM};}; + event.getForwardingCurve25519KeyChain = () => ["not empty"]; + event.isKeySourceUntrusted = () => false; + event.getClaimedEd25519Key = () => 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; + + encryptionInfo = client.getEventEncryptionInfo(event); + expect(encryptionInfo.encrypted).toBeTruthy(); + expect(encryptionInfo.authenticated).toBeFalsy(); + expect(encryptionInfo.sender).toBeFalsy(); + + // known sender, megolm key from backup + event.getForwardingCurve25519KeyChain = () => []; + event.isKeySourceUntrusted = () => true; + const device = new DeviceInfo("FLIBBLE"); + device.keys["curve25519:FLIBBLE"] = 'YmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmI'; + device.keys["ed25519:FLIBBLE"] = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; + client._crypto._deviceList.getDeviceByIdentityKey = () => device; + + encryptionInfo = client.getEventEncryptionInfo(event); + expect(encryptionInfo.encrypted).toBeTruthy(); + expect(encryptionInfo.authenticated).toBeFalsy(); + expect(encryptionInfo.sender).toBeTruthy(); + expect(encryptionInfo.mismatchedSender).toBeFalsy(); + + // known sender, trusted megolm key, but bad ed25519key + event.isKeySourceUntrusted = () => false; + device.keys["ed25519:FLIBBLE"] = 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'; + + encryptionInfo = client.getEventEncryptionInfo(event); + expect(encryptionInfo.encrypted).toBeTruthy(); + expect(encryptionInfo.authenticated).toBeTruthy(); + expect(encryptionInfo.sender).toBeTruthy(); + expect(encryptionInfo.mismatchedSender).toBeTruthy(); + + client.stopClient(); + }); + }); describe('Session management', function() { const otkResponse = { diff --git a/src/client.js b/src/client.js index 65c15c79d..fc89de3c5 100644 --- a/src/client.js +++ b/src/client.js @@ -1193,13 +1193,15 @@ MatrixClient.prototype.checkEventSenderTrust = async function(event) { * @param {module:models/event.MatrixEvent} event event to be checked * * @return {object} An object with the fields: - * - encrypted: whether the event is encrypted + * - encrypted: whether the event is encrypted (if not encrypted, some of the + * other properties may not be set) * - senderKey: the sender's key * - algorithm: the algorithm used to encrypt the event * - authenticated: whether we can be sure that the owner of the senderKey * sent the event - * - sender: the sender's device information + * - sender: the sender's device information, if available * - mismatchedSender: if the event's ed25519 and curve25519 keys don't match + * (only meaningful if `sender` is set) */ /** diff --git a/src/crypto/index.js b/src/crypto/index.js index 272f7c79b..0d3f4153c 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -2292,13 +2292,15 @@ Crypto.prototype.getEventSenderDeviceInfo = function(event) { * @param {module:models/event.MatrixEvent} event event to be checked * * @return {object} An object with the fields: - * - encrypted: whether the event is encrypted + * - encrypted: whether the event is encrypted (if not encrypted, some of the + * other properties may not be set) * - senderKey: the sender's key * - algorithm: the algorithm used to encrypt the event * - authenticated: whether we can be sure that the owner of the senderKey * sent the event - * - sender: the sender's device information + * - sender: the sender's device information, if available * - mismatchedSender: if the event's ed25519 and curve25519 keys don't match + * (only meaningful if `sender` is set) */ Crypto.prototype.getEventEncryptionInfo = function(event) { const ret = {}; @@ -2345,7 +2347,7 @@ Crypto.prototype.getEventEncryptionInfo = function(event) { ret.mismatchedSender = true; } - if (claimedKey !== ret.sender.getFingerprint()) { + if (ret.sender && claimedKey !== ret.sender.getFingerprint()) { logger.warn( "Event " + event.getId() + " claims ed25519 key " + claimedKey + "but sender device has key " + ret.sender.getFingerprint());