convert megolm/Decryption to typescript and adapt to KeyLoader
This commit is contained in:
parent
2ddb3fbf72
commit
d6e243321b
1 changed files with 41 additions and 56 deletions
|
@ -15,23 +15,26 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {DecryptionError} from "../common.js";
|
import {DecryptionError} from "../common.js";
|
||||||
import * as RoomKey from "./decryption/RoomKey";
|
|
||||||
import {SessionInfo} from "./decryption/SessionInfo.js";
|
|
||||||
import {DecryptionPreparation} from "./decryption/DecryptionPreparation.js";
|
import {DecryptionPreparation} from "./decryption/DecryptionPreparation.js";
|
||||||
import {SessionDecryption} from "./decryption/SessionDecryption.js";
|
import {SessionDecryption} from "./decryption/SessionDecryption";
|
||||||
import {SessionCache} from "./decryption/SessionCache.js";
|
|
||||||
import {MEGOLM_ALGORITHM} from "../common.js";
|
import {MEGOLM_ALGORITHM} from "../common.js";
|
||||||
import {validateEvent, groupEventsBySession} from "./decryption/utils.js";
|
import {validateEvent, groupEventsBySession} from "./decryption/utils";
|
||||||
|
import {keyFromStorage, keyFromDeviceMessage, keyFromBackup} from "./decryption/RoomKey";
|
||||||
|
import type {IRoomKey, IIncomingRoomKey} from "./decryption/RoomKey";
|
||||||
|
import type {KeyLoader} from "./decryption/KeyLoader";
|
||||||
|
import type {OlmWorker} from "../OlmWorker";
|
||||||
|
import type {Transaction} from "../../storage/idb/Transaction";
|
||||||
|
import type {TimelineEvent} from "../../storage/types";
|
||||||
|
import type {DecryptionResult} from "../DecryptionResult";
|
||||||
|
import type {LogItem} from "../../../logging/LogItem";
|
||||||
|
|
||||||
export class Decryption {
|
export class Decryption {
|
||||||
constructor({pickleKey, olm, olmWorker}) {
|
private keyLoader: KeyLoader;
|
||||||
this._pickleKey = pickleKey;
|
private olmWorker?: OlmWorker;
|
||||||
this._olm = olm;
|
|
||||||
this._olmWorker = olmWorker;
|
|
||||||
}
|
|
||||||
|
|
||||||
createSessionCache(size) {
|
constructor(keyLoader: KeyLoader, olmWorker: OlmWorker | undefined) {
|
||||||
return new SessionCache(size);
|
this.keyLoader = keyLoader;
|
||||||
|
this.olmWorker = olmWorker;
|
||||||
}
|
}
|
||||||
|
|
||||||
async addMissingKeyEventIds(roomId, senderKey, sessionId, eventIds, txn) {
|
async addMissingKeyEventIds(roomId, senderKey, sessionId, eventIds, txn) {
|
||||||
|
@ -75,9 +78,9 @@ export class Decryption {
|
||||||
* @param {[type]} txn [description]
|
* @param {[type]} txn [description]
|
||||||
* @return {DecryptionPreparation}
|
* @return {DecryptionPreparation}
|
||||||
*/
|
*/
|
||||||
async prepareDecryptAll(roomId, events, newKeys, sessionCache, txn) {
|
async prepareDecryptAll(roomId: string, events: TimelineEvent[], newKeys: IIncomingRoomKey[] | undefined, txn: Transaction) {
|
||||||
const errors = new Map();
|
const errors = new Map();
|
||||||
const validEvents = [];
|
const validEvents: TimelineEvent[] = [];
|
||||||
|
|
||||||
for (const event of events) {
|
for (const event of events) {
|
||||||
if (validateEvent(event)) {
|
if (validateEvent(event)) {
|
||||||
|
@ -89,11 +92,11 @@ export class Decryption {
|
||||||
|
|
||||||
const eventsBySession = groupEventsBySession(validEvents);
|
const eventsBySession = groupEventsBySession(validEvents);
|
||||||
|
|
||||||
const sessionDecryptions = [];
|
const sessionDecryptions: SessionDecryption[] = [];
|
||||||
await Promise.all(Array.from(eventsBySession.values()).map(async group => {
|
await Promise.all(Array.from(eventsBySession.values()).map(async group => {
|
||||||
const sessionInfo = await this._getSessionInfo(roomId, group.senderKey, group.sessionId, newKeys, sessionCache, txn);
|
const key = await this.getRoomKey(roomId, group.senderKey!, group.sessionId!, newKeys, txn);
|
||||||
if (sessionInfo) {
|
if (key) {
|
||||||
sessionDecryptions.push(new SessionDecryption(sessionInfo, group.events, this._olmWorker));
|
sessionDecryptions.push(new SessionDecryption(key, group.events, this.olmWorker, this.keyLoader));
|
||||||
} else {
|
} else {
|
||||||
for (const event of group.events) {
|
for (const event of group.events) {
|
||||||
errors.set(event.event_id, new DecryptionError("MEGOLM_NO_SESSION", event));
|
errors.set(event.event_id, new DecryptionError("MEGOLM_NO_SESSION", event));
|
||||||
|
@ -104,64 +107,43 @@ export class Decryption {
|
||||||
return new DecryptionPreparation(roomId, sessionDecryptions, errors);
|
return new DecryptionPreparation(roomId, sessionDecryptions, errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _getSessionInfo(roomId, senderKey, sessionId, newKeys, sessionCache, txn) {
|
private async getRoomKey(roomId: string, senderKey: string, sessionId: string, newKeys: IIncomingRoomKey[] | undefined, txn: Transaction): Promise<IRoomKey | undefined> {
|
||||||
let sessionInfo;
|
|
||||||
if (newKeys) {
|
if (newKeys) {
|
||||||
const key = newKeys.find(k => k.roomId === roomId && k.senderKey === senderKey && k.sessionId === sessionId);
|
const key = newKeys.find(k => k.roomId === roomId && k.senderKey === senderKey && k.sessionId === sessionId);
|
||||||
if (key) {
|
if (key && await key.checkBetterThanKeyInStorage(this.keyLoader, txn)) {
|
||||||
sessionInfo = await key.createSessionInfo(this._olm, this._pickleKey, txn);
|
return key;
|
||||||
if (sessionInfo) {
|
|
||||||
sessionCache.add(sessionInfo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// look only in the cache after looking into newKeys as it may contains that are better
|
// look only in the cache after looking into newKeys as it may contains that are better
|
||||||
if (!sessionInfo) {
|
const cachedKey = this.keyLoader.getCachedKey(roomId, senderKey, sessionId);
|
||||||
sessionInfo = sessionCache.get(roomId, senderKey, sessionId);
|
if (cachedKey) {
|
||||||
// TODO: shouldn't we retain here?
|
return cachedKey;
|
||||||
}
|
}
|
||||||
if (!sessionInfo) {
|
const storageKey = await keyFromStorage(roomId, senderKey, sessionId, txn);
|
||||||
const sessionEntry = await txn.inboundGroupSessions.get(roomId, senderKey, sessionId);
|
if (storageKey && storageKey.serializationKey) {
|
||||||
if (sessionEntry && sessionEntry.session) {
|
return storageKey;
|
||||||
let session = new this._olm.InboundGroupSession();
|
|
||||||
try {
|
|
||||||
session.unpickle(this._pickleKey, sessionEntry.session);
|
|
||||||
sessionInfo = new SessionInfo(roomId, senderKey, session, sessionEntry.claimedKeys);
|
|
||||||
} catch (err) {
|
|
||||||
session.free();
|
|
||||||
throw err;
|
|
||||||
}
|
}
|
||||||
sessionCache.add(sessionInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sessionInfo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the key as an inbound group session if there is not already a better key in the store
|
* Writes the key as an inbound group session if there is not already a better key in the store
|
||||||
* @param {RoomKey} key
|
|
||||||
* @param {Transaction} txn a storage transaction with read/write on inboundGroupSessions
|
|
||||||
* @return {Promise<boolean>} whether the key was the best for the sessio id and was written
|
|
||||||
*/
|
*/
|
||||||
writeRoomKey(key, txn) {
|
writeRoomKey(key: IIncomingRoomKey, txn: Transaction): Promise<boolean> {
|
||||||
return key.write(this._olm, this._pickleKey, txn);
|
return key.write(this.keyLoader, txn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts room keys from decrypted device messages.
|
* Extracts room keys from decrypted device messages.
|
||||||
* The key won't be persisted yet, you need to call RoomKey.write for that.
|
* The key won't be persisted yet, you need to call RoomKey.write for that.
|
||||||
*
|
|
||||||
* @param {Array<OlmDecryptionResult>} decryptionResults, any non megolm m.room_key messages will be ignored.
|
|
||||||
* @return {Array<RoomKey>} an array with validated RoomKey's. Note that it is possible we already have a better version of this key in storage though; writing the key will tell you so.
|
|
||||||
*/
|
*/
|
||||||
roomKeysFromDeviceMessages(decryptionResults, log) {
|
roomKeysFromDeviceMessages(decryptionResults: DecryptionResult[], log: LogItem): IIncomingRoomKey[] {
|
||||||
let keys = [];
|
let keys: IIncomingRoomKey[] = [];
|
||||||
for (const dr of decryptionResults) {
|
for (const dr of decryptionResults) {
|
||||||
if (dr.event?.type !== "m.room_key" || dr.event.content?.algorithm !== MEGOLM_ALGORITHM) {
|
if (dr.event?.type !== "m.room_key" || dr.event.content?.algorithm !== MEGOLM_ALGORITHM) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
log.wrap("room_key", log => {
|
log.wrap("room_key", log => {
|
||||||
const key = RoomKey.fromDeviceMessage(dr);
|
const key = keyFromDeviceMessage(dr);
|
||||||
if (key) {
|
if (key) {
|
||||||
log.set("roomId", key.roomId);
|
log.set("roomId", key.roomId);
|
||||||
log.set("id", key.sessionId);
|
log.set("id", key.sessionId);
|
||||||
|
@ -175,8 +157,11 @@ export class Decryption {
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
roomKeyFromBackup(roomId, sessionId, sessionInfo) {
|
roomKeyFromBackup(roomId: string, sessionId: string, sessionInfo: string): IIncomingRoomKey | undefined {
|
||||||
return RoomKey.fromBackup(roomId, sessionId, sessionInfo);
|
return keyFromBackup(roomId, sessionId, sessionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this.keyLoader.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue