1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-26 17:03:12 +03:00

Element-R: support for manual import/export of Room keys (#3364)

* Rust manual import/export for keys

* code review

* code review

* post merge fix

* code review

* doc: comma splice

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* Better test name

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* quick doc update

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

---------

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
This commit is contained in:
Valere
2023-06-13 12:28:35 +02:00
committed by GitHub
parent b47c87f909
commit 9f6073478f
6 changed files with 127 additions and 40 deletions

View File

@@ -28,6 +28,7 @@ import { CryptoBackend } from "../../../src/common-crypto/CryptoBackend";
import { IEventDecryptionResult } from "../../../src/@types/crypto";
import { OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor";
import { ServerSideSecretStorage } from "../../../src/secret-storage";
import { ImportRoomKeysOpts } from "../../../src/crypto-api";
afterEach(() => {
// reset fake-indexeddb after each test, to make sure we don't leak connections
@@ -40,16 +41,59 @@ const TEST_USER = "@alice:example.com";
const TEST_DEVICE_ID = "TEST_DEVICE";
describe("RustCrypto", () => {
describe(".exportRoomKeys", () => {
describe(".importRoomKeys and .exportRoomKeys", () => {
let rustCrypto: RustCrypto;
beforeEach(async () => {
rustCrypto = await makeTestRustCrypto();
});
it("should return a list", async () => {
it("should import and export keys", async () => {
const someRoomKeys = [
{
algorithm: "m.megolm.v1.aes-sha2",
room_id: "!cLDYAnjpiQXIrSwngM:localhost:8480",
sender_key: "C9FMqTD20C0VaGWE/aSImkimuE6HDa/RyYj5gRUg3gY",
session_id: "iGQG5GaP1/B3dSH6zCQDQqrNuotrtQjVC7w1OsUDwbg",
session_key:
"AQAAAADaCbP2gdOy8jrhikjploKgSBaFSJ5rvHcziaADbwNEzeCSrfuAUlXvCvxik8kU+MfCHIi5arN2M7UM5rGKdzkHnkReoIByFkeMdbjKWk5SFpVQexcM74eDhBGj+ICkQqOgApfnEbSswrmreB0+MhHHyLStwW5fy5f8A9QW1sbPuohkBuRmj9fwd3Uh+swkA0KqzbqLa7UI1Qu8NTrFA8G4",
sender_claimed_keys: {
ed25519: "RSq0Xw0RR0DeqlJ/j3qrF5qbN0D96fKk8lz9kZJlG9k",
},
forwarding_curve25519_key_chain: [],
},
{
algorithm: "m.megolm.v1.aes-sha2",
room_id: "!cLDYAnjpiQXIrSwngM:localhost:8480",
sender_key: "C9FMqTD20C0VaGWE/aSImkimuE6HDa/RyYj5gRUg3gY",
session_id: "P/Jy9Tog4CMtLseeS4Fe2AEXZov3k6cibcop/uyhr78",
session_key:
"AQAAAAATyAVm0c9c9DW9Od72MxvfSDYoysBw3C6yMJ3bYuTmssHN7yNGm59KCtKeFp2Y5qO7lvUmwOfSTvTASUb7HViE7Lt+Bvp5WiMTJ2Pv6m+N12ihyowV5lgtKFWI18Wxd0AugMTVQRwjBK6aMobf86NXWD2hiKm3N6kWbC0PXmqV7T/ycvU6IOAjLS7HnkuBXtgBF2aL95OnIm3KKf7soa+/",
sender_claimed_keys: {
ed25519: "RSq0Xw0RR0DeqlJ/j3qrF5qbN0D96fKk8lz9kZJlG9k",
},
forwarding_curve25519_key_chain: [],
},
];
let importTotal = 0;
const opt: ImportRoomKeysOpts = {
progressCallback: (stage) => {
importTotal = stage.total;
},
};
await rustCrypto.importRoomKeys(someRoomKeys, opt);
expect(importTotal).toBe(2);
const keys = await rustCrypto.exportRoomKeys();
expect(Array.isArray(keys)).toBeTruthy();
expect(keys.length).toBe(2);
const aSession = someRoomKeys[0];
const exportedKey = keys.find((k) => k.session_id == aSession.session_id);
expect(aSession).toStrictEqual(exportedKey);
});
});

View File

@@ -103,13 +103,7 @@ import { MatrixScheduler } from "./scheduler";
import { BeaconEvent, BeaconEventHandlerMap } from "./models/beacon";
import { IAuthData, IAuthDict } from "./interactive-auth";
import { IMinimalEvent, IRoomEvent, IStateEvent } from "./sync-accumulator";
import {
CrossSigningKey,
ICreateSecretStorageOpts,
IEncryptedEventInfo,
IImportRoomKeysOpts,
IRecoveryKey,
} from "./crypto/api";
import { CrossSigningKey, ICreateSecretStorageOpts, IEncryptedEventInfo, IRecoveryKey } from "./crypto/api";
import { EventTimelineSet } from "./models/event-timeline-set";
import { VerificationRequest } from "./crypto/verification/request/VerificationRequest";
import { VerificationBase as Verification } from "./crypto/verification/Base";
@@ -207,7 +201,7 @@ import { LocalNotificationSettings } from "./@types/local_notifications";
import { buildFeatureSupportMap, Feature, ServerSupport } from "./feature";
import { CryptoBackend } from "./common-crypto/CryptoBackend";
import { RUST_SDK_STORE_PREFIX } from "./rust-crypto/constants";
import { BootstrapCrossSigningOpts, CryptoApi } from "./crypto-api";
import { BootstrapCrossSigningOpts, CryptoApi, ImportRoomKeysOpts } from "./crypto-api";
import { DeviceInfoMap } from "./crypto/DeviceList";
import {
AddSecretStorageKeyOpts,
@@ -3195,14 +3189,20 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* Import a list of room keys previously exported by exportRoomKeys
*
* @param keys - a list of session export objects
* @param opts - options object
*
* @returns a promise which resolves when the keys have been imported
*
* @deprecated Prefer {@link CryptoApi.importRoomKeys | `CryptoApi.importRoomKeys`}:
* ```javascript
* await client.getCrypto()?.importRoomKeys([..]);
* ```
*/
public importRoomKeys(keys: IMegolmSessionData[], opts?: IImportRoomKeysOpts): Promise<void> {
if (!this.crypto) {
public importRoomKeys(keys: IMegolmSessionData[], opts?: ImportRoomKeysOpts): Promise<void> {
if (!this.cryptoBackend) {
throw new Error("End-to-end encryption disabled");
}
return this.crypto.importRoomKeys(keys, opts);
return this.cryptoBackend.importRoomKeys(keys, opts);
}
/**
@@ -3810,7 +3810,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
algorithm.free();
}
await this.importRoomKeys(keys, {
await this.getCrypto()?.importRoomKeys(keys, {
progressCallback,
untrusted,
source: "backup",

View File

@@ -82,6 +82,15 @@ export interface CryptoApi {
*/
exportRoomKeys(): Promise<IMegolmSessionData[]>;
/**
* Import a list of room keys previously exported by exportRoomKeys
*
* @param keys - a list of session export objects
* @param opts - options object
* @returns a promise which resolves once the keys have been imported
*/
importRoomKeys(keys: IMegolmSessionData[], opts?: ImportRoomKeysOpts): Promise<void>;
/**
* Get the device information for the given list of users.
*
@@ -269,6 +278,29 @@ export class DeviceVerificationStatus {
}
}
/**
* Room key import progress report.
* Used when calling {@link CryptoApi#importRoomKeys} as the parameter of
* the progressCallback. Used to display feedback.
*/
export interface ImportRoomKeyProgressData {
stage: string; // TODO: Enum
successes: number;
failures: number;
total: number;
}
/**
* Options object for {@link CryptoApi#importRoomKeys}.
*/
export interface ImportRoomKeysOpts {
/** Reports ongoing progress of the import process. Can be used for feedback. */
progressCallback?: (stage: ImportRoomKeyProgressData) => void;
// TODO, the rust SDK will always such imported keys as untrusted
untrusted?: boolean;
source?: String; // TODO: Enum (backup, file, ??)
}
export * from "./crypto-api/verification";
/**

View File

@@ -20,6 +20,12 @@ import type { AddSecretStorageKeyOpts } from "../secret-storage";
/* re-exports for backwards compatibility. */
export { CrossSigningKey } from "../crypto-api";
export type {
ImportRoomKeyProgressData as IImportOpts,
ImportRoomKeysOpts as IImportRoomKeysOpts,
} from "../crypto-api";
export type {
AddSecretStorageKeyOpts as IAddSecretStorageKeyOpts,
PassphraseInfo as IPassphraseInfo,
@@ -100,17 +106,3 @@ export interface ICreateSecretStorageOpts {
*/
getKeyBackupPassphrase?: () => Promise<Uint8Array>;
}
export interface IImportOpts {
stage: string; // TODO: Enum
successes: number;
failures: number;
total: number;
}
export interface IImportRoomKeysOpts {
/** called with an object that has a "stage" param */
progressCallback?: (stage: IImportOpts) => void;
untrusted?: boolean;
source?: string; // TODO: Enum
}

View File

@@ -35,13 +35,7 @@ import * as algorithms from "./algorithms";
import { createCryptoStoreCacheCallbacks, CrossSigningInfo, DeviceTrustLevel, UserTrustLevel } from "./CrossSigning";
import { EncryptionSetupBuilder } from "./EncryptionSetup";
import { SecretStorage as LegacySecretStorage } from "./SecretStorage";
import {
CrossSigningKey,
ICreateSecretStorageOpts,
IEncryptedEventInfo,
IImportRoomKeysOpts,
IRecoveryKey,
} from "./api";
import { CrossSigningKey, ICreateSecretStorageOpts, IEncryptedEventInfo, IRecoveryKey } from "./api";
import { OutgoingRoomKeyRequestManager } from "./OutgoingRoomKeyRequestManager";
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store";
import { VerificationBase } from "./verification/Base";
@@ -93,7 +87,12 @@ import {
ServerSideSecretStorageImpl,
} from "../secret-storage";
import { ISecretRequest } from "./SecretSharing";
import { BootstrapCrossSigningOpts, CrossSigningStatus, DeviceVerificationStatus } from "../crypto-api";
import {
BootstrapCrossSigningOpts,
CrossSigningStatus,
DeviceVerificationStatus,
ImportRoomKeysOpts,
} from "../crypto-api";
import { Device, DeviceMap } from "../models/device";
import { deviceInfoToDevice } from "./device-converter";
@@ -2852,7 +2851,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
* @param keys - a list of session export objects
* @returns a promise which resolves once the keys have been imported
*/
public importRoomKeys(keys: IMegolmSessionData[], opts: IImportRoomKeysOpts = {}): Promise<void> {
public importRoomKeys(keys: IMegolmSessionData[], opts: ImportRoomKeysOpts = {}): Promise<void> {
let successes = 0;
let failures = 0;
const total = keys.length;

View File

@@ -30,7 +30,13 @@ import { RoomEncryptor } from "./RoomEncryptor";
import { OutgoingRequest, OutgoingRequestProcessor } from "./OutgoingRequestProcessor";
import { KeyClaimManager } from "./KeyClaimManager";
import { MapWithDefault } from "../utils";
import { BootstrapCrossSigningOpts, CrossSigningStatus, DeviceVerificationStatus } from "../crypto-api";
import {
BootstrapCrossSigningOpts,
CrossSigningStatus,
DeviceVerificationStatus,
ImportRoomKeyProgressData,
ImportRoomKeysOpts,
} from "../crypto-api";
import { deviceKeysToDeviceMap, rustDeviceToJsDevice } from "./device-converter";
import { IDownloadKeyResult, IQueryKeysRequest } from "../client";
import { Device, DeviceMap } from "../models/device";
@@ -207,8 +213,22 @@ export class RustCrypto implements CryptoBackend {
}
public async exportRoomKeys(): Promise<IMegolmSessionData[]> {
// TODO
return [];
const raw = await this.olmMachine.exportRoomKeys(() => true);
return JSON.parse(raw);
}
public async importRoomKeys(keys: IMegolmSessionData[], opts?: ImportRoomKeysOpts): Promise<void> {
// TODO when backup support will be added we would need to expose the `from_backup` flag in the bindings
const jsonKeys = JSON.stringify(keys);
await this.olmMachine.importRoomKeys(jsonKeys, (progress: BigInt, total: BigInt) => {
const importOpt: ImportRoomKeyProgressData = {
total: Number(total),
successes: Number(progress),
stage: "load_keys",
failures: 0,
};
opts?.progressCallback?.(importOpt);
});
}
/**