Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Element-R: Refactor per-session key backup download #3929

Merged
merged 34 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
328b85e
initial commit
BillCarsonFr Nov 29, 2023
7f1809b
Merge branch 'develop' into valere/element-r/backup/per_session_download
BillCarsonFr Nov 29, 2023
00df273
new interation test
BillCarsonFr Nov 29, 2023
3f8c99e
more comments
BillCarsonFr Nov 29, 2023
bcdc67f
Merge branch 'develop' into valere/element-r/backup/per_session_download
BillCarsonFr Nov 29, 2023
260620f
Merge branch 'develop' into valere/element-r/backup/per_session_download
BillCarsonFr Nov 30, 2023
c9ad13b
fix test, quick refactor on request version
BillCarsonFr Dec 1, 2023
c58f3d1
cleaning and logs
BillCarsonFr Dec 1, 2023
f091251
fix type
BillCarsonFr Dec 1, 2023
c528e52
Merge branch 'develop' into valere/element-r/backup/per_session_download
BillCarsonFr Dec 1, 2023
f1235c8
cleaning
BillCarsonFr Dec 1, 2023
d503411
remove delegate stuff
BillCarsonFr Dec 5, 2023
d6d1a7d
remove events and use timer mocks
BillCarsonFr Dec 5, 2023
9823cb0
Merge branch 'develop' into valere/element-r/backup/per_session_download
BillCarsonFr Dec 5, 2023
2ad906a
fix import
BillCarsonFr Dec 5, 2023
26ea4ae
ts ignore in tests
BillCarsonFr Dec 5, 2023
2951a9a
Quick cleaning
BillCarsonFr Dec 5, 2023
db3c736
code review
BillCarsonFr Dec 6, 2023
d45f8ec
Merge branch 'develop' into valere/element-r/backup/per_session_download
BillCarsonFr Dec 6, 2023
c5abd6f
Use Errors instead of Results
BillCarsonFr Dec 7, 2023
09505d2
Merge branch 'develop' into valere/element-r/backup/per_session_download
BillCarsonFr Dec 7, 2023
2245bc2
cleaning
BillCarsonFr Dec 7, 2023
f9ad97d
Merge branch 'develop' into valere/element-r/backup/per_session_download
BillCarsonFr Dec 7, 2023
c0cd300
review
BillCarsonFr Dec 7, 2023
ddbd3b5
remove forceCheck as not useful
BillCarsonFr Dec 7, 2023
5418dc0
bad naming
BillCarsonFr Dec 7, 2023
9d05476
inline pauseLoop
BillCarsonFr Dec 7, 2023
1545127
mark as paused in finally
BillCarsonFr Dec 7, 2023
e890727
code review
BillCarsonFr Dec 7, 2023
b244fd0
post merge fix
BillCarsonFr Dec 7, 2023
e067b61
Merge branch 'develop' into valere/element-r/backup/per_session_download
BillCarsonFr Dec 7, 2023
b34f15f
rename KeyDownloadRateLimit
BillCarsonFr Dec 7, 2023
336e9f6
use same config in loop and pass along
BillCarsonFr Dec 7, 2023
3396635
Merge branch 'develop' into valere/element-r/backup/per_session_download
BillCarsonFr Dec 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 146 additions & 1 deletion spec/integ/crypto/megolm-backup.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import fetchMock from "fetch-mock-jest";
import "fake-indexeddb/auto";
import { IDBFactory } from "fake-indexeddb";

import { createClient, CryptoEvent, ICreateClientOpts, MatrixClient, TypedEventEmitter } from "../../../src";
import { createClient, CryptoEvent, ICreateClientOpts, IEvent, MatrixClient, TypedEventEmitter } from "../../../src";
import { SyncResponder } from "../../test-utils/SyncResponder";
import { E2EKeyReceiver } from "../../test-utils/E2EKeyReceiver";
import { E2EKeyResponder } from "../../test-utils/E2EKeyResponder";
Expand All @@ -34,6 +34,7 @@ import * as testData from "../../test-utils/test-data";
import { KeyBackupInfo } from "../../../src/crypto-api/keybackup";
import { IKeyBackup } from "../../../src/crypto/backup";
import { flushPromises } from "../../test-utils/flushPromises";
import { defer, IDeferred } from "../../../src/utils";

const ROOM_ID = testData.TEST_ROOM_ID;

Expand Down Expand Up @@ -888,6 +889,150 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe
});
});

describe("Backup Changed from other sessions", () => {
beforeEach(async () => {
fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA);

// ignore requests to send room key requests
fetchMock.put("express:/_matrix/client/v3/sendToDevice/m.room_key_request/:request_id", {});

aliceClient = await initTestClient();
const aliceCrypto = aliceClient.getCrypto()!;
await aliceCrypto.storeSessionBackupPrivateKey(
Buffer.from(testData.BACKUP_DECRYPTION_KEY_BASE64, "base64"),
testData.SIGNED_BACKUP_DATA.version!,
);

// start after saving the private key
await aliceClient.startClient();

// tell Alice to trust the dummy device that signed the backup, and re-check the backup.
// XXX: should we automatically re-check after a device becomes verified?
await waitForDeviceList();
await aliceClient.getCrypto()!.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID);
await aliceClient.getCrypto()!.checkKeyBackupAndEnable();
});

