Skip to content

Commit bb18565

Browse files
committed
tests: Signature upload support in E2EKeyReceiver
Have `E2EKeyReceiver` collect uploaded device signatures, so that they can be returned by `E2EKeyResponder`.
1 parent d6a65c7 commit bb18565

File tree

5 files changed

+48
-23
lines changed

5 files changed

+48
-23
lines changed

spec/integ/crypto/cross-signing.spec.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,6 @@ describe("cross-signing", () => {
225225
await aliceClient.startClient();
226226
await syncPromise(aliceClient);
227227

228-
// we expect a request to upload signatures for our device ...
229-
fetchMock.post({ url: "path:/_matrix/client/v3/keys/signatures/upload", name: "upload-sigs" }, {});
230-
231228
// we expect the UserTrustStatusChanged event to be fired after the cross signing keys import
232229
const userTrustStatusChangedPromise = new Promise<string>((resolve) =>
233230
aliceClient.on(CryptoEvent.UserTrustStatusChanged, resolve),

spec/integ/crypto/device-dehydration.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,6 @@ async function initializeSecretStorage(
181181
const e2eKeyReceiver = new E2EKeyReceiver(homeserverUrl);
182182
const e2eKeyResponder = new E2EKeyResponder(homeserverUrl);
183183
e2eKeyResponder.addKeyReceiver(userId, e2eKeyReceiver);
184-
fetchMock.post("path:/_matrix/client/v3/keys/signatures/upload", {});
185184
const accountData: Map<string, object> = new Map();
186185
fetchMock.get("glob:http://*/_matrix/client/v3/user/*/account_data/*", (url, opts) => {
187186
const name = url.split("/").pop()!;

spec/test-utils/E2EKeyReceiver.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import debugFunc, { type Debugger } from "debug";
1818
import fetchMock from "fetch-mock-jest";
1919

2020
import type { IDeviceKeys, IOneTimeKey } from "../../src/@types/crypto";
21-
import type { CrossSigningKeys } from "../../src";
21+
import type { CrossSigningKeys, ISignedKey, KeySignatures } from "../../src";
22+
import type { CrossSigningKeyInfo } from "../../src/crypto-api";
2223

2324
/** Interface implemented by classes that intercept `/keys/upload` requests from test clients to catch the uploaded keys
2425
*
@@ -62,10 +63,15 @@ export class E2EKeyReceiver implements IE2EKeyReceiver {
6263
/**
6364
* Construct a new E2EKeyReceiver.
6465
*
65-
* It will immediately register an intercept of `/keys/uploads` and `/keys/device_signing/upload` requests for the given homeserverUrl.
66+
* It will immediately register an intercept of [`/keys/upload`][1], [`/keys/signatures/upload`][2] and
67+
* [`/keys/device_signing/upload`][3] requests for the given homeserverUrl.
6668
* Only requests made to this server will be intercepted: this allows a single test to use more than one
6769
* client and have the keys collected separately.
6870
*
71+
* [1]: https://spec.matrix.org/v1.14/client-server-api/#post_matrixclientv3keysupload
72+
* [2]: https://spec.matrix.org/v1.14/client-server-api/#post_matrixclientv3keyssignaturesupload
73+
* [3]: https://spec.matrix.org/v1.14/client-server-api/#post_matrixclientv3keysdevice_signingupload
74+
*
6975
* @param homeserverUrl - the Homeserver Url of the client under test.
7076
*/
7177
public constructor(homeserverUrl: string) {
@@ -79,6 +85,14 @@ export class E2EKeyReceiver implements IE2EKeyReceiver {
7985
fetchMock.post(new URL("/_matrix/client/v3/keys/upload", homeserverUrl).toString(), listener);
8086
});
8187

88+
fetchMock.post(
89+
{
90+
url: new URL("/_matrix/client/v3/keys/signatures/upload", homeserverUrl).toString(),
91+
name: "upload-sigs",
92+
},
93+
(url, options) => this.onSignaturesUploadRequest(options),
94+
);
95+
8296
fetchMock.post(
8397
{
8498
url: new URL("/_matrix/client/v3/keys/device_signing/upload", homeserverUrl).toString(),
@@ -96,8 +110,10 @@ export class E2EKeyReceiver implements IE2EKeyReceiver {
96110
if (this.deviceKeys) {
97111
throw new Error("Application attempted to upload E2E device keys multiple times");
98112
}
99-
this.debug(`received device keys`);
100113
this.deviceKeys = content.device_keys;
114+
this.debug(
115+
`received device keys for user ID ${this.deviceKeys!.user_id}, device ID ${this.deviceKeys!.device_id}`,
116+
);
101117
}
102118

103119
if (content.one_time_keys && Object.keys(content.one_time_keys).length > 0) {
@@ -122,6 +138,35 @@ export class E2EKeyReceiver implements IE2EKeyReceiver {
122138
};
123139
}
124140

141+
private async onSignaturesUploadRequest(request: RequestInit): Promise<object> {
142+
const content = JSON.parse(request.body as string) as KeySignatures;
143+
for (const [userId, userKeys] of Object.entries(content)) {
144+
for (const [deviceId, signedKey] of Object.entries(userKeys)) {
145+
this.onDeviceSignatureUpload(userId, deviceId, signedKey);
146+
}
147+
}
148+
149+
return {};
150+
}
151+
152+
private onDeviceSignatureUpload(userId: string, deviceId: string, signedKey: CrossSigningKeyInfo | ISignedKey) {
153+
if (!this.deviceKeys || userId != this.deviceKeys.user_id || deviceId != this.deviceKeys.device_id) {
154+
this.debug(
155+
`Ignoring device key signature upload for unknown device user ID ${userId}, device ID ${deviceId}`,
156+
);
157+
return;
158+
}
159+
160+
this.debug(`received device key signature for user ID ${userId}, device ID ${deviceId}`);
161+
this.deviceKeys.signatures ??= {};
162+
for (const [signingUser, signatures] of Object.entries(signedKey.signatures!)) {
163+
this.deviceKeys.signatures[signingUser] = Object.assign(
164+
this.deviceKeys.signatures[signingUser] ?? {},
165+
signatures,
166+
);
167+
}
168+
}
169+
125170
private async onSigningKeyUploadRequest(request: RequestInit): Promise<object> {
126171
const content = JSON.parse(request.body as string);
127172
if (this.crossSigningKeys) {

spec/test-utils/mockEndpoints.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,13 @@ export function mockInitialApiRequests(homeserverUrl: string, userId: string = "
4646
* Mock the requests needed to set up cross signing, besides those provided by {@link E2EKeyReceiver}.
4747
*
4848
* Return 404 error for `GET _matrix/client/v3/user/:userId/account_data/:type` request
49-
* Return `{}` for `POST _matrix/client/v3/keys/signatures/upload` request (named `upload-sigs` for fetchMock check)
5049
*/
5150
export function mockSetupCrossSigningRequests(): void {
5251
// have account_data requests return an empty object
5352
fetchMock.get("express:/_matrix/client/v3/user/:userId/account_data/:type", {
5453
status: 404,
5554
body: { errcode: "M_NOT_FOUND", error: "Account data not found." },
5655
});
57-
58-
// we expect a request to upload signatures for our device ...
59-
fetchMock.post({ url: "path:/_matrix/client/v3/keys/signatures/upload", name: "upload-sigs" }, {});
6056
}
6157

6258
/**

spec/unit/rust-crypto/rust-crypto.spec.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,10 +1548,6 @@ describe("RustCrypto", () => {
15481548
const e2eKeyReceiver = new E2EKeyReceiver("http://server");
15491549
const e2eKeyResponder = new E2EKeyResponder("http://server");
15501550
e2eKeyResponder.addKeyReceiver(TEST_USER, e2eKeyReceiver);
1551-
fetchMock.post("path:/_matrix/client/v3/keys/signatures/upload", {
1552-
status: 200,
1553-
body: {},
1554-
});
15551551
await rustCrypto.bootstrapCrossSigning({ setupNewCrossSigning: true });
15561552
await expect(rustCrypto.pinCurrentUserIdentity(TEST_USER)).rejects.toThrow(
15571553
"Cannot pin identity of own user",
@@ -1789,10 +1785,6 @@ describe("RustCrypto", () => {
17891785
error: "Not found",
17901786
},
17911787
});
1792-
fetchMock.post("path:/_matrix/client/v3/keys/signatures/upload", {
1793-
status: 200,
1794-
body: {},
1795-
});
17961788
const rustCrypto1 = await makeTestRustCrypto(makeMatrixHttpApi(), TEST_USER, TEST_DEVICE_ID, secretStorage);
17971789

17981790
// dehydration requires secret storage and cross signing
@@ -1926,10 +1918,6 @@ describe("RustCrypto", () => {
19261918
error: "Not found",
19271919
},
19281920
});
1929-
fetchMock.post("path:/_matrix/client/v3/keys/signatures/upload", {
1930-
status: 200,
1931-
body: {},
1932-
});
19331921
rustCrypto = await makeTestRustCrypto(makeMatrixHttpApi(), TEST_USER, TEST_DEVICE_ID, secretStorage);
19341922

19351923
// dehydration requires secret storage and cross signing

0 commit comments

Comments
 (0)