You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-08-10 21:23:02 +03:00
251 lines
10 KiB
JavaScript
251 lines
10 KiB
JavaScript
/*
|
|
Copyright 2020 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 {VerificationRequest, READY_TYPE, START_TYPE, DONE_TYPE} from
|
|
"../../../../src/crypto/verification/request/VerificationRequest";
|
|
import {InRoomChannel} from "../../../../src/crypto/verification/request/InRoomChannel";
|
|
import {ToDeviceChannel} from
|
|
"../../../../src/crypto/verification/request/ToDeviceChannel";
|
|
import {MatrixEvent} from "../../../../src/models/event";
|
|
import {setupWebcrypto, teardownWebcrypto} from "./util";
|
|
|
|
function makeMockClient(userId, deviceId) {
|
|
let counter = 1;
|
|
let events = [];
|
|
const deviceEvents = {};
|
|
return {
|
|
getUserId() { return userId; },
|
|
getDeviceId() { return deviceId; },
|
|
|
|
sendEvent(roomId, type, content) {
|
|
counter = counter + 1;
|
|
const eventId = `$${userId}-${deviceId}-${counter}`;
|
|
events.push(new MatrixEvent({
|
|
sender: userId,
|
|
event_id: eventId,
|
|
room_id: roomId,
|
|
type,
|
|
content,
|
|
origin_server_ts: Date.now(),
|
|
}));
|
|
return Promise.resolve({event_id: eventId});
|
|
},
|
|
|
|
sendToDevice(type, msgMap) {
|
|
for (const userId of Object.keys(msgMap)) {
|
|
const deviceMap = msgMap[userId];
|
|
for (const deviceId of Object.keys(deviceMap)) {
|
|
const content = deviceMap[deviceId];
|
|
const event = new MatrixEvent({content, type});
|
|
deviceEvents[userId] = deviceEvents[userId] || {};
|
|
deviceEvents[userId][deviceId] = deviceEvents[userId][deviceId] || [];
|
|
deviceEvents[userId][deviceId].push(event);
|
|
}
|
|
}
|
|
return Promise.resolve();
|
|
},
|
|
|
|
popEvents() {
|
|
const e = events;
|
|
events = [];
|
|
return e;
|
|
},
|
|
|
|
popDeviceEvents(userId, deviceId) {
|
|
const forDevice = deviceEvents[userId];
|
|
const events = forDevice && forDevice[deviceId];
|
|
const result = events || [];
|
|
if (events) {
|
|
delete forDevice[deviceId];
|
|
}
|
|
return result;
|
|
},
|
|
};
|
|
}
|
|
|
|
const MOCK_METHOD = "mock-verify";
|
|
class MockVerifier {
|
|
constructor(channel, client, userId, deviceId, startEvent) {
|
|
this._channel = channel;
|
|
this._startEvent = startEvent;
|
|
}
|
|
|
|
get events() {
|
|
return [DONE_TYPE];
|
|
}
|
|
|
|
async start() {
|
|
if (this._startEvent) {
|
|
await this._channel.send(DONE_TYPE, {});
|
|
} else {
|
|
await this._channel.send(START_TYPE, {method: MOCK_METHOD});
|
|
}
|
|
}
|
|
|
|
async handleEvent(event) {
|
|
if (event.getType() === DONE_TYPE && !this._startEvent) {
|
|
await this._channel.send(DONE_TYPE, {});
|
|
}
|
|
}
|
|
|
|
canSwitchStartEvent() {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function makeRemoteEcho(event) {
|
|
return new MatrixEvent(Object.assign({}, event.event, {
|
|
unsigned: {
|
|
transaction_id: "abc",
|
|
},
|
|
}));
|
|
}
|
|
|
|
async function distributeEvent(ownRequest, theirRequest, event) {
|
|
await ownRequest.channel.handleEvent(
|
|
makeRemoteEcho(event), ownRequest, true);
|
|
await theirRequest.channel.handleEvent(event, theirRequest, true);
|
|
}
|
|
|
|
describe("verification request unit tests", function() {
|
|
|
|
beforeAll(function() {
|
|
setupWebcrypto();
|
|
});
|
|
|
|
afterAll(() => {
|
|
teardownWebcrypto();
|
|
});
|
|
|
|
it("transition from UNSENT to DONE through happy path", async function() {
|
|
const alice = makeMockClient("@alice:matrix.tld", "device1");
|
|
const bob = makeMockClient("@bob:matrix.tld", "device1");
|
|
const aliceRequest = new VerificationRequest(
|
|
new InRoomChannel(alice, "!room", bob.getUserId()),
|
|
new Map([[MOCK_METHOD, MockVerifier]]), alice);
|
|
const bobRequest = new VerificationRequest(
|
|
new InRoomChannel(bob, "!room"),
|
|
new Map([[MOCK_METHOD, MockVerifier]]), bob);
|
|
expect(aliceRequest.invalid).toBe(true);
|
|
expect(bobRequest.invalid).toBe(true);
|
|
|
|
await aliceRequest.sendRequest();
|
|
const [requestEvent] = alice.popEvents();
|
|
expect(requestEvent.getType()).toBe("m.room.message");
|
|
await distributeEvent(aliceRequest, bobRequest, requestEvent);
|
|
expect(aliceRequest.requested).toBe(true);
|
|
expect(bobRequest.requested).toBe(true);
|
|
|
|
await bobRequest.accept();
|
|
const [readyEvent] = bob.popEvents();
|
|
expect(readyEvent.getType()).toBe(READY_TYPE);
|
|
await distributeEvent(bobRequest, aliceRequest, readyEvent);
|
|
expect(bobRequest.ready).toBe(true);
|
|
expect(aliceRequest.ready).toBe(true);
|
|
|
|
const verifier = aliceRequest.beginKeyVerification(MOCK_METHOD);
|
|
await verifier.start();
|
|
const [startEvent] = alice.popEvents();
|
|
expect(startEvent.getType()).toBe(START_TYPE);
|
|
await distributeEvent(aliceRequest, bobRequest, startEvent);
|
|
expect(aliceRequest.started).toBe(true);
|
|
expect(aliceRequest.verifier).toBeInstanceOf(MockVerifier);
|
|
expect(bobRequest.started).toBe(true);
|
|
expect(bobRequest.verifier).toBeInstanceOf(MockVerifier);
|
|
|
|
await bobRequest.verifier.start();
|
|
const [bobDoneEvent] = bob.popEvents();
|
|
expect(bobDoneEvent.getType()).toBe(DONE_TYPE);
|
|
await distributeEvent(bobRequest, aliceRequest, bobDoneEvent);
|
|
const [aliceDoneEvent] = alice.popEvents();
|
|
expect(aliceDoneEvent.getType()).toBe(DONE_TYPE);
|
|
await distributeEvent(aliceRequest, bobRequest, aliceDoneEvent);
|
|
expect(aliceRequest.done).toBe(true);
|
|
expect(bobRequest.done).toBe(true);
|
|
});
|
|
|
|
it("methods only contains common methods", async function() {
|
|
const alice = makeMockClient("@alice:matrix.tld", "device1");
|
|
const bob = makeMockClient("@bob:matrix.tld", "device1");
|
|
const aliceRequest = new VerificationRequest(
|
|
new InRoomChannel(alice, "!room", bob.getUserId()),
|
|
new Map([["c", function() {}], ["a", function() {}]]), alice);
|
|
const bobRequest = new VerificationRequest(
|
|
new InRoomChannel(bob, "!room"),
|
|
new Map([["c", function() {}], ["b", function() {}]]), bob);
|
|
await aliceRequest.sendRequest();
|
|
const [requestEvent] = alice.popEvents();
|
|
await distributeEvent(aliceRequest, bobRequest, requestEvent);
|
|
await bobRequest.accept();
|
|
const [readyEvent] = bob.popEvents();
|
|
await distributeEvent(bobRequest, aliceRequest, readyEvent);
|
|
expect(aliceRequest.methods).toStrictEqual(["c"]);
|
|
expect(bobRequest.methods).toStrictEqual(["c"]);
|
|
});
|
|
|
|
it("other client accepting request puts it in observeOnly mode", async function() {
|
|
const alice = makeMockClient("@alice:matrix.tld", "device1");
|
|
const bob1 = makeMockClient("@bob:matrix.tld", "device1");
|
|
const bob2 = makeMockClient("@bob:matrix.tld", "device2");
|
|
const aliceRequest = new VerificationRequest(
|
|
new InRoomChannel(alice, "!room", bob1.getUserId()), new Map(), alice);
|
|
await aliceRequest.sendRequest();
|
|
const [requestEvent] = alice.popEvents();
|
|
const bob1Request = new VerificationRequest(
|
|
new InRoomChannel(bob1, "!room"), new Map(), bob1);
|
|
const bob2Request = new VerificationRequest(
|
|
new InRoomChannel(bob2, "!room"), new Map(), bob2);
|
|
|
|
await bob1Request.channel.handleEvent(requestEvent, bob1Request, true);
|
|
await bob2Request.channel.handleEvent(requestEvent, bob2Request, true);
|
|
|
|
await bob1Request.accept();
|
|
const [readyEvent] = bob1.popEvents();
|
|
expect(bob2Request.observeOnly).toBe(false);
|
|
await bob2Request.channel.handleEvent(readyEvent, bob2Request, true);
|
|
expect(bob2Request.observeOnly).toBe(true);
|
|
});
|
|
|
|
it("verify own device with to_device messages", async function() {
|
|
const bob1 = makeMockClient("@bob:matrix.tld", "device1");
|
|
const bob2 = makeMockClient("@bob:matrix.tld", "device2");
|
|
const bob1Request = new VerificationRequest(
|
|
new ToDeviceChannel(bob1, bob1.getUserId(), ["device1", "device2"],
|
|
ToDeviceChannel.makeTransactionId(), "device2"),
|
|
new Map([[MOCK_METHOD, MockVerifier]]), bob1);
|
|
const to = {userId: "@bob:matrix.tld", deviceId: "device2"};
|
|
const verifier = bob1Request.beginKeyVerification(MOCK_METHOD, to);
|
|
expect(verifier).toBeInstanceOf(MockVerifier);
|
|
await verifier.start();
|
|
const [startEvent] = bob1.popDeviceEvents(to.userId, to.deviceId);
|
|
expect(startEvent.getType()).toBe(START_TYPE);
|
|
const bob2Request = new VerificationRequest(
|
|
new ToDeviceChannel(bob2, bob2.getUserId(), ["device1"]),
|
|
new Map([[MOCK_METHOD, MockVerifier]]), bob2);
|
|
|
|
await bob2Request.channel.handleEvent(startEvent, bob2Request, true);
|
|
await bob2Request.verifier.start();
|
|
const [doneEvent1] = bob2.popDeviceEvents("@bob:matrix.tld", "device1");
|
|
expect(doneEvent1.getType()).toBe(DONE_TYPE);
|
|
await bob1Request.channel.handleEvent(doneEvent1, bob1Request, true);
|
|
const [doneEvent2] = bob1.popDeviceEvents("@bob:matrix.tld", "device2");
|
|
expect(doneEvent2.getType()).toBe(DONE_TYPE);
|
|
await bob2Request.channel.handleEvent(doneEvent2, bob2Request, true);
|
|
|
|
expect(bob1Request.done).toBe(true);
|
|
expect(bob2Request.done).toBe(true);
|
|
});
|
|
});
|