You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-07-08 05:01:59 +03:00
Rewrite megolm integration tests with async arrow functions (#2519)
This commit is contained in:
@ -24,13 +24,13 @@ import {
|
||||
IContent,
|
||||
IEvent,
|
||||
IClaimOTKsResult,
|
||||
IJoinedRoom,
|
||||
ISyncResponse,
|
||||
IDownloadKeyResult,
|
||||
MatrixEvent,
|
||||
MatrixEventEvent,
|
||||
} from "../../src/matrix";
|
||||
import { IDeviceKeys } from "../../src/crypto/dehydration";
|
||||
import { ISignatures } from "../../src/@types/signed";
|
||||
|
||||
const ROOM_ID = "!room:id";
|
||||
|
||||
@ -75,18 +75,17 @@ function encryptOlmEvent(opts: {
|
||||
type: opts.plaintype || 'm.test',
|
||||
};
|
||||
|
||||
const event = {
|
||||
return {
|
||||
content: {
|
||||
algorithm: 'm.olm.v1.curve25519-aes-sha2',
|
||||
ciphertext: {},
|
||||
ciphertext: {
|
||||
[opts.recipient.getDeviceKey()]: opts.p2pSession.encrypt(JSON.stringify(plaintext)),
|
||||
},
|
||||
sender_key: opts.senderKey,
|
||||
},
|
||||
sender: opts.sender || '@bob:xyz',
|
||||
type: 'm.room.encrypted',
|
||||
};
|
||||
event.content.ciphertext[opts.recipient.getDeviceKey()] =
|
||||
opts.p2pSession.encrypt(JSON.stringify(plaintext));
|
||||
return event;
|
||||
}
|
||||
|
||||
// encrypt an event with megolm
|
||||
@ -151,43 +150,54 @@ 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 = {
|
||||
const roomResponse: IJoinedRoom = {
|
||||
summary: {
|
||||
"m.heroes": [],
|
||||
"m.joined_member_count": roomMembers.length,
|
||||
"m.invited_member_count": roomMembers.length,
|
||||
},
|
||||
state: {
|
||||
events: [
|
||||
testUtils.mkEvent({
|
||||
testUtils.mkEventCustom({
|
||||
sender: roomMembers[0],
|
||||
type: 'm.room.encryption',
|
||||
skey: '',
|
||||
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.mkMembership({
|
||||
mship: 'join',
|
||||
testUtils.mkMembershipCustom({
|
||||
membership: 'join',
|
||||
sender: roomMembers[i],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const syncResponse = {
|
||||
return {
|
||||
next_batch: "1",
|
||||
rooms: {
|
||||
join: {},
|
||||
join: { [ROOM_ID]: roomResponse },
|
||||
invite: {},
|
||||
leave: {},
|
||||
},
|
||||
account_data: { events: [] },
|
||||
};
|
||||
syncResponse.rooms.join[ROOM_ID] = roomResponse;
|
||||
return syncResponse;
|
||||
}
|
||||
|
||||
describe("megolm", function() {
|
||||
describe("megolm", () => {
|
||||
if (!global.Olm) {
|
||||
logger.warn('not running megolm tests: Olm not present');
|
||||
return;
|
||||
@ -218,21 +228,12 @@ describe("megolm", function() {
|
||||
};
|
||||
const j = anotherjson.stringify(testDeviceKeys);
|
||||
const sig = testOlmAccount.sign(j);
|
||||
testDeviceKeys.signatures = {};
|
||||
testDeviceKeys.signatures[userId] = {
|
||||
'ed25519:DEVICE_ID': sig,
|
||||
};
|
||||
testDeviceKeys.signatures = { [userId]: { 'ed25519:DEVICE_ID': sig } };
|
||||
|
||||
const queryResponse = {
|
||||
device_keys: {},
|
||||
return {
|
||||
device_keys: { [userId]: { 'DEVICE_ID': testDeviceKeys } },
|
||||
failures: {},
|
||||
};
|
||||
|
||||
queryResponse.device_keys[userId] = {
|
||||
'DEVICE_ID': testDeviceKeys,
|
||||
};
|
||||
|
||||
return queryResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -249,26 +250,21 @@ describe("megolm", function() {
|
||||
|
||||
const keyId = Object.keys(testOneTimeKeys.curve25519)[0];
|
||||
const oneTimeKey: string = testOneTimeKeys.curve25519[keyId];
|
||||
const keyResult: { key: string, signatures: ISignatures } = {
|
||||
key: oneTimeKey,
|
||||
signatures: {},
|
||||
};
|
||||
const j = anotherjson.stringify({ key: oneTimeKey });
|
||||
const unsignedKeyResult = { key: oneTimeKey };
|
||||
const j = anotherjson.stringify(unsignedKeyResult);
|
||||
const sig = testOlmAccount.sign(j);
|
||||
keyResult.signatures[userId] = {
|
||||
'ed25519:DEVICE_ID': sig,
|
||||
const keyResult = {
|
||||
...unsignedKeyResult,
|
||||
signatures: { [userId]: { 'ed25519:DEVICE_ID': sig } },
|
||||
};
|
||||
|
||||
const claimResponse = { one_time_keys: {}, failures: {} };
|
||||
claimResponse.one_time_keys[userId] = {
|
||||
'DEVICE_ID': {},
|
||||
return {
|
||||
one_time_keys: { [userId]: { 'DEVICE_ID': { ['signed_curve25519:' + keyId]: keyResult } } },
|
||||
failures: {},
|
||||
};
|
||||
claimResponse.one_time_keys[userId].DEVICE_ID['signed_curve25519:' + keyId] =
|
||||
keyResult;
|
||||
return claimResponse;
|
||||
}
|
||||
|
||||
beforeEach(async function() {
|
||||
beforeEach(async () => {
|
||||
aliceTestClient = new TestClient(
|
||||
"@alice:localhost", "xzcvb", "akjgkrgjs",
|
||||
);
|
||||
@ -280,14 +276,11 @@ describe("megolm", function() {
|
||||
testSenderKey = testE2eKeys.curve25519;
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
return aliceTestClient.stop();
|
||||
});
|
||||
afterEach(() => aliceTestClient.stop());
|
||||
|
||||
it("Alice receives a megolm message", function() {
|
||||
return aliceTestClient.start().then(() => {
|
||||
return createOlmSession(testOlmAccount, aliceTestClient);
|
||||
}).then((p2pSession) => {
|
||||
it("Alice receives a megolm message", async () => {
|
||||
await aliceTestClient.start();
|
||||
const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient);
|
||||
const groupSession = new Olm.OutboundGroupSession();
|
||||
groupSession.create();
|
||||
|
||||
@ -314,39 +307,31 @@ describe("megolm", function() {
|
||||
events: [roomKeyEncrypted],
|
||||
},
|
||||
rooms: {
|
||||
join: {},
|
||||
join: {
|
||||
[ROOM_ID]: { timeline: { events: [messageEncrypted] } },
|
||||
},
|
||||
};
|
||||
syncResponse.rooms.join[ROOM_ID] = {
|
||||
timeline: {
|
||||
events: [messageEncrypted],
|
||||
},
|
||||
};
|
||||
|
||||
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse);
|
||||
return aliceTestClient.flushSync();
|
||||
}).then(function() {
|
||||
await aliceTestClient.flushSync();
|
||||
|
||||
const room = aliceTestClient.client.getRoom(ROOM_ID);
|
||||
const event = room.getLiveTimeline().getEvents()[0];
|
||||
expect(event.isEncrypted()).toBe(true);
|
||||
return testUtils.awaitDecryption(event);
|
||||
}).then((event) => {
|
||||
expect(event.getContent().body).toEqual('42');
|
||||
});
|
||||
const decryptedEvent = await testUtils.awaitDecryption(event);
|
||||
expect(decryptedEvent.getContent().body).toEqual('42');
|
||||
});
|
||||
|
||||
it("Alice receives a megolm message before the session keys", function() {
|
||||
it("Alice receives a megolm message before the session keys", async () => {
|
||||
// https://github.com/vector-im/element-web/issues/2273
|
||||
let roomKeyEncrypted;
|
||||
|
||||
return aliceTestClient.start().then(() => {
|
||||
return createOlmSession(testOlmAccount, aliceTestClient);
|
||||
}).then((p2pSession) => {
|
||||
await aliceTestClient.start();
|
||||
const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient);
|
||||
const groupSession = new Olm.OutboundGroupSession();
|
||||
groupSession.create();
|
||||
|
||||
// make the room_key event, but don't send it yet
|
||||
roomKeyEncrypted = encryptGroupSessionKey({
|
||||
const roomKeyEncrypted = encryptGroupSessionKey({
|
||||
senderKey: testSenderKey,
|
||||
recipient: aliceTestClient,
|
||||
p2pSession: p2pSession,
|
||||
@ -362,58 +347,43 @@ describe("megolm", function() {
|
||||
});
|
||||
|
||||
// Alice just gets the message event to start with
|
||||
const syncResponse = {
|
||||
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, {
|
||||
next_batch: 1,
|
||||
rooms: {
|
||||
join: {},
|
||||
},
|
||||
};
|
||||
syncResponse.rooms.join[ROOM_ID] = {
|
||||
timeline: {
|
||||
events: [messageEncrypted],
|
||||
},
|
||||
};
|
||||
rooms: { join: { [ROOM_ID]: { timeline: { events: [messageEncrypted] } } } },
|
||||
});
|
||||
await aliceTestClient.flushSync();
|
||||
|
||||
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse);
|
||||
return aliceTestClient.flushSync();
|
||||
}).then(function() {
|
||||
const room = aliceTestClient.client.getRoom(ROOM_ID);
|
||||
const event = room.getLiveTimeline().getEvents()[0];
|
||||
expect(event.getContent().msgtype).toEqual('m.bad.encrypted');
|
||||
expect(room.getLiveTimeline().getEvents()[0].getContent().msgtype).toEqual('m.bad.encrypted');
|
||||
|
||||
// now she gets the room_key event
|
||||
const syncResponse = {
|
||||
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, {
|
||||
next_batch: 2,
|
||||
to_device: {
|
||||
events: [roomKeyEncrypted],
|
||||
},
|
||||
};
|
||||
});
|
||||
await aliceTestClient.flushSync();
|
||||
|
||||
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse);
|
||||
return aliceTestClient.flushSync();
|
||||
}).then(function() {
|
||||
const room = aliceTestClient.client.getRoom(ROOM_ID);
|
||||
const event = room.getLiveTimeline().getEvents()[0];
|
||||
|
||||
let decryptedEvent: MatrixEvent;
|
||||
if (event.getContent().msgtype != 'm.bad.encrypted') {
|
||||
return event;
|
||||
}
|
||||
|
||||
return new Promise<MatrixEvent>((resolve, reject) => {
|
||||
decryptedEvent = event;
|
||||
} else {
|
||||
decryptedEvent = await new Promise<MatrixEvent>((resolve) => {
|
||||
event.once(MatrixEventEvent.Decrypted, (ev) => {
|
||||
logger.log(`${Date.now()} event ${event.getId()} now decrypted`);
|
||||
resolve(ev);
|
||||
});
|
||||
});
|
||||
}).then((event) => {
|
||||
expect(event.getContent().body).toEqual('42');
|
||||
});
|
||||
}
|
||||
expect(decryptedEvent.getContent().body).toEqual('42');
|
||||
});
|
||||
|
||||
it("Alice gets a second room_key message", function() {
|
||||
return aliceTestClient.start().then(() => {
|
||||
return createOlmSession(testOlmAccount, aliceTestClient);
|
||||
}).then((p2pSession) => {
|
||||
it("Alice gets a second room_key message", async () => {
|
||||
await aliceTestClient.start();
|
||||
const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient);
|
||||
const groupSession = new Olm.OutboundGroupSession();
|
||||
groupSession.create();
|
||||
|
||||
@ -460,37 +430,26 @@ describe("megolm", function() {
|
||||
events: [roomKeyEncrypted2],
|
||||
},
|
||||
rooms: {
|
||||
join: {},
|
||||
},
|
||||
};
|
||||
syncResponse2.rooms.join[ROOM_ID] = {
|
||||
timeline: {
|
||||
events: [messageEncrypted],
|
||||
join: { [ROOM_ID]: { timeline: { events: [messageEncrypted] } } },
|
||||
},
|
||||
};
|
||||
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse2);
|
||||
|
||||
// flush both syncs
|
||||
return aliceTestClient.flushSync().then(() => {
|
||||
return aliceTestClient.flushSync();
|
||||
});
|
||||
}).then(async function() {
|
||||
await aliceTestClient.flushSync();
|
||||
await aliceTestClient.flushSync();
|
||||
|
||||
const room = aliceTestClient.client.getRoom(ROOM_ID);
|
||||
await room.decryptCriticalEvents();
|
||||
const event = room.getLiveTimeline().getEvents()[0];
|
||||
expect(event.getContent().body).toEqual('42');
|
||||
});
|
||||
});
|
||||
|
||||
it('Alice sends a megolm message', function() {
|
||||
let p2pSession;
|
||||
|
||||
it('Alice sends a megolm message', async () => {
|
||||
aliceTestClient.expectKeyQuery({ device_keys: { '@alice:localhost': {} }, failures: {} });
|
||||
return aliceTestClient.start().then(() => {
|
||||
await aliceTestClient.start();
|
||||
// establish an olm session with alice
|
||||
return createOlmSession(testOlmAccount, aliceTestClient);
|
||||
}).then((_p2pSession) => {
|
||||
p2pSession = _p2pSession;
|
||||
const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient);
|
||||
|
||||
const syncResponse = getSyncResponse(['@bob:xyz']);
|
||||
|
||||
@ -503,14 +462,14 @@ describe("megolm", function() {
|
||||
syncResponse.to_device = { events: [olmEvent] };
|
||||
|
||||
aliceTestClient.httpBackend.when('GET', '/sync').respond(200, syncResponse);
|
||||
return aliceTestClient.flushSync();
|
||||
}).then(function() {
|
||||
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'),
|
||||
);
|
||||
|
||||
return Promise.all([
|
||||
await Promise.all([
|
||||
aliceTestClient.client.sendTextMessage(ROOM_ID, 'test').then(() => {
|
||||
throw new Error("sendTextMessage failed on an unknown device");
|
||||
}, (e) => {
|
||||
@ -518,14 +477,14 @@ describe("megolm", function() {
|
||||
}),
|
||||
aliceTestClient.httpBackend.flushAllExpected(),
|
||||
]);
|
||||
}).then(function() {
|
||||
|
||||
// mark the device as known, and resend.
|
||||
aliceTestClient.client.setDeviceKnown('@bob:xyz', 'DEVICE_ID');
|
||||
|
||||
let inboundGroupSession;
|
||||
let inboundGroupSession: Olm.InboundGroupSession;
|
||||
aliceTestClient.httpBackend.when(
|
||||
'PUT', '/sendToDevice/m.room.encrypted/',
|
||||
).respond(200, function(path, content) {
|
||||
).respond(200, function(_path, content) {
|
||||
const m = content.messages['@bob:xyz'].DEVICE_ID;
|
||||
const ct = m.ciphertext[testSenderKey];
|
||||
const decrypted = JSON.parse(p2pSession.decrypt(ct.type, ct.body));
|
||||
@ -538,9 +497,9 @@ describe("megolm", function() {
|
||||
|
||||
aliceTestClient.httpBackend.when(
|
||||
'PUT', '/send/',
|
||||
).respond(200, function(path, content) {
|
||||
).respond(200, (_path, content: IContent) => {
|
||||
const ct = content.ciphertext;
|
||||
const r = inboundGroupSession.decrypt(ct);
|
||||
const r: any = inboundGroupSession.decrypt(ct);
|
||||
logger.log('Decrypted received megolm message', r);
|
||||
|
||||
expect(r.message_index).toEqual(0);
|
||||
@ -548,31 +507,25 @@ describe("megolm", function() {
|
||||
expect(decrypted.type).toEqual('m.room.message');
|
||||
expect(decrypted.content.body).toEqual('test');
|
||||
|
||||
return {
|
||||
event_id: '$event_id',
|
||||
};
|
||||
return { event_id: '$event_id' };
|
||||
});
|
||||
|
||||
const room = aliceTestClient.client.getRoom(ROOM_ID);
|
||||
const pendingMsg = room.getPendingEvents()[0];
|
||||
|
||||
return Promise.all([
|
||||
await Promise.all([
|
||||
aliceTestClient.client.resendEvent(pendingMsg, room),
|
||||
|
||||
// the crypto stuff can take a while, so give the requests a whole second.
|
||||
aliceTestClient.httpBackend.flushAllExpected({
|
||||
timeout: 1000,
|
||||
}),
|
||||
aliceTestClient.httpBackend.flushAllExpected({ timeout: 1000 }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it("We shouldn't attempt to send to blocked devices", function() {
|
||||
it("We shouldn't attempt to send to blocked devices", async () => {
|
||||
aliceTestClient.expectKeyQuery({ device_keys: { '@alice:localhost': {} }, failures: {} });
|
||||
return aliceTestClient.start().then(() => {
|
||||
await aliceTestClient.start();
|
||||
// establish an olm session with alice
|
||||
return createOlmSession(testOlmAccount, aliceTestClient);
|
||||
}).then((p2pSession) => {
|
||||
const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient);
|
||||
const syncResponse = getSyncResponse(['@bob:xyz']);
|
||||
|
||||
const olmEvent = encryptOlmEvent({
|
||||
@ -584,19 +537,19 @@ describe("megolm", function() {
|
||||
syncResponse.to_device = { events: [olmEvent] };
|
||||
aliceTestClient.httpBackend.when('GET', '/sync').respond(200, syncResponse);
|
||||
|
||||
return aliceTestClient.flushSync();
|
||||
}).then(function() {
|
||||
await aliceTestClient.flushSync();
|
||||
|
||||
logger.log('Forcing alice to download our device keys');
|
||||
|
||||
aliceTestClient.httpBackend.when('POST', '/keys/query').respond(
|
||||
200, getTestKeysQueryResponse('@bob:xyz'),
|
||||
);
|
||||
|
||||
return Promise.all([
|
||||
await Promise.all([
|
||||
aliceTestClient.client.downloadKeys(['@bob:xyz']),
|
||||
aliceTestClient.httpBackend.flush('/keys/query', 1),
|
||||
]);
|
||||
}).then(function() {
|
||||
|
||||
logger.log('Telling alice to block our device');
|
||||
aliceTestClient.client.setDeviceBlocked('@bob:xyz', 'DEVICE_ID');
|
||||
|
||||
@ -610,27 +563,19 @@ describe("megolm", function() {
|
||||
'PUT', '/sendToDevice/m.room_key.withheld/',
|
||||
).respond(200, {});
|
||||
|
||||
return Promise.all([
|
||||
await Promise.all([
|
||||
aliceTestClient.client.sendTextMessage(ROOM_ID, 'test'),
|
||||
|
||||
// the crypto stuff can take a while, so give the requests a whole second.
|
||||
aliceTestClient.httpBackend.flushAllExpected({
|
||||
timeout: 1000,
|
||||
}),
|
||||
aliceTestClient.httpBackend.flushAllExpected({ timeout: 1000 }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it("We should start a new megolm session when a device is blocked", function() {
|
||||
let p2pSession;
|
||||
let megolmSessionId;
|
||||
|
||||
it("We should start a new megolm session when a device is blocked", async () => {
|
||||
aliceTestClient.expectKeyQuery({ device_keys: { '@alice:localhost': {} }, failures: {} });
|
||||
return aliceTestClient.start().then(() => {
|
||||
await aliceTestClient.start();
|
||||
// establish an olm session with alice
|
||||
return createOlmSession(testOlmAccount, aliceTestClient);
|
||||
}).then((_p2pSession) => {
|
||||
p2pSession = _p2pSession;
|
||||
const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient);
|
||||
|
||||
const syncResponse = getSyncResponse(['@bob:xyz']);
|
||||
|
||||
@ -643,26 +588,26 @@ describe("megolm", function() {
|
||||
syncResponse.to_device = { events: [olmEvent] };
|
||||
aliceTestClient.httpBackend.when('GET', '/sync').respond(200, syncResponse);
|
||||
|
||||
return aliceTestClient.flushSync();
|
||||
}).then(function() {
|
||||
await aliceTestClient.flushSync();
|
||||
|
||||
logger.log("Fetching bob's devices and marking known");
|
||||
|
||||
aliceTestClient.httpBackend.when('POST', '/keys/query').respond(
|
||||
200, getTestKeysQueryResponse('@bob:xyz'),
|
||||
);
|
||||
|
||||
return Promise.all([
|
||||
await Promise.all([
|
||||
aliceTestClient.client.downloadKeys(['@bob:xyz']),
|
||||
aliceTestClient.httpBackend.flushAllExpected(),
|
||||
]).then((keys) => {
|
||||
aliceTestClient.client.setDeviceKnown('@bob:xyz', 'DEVICE_ID');
|
||||
});
|
||||
}).then(function() {
|
||||
]);
|
||||
await aliceTestClient.client.setDeviceKnown('@bob:xyz', 'DEVICE_ID');
|
||||
|
||||
logger.log('Telling alice to send a megolm message');
|
||||
|
||||
let megolmSessionId: string;
|
||||
aliceTestClient.httpBackend.when(
|
||||
'PUT', '/sendToDevice/m.room.encrypted/',
|
||||
).respond(200, function(path, content) {
|
||||
).respond(200, function(_path, content) {
|
||||
logger.log('sendToDevice: ', content);
|
||||
const m = content.messages['@bob:xyz'].DEVICE_ID;
|
||||
const ct = m.ciphertext[testSenderKey];
|
||||
@ -676,7 +621,7 @@ describe("megolm", function() {
|
||||
|
||||
aliceTestClient.httpBackend.when(
|
||||
'PUT', '/send/',
|
||||
).respond(200, function(path, content) {
|
||||
).respond(200, function(_path, content) {
|
||||
logger.log('/send:', content);
|
||||
expect(content.session_id).toEqual(megolmSessionId);
|
||||
return {
|
||||
@ -684,22 +629,20 @@ describe("megolm", function() {
|
||||
};
|
||||
});
|
||||
|
||||
return Promise.all([
|
||||
await Promise.all([
|
||||
aliceTestClient.client.sendTextMessage(ROOM_ID, 'test'),
|
||||
|
||||
// the crypto stuff can take a while, so give the requests a whole second.
|
||||
aliceTestClient.httpBackend.flushAllExpected({
|
||||
timeout: 1000,
|
||||
}),
|
||||
aliceTestClient.httpBackend.flushAllExpected({ timeout: 1000 }),
|
||||
]);
|
||||
}).then(function() {
|
||||
|
||||
logger.log('Telling alice to block our device');
|
||||
aliceTestClient.client.setDeviceBlocked('@bob:xyz', 'DEVICE_ID');
|
||||
|
||||
logger.log('Telling alice to send another megolm message');
|
||||
aliceTestClient.httpBackend.when(
|
||||
'PUT', '/send/',
|
||||
).respond(200, function(path, content) {
|
||||
).respond(200, function(_path, content) {
|
||||
logger.log('/send:', content);
|
||||
expect(content.session_id).not.toEqual(megolmSessionId);
|
||||
return {
|
||||
@ -710,80 +653,68 @@ describe("megolm", function() {
|
||||
'PUT', '/sendToDevice/m.room_key.withheld/',
|
||||
).respond(200, {});
|
||||
|
||||
return Promise.all([
|
||||
await Promise.all([
|
||||
aliceTestClient.client.sendTextMessage(ROOM_ID, 'test2'),
|
||||
aliceTestClient.httpBackend.flushAllExpected(),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
// https://github.com/vector-im/element-web/issues/2676
|
||||
it("Alice should send to her other devices", function() {
|
||||
it("Alice should send to her other devices", async () => {
|
||||
// for this test, we make the testOlmAccount be another of Alice's devices.
|
||||
// it ought to get included in messages Alice sends.
|
||||
|
||||
let p2pSession;
|
||||
let inboundGroupSession;
|
||||
let decrypted;
|
||||
|
||||
return aliceTestClient.start().then(function() {
|
||||
await aliceTestClient.start();
|
||||
// an encrypted room with just alice
|
||||
const syncResponse = {
|
||||
next_batch: 1,
|
||||
rooms: {
|
||||
join: {},
|
||||
},
|
||||
};
|
||||
syncResponse.rooms.join[ROOM_ID] = {
|
||||
state: {
|
||||
events: [
|
||||
rooms: { join: { [ROOM_ID]: { state: { events: [
|
||||
testUtils.mkEvent({
|
||||
type: 'm.room.encryption',
|
||||
skey: '',
|
||||
content: {
|
||||
algorithm: 'm.megolm.v1.aes-sha2',
|
||||
},
|
||||
content: { algorithm: 'm.megolm.v1.aes-sha2' },
|
||||
}),
|
||||
testUtils.mkMembership({
|
||||
mship: 'join',
|
||||
sender: aliceTestClient.userId,
|
||||
}),
|
||||
],
|
||||
},
|
||||
] } } } },
|
||||
};
|
||||
aliceTestClient.httpBackend.when('GET', '/sync').respond(200, syncResponse);
|
||||
|
||||
// the completion of the first initialsync hould make Alice
|
||||
// the completion of the first initialsync should make Alice
|
||||
// invalidate the device cache for all members in e2e rooms (ie,
|
||||
// herself), and do a key query.
|
||||
aliceTestClient.expectKeyQuery(
|
||||
getTestKeysQueryResponse(aliceTestClient.userId),
|
||||
);
|
||||
|
||||
return aliceTestClient.httpBackend.flushAllExpected();
|
||||
}).then(function() {
|
||||
await aliceTestClient.httpBackend.flushAllExpected();
|
||||
|
||||
// start out with the device unknown - the send should be rejected.
|
||||
return aliceTestClient.client.sendTextMessage(ROOM_ID, 'test').then(() => {
|
||||
throw new Error("sendTextMessage failed on an unknown device");
|
||||
}, (e) => {
|
||||
try {
|
||||
await aliceTestClient.client.sendTextMessage(ROOM_ID, 'test');
|
||||
throw new Error("sendTextMessage succeeded on an unknown device");
|
||||
} catch (e) {
|
||||
expect(e.name).toEqual("UnknownDeviceError");
|
||||
expect(Object.keys(e.devices)).toEqual([aliceTestClient.userId]);
|
||||
expect(Object.keys(e.devices[aliceTestClient.userId])).
|
||||
toEqual(['DEVICE_ID']);
|
||||
});
|
||||
}).then(function() {
|
||||
}
|
||||
|
||||
// mark the device as known, and resend.
|
||||
aliceTestClient.client.setDeviceKnown(aliceTestClient.userId, 'DEVICE_ID');
|
||||
aliceTestClient.httpBackend.when('POST', '/keys/claim').respond(
|
||||
200, function(path, content) {
|
||||
200, function(_path, content) {
|
||||
expect(content.one_time_keys[aliceTestClient.userId].DEVICE_ID)
|
||||
.toEqual("signed_curve25519");
|
||||
return getTestKeysClaimResponse(aliceTestClient.userId);
|
||||
});
|
||||
|
||||
let p2pSession: Olm.Session;
|
||||
let inboundGroupSession: Olm.InboundGroupSession;
|
||||
aliceTestClient.httpBackend.when(
|
||||
'PUT', '/sendToDevice/m.room.encrypted/',
|
||||
).respond(200, function(path, content) {
|
||||
).respond(200, function(_path, content) {
|
||||
logger.log("sendToDevice: ", content);
|
||||
const m = content.messages[aliceTestClient.userId].DEVICE_ID;
|
||||
const ct = m.ciphertext[testSenderKey];
|
||||
@ -799,11 +730,12 @@ describe("megolm", function() {
|
||||
return {};
|
||||
});
|
||||
|
||||
let decrypted: IEvent;
|
||||
aliceTestClient.httpBackend.when(
|
||||
'PUT', '/send/',
|
||||
).respond(200, function(path, content) {
|
||||
).respond(200, function(_path, content: IContent) {
|
||||
const ct = content.ciphertext;
|
||||
const r = inboundGroupSession.decrypt(ct);
|
||||
const r: any = inboundGroupSession.decrypt(ct);
|
||||
logger.log('Decrypted received megolm message', r);
|
||||
decrypted = JSON.parse(r.plaintext);
|
||||
|
||||
@ -818,7 +750,7 @@ describe("megolm", function() {
|
||||
expect(pendingEvents.length).toEqual(1);
|
||||
const unsentEvent = pendingEvents[0];
|
||||
|
||||
return Promise.all([
|
||||
await Promise.all([
|
||||
aliceTestClient.client.resendEvent(unsentEvent, room),
|
||||
|
||||
// the crypto stuff can take a while, so give the requests a whole second.
|
||||
@ -826,21 +758,17 @@ describe("megolm", function() {
|
||||
timeout: 1000,
|
||||
}),
|
||||
]);
|
||||
}).then(function() {
|
||||
|
||||
expect(decrypted.type).toEqual('m.room.message');
|
||||
expect(decrypted.content.body).toEqual('test');
|
||||
});
|
||||
});
|
||||
|
||||
it('Alice should wait for device list to complete when sending a megolm message', function() {
|
||||
let downloadPromise;
|
||||
let sendPromise;
|
||||
|
||||
it('Alice should wait for device list to complete when sending a megolm message', async () => {
|
||||
aliceTestClient.expectKeyQuery({ device_keys: { '@alice:localhost': {} }, failures: {} });
|
||||
return aliceTestClient.start().then(() => {
|
||||
await aliceTestClient.start();
|
||||
// establish an olm session with alice
|
||||
return createOlmSession(testOlmAccount, aliceTestClient);
|
||||
}).then((p2pSession) => {
|
||||
const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient);
|
||||
|
||||
const syncResponse = getSyncResponse(['@bob:xyz']);
|
||||
|
||||
const olmEvent = encryptOlmEvent({
|
||||
@ -852,14 +780,14 @@ describe("megolm", function() {
|
||||
syncResponse.to_device = { events: [olmEvent] };
|
||||
|
||||
aliceTestClient.httpBackend.when('GET', '/sync').respond(200, syncResponse);
|
||||
return aliceTestClient.flushSync();
|
||||
}).then(function() {
|
||||
await aliceTestClient.flushSync();
|
||||
|
||||
// this will block
|
||||
logger.log('Forcing alice to download our device keys');
|
||||
downloadPromise = aliceTestClient.client.downloadKeys(['@bob:xyz']);
|
||||
const downloadPromise = aliceTestClient.client.downloadKeys(['@bob:xyz']);
|
||||
|
||||
// so will this.
|
||||
sendPromise = aliceTestClient.client.sendTextMessage(ROOM_ID, 'test')
|
||||
const sendPromise = aliceTestClient.client.sendTextMessage(ROOM_ID, 'test')
|
||||
.then(() => {
|
||||
throw new Error("sendTextMessage failed on an unknown device");
|
||||
}, (e) => {
|
||||
@ -870,20 +798,16 @@ describe("megolm", function() {
|
||||
200, getTestKeysQueryResponse('@bob:xyz'),
|
||||
);
|
||||
|
||||
return aliceTestClient.httpBackend.flushAllExpected();
|
||||
}).then(function() {
|
||||
return Promise.all([downloadPromise, sendPromise]);
|
||||
});
|
||||
await aliceTestClient.httpBackend.flushAllExpected();
|
||||
await Promise.all([downloadPromise, sendPromise]);
|
||||
});
|
||||
|
||||
it("Alice exports megolm keys and imports them to a new device", function() {
|
||||
let messageEncrypted;
|
||||
|
||||
it("Alice exports megolm keys and imports them to a new device", async () => {
|
||||
aliceTestClient.expectKeyQuery({ device_keys: { '@alice:localhost': {} }, failures: {} });
|
||||
return aliceTestClient.start().then(() => {
|
||||
await aliceTestClient.start();
|
||||
// establish an olm session with alice
|
||||
return createOlmSession(testOlmAccount, aliceTestClient);
|
||||
}).then((p2pSession) => {
|
||||
const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient);
|
||||
|
||||
const groupSession = new Olm.OutboundGroupSession();
|
||||
groupSession.create();
|
||||
|
||||
@ -897,74 +821,56 @@ describe("megolm", function() {
|
||||
});
|
||||
|
||||
// encrypt a message with the group session
|
||||
messageEncrypted = encryptMegolmEvent({
|
||||
const messageEncrypted = encryptMegolmEvent({
|
||||
senderKey: testSenderKey,
|
||||
groupSession: groupSession,
|
||||
room_id: ROOM_ID,
|
||||
});
|
||||
|
||||
// Alice gets both the events in a single sync
|
||||
const syncResponse = {
|
||||
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, {
|
||||
next_batch: 1,
|
||||
to_device: {
|
||||
events: [roomKeyEncrypted],
|
||||
},
|
||||
rooms: {
|
||||
join: {},
|
||||
join: { [ROOM_ID]: { timeline: { events: [messageEncrypted] } } },
|
||||
},
|
||||
};
|
||||
syncResponse.rooms.join[ROOM_ID] = {
|
||||
timeline: {
|
||||
events: [messageEncrypted],
|
||||
},
|
||||
};
|
||||
});
|
||||
await aliceTestClient.flushSync();
|
||||
|
||||
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse);
|
||||
return aliceTestClient.flushSync();
|
||||
}).then(async function() {
|
||||
const room = aliceTestClient.client.getRoom(ROOM_ID);
|
||||
await room.decryptCriticalEvents();
|
||||
const event = room.getLiveTimeline().getEvents()[0];
|
||||
expect(event.getContent().body).toEqual('42');
|
||||
expect(room.getLiveTimeline().getEvents()[0].getContent().body).toEqual('42');
|
||||
|
||||
const exported = await aliceTestClient.client.exportRoomKeys();
|
||||
|
||||
return aliceTestClient.client.exportRoomKeys();
|
||||
}).then(function(exported) {
|
||||
// start a new client
|
||||
aliceTestClient.stop();
|
||||
|
||||
aliceTestClient = new TestClient(
|
||||
"@alice:localhost", "device2", "access_token2",
|
||||
);
|
||||
return aliceTestClient.client.initCrypto().then(() => {
|
||||
aliceTestClient.client.importRoomKeys(exported);
|
||||
return aliceTestClient.start();
|
||||
});
|
||||
}).then(function() {
|
||||
await aliceTestClient.client.initCrypto();
|
||||
await aliceTestClient.client.importRoomKeys(exported);
|
||||
await aliceTestClient.start();
|
||||
|
||||
const syncResponse = {
|
||||
next_batch: 1,
|
||||
rooms: {
|
||||
join: {},
|
||||
},
|
||||
};
|
||||
syncResponse.rooms.join[ROOM_ID] = {
|
||||
timeline: {
|
||||
events: [messageEncrypted],
|
||||
join: { [ROOM_ID]: { timeline: { events: [messageEncrypted] } } },
|
||||
},
|
||||
};
|
||||
|
||||
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse);
|
||||
return aliceTestClient.flushSync();
|
||||
}).then(function() {
|
||||
const room = aliceTestClient.client.getRoom(ROOM_ID);
|
||||
await aliceTestClient.flushSync();
|
||||
|
||||
const event = room.getLiveTimeline().getEvents()[0];
|
||||
expect(event.getContent().body).toEqual('42');
|
||||
});
|
||||
});
|
||||
|
||||
it("Alice receives an untrusted megolm key, only to receive the trusted one shortly after", function() {
|
||||
const testClient = new TestClient(
|
||||
"@alice:localhost", "device2", "access_token2",
|
||||
);
|
||||
it("Alice receives an untrusted megolm key, only to receive the trusted one shortly after", async () => {
|
||||
const testClient = new TestClient("@alice:localhost", "device2", "access_token2");
|
||||
const groupSession = new Olm.OutboundGroupSession();
|
||||
groupSession.create();
|
||||
const inboundGroupSession = new Olm.InboundGroupSession();
|
||||
@ -974,7 +880,7 @@ describe("megolm", function() {
|
||||
groupSession: groupSession,
|
||||
room_id: ROOM_ID,
|
||||
});
|
||||
return testClient.client.initCrypto().then(() => {
|
||||
await testClient.client.initCrypto();
|
||||
const keys = [{
|
||||
room_id: ROOM_ID,
|
||||
algorithm: 'm.megolm.v1.aes-sha2',
|
||||
@ -984,18 +890,17 @@ describe("megolm", function() {
|
||||
forwarding_curve25519_key_chain: [],
|
||||
sender_claimed_keys: {},
|
||||
}];
|
||||
return testClient.client.importRoomKeys(keys, { untrusted: true });
|
||||
}).then(() => {
|
||||
const event = testUtils.mkEvent({
|
||||
await testClient.client.importRoomKeys(keys, { untrusted: true });
|
||||
|
||||
const event1 = testUtils.mkEvent({
|
||||
event: true,
|
||||
...rawEvent,
|
||||
room: ROOM_ID,
|
||||
});
|
||||
return event.attemptDecryption(testClient.client.crypto, { isRetry: true }).then(() => {
|
||||
expect(event.isKeySourceUntrusted()).toBeTruthy();
|
||||
});
|
||||
}).then(() => {
|
||||
const event = testUtils.mkEvent({
|
||||
await event1.attemptDecryption(testClient.client.crypto, { isRetry: true });
|
||||
expect(event1.isKeySourceUntrusted()).toBeTruthy();
|
||||
|
||||
const event2 = testUtils.mkEvent({
|
||||
type: 'm.room_key',
|
||||
content: {
|
||||
room_id: ROOM_ID,
|
||||
@ -1006,26 +911,23 @@ describe("megolm", function() {
|
||||
event: true,
|
||||
});
|
||||
// @ts-ignore - private
|
||||
event.senderCurve25519Key = testSenderKey;
|
||||
event2.senderCurve25519Key = testSenderKey;
|
||||
// @ts-ignore - private
|
||||
return testClient.client.crypto.onRoomKeyEvent(event);
|
||||
}).then(() => {
|
||||
const event = testUtils.mkEvent({
|
||||
testClient.client.crypto.onRoomKeyEvent(event2);
|
||||
|
||||
const event3 = testUtils.mkEvent({
|
||||
event: true,
|
||||
...rawEvent,
|
||||
room: ROOM_ID,
|
||||
});
|
||||
return event.attemptDecryption(testClient.client.crypto, { isRetry: true }).then(() => {
|
||||
expect(event.isKeySourceUntrusted()).toBeFalsy();
|
||||
await event3.attemptDecryption(testClient.client.crypto, { isRetry: true });
|
||||
expect(event3.isKeySourceUntrusted()).toBeFalsy();
|
||||
testClient.stop();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("Alice can decrypt a message with falsey content", function() {
|
||||
return aliceTestClient.start().then(() => {
|
||||
return createOlmSession(testOlmAccount, aliceTestClient);
|
||||
}).then((p2pSession) => {
|
||||
it("Alice can decrypt a message with falsey content", async () => {
|
||||
await aliceTestClient.start();
|
||||
const p2pSession = await createOlmSession(testOlmAccount, aliceTestClient);
|
||||
const groupSession = new Olm.OutboundGroupSession();
|
||||
groupSession.create();
|
||||
|
||||
@ -1063,26 +965,19 @@ describe("megolm", function() {
|
||||
events: [roomKeyEncrypted],
|
||||
},
|
||||
rooms: {
|
||||
join: {},
|
||||
},
|
||||
};
|
||||
syncResponse.rooms.join[ROOM_ID] = {
|
||||
timeline: {
|
||||
events: [messageEncrypted],
|
||||
join: { [ROOM_ID]: { timeline: { events: [messageEncrypted] } } },
|
||||
},
|
||||
};
|
||||
|
||||
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse);
|
||||
return aliceTestClient.flushSync();
|
||||
}).then(function() {
|
||||
await aliceTestClient.flushSync();
|
||||
|
||||
const room = aliceTestClient.client.getRoom(ROOM_ID);
|
||||
const event = room.getLiveTimeline().getEvents()[0];
|
||||
expect(event.isEncrypted()).toBe(true);
|
||||
return testUtils.awaitDecryption(event);
|
||||
}).then((event) => {
|
||||
expect(event.getRoomId()).toEqual(ROOM_ID);
|
||||
expect(event.getContent()).toEqual({});
|
||||
expect(event.getClearContent()).toBeUndefined();
|
||||
});
|
||||
const decryptedEvent = await testUtils.awaitDecryption(event);
|
||||
expect(decryptedEvent.getRoomId()).toEqual(ROOM_ID);
|
||||
expect(decryptedEvent.getContent()).toEqual({});
|
||||
expect(decryptedEvent.getClearContent()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
@ -129,6 +129,21 @@ export function mkEvent(opts: IEventOpts & { event?: boolean }, client?: MatrixC
|
||||
return opts.event ? new MatrixEvent(event) : event;
|
||||
}
|
||||
|
||||
type GeneratedMetadata = {
|
||||
event_id: string;
|
||||
txn_id: string;
|
||||
origin_server_ts: number;
|
||||
};
|
||||
|
||||
export function mkEventCustom<T>(base: T): T & GeneratedMetadata {
|
||||
return {
|
||||
event_id: "$" + testEventIndex++ + "-" + Math.random() + "-" + Math.random(),
|
||||
txn_id: "~" + Math.random(),
|
||||
origin_server_ts: Date.now(),
|
||||
...base,
|
||||
};
|
||||
}
|
||||
|
||||
interface IPresenceOpts {
|
||||
user?: string;
|
||||
sender?: string;
|
||||
@ -208,6 +223,18 @@ export function mkMembership(opts: IMembershipOpts & { event?: boolean }): Parti
|
||||
return mkEvent(eventOpts);
|
||||
}
|
||||
|
||||
export function mkMembershipCustom<T>(
|
||||
base: T & { membership: string, sender: string, content?: IContent },
|
||||
): T & { type: EventType, sender: string, state_key: string, content: IContent } & GeneratedMetadata {
|
||||
const content = base.content || {};
|
||||
return mkEventCustom({
|
||||
...base,
|
||||
content: { ...content, membership: base.membership },
|
||||
type: EventType.RoomMember,
|
||||
state_key: base.sender,
|
||||
});
|
||||
}
|
||||
|
||||
interface IMessageOpts {
|
||||
room?: string;
|
||||
user: string;
|
||||
|
Reference in New Issue
Block a user