Skip to content

Commit 326a13b

Browse files
authored
Rearrange the verification integration tests, again (#3504)
* Element-R: Implement `CryptoApi.getVerificationRequestsToDeviceInProgress` * Element-R: Implement `requestOwnUserVerification` * init aliceClient *after* the fetch interceptors * Initialise the test client separately for each test * Avoid running all the tests twice Currently all of these tests are running twice, with different client configurations. That's not really adding much value; we just need to run specific tests that way. * Factor out functions for building responses
1 parent e8fb47f commit 326a13b

File tree

1 file changed

+75
-89
lines changed

1 file changed

+75
-89
lines changed

spec/integ/crypto/verification.spec.ts

Lines changed: 75 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { MockResponse } from "fetch-mock";
2020
import fetchMock from "fetch-mock-jest";
2121
import { IDBFactory } from "fake-indexeddb";
2222

23-
import { createClient, CryptoEvent, MatrixClient } from "../../../src";
23+
import { createClient, CryptoEvent, ICreateClientOpts, MatrixClient } from "../../../src";
2424
import {
2525
canAcceptVerificationRequest,
2626
ShowQrCodeCallbacks,
@@ -88,6 +88,9 @@ afterAll(() => {
8888
}
8989
});
9090

91+
/** The homeserver url that we give to the test client, and where we intercept /sync, /keys, etc requests. */
92+
const TEST_HOMESERVER_URL = "https://alice-server.com";
93+
9194
/**
9295
* Integration tests for verification functionality.
9396
*
@@ -96,59 +99,39 @@ afterAll(() => {
9699
*/
97100
// we test with both crypto stacks...
98101
describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: string, initCrypto: InitCrypto) => {
99-
// and with (1) the default verification method list, (2) a custom verification method list.
100-
describe.each([undefined, ["m.sas.v1", "m.qr_code.show.v1", "m.reciprocate.v1"]])(
101-
"supported methods=%s",
102-
(methods) => {
103-
runTests(backend, initCrypto, methods);
104-
},
105-
);
106-
});
107-
108-
function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | undefined) {
109102
// oldBackendOnly is an alternative to `it` or `test` which will skip the test if we are running against the
110103
// Rust backend. Once we have full support in the rust sdk, it will go away.
111104
const oldBackendOnly = backend === "rust-sdk" ? test.skip : test;
112105

113106
/** the client under test */
114107
let aliceClient: MatrixClient;
115108

116-
/** an object which intercepts `/sync` requests from {@link #aliceClient} */
109+
/** an object which intercepts `/sync` requests on the test homeserver */
117110
let syncResponder: SyncResponder;
118111

119-
/** an object which intercepts `/keys/query` requests from {@link #aliceClient} */
112+
/** an object which intercepts `/keys/query` requests on the test homeserver */
120113
let e2eKeyResponder: E2EKeyResponder;
121114

122-
/** an object which intercepts `/keys/upload` requests from {@link #aliceClient} */
115+
/** an object which intercepts `/keys/upload` requests on the test homeserver */
123116
let e2eKeyReceiver: E2EKeyReceiver;
124117

125118
beforeEach(async () => {
126119
// anything that we don't have a specific matcher for silently returns a 404
127120
fetchMock.catch(404);
128121
fetchMock.config.warnOnFallback = false;
129122

130-
const homeserverUrl = "https://alice-server.com";
131-
aliceClient = createClient({
132-
baseUrl: homeserverUrl,
133-
userId: TEST_USER_ID,
134-
accessToken: "akjgkrgjs",
135-
deviceId: "device_under_test",
136-
verificationMethods: methods,
137-
});
138-
139-
await initCrypto(aliceClient);
140-
141-
e2eKeyReceiver = new E2EKeyReceiver(aliceClient.getHomeserverUrl());
142-
e2eKeyResponder = new E2EKeyResponder(aliceClient.getHomeserverUrl());
123+
e2eKeyReceiver = new E2EKeyReceiver(TEST_HOMESERVER_URL);
124+
e2eKeyResponder = new E2EKeyResponder(TEST_HOMESERVER_URL);
143125
e2eKeyResponder.addKeyReceiver(TEST_USER_ID, e2eKeyReceiver);
126+
syncResponder = new SyncResponder(TEST_HOMESERVER_URL);
144127

145-
syncResponder = new SyncResponder(aliceClient.getHomeserverUrl());
146-
mockInitialApiRequests(aliceClient.getHomeserverUrl());
147-
await aliceClient.startClient();
128+
mockInitialApiRequests(TEST_HOMESERVER_URL);
148129
});
149130

150131
afterEach(async () => {
151-
await aliceClient.stopClient();
132+
if (aliceClient !== undefined) {
133+
await aliceClient.stopClient();
134+
}
152135

153136
// Allow in-flight things to complete before we tear down the test
154137
await jest.runAllTimersAsync();
@@ -162,7 +145,10 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
162145
e2eKeyResponder.addDeviceKeys(TEST_USER_ID, TEST_DEVICE_ID, SIGNED_TEST_DEVICE_DATA);
163146
});
164147

165-
it("can verify another device via SAS", async () => {
148+
// test with (1) the default verification method list, (2) a custom verification method list.
149+
const TEST_METHODS = ["m.sas.v1", "m.qr_code.show.v1", "m.reciprocate.v1"];
150+
it.each([undefined, TEST_METHODS])("can verify via SAS (supported methods=%s)", async (methods) => {
151+
aliceClient = await startTestClient({ verificationMethods: methods });
166152
await waitForDeviceList();
167153

168154
// initially there should be no verifications in progress
@@ -176,7 +162,7 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
176162
expectSendToDeviceMessage("m.key.verification.request"),
177163
aliceClient.getCrypto()!.requestDeviceVerification(TEST_USER_ID, TEST_DEVICE_ID),
178164
]);
179-
const transactionId = request.transactionId;
165+
const transactionId = request.transactionId!;
180166
expect(transactionId).toBeDefined();
181167
expect(request.phase).toEqual(VerificationPhase.Requested);
182168
expect(request.roomId).toBeUndefined();
@@ -202,32 +188,14 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
202188
}
203189

204190
// The dummy device replies with an m.key.verification.ready...
205-
returnToDeviceMessageFromSync({
206-
type: "m.key.verification.ready",
207-
content: {
208-
from_device: TEST_DEVICE_ID,
209-
methods: ["m.sas.v1"],
210-
transaction_id: transactionId,
211-
},
212-
});
191+
returnToDeviceMessageFromSync(buildReadyMessage(transactionId, ["m.sas.v1"]));
213192
await waitForVerificationRequestChanged(request);
214193
expect(request.phase).toEqual(VerificationPhase.Ready);
215194
expect(request.otherDeviceId).toEqual(TEST_DEVICE_ID);
216195

217196
// ... and picks a method with m.key.verification.start
218-
returnToDeviceMessageFromSync({
219-
type: "m.key.verification.start",
220-
content: {
221-
from_device: TEST_DEVICE_ID,
222-
method: "m.sas.v1",
223-
transaction_id: transactionId,
224-
hashes: ["sha256"],
225-
key_agreement_protocols: ["curve25519-hkdf-sha256"],
226-
message_authentication_codes: ["hkdf-hmac-sha256.v2"],
227-
// we have to include "decimal" per the spec.
228-
short_authentication_string: ["decimal", "emoji"],
229-
},
230-
});
197+
returnToDeviceMessageFromSync(buildSasStartMessage(transactionId));
198+
231199
// as soon as the Changed event arrives, `verifier` should be defined
232200
const verifier = await new Promise<Verifier>((resolve) => {
233201
function onChange() {
@@ -327,6 +295,7 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
327295
});
328296

329297
it("Can make a verification request to *all* devices", async () => {
298+
aliceClient = await startTestClient();
330299
// we need an existing cross-signing key for this
331300
e2eKeyResponder.addCrossSigningData(SIGNED_CROSS_SIGNING_KEYS_DATA);
332301
await waitForDeviceList();
@@ -356,6 +325,7 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
356325
});
357326

358327
oldBackendOnly("can verify another via QR code with an untrusted cross-signing key", async () => {
328+
aliceClient = await startTestClient();
359329
// QRCode fails if we don't yet have the cross-signing keys, so make sure we have them now.
360330
e2eKeyResponder.addCrossSigningData(SIGNED_CROSS_SIGNING_KEYS_DATA);
361331
await waitForDeviceList();
@@ -366,26 +336,17 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
366336
expectSendToDeviceMessage("m.key.verification.request"),
367337
aliceClient.getCrypto()!.requestDeviceVerification(TEST_USER_ID, TEST_DEVICE_ID),
368338
]);
369-
const transactionId = request.transactionId;
339+
const transactionId = request.transactionId!;
370340

371341
const toDeviceMessage = requestBody.messages[TEST_USER_ID][TEST_DEVICE_ID];
372342
expect(toDeviceMessage.methods).toContain("m.qr_code.show.v1");
373343
expect(toDeviceMessage.methods).toContain("m.reciprocate.v1");
374-
if (methods === undefined) {
375-
expect(toDeviceMessage.methods).toContain("m.qr_code.scan.v1");
376-
}
344+
expect(toDeviceMessage.methods).toContain("m.qr_code.scan.v1");
377345
expect(toDeviceMessage.from_device).toEqual(aliceClient.deviceId);
378346
expect(toDeviceMessage.transaction_id).toEqual(transactionId);
379347

380348
// The dummy device replies with an m.key.verification.ready, with an indication we can scan the QR code
381-
returnToDeviceMessageFromSync({
382-
type: "m.key.verification.ready",
383-
content: {
384-
from_device: TEST_DEVICE_ID,
385-
methods: ["m.qr_code.scan.v1"],
386-
transaction_id: transactionId,
387-
},
388-
});
349+
returnToDeviceMessageFromSync(buildReadyMessage(transactionId, ["m.qr_code.scan.v1"]));
389350
await waitForVerificationRequestChanged(request);
390351
expect(request.phase).toEqual(VerificationPhase.Ready);
391352

@@ -447,40 +408,22 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
447408
});
448409

449410
it("can cancel during the SAS phase", async () => {
411+
aliceClient = await startTestClient();
450412
await waitForDeviceList();
451413

452414
// have alice initiate a verification. She should send a m.key.verification.request
453415
const [, request] = await Promise.all([
454416
expectSendToDeviceMessage("m.key.verification.request"),
455417
aliceClient.getCrypto()!.requestDeviceVerification(TEST_USER_ID, TEST_DEVICE_ID),
456418
]);
457-
const transactionId = request.transactionId;
419+
const transactionId = request.transactionId!;
458420

459421
// The dummy device replies with an m.key.verification.ready...
460-
returnToDeviceMessageFromSync({
461-
type: "m.key.verification.ready",
462-
content: {
463-
from_device: TEST_DEVICE_ID,
464-
methods: ["m.sas.v1"],
465-
transaction_id: transactionId,
466-
},
467-
});
422+
returnToDeviceMessageFromSync(buildReadyMessage(transactionId, ["m.sas.v1"]));
468423
await waitForVerificationRequestChanged(request);
469424

470425
// ... and picks a method with m.key.verification.start
471-
returnToDeviceMessageFromSync({
472-
type: "m.key.verification.start",
473-
content: {
474-
from_device: TEST_DEVICE_ID,
475-
method: "m.sas.v1",
476-
transaction_id: transactionId,
477-
hashes: ["sha256"],
478-
key_agreement_protocols: ["curve25519-hkdf-sha256"],
479-
message_authentication_codes: ["hkdf-hmac-sha256.v2"],
480-
// we have to include "decimal" per the spec.
481-
short_authentication_string: ["decimal", "emoji"],
482-
},
483-
});
426+
returnToDeviceMessageFromSync(buildSasStartMessage(transactionId));
484427
await waitForVerificationRequestChanged(request);
485428
expect(request.phase).toEqual(VerificationPhase.Started);
486429

@@ -514,6 +457,7 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
514457
});
515458

516459
oldBackendOnly("Incoming verification: can accept", async () => {
460+
aliceClient = await startTestClient();
517461
const TRANSACTION_ID = "abcd";
518462

519463
// Initiate the request by sending a to-device message
@@ -551,6 +495,19 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
551495
});
552496
});
553497

