reattempt decryption for timeline items

This commit is contained in:
Bruno Windels 2021-03-01 22:30:33 +01:00
parent 061e7abd50
commit 8d080163b3
3 changed files with 53 additions and 5 deletions

View File

@ -400,6 +400,18 @@ export class RoomEncryption {
await hsApi.sendToDevice(type, payload, txnId, {log}).response(); await hsApi.sendToDevice(type, payload, txnId, {log}).response();
} }
filterEventEntriesForKeys(entries, keys) {
return entries.filter(entry => {
const {event} = entry;
if (event) {
const senderKey = event.content?.["sender_key"];
const sessionId = event.content?.["session_id"];
return keys.some(key => senderKey === key.senderKey && sessionId === key.sessionId);
}
return false;
});
}
dispose() { dispose() {
this._disposed = true; this._disposed = true;
this._megolmBackfillCache.dispose(); this._megolmBackfillCache.dispose();

View File

@ -189,10 +189,22 @@ export class Room extends EventEmitter {
roomEncryption = this._createRoomEncryption(this, summaryChanges.encryption); roomEncryption = this._createRoomEncryption(this, summaryChanges.encryption);
} }
let retryEntries;
let decryptPreparation; let decryptPreparation;
if (roomEncryption) { if (roomEncryption) {
// also look for events in timeline here // also look for events in timeline here
let events = roomResponse?.timeline?.events || []; let events = roomResponse?.timeline?.events || [];
// when new keys arrive, also see if any events currently loaded in the timeline
// can now be retried to decrypt
if (this._timeline && newKeys) {
retryEntries = roomEncryption.filterEventEntriesForKeys(
this._timeline.remoteEntries, newKeys);
if (retryEntries.length) {
log.set("retry", retryEntries.length);
events = events.concat(retryEntries.map(entry => entry.event));
}
}
if (events.length) { if (events.length) {
const eventsToDecrypt = events.filter(event => { const eventsToDecrypt = events.filter(event => {
return event?.type === EVENT_ENCRYPTED_TYPE; return event?.type === EVENT_ENCRYPTED_TYPE;
@ -207,6 +219,7 @@ export class Room extends EventEmitter {
summaryChanges, summaryChanges,
decryptPreparation, decryptPreparation,
decryptChanges: null, decryptChanges: null,
retryEntries
}; };
} }
@ -221,13 +234,24 @@ export class Room extends EventEmitter {
} }
/** @package */ /** @package */
async writeSync(roomResponse, isInitialSync, {summaryChanges, decryptChanges, roomEncryption}, txn, log) { async writeSync(roomResponse, isInitialSync, {summaryChanges, decryptChanges, roomEncryption, retryEntries}, txn, log) {
log.set("id", this.id); log.set("id", this.id);
const {entries, newLiveKey, memberChanges} = const {entries, newLiveKey, memberChanges} =
await log.wrap("syncWriter", log => this._syncWriter.writeSync(roomResponse, txn, log), log.level.Detail); await log.wrap("syncWriter", log => this._syncWriter.writeSync(roomResponse, txn, log), log.level.Detail);
if (decryptChanges) { if (decryptChanges) {
const decryption = await decryptChanges.write(txn); const decryption = await decryptChanges.write(txn);
decryption.applyToEntries(entries); decryption.applyToEntries(entries);
// now prepend updated copies of retryEntries
// so the originals don't get modified by the decryption
// (not needed for entries as they were just created by syncWriter)
if (retryEntries?.length) {
// TODO: this will modify existing timeline entries (which we should not do in writeSync),
// but it is a temporary way of reattempting decryption while timeline is open
// won't need copies when tracking missing sessions properly
const updatedEntries = decryption.applyToEntries(retryEntries);
// prepend the retried entries, as we know they are older (not that it should matter much for the summary)
entries.unshift(...updatedEntries);
}
} }
// pass member changes to device tracker // pass member changes to device tracker
if (roomEncryption && this.isTrackingMembers && memberChanges?.size) { if (roomEncryption && this.isTrackingMembers && memberChanges?.size) {
@ -255,7 +279,7 @@ export class Room extends EventEmitter {
return { return {
summaryChanges, summaryChanges,
roomEncryption, roomEncryption,
newTimelineEntries: entries, newAndUpdatedEntries: entries,
newLiveKey, newLiveKey,
removedPendingEvents, removedPendingEvents,
memberChanges, memberChanges,
@ -268,7 +292,7 @@ export class Room extends EventEmitter {
* Called with the changes returned from `writeSync` to apply them and emit changes. * Called with the changes returned from `writeSync` to apply them and emit changes.
* No storage or network operations should be done here. * No storage or network operations should be done here.
*/ */
afterSync({summaryChanges, newTimelineEntries, newLiveKey, removedPendingEvents, memberChanges, heroChanges, roomEncryption}, log) { afterSync({summaryChanges, newAndUpdatedEntries, newLiveKey, removedPendingEvents, memberChanges, heroChanges, roomEncryption}, log) {
log.set("id", this.id); log.set("id", this.id);
this._syncWriter.afterSync(newLiveKey); this._syncWriter.afterSync(newLiveKey);
this._setEncryption(roomEncryption); this._setEncryption(roomEncryption);
@ -301,10 +325,10 @@ export class Room extends EventEmitter {
this._emitUpdate(); this._emitUpdate();
} }
if (this._timeline) { if (this._timeline) {
this._timeline.appendLiveEntries(newTimelineEntries); this._timeline.appendLiveEntries(newAndUpdatedEntries);
} }
if (this._observedEvents) { if (this._observedEvents) {
this._observedEvents.updateEvents(newTimelineEntries); this._observedEvents.updateEvents(newAndUpdatedEntries);
} }
if (removedPendingEvents) { if (removedPendingEvents) {
this._sendQueue.emitRemovals(removedPendingEvents); this._sendQueue.emitRemovals(removedPendingEvents);
@ -522,6 +546,10 @@ export class Room extends EventEmitter {
return !!this._summary.data.encryption; return !!this._summary.data.encryption;
} }
get membership() {
return this._summary.data.membership;
}
enableSessionBackup(sessionBackup) { enableSessionBackup(sessionBackup) {
this._roomEncryption?.enableSessionBackup(sessionBackup); this._roomEncryption?.enableSessionBackup(sessionBackup);
} }

View File

@ -116,6 +116,14 @@ export class Timeline {
return this._allEntries; return this._allEntries;
} }
/**
* @internal
* @return {Array<EventEntry>} remote event entries, should not be modified
*/
get remoteEntries() {
return this._remoteEntries.array;
}
/** @public */ /** @public */
dispose() { dispose() {
if (this._closeCallback) { if (this._closeCallback) {