1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-12-07 05:22:15 +03:00

Begin factoring out a CryptoBackend interface (#2955)

Part of https://github.com/vector-im/element-web/issues/21972. Eventually I want to replace the whole of the current `Crypto` implementation with an alternative implementation, but in order to get from here to there, I'm factoring out a common interface which will be implemented by both implementations.

I'm also determined to fix the problem where the innards of the crypto implementation are exposed to applications via the `MatrixClient.crypto` property.

It's not (yet) entirely clear what shape this interface should be, so I'm going with a minimal approach and adding things as we know we need them. This means that we need to keep the old `client.crypto` property around as well as a new `client.cryptoBackend` property. Eventually `client.crypto` will go away, but that will be a breaking change in the js-sdk.
This commit is contained in:
Richard van der Hoff
2022-12-12 17:49:39 +00:00
committed by GitHub
parent 8293011ee2
commit 9c17eb6c14
11 changed files with 255 additions and 56 deletions

View File

@@ -650,6 +650,131 @@ describe("megolm", () => {
]); ]);
}); });
describe("get|setGlobalErrorOnUnknownDevices", () => {
it("should raise an error if crypto is disabled", () => {
aliceTestClient.client["cryptoBackend"] = undefined;
expect(() => aliceTestClient.client.setGlobalErrorOnUnknownDevices(true)).toThrowError(
"encryption disabled",
);
expect(() => aliceTestClient.client.getGlobalErrorOnUnknownDevices()).toThrowError("encryption disabled");
});
it("should permit sending to unknown devices", async () => {
expect(aliceTestClient.client.getGlobalErrorOnUnknownDevices()).toBeTruthy();
aliceTestClient.expectKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} });
await aliceTestClient.start();
const p2pSession = await establishOlmSession(aliceTestClient, testOlmAccount);
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, getSyncResponse(["@bob:xyz"]));
await aliceTestClient.flushSync();
// start out with the device unknown - the send should be rejected.
aliceTestClient.httpBackend.when("POST", "/keys/query").respond(200, getTestKeysQueryResponse("@bob:xyz"));
aliceTestClient.httpBackend.when("POST", "/keys/query").respond(200, getTestKeysQueryResponse("@bob:xyz"));
await Promise.all([
aliceTestClient.client.sendTextMessage(ROOM_ID, "test").then(
() => {
throw new Error("sendTextMessage failed on an unknown device");
},
(e) => {
expect(e.name).toEqual("UnknownDeviceError");
},
),
aliceTestClient.httpBackend.flushAllExpected(),
]);
// enable sending to unknown devices, and resend
aliceTestClient.client.setGlobalErrorOnUnknownDevices(false);
expect(aliceTestClient.client.getGlobalErrorOnUnknownDevices()).toBeFalsy();
const room = aliceTestClient.client.getRoom(ROOM_ID)!;
const pendingMsg = room.getPendingEvents()[0];
const inboundGroupSessionPromise = expectSendRoomKey(
aliceTestClient.httpBackend,
"@bob:xyz",
testOlmAccount,
p2pSession,
);
await Promise.all([
aliceTestClient.client.resendEvent(pendingMsg, room),
expectSendMegolmMessage(aliceTestClient.httpBackend, inboundGroupSessionPromise),
]);
});
});
describe("get|setGlobalBlacklistUnverifiedDevices", () => {
it("should raise an error if crypto is disabled", () => {
aliceTestClient.client["cryptoBackend"] = undefined;
expect(() => aliceTestClient.client.setGlobalBlacklistUnverifiedDevices(true)).toThrowError(
"encryption disabled",
);
expect(() => aliceTestClient.client.getGlobalBlacklistUnverifiedDevices()).toThrowError(
"encryption disabled",
);
});
it("should disable sending to unverified devices", async () => {
aliceTestClient.expectKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} });
await aliceTestClient.start();
const p2pSession = await establishOlmSession(aliceTestClient, testOlmAccount);
// tell alice we share a room with bob
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, getSyncResponse(["@bob:xyz"]));
await aliceTestClient.flushSync();
logger.log("Forcing alice to download our device keys");
aliceTestClient.httpBackend.when("POST", "/keys/query").respond(200, getTestKeysQueryResponse("@bob:xyz"));
aliceTestClient.httpBackend.when("POST", "/keys/query").respond(200, getTestKeysQueryResponse("@bob:xyz"));
await Promise.all([
aliceTestClient.client.downloadKeys(["@bob:xyz"]),
aliceTestClient.httpBackend.flush("/keys/query", 2),
]);
logger.log("Telling alice to block messages to unverified devices");
expect(aliceTestClient.client.getGlobalBlacklistUnverifiedDevices()).toBeFalsy();
aliceTestClient.client.setGlobalBlacklistUnverifiedDevices(true);
expect(aliceTestClient.client.getGlobalBlacklistUnverifiedDevices()).toBeTruthy();
logger.log("Telling alice to send a megolm message");
aliceTestClient.httpBackend.when("PUT", "/send/").respond(200, { event_id: "$event_id" });
aliceTestClient.httpBackend.when("PUT", "/sendToDevice/m.room_key.withheld/").respond(200, {});
await Promise.all([
aliceTestClient.client.sendTextMessage(ROOM_ID, "test"),
aliceTestClient.httpBackend.flushAllExpected({ timeout: 1000 }),
]);
// Now, let's mark the device as verified, and check that keys are sent to it.
logger.log("Marking the device as verified");
// XXX: this is an integration test; we really ought to do this via the cross-signing dance
const d = aliceTestClient.client.crypto!.deviceList.getStoredDevice("@bob:xyz", "DEVICE_ID")!;
d.verified = DeviceInfo.DeviceVerification.VERIFIED;
aliceTestClient.client.crypto?.deviceList.storeDevicesForUser("@bob:xyz", { DEVICE_ID: d });
const inboundGroupSessionPromise = expectSendRoomKey(
aliceTestClient.httpBackend,
"@bob:xyz",
testOlmAccount,
p2pSession,
);
logger.log("Asking alice to re-send");
await Promise.all([
expectSendMegolmMessage(aliceTestClient.httpBackend, inboundGroupSessionPromise).then((decrypted) => {
expect(decrypted.type).toEqual("m.room.message");
expect(decrypted.content!.body).toEqual("test");
}),
aliceTestClient.client.sendTextMessage(ROOM_ID, "test"),
]);
});
});
it("We should start a new megolm session when a device is blocked", async () => { it("We should start a new megolm session when a device is blocked", async () => {
aliceTestClient.expectKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); aliceTestClient.expectKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} });
await aliceTestClient.start(); await aliceTestClient.start();