498+
async function startTestClient(opts: Partial<ICreateClientOpts> = {}): Promise<MatrixClient> {
499+
const client = createClient({
500+
baseUrl: TEST_HOMESERVER_URL,
501+
userId: TEST_USER_ID,
502+
accessToken: "akjgkrgjs",
503+
deviceId: "device_under_test",
504+
...opts,
505+
});
506+
await initCrypto(client);
507+
await client.startClient();
508+
return client;
509+
}
510+
554511
/** make sure that the client knows about the dummy device */
555512
async function waitForDeviceList(): Promise<void> {
556513
// Completing the initial sync will make the device list download outdated device lists (of which our own
@@ -568,7 +525,7 @@ function runTests(backend: string, initCrypto: InitCrypto, methods: string[] | u
568525
ev.sender ??= TEST_USER_ID;
569526
syncResponder.sendOrQueueSyncResponse({ to_device: { events: [ev] } });
570527
}
571-
}
528+
});
572529

573530
/**
574531
* Wait for the client under test to send a to-device message of the given type.
@@ -613,3 +570,32 @@ function calculateMAC(olmSAS: Olm.SAS, input: string, info: string): string {
613570
function encodeUnpaddedBase64(uint8Array: ArrayBuffer | Uint8Array): string {
614571
return Buffer.from(uint8Array).toString("base64").replace(/=+$/g, "");
615572
}
573+
574+
/** build an m.key.verification.ready to-device message originating from the dummy device */
575+
function buildReadyMessage(transactionId: string, methods: string[]): { type: string; content: object } {
576+
return {
577+
type: "m.key.verification.ready",
578+
content: {
579+
from_device: TEST_DEVICE_ID,
580+
methods: methods,
581+
transaction_id: transactionId,
582+
},
583+
};
584+
}
585+
586+
/** build an m.key.verification.start to-device message suitable for the SAS flow, originating from the dummy device */
587+
function buildSasStartMessage(transactionId: string): { type: string; content: object } {
588+
return {
589+
type: "m.key.verification.start",
590+
content: {
591+
from_device: TEST_DEVICE_ID,
592+
method: "m.sas.v1",
593+
transaction_id: transactionId,
594+
hashes: ["sha256"],
595+
key_agreement_protocols: ["curve25519-hkdf-sha256"],
596+
message_authentication_codes: ["hkdf-hmac-sha256.v2"],
597+
// we have to include "decimal" per the spec.
598+
short_authentication_string: ["decimal", "emoji"],
599+
},
600+
};
601+
}

0 commit comments

Comments
 (0)