// let aliceClient: MatrixClient;

const SYNC_RESPONSE = {
next_batch: 1,
rooms: { join: { [ROOM_ID]: { timeline: { events: [testData.ENCRYPTED_EVENT] } } } },
};

it("If current backup has changed, the manager should switch to the new one on UTD", async () => {
// fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA);

// aliceClient = await initTestClient();

// =====
// First ensure that the client checks for keys using the backup version 1
/// =====

fetchMock.get(
"express:/_matrix/client/v3/room_keys/keys/:room_id/:session_id",
(url, request) => {
// check that the version is correct
const version = new URLSearchParams(new URL(url).search).get("version");
if (version == "1") {
return testData.CURVE25519_KEY_BACKUP_DATA;
} else {
return {
status: 403,
body: {
current_version: "1",
errcode: "M_WRONG_ROOM_KEYS_VERSION",
error: "Wrong backup version.",
},
};
}
},
{ overwriteRoutes: true },
);

// Send Alice a message that she won't be able to decrypt, and check that she fetches the key from the backup.
syncResponder.sendOrQueueSyncResponse(SYNC_RESPONSE);
await syncPromise(aliceClient);

const room = aliceClient.getRoom(ROOM_ID)!;
const event = room.getLiveTimeline().getEvents()[0];
await advanceTimersUntil(awaitDecryption(event, { waitOnDecryptionFailure: true }));

expect(event.getContent()).toEqual(testData.CLEAR_EVENT.content);

// =====
// Second suppose now that the backup has changed to version 2
/// =====

const newBackup = {
...testData.SIGNED_BACKUP_DATA,
version: "2",
};

fetchMock.get("path:/_matrix/client/v3/room_keys/version", newBackup, { overwriteRoutes: true });
// suppose the new key is now known
const aliceCrypto = aliceClient.getCrypto()!;
await aliceCrypto.storeSessionBackupPrivateKey(
Buffer.from(testData.BACKUP_DECRYPTION_KEY_BASE64, "base64"),
newBackup.version,
);

// A check backup should happen at some point
await aliceCrypto.checkKeyBackupAndEnable();

const awaitHasQueriedNewBackup: IDeferred<void> = defer<void>();

fetchMock.get(
"express:/_matrix/client/v3/room_keys/keys/:room_id/:session_id",
(url, request) => {
// check that the version is correct
const version = new URLSearchParams(new URL(url).search).get("version");
if (version == newBackup.version) {
awaitHasQueriedNewBackup.resolve();
return testData.CURVE25519_KEY_BACKUP_DATA;
} else {
// awaitHasQueriedOldBackup.resolve();
return {
status: 403,
body: {
current_version: "2",
errcode: "M_WRONG_ROOM_KEYS_VERSION",
error: "Wrong backup version.",
},
};
}
},
{ overwriteRoutes: true },
);

// Send Alice a message that she won't be able to decrypt, and check that she fetches the key from the new backup.
const newMessage: Partial<IEvent> = {
type: "m.room.encrypted",
room_id: "!room:id",
sender: "@alice:localhost",
content: {
algorithm: "m.megolm.v1.aes-sha2",
ciphertext:
"AwgAEpABKvf9FqPW52zeHfeVTn90a3jlBLlx7g6VDEkc2089RQUJoWpSJRiK13E83rN41wgGFJccyfoCr7ZDGJeuGYMGETTrgnLQhLs6JmyPf37JYkzxW8uS8rGUKEqTFQriKhibHVLvVacOlSIObUiKU/V3r176XuixqZF/4eyK9A22JNpInbgI10ZUT6LnApH9LR3FpZbE2zImf1uNPuvp7r0xQbW7CcJjqpH+qTPBD5zFdFnMkc2SnbXCsIOaX11Dm0krWfQz7iA26ZnI1nyZnyh7XPrCnJCRsuQH",
device_id: "WVMJGTSSVB",
sender_key: "E5RiY/YCIrHWaF4u416CqvblC6udK2jt9SJ/h1QeLS0",
session_id: "ybnW+LGdUhoS4fHm1DAEphukO3sZ1GCqZD7UQz7L+GA",
},
event_id: "$event2",
origin_server_ts: 1507753887000,
};

const nextSyncResponse = {
next_batch: 2,
rooms: { join: { [ROOM_ID]: { timeline: { events: [newMessage] } } } },
};
syncResponder.sendOrQueueSyncResponse(nextSyncResponse);
await syncPromise(aliceClient);

await awaitHasQueriedNewBackup.promise;
});
});

/** make sure that the client knows about the dummy device */
async function waitForDeviceList(): Promise<void> {
// Completing the initial sync will make the device list download outdated device lists (of which our own
Expand Down
Loading
Loading