Merge pull request #275 from vector-im/bwindels/retryeventidsforkeybackup

Extract retry event ids for key before overwriting key on key backup
This commit is contained in:
Bruno Windels 2021-03-15 12:42:55 +00:00 committed by GitHub
commit a9027e1fee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 34 additions and 20 deletions

View file

@ -210,11 +210,14 @@ export class RoomEncryption {
} }
let roomKey = this._megolmDecryption.roomKeyFromBackup(this._room.id, sessionId, session); let roomKey = this._megolmDecryption.roomKeyFromBackup(this._room.id, sessionId, session);
if (roomKey) { if (roomKey) {
let keyIsBestOne = false; let retryEventIds;
try { try {
const txn = await this._storage.readWriteTxn([this._storage.storeNames.inboundGroupSessions]); const txn = await this._storage.readWriteTxn([this._storage.storeNames.inboundGroupSessions]);
try { try {
keyIsBestOne = await this._megolmDecryption.writeRoomKey(roomKey, txn); const keyIsBestOne = await this._megolmDecryption.writeRoomKey(roomKey, txn);
if (keyIsBestOne) {
retryEventIds = roomKey.eventIds;
}
} catch (err) { } catch (err) {
txn.abort(); txn.abort();
throw err; throw err;
@ -225,9 +228,8 @@ export class RoomEncryption {
// this is just clearing the internal sessionInfo // this is just clearing the internal sessionInfo
roomKey.dispose(); roomKey.dispose();
} }
if (keyIsBestOne) { if (retryEventIds?.length) {
// wrote the key, meaning we didn't have a better one, go ahead and reattempt decryption await this._room.retryDecryption(retryEventIds);
await this._room.notifyRoomKey(roomKey);
} }
} }
} else if (session?.algorithm) { } else if (session?.algorithm) {

View file

@ -4,6 +4,7 @@ export class BaseRoomKey {
constructor() { constructor() {
this._sessionInfo = null; this._sessionInfo = null;
this._isBetter = null; this._isBetter = null;
this._eventIds = null;
} }
async createSessionInfo(olm, pickleKey, txn) { async createSessionInfo(olm, pickleKey, txn) {
@ -44,6 +45,11 @@ export class BaseRoomKey {
existingSession.free(); existingSession.free();
} }
} }
// store the event ids that can be decrypted with this key
// before we overwrite them if called from `write`.
if (existingSessionEntry?.eventIds) {
this._eventIds = existingSessionEntry.eventIds;
}
return isBetter; return isBetter;
} }
@ -71,6 +77,10 @@ export class BaseRoomKey {
return false; return false;
} }
get eventIds() {
return this._eventIds;
}
dispose() { dispose() {
if (this._sessionInfo) { if (this._sessionInfo) {
this._sessionInfo.release(); this._sessionInfo.release();

View file

@ -55,27 +55,24 @@ export class Room extends EventEmitter {
this._observedEvents = null; this._observedEvents = null;
} }
async _getRetryDecryptEntriesForKey(roomKey, roomEncryption, txn) { async _eventIdsToEntries(eventIds, txn) {
const retryEventIds = await roomEncryption.getEventIdsForMissingKey(roomKey, txn);
const retryEntries = []; const retryEntries = [];
if (retryEventIds) { await Promise.all(eventIds.map(async eventId => {
for (const eventId of retryEventIds) {
const storageEntry = await txn.timelineEvents.getByEventId(this._roomId, eventId); const storageEntry = await txn.timelineEvents.getByEventId(this._roomId, eventId);
if (storageEntry) { if (storageEntry) {
retryEntries.push(new EventEntry(storageEntry, this._fragmentIdComparer)); retryEntries.push(new EventEntry(storageEntry, this._fragmentIdComparer));
} }
} }));
}
return retryEntries; return retryEntries;
} }
/** /**
* Used for keys received from other sources than sync, like key backup. * Used for retrying decryption from other sources than sync, like key backup.
* @internal * @internal
* @param {RoomKey} roomKey * @param {Array<string>} eventIds
* @return {Promise} * @return {Promise}
*/ */
async notifyRoomKey(roomKey) { async retryDecryption(eventIds) {
if (!this._roomEncryption) { if (!this._roomEncryption) {
return; return;
} }
@ -83,7 +80,7 @@ export class Room extends EventEmitter {
this._storage.storeNames.timelineEvents, this._storage.storeNames.timelineEvents,
this._storage.storeNames.inboundGroupSessions, this._storage.storeNames.inboundGroupSessions,
]); ]);
const retryEntries = await this._getRetryDecryptEntriesForKey(roomKey, this._roomEncryption, txn); const retryEntries = await this._eventIdsToEntries(eventIds, txn);
if (retryEntries.length) { if (retryEntries.length) {
const decryptRequest = this._decryptEntries(DecryptionSource.Retry, retryEntries, txn); const decryptRequest = this._decryptEntries(DecryptionSource.Retry, retryEntries, txn);
// this will close txn while awaiting decryption // this will close txn while awaiting decryption
@ -157,8 +154,13 @@ export class Room extends EventEmitter {
} }
async _getSyncRetryDecryptEntries(newKeys, roomEncryption, txn) { async _getSyncRetryDecryptEntries(newKeys, roomEncryption, txn) {
const entriesPerKey = await Promise.all(newKeys.map(key => this._getRetryDecryptEntriesForKey(key, roomEncryption, txn))); const entriesPerKey = await Promise.all(newKeys.map(async key => {
let retryEntries = entriesPerKey.reduce((allEntries, entries) => allEntries.concat(entries), []); const retryEventIds = await roomEncryption.getEventIdsForMissingKey(key, txn);
if (retryEventIds) {
return this._eventIdsToEntries(retryEventIds, txn);
}
}));
let retryEntries = entriesPerKey.reduce((allEntries, entries) => entries ? allEntries.concat(entries) : allEntries, []);
// If we have the timeline open, see if there are more entries for the new keys // If we have the timeline open, see if there are more entries for the new keys
// as we only store missing session information for synced events, not backfilled. // as we only store missing session information for synced events, not backfilled.
// We want to decrypt all events we can though if the user is looking // We want to decrypt all events we can though if the user is looking