1
0
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:
Faye Duxovni
2022-07-21 06:41:46 -04:00
committed by GitHub
parent 32f55de383
commit 45db39ec88
2 changed files with 693 additions and 771 deletions

View File

@ -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();
});
});

View File

@ -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;