View File

@@ -1147,7 +1147,7 @@ describe("userHasCrossSigningKeys", function () {
}); });
it("throws an error if crypto is disabled", () => { it("throws an error if crypto is disabled", () => {
aliceClient.crypto = undefined; aliceClient["cryptoBackend"] = undefined;
expect(() => aliceClient.userHasCrossSigningKeys()).toThrowError("encryption disabled"); expect(() => aliceClient.userHasCrossSigningKeys()).toThrowError("encryption disabled");
}); });
}); });

View File

@@ -3131,7 +3131,7 @@ describe("Room", function () {
it("should load pending events from from the store and decrypt if needed", async () => { it("should load pending events from from the store and decrypt if needed", async () => {
const client = new TestClient(userA).client; const client = new TestClient(userA).client;
client.crypto = { client.crypto = client["cryptoBackend"] = {
decryptEvent: jest.fn().mockResolvedValue({ clearEvent: { body: "enc" } }), decryptEvent: jest.fn().mockResolvedValue({ clearEvent: { body: "enc" } }),
} as unknown as Crypto; } as unknown as Crypto;
client.store.getPendingEvents = jest.fn(async (roomId) => [ client.store.getPendingEvents = jest.fn(async (roomId) => [

View File

@@ -14,7 +14,33 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import type { IClearEvent } from "../models/event";
export type OlmGroupSessionExtraData = { export type OlmGroupSessionExtraData = {
untrusted?: boolean; untrusted?: boolean;
sharedHistory?: boolean; sharedHistory?: boolean;
}; };
/**
* The result of a (successful) call to {@link Crypto.decryptEvent}
*/
export interface IEventDecryptionResult {
/**
* The plaintext payload for the event (typically containing <tt>type</tt> and <tt>content</tt> fields).
*/
clearEvent: IClearEvent;
/**
* List of curve25519 keys involved in telling us about the senderCurve25519Key and claimedEd25519Key.
* See {@link MatrixEvent#getForwardingCurve25519KeyChain}.
*/
forwardingCurve25519KeyChain?: string[];
/**
* Key owned by the sender of this event. See {@link MatrixEvent#getSenderKey}.
*/
senderCurve25519Key?: string;
/**
* ed25519 key claimed by the sender of this event. See {@link MatrixEvent#getClaimedEd25519Key}.
*/
claimedEd25519Key?: string;
untrusted?: boolean;
}

View File

@@ -210,6 +210,7 @@ import { UIARequest, UIAResponse } from "./@types/uia";
import { LocalNotificationSettings } from "./@types/local_notifications"; import { LocalNotificationSettings } from "./@types/local_notifications";
import { UNREAD_THREAD_NOTIFICATIONS } from "./@types/sync"; import { UNREAD_THREAD_NOTIFICATIONS } from "./@types/sync";
import { buildFeatureSupportMap, Feature, ServerSupport } from "./feature"; import { buildFeatureSupportMap, Feature, ServerSupport } from "./feature";
import { CryptoBackend } from "./common-crypto/CryptoBackend";
export type Store = IStore; export type Store = IStore;
@@ -1147,7 +1148,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
public urlPreviewCache: { [key: string]: Promise<IPreviewUrlResponse> } = {}; public urlPreviewCache: { [key: string]: Promise<IPreviewUrlResponse> } = {};
public identityServer?: IIdentityServerProvider; public identityServer?: IIdentityServerProvider;
public http: MatrixHttpApi<IHttpOpts & { onlyData: true }>; // XXX: Intended private, used in code. public http: MatrixHttpApi<IHttpOpts & { onlyData: true }>; // XXX: Intended private, used in code.
public crypto?: Crypto; // XXX: Intended private, used in code. public crypto?: Crypto; // libolm crypto implementation. XXX: Intended private, used in code. Being replaced by cryptoBackend
private cryptoBackend?: CryptoBackend; // one of crypto or rustCrypto
public cryptoCallbacks: ICryptoCallbacks; // XXX: Intended private, used in code. public cryptoCallbacks: ICryptoCallbacks; // XXX: Intended private, used in code.
public callEventHandler?: CallEventHandler; // XXX: Intended private, used in code. public callEventHandler?: CallEventHandler; // XXX: Intended private, used in code.
public groupCallEventHandler?: GroupCallEventHandler; public groupCallEventHandler?: GroupCallEventHandler;
@@ -1455,7 +1457,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* clean shutdown. * clean shutdown.
*/ */
public stopClient(): void { public stopClient(): void {
this.crypto?.stop(); // crypto might have been initialised even if the client wasn't fully started this.cryptoBackend?.stop(); // crypto might have been initialised even if the client wasn't fully started
if (!this.clientRunning) return; // already stopped if (!this.clientRunning) return; // already stopped
@@ -1959,7 +1961,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
} }
/** /**
* Initialise support for end-to-end encryption in this client * Initialise support for end-to-end encryption in this client, using libolm.
* *
* You should call this method after creating the matrixclient, but *before* * You should call this method after creating the matrixclient, but *before*
* calling `startClient`, if you want to support end-to-end encryption. * calling `startClient`, if you want to support end-to-end encryption.
@@ -1975,7 +1977,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
); );
} }
if (this.crypto) { if (this.cryptoBackend) {
logger.warn("Attempt to re-initialise e2e encryption on MatrixClient"); logger.warn("Attempt to re-initialise e2e encryption on MatrixClient");
return; return;
} }
@@ -2040,7 +2042,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
// if crypto initialisation was successful, tell it to attach its event handlers. // if crypto initialisation was successful, tell it to attach its event handlers.
crypto.registerEventHandlers(this as Parameters<Crypto["registerEventHandlers"]>[0]); crypto.registerEventHandlers(this as Parameters<Crypto["registerEventHandlers"]>[0]);
this.crypto = crypto; this.cryptoBackend = this.crypto = crypto;
// upload our keys in the background // upload our keys in the background
this.crypto.uploadDeviceKeys().catch((e) => { this.crypto.uploadDeviceKeys().catch((e) => {
@@ -2054,7 +2056,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @returns True if end-to-end is enabled. * @returns True if end-to-end is enabled.
*/ */
public isCryptoEnabled(): boolean { public isCryptoEnabled(): boolean {
return !!this.crypto; return !!this.cryptoBackend;
} }
/** /**
@@ -2299,10 +2301,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param value - whether to blacklist all unverified devices by default * @param value - whether to blacklist all unverified devices by default
*/ */
public setGlobalBlacklistUnverifiedDevices(value: boolean): boolean { public setGlobalBlacklistUnverifiedDevices(value: boolean): boolean {
if (!this.crypto) { if (!this.cryptoBackend) {
throw new Error("End-to-end encryption disabled"); throw new Error("End-to-end encryption disabled");
} }
this.crypto.globalBlacklistUnverifiedDevices = value; this.cryptoBackend.globalBlacklistUnverifiedDevices = value;
return value; return value;
} }
@@ -2310,10 +2312,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @returns whether to blacklist all unverified devices by default * @returns whether to blacklist all unverified devices by default
*/ */
public getGlobalBlacklistUnverifiedDevices(): boolean { public getGlobalBlacklistUnverifiedDevices(): boolean {
if (!this.crypto) { if (!this.cryptoBackend) {
throw new Error("End-to-end encryption disabled"); throw new Error("End-to-end encryption disabled");
} }
return this.crypto.globalBlacklistUnverifiedDevices; return this.cryptoBackend.globalBlacklistUnverifiedDevices;
} }
/** /**
@@ -2327,10 +2329,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* @param value - whether error on unknown devices * @param value - whether error on unknown devices
*/ */
public setGlobalErrorOnUnknownDevices(value: boolean): void { public setGlobalErrorOnUnknownDevices(value: boolean): void {
if (!this.crypto) { if (!this.cryptoBackend) {
throw new Error("End-to-end encryption disabled"); throw new Error("End-to-end encryption disabled");
} }
this.crypto.globalErrorOnUnknownDevices = value; this.cryptoBackend.globalErrorOnUnknownDevices = value;
} }
/** /**
@@ -2339,10 +2341,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* This API is currently UNSTABLE and may change or be removed without notice. * This API is currently UNSTABLE and may change or be removed without notice.
*/ */
public getGlobalErrorOnUnknownDevices(): boolean { public getGlobalErrorOnUnknownDevices(): boolean {
if (!this.crypto) { if (!this.cryptoBackend) {
throw new Error("End-to-end encryption disabled"); throw new Error("End-to-end encryption disabled");
} }
return this.crypto.globalErrorOnUnknownDevices; return this.cryptoBackend.globalErrorOnUnknownDevices;
} }
/** /**
@@ -2482,10 +2484,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* the cross-signing pseudo-device. * the cross-signing pseudo-device.
*/ */
public userHasCrossSigningKeys(): Promise<boolean> { public userHasCrossSigningKeys(): Promise<boolean> {
if (!this.crypto) { if (!this.cryptoBackend) {
throw new Error("End-to-end encryption disabled"); throw new Error("End-to-end encryption disabled");
} }
return this.crypto.userHasCrossSigningKeys(); return this.cryptoBackend.userHasCrossSigningKeys();
} }
/** /**
@@ -7162,7 +7164,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
*/ */
public decryptEventIfNeeded(event: MatrixEvent, options?: IDecryptOptions): Promise<void> { public decryptEventIfNeeded(event: MatrixEvent, options?: IDecryptOptions): Promise<void> {
if (event.shouldAttemptDecryption() && this.isCryptoEnabled()) { if (event.shouldAttemptDecryption() && this.isCryptoEnabled()) {
event.attemptDecryption(this.crypto!, options); event.attemptDecryption(this.cryptoBackend!, options);
} }
if (event.isBeingDecrypted()) { if (event.isBeingDecrypted()) {

View File

@@ -0,0 +1,63 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import type { IEventDecryptionResult } from "../@types/crypto";
import { MatrixEvent } from "../models/event";
/**
* Common interface for the crypto implementations
*/
export interface CryptoBackend {
/**
* Global override for whether the client should ever send encrypted
* messages to unverified devices. This provides the default for rooms which
* do not specify a value.
*
* If true, all unverified devices will be blacklisted by default
*/
globalBlacklistUnverifiedDevices: boolean;
/**
* Whether sendMessage in a room with unknown and unverified devices
* should throw an error and not send the message. This has 'Global' for
* symmetry with setGlobalBlacklistUnverifiedDevices but there is currently
* no room-level equivalent for this setting.
*/
globalErrorOnUnknownDevices: boolean;
/**
* Shut down any background processes related to crypto
*/
stop(): void;
/**
* Checks if the user has previously published cross-signing keys
*
* This means downloading the devicelist for the user and checking if the list includes
* the cross-signing pseudo-device.
* @returns true if the user has previously published cross-signing keys
*/
userHasCrossSigningKeys(): Promise<boolean>;
/**
* Decrypt a received event
*
* @returns a promise which resolves once we have finished decrypting.
* Rejects with an error if there is a problem decrypting the event.
*/
decryptEvent(event: MatrixEvent): Promise<IEventDecryptionResult>;
}

View File

@@ -0,0 +1,4 @@
This directory contains functionality which is common to both the legacy (libolm-based) crypto implementation,
and the new rust-based implementation.
It is an internal module, and is _not_ directly exposed to applications.

View File

@@ -20,6 +20,7 @@ limitations under the License.
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import type { IEventDecryptionResult } from "../../@types/crypto";
import { logger } from "../../logger"; import { logger } from "../../logger";
import * as olmlib from "../olmlib"; import * as olmlib from "../olmlib";
import { import {
@@ -38,13 +39,7 @@ import { IOlmSessionResult } from "../olmlib";
import { DeviceInfoMap } from "../DeviceList"; import { DeviceInfoMap } from "../DeviceList";
import { IContent, MatrixEvent } from "../../models/event"; import { IContent, MatrixEvent } from "../../models/event";
import { EventType, MsgType, ToDeviceMessageId } from "../../@types/event"; import { EventType, MsgType, ToDeviceMessageId } from "../../@types/event";
import { import { IMegolmEncryptedContent, IMegolmSessionData, IncomingRoomKeyRequest, IEncryptedContent } from "../index";
IMegolmEncryptedContent,
IEventDecryptionResult,
IMegolmSessionData,
IncomingRoomKeyRequest,
IEncryptedContent,
} from "../index";
import { RoomKeyRequestState } from "../OutgoingRoomKeyRequestManager"; import { RoomKeyRequestState } from "../OutgoingRoomKeyRequestManager";
import { OlmGroupSessionExtraData } from "../../@types/crypto"; import { OlmGroupSessionExtraData } from "../../@types/crypto";
import { MatrixError } from "../../http-api"; import { MatrixError } from "../../http-api";

View File

@@ -18,13 +18,14 @@ limitations under the License.
* Defines m.olm encryption/decryption * Defines m.olm encryption/decryption
*/ */
import type { IEventDecryptionResult } from "../../@types/crypto";
import { logger } from "../../logger"; import { logger } from "../../logger";
import * as olmlib from "../olmlib"; import * as olmlib from "../olmlib";
import { DeviceInfo } from "../deviceinfo"; import { DeviceInfo } from "../deviceinfo";
import { DecryptionAlgorithm, DecryptionError, EncryptionAlgorithm, registerAlgorithm } from "./base"; import { DecryptionAlgorithm, DecryptionError, EncryptionAlgorithm, registerAlgorithm } from "./base";
import { Room } from "../../models/room"; import { Room } from "../../models/room";
import { IContent, MatrixEvent } from "../../models/event"; import { IContent, MatrixEvent } from "../../models/event";
import { IEncryptedContent, IEventDecryptionResult, IOlmEncryptedContent } from "../index"; import { IEncryptedContent, IOlmEncryptedContent } from "../index";
import { IInboundSession } from "../OlmDevice"; import { IInboundSession } from "../OlmDevice";
const DeviceVerification = DeviceInfo.DeviceVerification; const DeviceVerification = DeviceInfo.DeviceVerification;

View File

@@ -20,6 +20,7 @@ limitations under the License.
import anotherjson from "another-json"; import anotherjson from "another-json";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import type { IEventDecryptionResult } from "../@types/crypto";
import type { PkDecryption, PkSigning } from "@matrix-org/olm"; import type { PkDecryption, PkSigning } from "@matrix-org/olm";
import { EventType, ToDeviceMessageId } from "../@types/event"; import { EventType, ToDeviceMessageId } from "../@types/event";
import { TypedReEmitter } from "../ReEmitter"; import { TypedReEmitter } from "../ReEmitter";
@@ -67,7 +68,7 @@ import { BackupManager } from "./backup";
import { IStore } from "../store"; import { IStore } from "../store";
import { Room, RoomEvent } from "../models/room"; import { Room, RoomEvent } from "../models/room";
import { RoomMember, RoomMemberEvent } from "../models/room-member"; import { RoomMember, RoomMemberEvent } from "../models/room-member";
import { EventStatus, IClearEvent, IEvent, MatrixEvent, MatrixEventEvent } from "../models/event"; import { EventStatus, IEvent, MatrixEvent, MatrixEventEvent } from "../models/event";
import { ToDeviceBatch } from "../models/ToDeviceMessage"; import { ToDeviceBatch } from "../models/ToDeviceMessage";
import { import {
ClientEvent, ClientEvent,
@@ -87,6 +88,7 @@ import { IContent } from "../models/event";
import { ISyncResponse } from "../sync-accumulator"; import { ISyncResponse } from "../sync-accumulator";
import { ISignatures } from "../@types/signed"; import { ISignatures } from "../@types/signed";
import { IMessage } from "./algorithms/olm"; import { IMessage } from "./algorithms/olm";
import { CryptoBackend } from "../common-crypto/CryptoBackend";
const DeviceVerification = DeviceInfo.DeviceVerification; const DeviceVerification = DeviceInfo.DeviceVerification;
@@ -218,30 +220,6 @@ interface ISignableObject {
unsigned?: object; unsigned?: object;
} }
/**
* The result of a (successful) call to decryptEvent.
*/
export interface IEventDecryptionResult {
/**
* The plaintext payload for the event (typically containing <tt>type</tt> and <tt>content</tt> fields).
*/
clearEvent: IClearEvent;
/**
* List of curve25519 keys involved in telling us about the senderCurve25519Key and claimedEd25519Key.
* See {@link MatrixEvent#getForwardingCurve25519KeyChain}.
*/
forwardingCurve25519KeyChain?: string[];
/**
* Key owned by the sender of this event. See {@link MatrixEvent#getSenderKey}.
*/
senderCurve25519Key?: string;
/**
* ed25519 key claimed by the sender of this event. See {@link MatrixEvent#getClaimedEd25519Key}.
*/
claimedEd25519Key?: string;
untrusted?: boolean;
}
export interface IRequestsMap { export interface IRequestsMap {
getRequest(event: MatrixEvent): VerificationRequest | undefined; getRequest(event: MatrixEvent): VerificationRequest | undefined;
getRequestByChannel(channel: IVerificationChannel): VerificationRequest | undefined; getRequestByChannel(channel: IVerificationChannel): VerificationRequest | undefined;
@@ -380,7 +358,7 @@ export type CryptoEventHandlerMap = {
[CryptoEvent.UserCrossSigningUpdated]: (userId: string) => void; [CryptoEvent.UserCrossSigningUpdated]: (userId: string) => void;
}; };
export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap> { export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap> implements CryptoBackend {
/** /**
* @returns The version of Olm. * @returns The version of Olm.
*/ */
@@ -3915,3 +3893,6 @@ class IncomingRoomKeyRequestCancellation {
this.requestId = content.request_id; this.requestId = content.request_id;
} }
} }
// IEventDecryptionResult is re-exported for backwards compatibility, in case any applications are referencing it.
export type { IEventDecryptionResult } from "../@types/crypto";

View File

@@ -21,10 +21,11 @@ limitations under the License.
import { ExtensibleEvent, ExtensibleEvents, Optional } from "matrix-events-sdk"; import { ExtensibleEvent, ExtensibleEvents, Optional } from "matrix-events-sdk";
import type { IEventDecryptionResult } from "../@types/crypto";
import { logger } from "../logger"; import { logger } from "../logger";
import { VerificationRequest } from "../crypto/verification/request/VerificationRequest"; import { VerificationRequest } from "../crypto/verification/request/VerificationRequest";
import { EVENT_VISIBILITY_CHANGE_TYPE, EventType, MsgType, RelationType } from "../@types/event"; import { EVENT_VISIBILITY_CHANGE_TYPE, EventType, MsgType, RelationType } from "../@types/event";
import { Crypto, IEventDecryptionResult } from "../crypto"; import { Crypto } from "../crypto";
import { deepSortedObjectEntries, internaliseString } from "../utils"; import { deepSortedObjectEntries, internaliseString } from "../utils";
import { RoomMember } from "./room-member"; import { RoomMember } from "./room-member";
import { Thread, ThreadEvent, EventHandlerMap as ThreadEventHandlerMap, THREAD_RELATION_TYPE } from "./thread"; import { Thread, ThreadEvent, EventHandlerMap as ThreadEventHandlerMap, THREAD_RELATION_TYPE } from "./thread";
@@ -34,6 +35,7 @@ import { MatrixError } from "../http-api";
import { TypedEventEmitter } from "./typed-event-emitter"; import { TypedEventEmitter } from "./typed-event-emitter";
import { EventStatus } from "./event-status"; import { EventStatus } from "./event-status";
import { DecryptionError } from "../crypto/algorithms"; import { DecryptionError } from "../crypto/algorithms";
import { CryptoBackend } from "../common-crypto/CryptoBackend";
export { EventStatus } from "./event-status"; export { EventStatus } from "./event-status";
@@ -725,7 +727,7 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
* @returns promise which resolves (to undefined) when the decryption * @returns promise which resolves (to undefined) when the decryption
* attempt is completed. * attempt is completed.
*/ */
public async attemptDecryption(crypto: Crypto, options: IDecryptOptions = {}): Promise<void> { public async attemptDecryption(crypto: CryptoBackend, options: IDecryptOptions = {}): Promise<void> {
// start with a couple of sanity checks. // start with a couple of sanity checks.
if (!this.isEncrypted()) { if (!this.isEncrypted()) {
throw new Error("Attempt to decrypt event which isn't encrypted"); throw new Error("Attempt to decrypt event which isn't encrypted");
@@ -803,7 +805,7 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
return recipients; return recipients;
} }
private async decryptionLoop(crypto: Crypto, options: IDecryptOptions = {}): Promise<void> { private async decryptionLoop(crypto: CryptoBackend, options: IDecryptOptions = {}): Promise<void> {
// make sure that this method never runs completely synchronously. // make sure that this method never runs completely synchronously.
// (doing so would mean that we would clear decryptionPromise *before* // (doing so would mean that we would clear decryptionPromise *before*
// it is set in attemptDecryption - and hence end up with a stuck // it is set in attemptDecryption - and hence end up with a stuck