1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-08-06 12:02:40 +03:00

Experimental support for sharing encrypted history on invite (#4920)

* tests: Cross-signing keys support in `E2EKeyReceiver`

Have `E2EKeyReceiver` collect uploaded cross-signing keys, so that they can be
returned by `E2EKeyResponder`.

* tests: Signature upload support in `E2EKeyReceiver`

Have `E2EKeyReceiver` collect uploaded device signatures, so that they can be
returned by `E2EKeyResponder`.

* tests: Implement `E2EOTKClaimResponder` class

A new test helper, which intercepts `/keys/claim`, allowing clients under test
to claim OTKs uploaded by other devices.

* Expose experimental settings for encrypted history sharing

Add options to `MatrixClient.invite` and `MatrixClient.joinRoom` to share and
accept encrypted history on invite, per MSC4268.

* Clarify pre-join-membership logic

* Improve tests

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

Co-authored-by: Hubert Chathi <hubertc@matrix.org>

---------

Co-authored-by: Hubert Chathi <hubertc@matrix.org>
This commit is contained in:
Richard van der Hoff
2025-07-29 16:42:35 +01:00
committed by GitHub
parent 56b24c0bdc
commit c4e1e0723e
13 changed files with 694 additions and 89 deletions

View File

@@ -17,7 +17,7 @@ limitations under the License.
import fetchMock from "fetch-mock-jest";
import { MapWithDefault } from "../../src/utils";
import { type IDownloadKeyResult } from "../../src";
import { type IDownloadKeyResult, type SigningKeys } from "../../src";
import { type IDeviceKeys } from "../../src/@types/crypto";
import { type E2EKeyReceiver } from "./E2EKeyReceiver";
@@ -50,18 +50,14 @@ export class E2EKeyResponder {
const content = JSON.parse(options.body as string);
const usersToReturn = Object.keys(content["device_keys"]);
const response = {
device_keys: {} as { [userId: string]: any },
master_keys: {} as { [userId: string]: any },
self_signing_keys: {} as { [userId: string]: any },
user_signing_keys: {} as { [userId: string]: any },
failures: {} as { [serverName: string]: any },
};
device_keys: {},
master_keys: {},
self_signing_keys: {},
user_signing_keys: {},
failures: {},
} as IDownloadKeyResult;
for (const user of usersToReturn) {
const userKeys = this.deviceKeysByUserByDevice.get(user);
if (userKeys !== undefined) {
response.device_keys[user] = Object.fromEntries(userKeys.entries());
}
// First see if we have an E2EKeyReceiver for this user, and if so, return any keys that have been uploaded
const e2eKeyReceiver = this.e2eKeyReceiversByUser.get(user);
if (e2eKeyReceiver !== undefined) {
const deviceKeys = e2eKeyReceiver.getUploadedDeviceKeys();
@@ -69,16 +65,27 @@ export class E2EKeyResponder {
response.device_keys[user] ??= {};
response.device_keys[user][deviceKeys.device_id] = deviceKeys;
}
const crossSigningKeys = e2eKeyReceiver.getUploadedCrossSigningKeys();
if (crossSigningKeys !== null) {
response.master_keys![user] = crossSigningKeys["master_key"];
response.self_signing_keys![user] = crossSigningKeys["self_signing_key"] as SigningKeys;
}
}
// Mix in any keys that have been added explicitly to this E2EKeyResponder.
const userKeys = this.deviceKeysByUserByDevice.get(user);
if (userKeys !== undefined) {
response.device_keys[user] ??= {};
Object.assign(response.device_keys[user], Object.fromEntries(userKeys.entries()));
}
if (this.masterKeysByUser.hasOwnProperty(user)) {
response.master_keys[user] = this.masterKeysByUser[user];
response.master_keys![user] = this.masterKeysByUser[user];
}
if (this.selfSigningKeysByUser.hasOwnProperty(user)) {
response.self_signing_keys[user] = this.selfSigningKeysByUser[user];
response.self_signing_keys![user] = this.selfSigningKeysByUser[user];
}
if (this.userSigningKeysByUser.hasOwnProperty(user)) {
response.user_signing_keys[user] = this.userSigningKeysByUser[user];
response.user_signing_keys![user] = this.userSigningKeysByUser[user];
}
}
return response;