From c32ac2c764f839d449db5bf6defc771bc67d8f3c Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 8 Sep 2020 10:50:39 +0200 Subject: [PATCH] use decryption result to show message verification status in timeline --- .../session/room/timeline/tiles/MessageTile.js | 4 ++++ src/matrix/Sync.js | 1 + src/matrix/e2ee/DeviceTracker.js | 4 ++++ src/matrix/e2ee/RoomEncryption.js | 18 ++++++++++++++++++ src/matrix/room/Room.js | 2 ++ src/matrix/room/timeline/entries/EventEntry.js | 10 ++++++++++ .../timeline/persistence/TimelineReader.js | 1 + src/matrix/storage/idb/schema.js | 7 +++++++ .../storage/idb/stores/DeviceIdentityStore.js | 4 ++++ src/ui/web/css/themes/element/theme.css | 4 ++++ src/ui/web/session/room/timeline/common.js | 1 + 11 files changed, 56 insertions(+) diff --git a/src/domain/session/room/timeline/tiles/MessageTile.js b/src/domain/session/room/timeline/tiles/MessageTile.js index 5b3bb603..99513fbe 100644 --- a/src/domain/session/room/timeline/tiles/MessageTile.js +++ b/src/domain/session/room/timeline/tiles/MessageTile.js @@ -71,6 +71,10 @@ export class MessageTile extends SimpleTile { return this._isContinuation; } + get isUnverified() { + return this._entry.isUnverified; + } + _getContent() { return this._entry.content; } diff --git a/src/matrix/Sync.js b/src/matrix/Sync.js index 09ed1824..4198618a 100644 --- a/src/matrix/Sync.js +++ b/src/matrix/Sync.js @@ -135,6 +135,7 @@ export class Sync { storeNames.userIdentities, storeNames.inboundGroupSessions, storeNames.groupSessionDecryptions, + storeNames.deviceIdentities, ]); const roomChanges = []; let sessionChanges; diff --git a/src/matrix/e2ee/DeviceTracker.js b/src/matrix/e2ee/DeviceTracker.js index 84da2f37..0b4c73a2 100644 --- a/src/matrix/e2ee/DeviceTracker.js +++ b/src/matrix/e2ee/DeviceTracker.js @@ -270,4 +270,8 @@ export class DeviceTracker { }); return devices; } + + async getDeviceByCurve25519Key(curve25519Key, txn) { + return await txn.deviceIdentities.getByCurve25519Key(curve25519Key); + } } diff --git a/src/matrix/e2ee/RoomEncryption.js b/src/matrix/e2ee/RoomEncryption.js index d81908df..7d540784 100644 --- a/src/matrix/e2ee/RoomEncryption.js +++ b/src/matrix/e2ee/RoomEncryption.js @@ -34,12 +34,14 @@ export class RoomEncryption { this._megolmSyncCache = this._megolmDecryption.createSessionCache(); // not `event_id`, but an internal event id passed in to the decrypt methods this._eventIdsByMissingSession = new Map(); + this._senderDeviceCache = new Map(); } notifyTimelineClosed() { // empty the backfill cache when closing the timeline this._megolmBackfillCache.dispose(); this._megolmBackfillCache = this._megolmDecryption.createSessionCache(); + this._senderDeviceCache = new Map(); // purge the sender device cache } async writeMemberChanges(memberChanges, txn) { @@ -56,9 +58,25 @@ export class RoomEncryption { if (!result) { this._addMissingSessionEvent(event, isSync, retryData); } + if (result && isTimelineOpen) { + await this._verifyDecryptionResult(result, txn); + } return result; } + async _verifyDecryptionResult(result, txn) { + let device = this._senderDeviceCache.get(result.senderCurve25519Key); + if (!device) { + device = await this._deviceTracker.getDeviceByCurve25519Key(result.senderCurve25519Key, txn); + this._senderDeviceCache.set(result.senderCurve25519Key, device); + } + if (device) { + result.setDevice(device); + } else if (!this._room.isTrackingMembers) { + result.setRoomNotTrackedYet(); + } + } + _addMissingSessionEvent(event, isSync, data) { const senderKey = event.content?.["sender_key"]; const sessionId = event.content?.["session_id"]; diff --git a/src/matrix/room/Room.js b/src/matrix/room/Room.js index 5f7b0941..a26be995 100644 --- a/src/matrix/room/Room.js +++ b/src/matrix/room/Room.js @@ -57,6 +57,7 @@ export class Room extends EventEmitter { this._storage.storeNames.timelineEvents, this._storage.storeNames.inboundGroupSessions, this._storage.storeNames.groupSessionDecryptions, + this._storage.storeNames.deviceIdentities, ]); try { for (const retryEntry of retryEntries) { @@ -280,6 +281,7 @@ export class Room extends EventEmitter { stores = stores.concat([ this._storage.storeNames.inboundGroupSessions, this._storage.storeNames.groupSessionDecryptions, + this._storage.storeNames.deviceIdentities, ]); } const txn = await this._storage.readWriteTxn(stores); diff --git a/src/matrix/room/timeline/entries/EventEntry.js b/src/matrix/room/timeline/entries/EventEntry.js index def94b79..8c7029d4 100644 --- a/src/matrix/room/timeline/entries/EventEntry.js +++ b/src/matrix/room/timeline/entries/EventEntry.js @@ -78,6 +78,16 @@ export class EventEntry extends BaseEntry { this._decryptionResult = result; } + get isEncrypted() { + return this._eventEntry.event.type === "m.room.encrypted"; + } + + get isVerified() { + return this.isEncrypted && this._decryptionResult?.isVerified; + } + + get isUnverified() { + return this.isEncrypted && this._decryptionResult?.isUnverified; } setDecryptionError(err) { diff --git a/src/matrix/room/timeline/persistence/TimelineReader.js b/src/matrix/room/timeline/persistence/TimelineReader.js index 6b3ab23e..4446eaf1 100644 --- a/src/matrix/room/timeline/persistence/TimelineReader.js +++ b/src/matrix/room/timeline/persistence/TimelineReader.js @@ -38,6 +38,7 @@ export class TimelineReader { this._storage.storeNames.timelineFragments, this._storage.storeNames.inboundGroupSessions, this._storage.storeNames.groupSessionDecryptions, + this._storage.storeNames.deviceIdentities, ]); } else { diff --git a/src/matrix/storage/idb/schema.js b/src/matrix/storage/idb/schema.js index 63458916..f1817c24 100644 --- a/src/matrix/storage/idb/schema.js +++ b/src/matrix/storage/idb/schema.js @@ -14,6 +14,7 @@ export const schema = [ createInboundGroupSessionsStore, createOutboundGroupSessionsStore, createGroupSessionDecryptions, + addSenderKeyIndexToDeviceStore ]; // TODO: how to deal with git merge conflicts of this array? @@ -94,3 +95,9 @@ function createOutboundGroupSessionsStore(db) { function createGroupSessionDecryptions(db) { db.createObjectStore("groupSessionDecryptions", {keyPath: "key"}); } + +//v9 +function addSenderKeyIndexToDeviceStore(db, txn) { + const deviceIdentities = txn.objectStore("deviceIdentities"); + deviceIdentities.createIndex("byCurve25519Key", "curve25519Key", {unique: true}); +} diff --git a/src/matrix/storage/idb/stores/DeviceIdentityStore.js b/src/matrix/storage/idb/stores/DeviceIdentityStore.js index aec337fc..d1bf7eaa 100644 --- a/src/matrix/storage/idb/stores/DeviceIdentityStore.js +++ b/src/matrix/storage/idb/stores/DeviceIdentityStore.js @@ -38,4 +38,8 @@ export class DeviceIdentityStore { deviceIdentity.key = encodeKey(deviceIdentity.userId, deviceIdentity.deviceId); return this._store.put(deviceIdentity); } + + getByCurve25519Key(curve25519Key) { + return this._store.index("byCurve25519Key").get(curve25519Key); + } } diff --git a/src/ui/web/css/themes/element/theme.css b/src/ui/web/css/themes/element/theme.css index bf81bcd0..bdcd599f 100644 --- a/src/ui/web/css/themes/element/theme.css +++ b/src/ui/web/css/themes/element/theme.css @@ -373,6 +373,10 @@ ul.Timeline > li.continuation time { color: #ccc; } +.TextMessageView.unverified .message-container { + color: #ff4b55; +} + .message-container p { margin: 3px 0; line-height: 2.2rem; diff --git a/src/ui/web/session/room/timeline/common.js b/src/ui/web/session/room/timeline/common.js index 36ccb624..00751f4c 100644 --- a/src/ui/web/session/room/timeline/common.js +++ b/src/ui/web/session/room/timeline/common.js @@ -22,6 +22,7 @@ export function renderMessage(t, vm, children) { "TextMessageView": true, own: vm.isOwn, pending: vm.isPending, + unverified: vm.isUnverified, continuation: vm => vm.isContinuation, };