You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-07-30 04:23:07 +03:00
ElementR: Add CryptoApi.findVerificationRequestDMInProgress
(#3601)
* Add `CryptoApi.findVerificationRequestDMInProgress` * Fix linting and missing parameters * Move `ROOM_ID` into `test-data` * Remove verification request from `EventDecryptor` pending list * Fix duplicate timeline event processing * Add extra documentation * Try to fix sonar error * Use `roomId` * Fix typo * Review changes * Review changes * Fix `initRustCrypto` jsdoc * Listen to `ClientEvent.Event` instead of `RoomEvent.Timeline` * Fix missing room id in `generate-test-data.py` * Review changes * Review changes * Handle encrypted event * Fix linting * Comments and run timers * Ignore 404 * Fix test
This commit is contained in:
@ -20,22 +20,23 @@ import fetchMock from "fetch-mock-jest";
|
||||
import "fake-indexeddb/auto";
|
||||
import { IDBFactory } from "fake-indexeddb";
|
||||
import { MockResponse, MockResponseFunction } from "fetch-mock";
|
||||
import Olm from "@matrix-org/olm";
|
||||
|
||||
import type { IDeviceKeys } from "../../../src/@types/crypto";
|
||||
import * as testUtils from "../../test-utils/test-utils";
|
||||
import { CRYPTO_BACKENDS, InitCrypto, syncPromise } from "../../test-utils/test-utils";
|
||||
import { CRYPTO_BACKENDS, getSyncResponse, InitCrypto, syncPromise } from "../../test-utils/test-utils";
|
||||
import { TEST_ROOM_ID, TEST_ROOM_ID as ROOM_ID, TEST_USER_ID } from "../../test-utils/test-data";
|
||||
import { TestClient } from "../../TestClient";
|
||||
import { logger } from "../../../src/logger";
|
||||
import {
|
||||
Category,
|
||||
createClient,
|
||||
IClaimOTKsResult,
|
||||
IContent,
|
||||
IDownloadKeyResult,
|
||||
IEvent,
|
||||
IJoinedRoom,
|
||||
IndexedDBCryptoStore,
|
||||
IStartClientOpts,
|
||||
ISyncResponse,
|
||||
MatrixClient,
|
||||
MatrixEvent,
|
||||
MatrixEventEvent,
|
||||
@ -43,6 +44,7 @@ import {
|
||||
Room,
|
||||
RoomMember,
|
||||
RoomStateEvent,
|
||||
IRoomEvent,
|
||||
} from "../../../src/matrix";
|
||||
import { DeviceInfo } from "../../../src/crypto/deviceinfo";
|
||||
import { E2EKeyReceiver, IE2EKeyReceiver } from "../../test-utils/E2EKeyReceiver";
|
||||
@ -53,8 +55,7 @@ import { flushPromises } from "../../test-utils/flushPromises";
|
||||
import { mockInitialApiRequests, mockSetupCrossSigningRequests } from "../../test-utils/mockEndpoints";
|
||||
import { AddSecretStorageKeyOpts, SECRET_STORAGE_ALGORITHM_V1_AES } from "../../../src/secret-storage";
|
||||
import { CryptoCallbacks } from "../../../src/crypto-api";
|
||||
|
||||
const ROOM_ID = "!room:id";
|
||||
import { E2EKeyResponder } from "../../test-utils/E2EKeyResponder";
|
||||
|
||||
afterEach(() => {
|
||||
// reset fake-indexeddb after each test, to make sure we don't leak connections
|
||||
@ -156,18 +157,23 @@ function encryptMegolmEvent(opts: {
|
||||
expect(opts.room_id).toBeTruthy();
|
||||
plaintext.room_id = opts.room_id;
|
||||
}
|
||||
return encryptMegolmEventRawPlainText({ senderKey: opts.senderKey, groupSession: opts.groupSession, plaintext });
|
||||
return encryptMegolmEventRawPlainText({
|
||||
senderKey: opts.senderKey,
|
||||
groupSession: opts.groupSession,
|
||||
plaintext,
|
||||
});
|
||||
}
|
||||
|
||||
function encryptMegolmEventRawPlainText(opts: {
|
||||
senderKey: string;
|
||||
groupSession: Olm.OutboundGroupSession;
|
||||
plaintext: Partial<IEvent>;
|
||||
origin_server_ts?: number;
|
||||
}): IEvent {
|
||||
return {
|
||||
event_id: "$test_megolm_event_" + Math.random(),
|
||||
sender: "@not_the_real_sender:example.com",
|
||||
origin_server_ts: 1672944778000,
|
||||
sender: opts.plaintext.sender ?? "@not_the_real_sender:example.com",
|
||||
origin_server_ts: opts.plaintext.origin_server_ts ?? 1672944778000,
|
||||
content: {
|
||||
algorithm: "m.megolm.v1.aes-sha2",
|
||||
ciphertext: opts.groupSession.encrypt(JSON.stringify(opts.plaintext)),
|
||||
@ -213,55 +219,6 @@ function encryptGroupSessionKey(opts: {
|
||||
});
|
||||
}
|
||||
|
||||
// get a /sync response which contains a single room (ROOM_ID), with the members given
|
||||
function getSyncResponse(roomMembers: string[]): ISyncResponse {
|
||||
const roomResponse: IJoinedRoom = {
|
||||
summary: {
|
||||
"m.heroes": [],
|
||||
"m.joined_member_count": roomMembers.length,
|
||||
"m.invited_member_count": roomMembers.length,
|
||||
},
|
||||
state: {
|
||||
events: [
|
||||
testUtils.mkEventCustom({
|
||||
sender: roomMembers[0],
|
||||
type: "m.room.encryption",
|
||||
state_key: "",
|
||||
content: {
|
||||
algorithm: "m.megolm.v1.aes-sha2",
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
timeline: {
|
||||
events: [],
|
||||
prev_batch: "",
|
||||
},
|
||||
ephemeral: { events: [] },
|
||||
account_data: { events: [] },
|
||||
unread_notifications: {},
|
||||
};
|
||||
|
||||
for (let i = 0; i < roomMembers.length; i++) {
|
||||
roomResponse.state.events.push(
|
||||
testUtils.mkMembershipCustom({
|
||||
membership: "join",
|
||||
sender: roomMembers[i],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
next_batch: "1",
|
||||
rooms: {
|
||||
join: { [ROOM_ID]: roomResponse },
|
||||
invite: {},
|
||||
leave: {},
|
||||
},
|
||||
account_data: { events: [] },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish an Olm Session with the test user
|
||||
*
|
||||
@ -415,7 +372,10 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
|
||||
let aliceClient: MatrixClient;
|
||||
|
||||
/** an object which intercepts `/keys/upload` requests from {@link #aliceClient} to catch the uploaded keys */
|
||||
let keyReceiver: IE2EKeyReceiver;
|
||||
let keyReceiver: E2EKeyReceiver;
|
||||
|
||||
/** an object which intercepts `/keys/query` requests on the test homeserver */
|
||||
let keyResponder: E2EKeyResponder;
|
||||
|
||||
/** an object which intercepts `/sync` requests from {@link #aliceClient} */
|
||||
let syncResponder: ISyncResponder;
|
||||
@ -586,6 +546,10 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
|
||||
|
||||
afterEach(async () => {
|
||||
await aliceClient.stopClient();
|
||||
|
||||
// Allow in-flight things to complete before we tear down the test
|
||||
await jest.runAllTimersAsync();
|
||||
|
||||
fetchMock.mockReset();
|
||||
});
|
||||
|
||||
@ -708,6 +672,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
|
||||
await syncPromise(aliceClient);
|
||||
|
||||
await testUtils.awaitDecryption(event, { waitOnDecryptionFailure: true });
|
||||
expect(event.isDecryptionFailure()).toBeFalsy();
|
||||
expect(event.getContent().body).toEqual("42");
|
||||
});
|
||||
|
||||
@ -2411,4 +2376,193 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
|
||||
expect(selfSigningKey[secretStorageKey]).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Incoming verification in a DM", () => {
|
||||
beforeEach(async () => {
|
||||
// anything that we don't have a specific matcher for silently returns a 404
|
||||
fetchMock.catch(404);
|
||||
|
||||
keyResponder = new E2EKeyResponder(aliceClient.getHomeserverUrl());
|
||||
keyResponder.addKeyReceiver(TEST_USER_ID, keyReceiver);
|
||||
|
||||
expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} });
|
||||
await startClientAndAwaitFirstSync();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
/**
|
||||
* Return a verification request event from Bob
|
||||
* @see https://spec.matrix.org/v1.7/client-server-api/#mkeyverificationrequest
|
||||
*/
|
||||
function createVerificationRequestEvent(): IRoomEvent {
|
||||
return {
|
||||
content: {
|
||||
body: "Verification request from Bob to Alice",
|
||||
from_device: "BobDevice",
|
||||
methods: ["m.sas.v1"],
|
||||
msgtype: "m.key.verification.request",
|
||||
to: aliceClient.getUserId()!,
|
||||
},
|
||||
event_id: "$143273582443PhrSn:example.org",
|
||||
origin_server_ts: Date.now(),
|
||||
room_id: TEST_ROOM_ID,
|
||||
sender: "@bob:xyz",
|
||||
type: "m.room.message",
|
||||
unsigned: {
|
||||
age: 1234,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a to-device event
|
||||
* @param groupSession
|
||||
* @param p2pSession
|
||||
*/
|
||||
function createToDeviceEvent(groupSession: Olm.OutboundGroupSession, p2pSession: Olm.Session): Partial<IEvent> {
|
||||
return encryptGroupSessionKey({
|
||||
recipient: aliceClient.getUserId()!,
|
||||
recipientCurve25519Key: keyReceiver.getDeviceKey(),
|
||||
recipientEd25519Key: keyReceiver.getSigningKey(),
|
||||
olmAccount: testOlmAccount,
|
||||
p2pSession: p2pSession,
|
||||
groupSession: groupSession,
|
||||
room_id: ROOM_ID,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and encrypt a verification request event
|
||||
* @param groupSession
|
||||
*/
|
||||
function createEncryptedMessage(groupSession: Olm.OutboundGroupSession): IEvent {
|
||||
return encryptMegolmEvent({
|
||||
senderKey: testSenderKey,
|
||||
groupSession: groupSession,
|
||||
room_id: ROOM_ID,
|
||||
plaintext: createVerificationRequestEvent(),
|
||||
});
|
||||
}
|
||||
|
||||
newBackendOnly("Verification request from Bob to Alice", async () => {
|
||||
// Tell alice she is sharing a room with bob
|
||||
const syncResponse = getSyncResponse(["@bob:xyz"]);
|
||||
|
||||
// Add verification request from Bob to Alice in the DM between them
|
||||
syncResponse.rooms[Category.Join][TEST_ROOM_ID].timeline.events.push(createVerificationRequestEvent());
|
||||
syncResponder.sendOrQueueSyncResponse(syncResponse);
|
||||
// Wait for the sync response to be processed
|
||||
await syncPromise(aliceClient);
|
||||
|
||||
const request = aliceClient.getCrypto()!.findVerificationRequestDMInProgress(TEST_ROOM_ID, "@bob:xyz");
|
||||
// Expect to find the verification request received during the sync
|
||||
expect(request?.roomId).toBe(TEST_ROOM_ID);
|
||||
expect(request?.isSelfVerification).toBe(false);
|
||||
expect(request?.otherUserId).toBe("@bob:xyz");
|
||||
});
|
||||
|
||||
newBackendOnly("Verification request not found", async () => {
|
||||
// Tell alice she is sharing a room with bob
|
||||
syncResponder.sendOrQueueSyncResponse(getSyncResponse(["@bob:xyz"]));
|
||||
// Wait for the sync response to be processed
|
||||
await syncPromise(aliceClient);
|
||||
|
||||
// Expect to not find any verification request
|
||||
const request = aliceClient.getCrypto()!.findVerificationRequestDMInProgress(TEST_ROOM_ID, "@bob:xyz");
|
||||
expect(request).not.toBeDefined();
|
||||
});
|
||||
|
||||
newBackendOnly("Process encrypted verification request", async () => {
|
||||
const p2pSession = await createOlmSession(testOlmAccount, keyReceiver);
|
||||
const groupSession = new Olm.OutboundGroupSession();
|
||||
groupSession.create();
|
||||
|
||||
// make the room_key event, but don't send it yet
|
||||
const toDeviceEvent = createToDeviceEvent(groupSession, p2pSession);
|
||||
|
||||
// Add verification request from Bob to Alice in the DM between them
|
||||
syncResponder.sendOrQueueSyncResponse({
|
||||
next_batch: 1,
|
||||
rooms: { join: { [ROOM_ID]: { timeline: { events: [createEncryptedMessage(groupSession)] } } } },
|
||||
});
|
||||
// Wait for the sync response to be processed
|
||||
await syncPromise(aliceClient);
|
||||
|
||||
const room = aliceClient.getRoom(ROOM_ID)!;
|
||||
const matrixEvent = room.getLiveTimeline().getEvents()[0];
|
||||
|
||||
// wait for a first attempt at decryption: should fail
|
||||
await testUtils.awaitDecryption(matrixEvent);
|
||||
expect(matrixEvent.getContent().msgtype).toEqual("m.bad.encrypted");
|
||||
|
||||
// Send the Bob's keys
|
||||
syncResponder.sendOrQueueSyncResponse({
|
||||
next_batch: 2,
|
||||
to_device: {
|
||||
events: [toDeviceEvent],
|
||||
},
|
||||
});
|
||||
await syncPromise(aliceClient);
|
||||
|
||||
// Wait for the message to be decrypted
|
||||
await testUtils.awaitDecryption(matrixEvent, { waitOnDecryptionFailure: true });
|
||||
|
||||
const request = aliceClient.getCrypto()!.findVerificationRequestDMInProgress(TEST_ROOM_ID, "@bob:xyz");
|
||||
// Expect to find the verification request received during the sync
|
||||
expect(request?.roomId).toBe(TEST_ROOM_ID);
|
||||
expect(request?.isSelfVerification).toBe(false);
|
||||
expect(request?.otherUserId).toBe("@bob:xyz");
|
||||
});
|
||||
|
||||
newBackendOnly(
|
||||
"If Bob keys are not received in the 5mins after the verification request, the request is ignored",
|
||||
async () => {
|
||||
const p2pSession = await createOlmSession(testOlmAccount, keyReceiver);
|
||||
const groupSession = new Olm.OutboundGroupSession();
|
||||
groupSession.create();
|
||||
|
||||
// make the room_key event, but don't send it yet
|
||||
const toDeviceEvent = createToDeviceEvent(groupSession, p2pSession);
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
// Add verification request from Bob to Alice in the DM between them
|
||||
syncResponder.sendOrQueueSyncResponse({
|
||||
next_batch: 1,
|
||||
rooms: { join: { [ROOM_ID]: { timeline: { events: [createEncryptedMessage(groupSession)] } } } },
|
||||
});
|
||||
// Wait for the sync response to be processed
|
||||
await syncPromise(aliceClient);
|
||||
|
||||
const room = aliceClient.getRoom(ROOM_ID)!;
|
||||
const matrixEvent = room.getLiveTimeline().getEvents()[0];
|
||||
|
||||
// wait for a first attempt at decryption: should fail
|
||||
await testUtils.awaitDecryption(matrixEvent);
|
||||
expect(matrixEvent.getContent().msgtype).toEqual("m.bad.encrypted");
|
||||
|
||||
// Advance time by 5mins, the verification request should be ignored after that
|
||||
jest.advanceTimersByTime(5 * 60 * 1000);
|
||||
|
||||
// Send the Bob's keys
|
||||
syncResponder.sendOrQueueSyncResponse({
|
||||
next_batch: 2,
|
||||
to_device: {
|
||||
events: [toDeviceEvent],
|
||||
},
|
||||
});
|
||||
await syncPromise(aliceClient);
|
||||
|
||||
// Wait for the message to be decrypted
|
||||
await testUtils.awaitDecryption(matrixEvent, { waitOnDecryptionFailure: true });
|
||||
|
||||
const request = aliceClient.getCrypto()!.findVerificationRequestDMInProgress(TEST_ROOM_ID, "@bob:xyz");
|
||||
// the request should not be present
|
||||
expect(request).not.toBeDefined();
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -34,6 +34,7 @@ from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
|
||||
# input data
|
||||
TEST_USER_ID = "@alice:localhost"
|
||||
TEST_DEVICE_ID = "test_device"
|
||||
TEST_ROOM_ID = "!room:id"
|
||||
# any 32-byte string can be an ed25519 private key.
|
||||
TEST_DEVICE_PRIVATE_KEY_BYTES = b"deadbeefdeadbeefdeadbeefdeadbeef"
|
||||
|
||||
@ -130,6 +131,7 @@ import {{ KeyBackupInfo }} from "../../../src/crypto-api";
|
||||
|
||||
export const TEST_USER_ID = "{TEST_USER_ID}";
|
||||
export const TEST_DEVICE_ID = "{TEST_DEVICE_ID}";
|
||||
export const TEST_ROOM_ID = "{TEST_ROOM_ID}";
|
||||
|
||||
/** The base64-encoded public ed25519 key for this device */
|
||||
export const TEST_DEVICE_PUBLIC_ED25519_KEY_BASE64 = "{b64_public_key}";
|
||||
|
@ -11,6 +11,7 @@ import { KeyBackupInfo } from "../../../src/crypto-api";
|
||||
|
||||
export const TEST_USER_ID = "@alice:localhost";
|
||||
export const TEST_DEVICE_ID = "test_device";
|
||||
export const TEST_ROOM_ID = "!room:id";
|
||||
|
||||
/** The base64-encoded public ed25519 key for this device */
|
||||
export const TEST_DEVICE_PUBLIC_ED25519_KEY_BASE64 = "YI/7vbGVLpGdYtuceQR8MSsKB/QjgfMXM1xqnn+0NWU";
|
||||
|
@ -6,9 +6,19 @@ import "../olm-loader";
|
||||
|
||||
import { logger } from "../../src/logger";
|
||||
import { IContent, IEvent, IEventRelation, IUnsigned, MatrixEvent, MatrixEventEvent } from "../../src/models/event";
|
||||
import { ClientEvent, EventType, IPusher, MatrixClient, MsgType, RelationType } from "../../src";
|
||||
import {
|
||||
ClientEvent,
|
||||
EventType,
|
||||
IJoinedRoom,
|
||||
IPusher,
|
||||
ISyncResponse,
|
||||
MatrixClient,
|
||||
MsgType,
|
||||
RelationType,
|
||||
} from "../../src";
|
||||
import { SyncState } from "../../src/sync";
|
||||
import { eventMapperFor } from "../../src/event-mapper";
|
||||
import { TEST_ROOM_ID } from "./test-data";
|
||||
|
||||
/**
|
||||
* Return a promise that is resolved when the client next emits a
|
||||
@ -39,6 +49,61 @@ export function syncPromise(client: MatrixClient, count = 1): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a sync response which contains a single room (by default TEST_ROOM_ID), with the members given
|
||||
* @param roomMembers
|
||||
* @param roomId
|
||||
*
|
||||
* @returns the sync response
|
||||
*/
|
||||
export function getSyncResponse(roomMembers: string[], roomId = TEST_ROOM_ID): ISyncResponse {
|
||||
const roomResponse: IJoinedRoom = {
|
||||
summary: {
|
||||
"m.heroes": [],
|
||||
"m.joined_member_count": roomMembers.length,
|
||||
"m.invited_member_count": roomMembers.length,
|
||||
},
|
||||
state: {
|
||||
events: [
|
||||
mkEventCustom({
|
||||
sender: roomMembers[0],
|
||||
type: "m.room.encryption",
|
||||
state_key: "",
|
||||
content: {
|
||||
algorithm: "m.megolm.v1.aes-sha2",
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
timeline: {
|
||||
events: [],
|
||||
prev_batch: "",
|
||||
},
|
||||
ephemeral: { events: [] },
|
||||
account_data: { events: [] },
|
||||
unread_notifications: {},
|
||||
};
|
||||
|
||||
for (let i = 0; i < roomMembers.length; i++) {
|
||||
roomResponse.state.events.push(
|
||||
mkMembershipCustom({
|
||||
membership: "join",
|
||||
sender: roomMembers[i],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
next_batch: "1",
|
||||
rooms: {
|
||||
join: { [roomId]: roomResponse },
|
||||
invite: {},
|
||||
leave: {},
|
||||
},
|
||||
account_data: { events: [] },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a spy for an object and automatically spy its methods.
|
||||
* @param constr - The class constructor (used with 'new')
|
||||
|
@ -576,6 +576,13 @@ describe("RustCrypto", () => {
|
||||
expect(await rustCrypto.getActiveSessionBackupVersion()).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("findVerificationRequestDMInProgress", () => {
|
||||
it("returns undefined if the userId is not provided", async () => {
|
||||
const rustCrypto = await makeTestRustCrypto();
|
||||
expect(rustCrypto.findVerificationRequestDMInProgress(testData.TEST_ROOM_ID)).not.toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/** build a basic RustCrypto instance for testing
|
||||
|
@ -2243,6 +2243,9 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
||||
|
||||
// attach the event listeners needed by RustCrypto
|
||||
this.on(RoomMemberEvent.Membership, rustCrypto.onRoomMembership.bind(rustCrypto));
|
||||
this.on(ClientEvent.Event, (event) => {
|
||||
rustCrypto.onLiveEventFromSync(event);
|
||||
});
|
||||
|
||||
// re-emit the events emitted by the crypto impl
|
||||
this.reEmitter.reEmit(rustCrypto, [
|
||||
|
@ -267,9 +267,20 @@ export interface CryptoApi {
|
||||
* @param roomId - the room to use for verification
|
||||
*
|
||||
* @returns the VerificationRequest that is in progress, if any
|
||||
* @deprecated prefer `userId` parameter variant.
|
||||
*/
|
||||
findVerificationRequestDMInProgress(roomId: string): VerificationRequest | undefined;
|
||||
|
||||
/**
|
||||
* Finds a DM verification request that is already in progress for the given room and user.
|
||||
*
|
||||
* @param roomId - the room to use for verification.
|
||||
* @param userId - search for a verification request for the given user.
|
||||
*
|
||||
* @returns the VerificationRequest that is in progress, if any.
|
||||
*/
|
||||
findVerificationRequestDMInProgress(roomId: string, userId?: string): VerificationRequest | undefined;
|
||||
|
||||
/**
|
||||
* Send a verification request to our other devices.
|
||||
*
|
||||
|
@ -2380,8 +2380,8 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
||||
return deviceObj;
|
||||
}
|
||||
|
||||
public findVerificationRequestDMInProgress(roomId: string): VerificationRequest | undefined {
|
||||
return this.inRoomVerificationRequests.findRequestInProgress(roomId);
|
||||
public findVerificationRequestDMInProgress(roomId: string, userId?: string): VerificationRequest | undefined {
|
||||
return this.inRoomVerificationRequests.findRequestInProgress(roomId, userId);
|
||||
}
|
||||
|
||||
public getVerificationRequestsToDeviceInProgress(userId: string): VerificationRequest[] {
|
||||
|
@ -350,11 +350,11 @@ export class InRoomRequests implements IRequestsMap {
|
||||
}
|
||||
}
|
||||
|
||||
public findRequestInProgress(roomId: string): VerificationRequest | undefined {
|
||||
public findRequestInProgress(roomId: string, userId?: string): VerificationRequest | undefined {
|
||||
const requestsByTxnId = this.requestsByRoomId.get(roomId);
|
||||
if (requestsByTxnId) {
|
||||
for (const request of requestsByTxnId.values()) {
|
||||
if (request.pending) {
|
||||
if (request.pending && (userId === undefined || request.requestingUserId === userId)) {
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import type { IEventDecryptionResult, IMegolmSessionData } from "../@types/crypto";
|
||||
import type { IDeviceLists, IToDeviceEvent } from "../sync-accumulator";
|
||||
import type { IEncryptedEventInfo } from "../crypto/api";
|
||||
import { IContent, MatrixEvent } from "../models/event";
|
||||
import { IContent, MatrixEvent, MatrixEventEvent } from "../models/event";
|
||||
import { Room } from "../models/room";
|
||||
import { RoomMember } from "../models/room-member";
|
||||
import { CryptoBackend, OnSyncCompletedData } from "../common-crypto/CryptoBackend";
|
||||
@ -35,6 +35,7 @@ import {
|
||||
BootstrapCrossSigningOpts,
|
||||
CreateSecretStorageOpts,
|
||||
CrossSigningKey,
|
||||
CrossSigningKeyInfo,
|
||||
CrossSigningStatus,
|
||||
CryptoCallbacks,
|
||||
DeviceVerificationStatus,
|
||||
@ -43,7 +44,6 @@ import {
|
||||
ImportRoomKeysOpts,
|
||||
KeyBackupInfo,
|
||||
VerificationRequest,
|
||||
CrossSigningKeyInfo,
|
||||
} from "../crypto-api";
|
||||
import { deviceKeysToDeviceMap, rustDeviceToJsDevice } from "./device-converter";
|
||||
import { IDownloadKeyResult, IQueryKeysRequest } from "../client";
|
||||
@ -55,7 +55,7 @@ import { keyFromPassphrase } from "../crypto/key_passphrase";
|
||||
import { encodeRecoveryKey } from "../crypto/recoverykey";
|
||||
import { crypto } from "../crypto/crypto";
|
||||
import { RustVerificationRequest, verificationMethodIdentifierToMethod } from "./verification";
|
||||
import { EventType } from "../@types/event";
|
||||
import { EventType, MsgType } from "../@types/event";
|
||||
import { CryptoEvent } from "../crypto";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter";
|
||||
import { RustBackupManager } from "./backup";
|
||||
@ -680,13 +680,29 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
|
||||
* Implementation of {@link CryptoApi#findVerificationRequestDMInProgress}
|
||||
*
|
||||
* @param roomId - the room to use for verification
|
||||
* @param userId - search the verification request for the given user
|
||||
*
|
||||
* @returns the VerificationRequest that is in progress, if any
|
||||
*
|
||||
*/
|
||||
public findVerificationRequestDMInProgress(roomId: string): undefined {
|
||||
// TODO
|
||||
return;
|
||||
public findVerificationRequestDMInProgress(roomId: string, userId?: string): VerificationRequest | undefined {
|
||||
// TODO raise an error
|
||||
if (!userId) return;
|
||||
|
||||
const requests: RustSdkCryptoJs.VerificationRequest[] = this.olmMachine.getVerificationRequests(
|
||||
new RustSdkCryptoJs.UserId(userId),
|
||||
);
|
||||
|
||||
// Search for the verification request for the given room id
|
||||
const request = requests.find((request) => request.roomId?.toString() === roomId);
|
||||
|
||||
if (request) {
|
||||
return new RustVerificationRequest(
|
||||
request,
|
||||
this.outgoingRequestProcessor,
|
||||
this._supportedVerificationMethods,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1018,6 +1034,73 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a live event received via /sync.
|
||||
* See {@link ClientEventHandlerMap#event}
|
||||
*
|
||||
* @param event - live event
|
||||
*/
|
||||
public async onLiveEventFromSync(event: MatrixEvent): Promise<void> {
|
||||
// Ignore state event
|
||||
if (event.isState()) return;
|
||||
|
||||
const processEvent = async (evt: MatrixEvent): Promise<void> => {
|
||||
// Process only key validation request
|
||||
if (
|
||||
evt.getType() === EventType.RoomMessage &&
|
||||
evt.getContent().msgtype === MsgType.KeyVerificationRequest
|
||||
) {
|
||||
await this.onKeyVerificationRequest(evt);
|
||||
}
|
||||
};
|
||||
|
||||
// If the event is encrypted of in failure, we wait for decryption
|
||||
if (event.isDecryptionFailure() || event.isEncrypted()) {
|
||||
// 5 mins
|
||||
const TIMEOUT_DELAY = 5 * 60 * 1000;
|
||||
|
||||
const timeoutId = setTimeout(() => event.off(MatrixEventEvent.Decrypted, onDecrypted), TIMEOUT_DELAY);
|
||||
|
||||
const onDecrypted = (decryptedEvent: MatrixEvent, error?: Error): void => {
|
||||
if (error) return;
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
event.off(MatrixEventEvent.Decrypted, onDecrypted);
|
||||
processEvent(decryptedEvent);
|
||||
};
|
||||
// After 5mins, we are not expecting the event to be decrypted
|
||||
|
||||
event.on(MatrixEventEvent.Decrypted, onDecrypted);
|
||||
} else {
|
||||
await processEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle key verification request.
|
||||
*
|
||||
* @param event - a key validation request event.
|
||||
*/
|
||||
private async onKeyVerificationRequest(event: MatrixEvent): Promise<void> {
|
||||
const roomId = event.getRoomId();
|
||||
|
||||
if (!roomId) {
|
||||
throw new Error("missing roomId in the event");
|
||||
}
|
||||
|
||||
await this.olmMachine.receiveVerificationEvent(
|
||||
JSON.stringify({
|
||||
event_id: event.getId(),
|
||||
type: event.getType(),
|
||||
sender: event.getSender(),
|
||||
state_key: event.getStateKey(),
|
||||
content: event.getContent(),
|
||||
origin_server_ts: event.getTs(),
|
||||
}),
|
||||
new RustSdkCryptoJs.RoomId(roomId),
|
||||
);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Outgoing requests
|
||||
|
Reference in New Issue
Block a user