You've already forked matrix-js-sdk
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:
@@ -28,6 +28,7 @@ import { CryptoBackend } from "../../../src/common-crypto/CryptoBackend";
|
|||||||
import { IEventDecryptionResult } from "../../../src/@types/crypto";
|
import { IEventDecryptionResult } from "../../../src/@types/crypto";
|
||||||
import { OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor";
|
import { OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor";
|
||||||
import { ServerSideSecretStorage } from "../../../src/secret-storage";
|
import { ServerSideSecretStorage } from "../../../src/secret-storage";
|
||||||
|
import { ImportRoomKeysOpts } from "../../../src/crypto-api";
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
// reset fake-indexeddb after each test, to make sure we don't leak connections
|
// 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";
|
const TEST_DEVICE_ID = "TEST_DEVICE";
|
||||||
|
|
||||||
describe("RustCrypto", () => {
|
describe("RustCrypto", () => {
|
||||||
describe(".exportRoomKeys", () => {
|
describe(".importRoomKeys and .exportRoomKeys", () => {
|
||||||
let rustCrypto: RustCrypto;
|
let rustCrypto: RustCrypto;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
rustCrypto = await makeTestRustCrypto();
|
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();
|
const keys = await rustCrypto.exportRoomKeys();
|
||||||
expect(Array.isArray(keys)).toBeTruthy();
|
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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -103,13 +103,7 @@ import { MatrixScheduler } from "./scheduler";
|
|||||||
import { BeaconEvent, BeaconEventHandlerMap } from "./models/beacon";
|
import { BeaconEvent, BeaconEventHandlerMap } from "./models/beacon";
|
||||||
import { IAuthData, IAuthDict } from "./interactive-auth";
|
import { IAuthData, IAuthDict } from "./interactive-auth";
|
||||||
import { IMinimalEvent, IRoomEvent, IStateEvent } from "./sync-accumulator";
|
import { IMinimalEvent, IRoomEvent, IStateEvent } from "./sync-accumulator";
|
||||||
import {
|
import { CrossSigningKey, ICreateSecretStorageOpts, IEncryptedEventInfo, IRecoveryKey } from "./crypto/api";
|
||||||
CrossSigningKey,
|
|
||||||
ICreateSecretStorageOpts,
|
|
||||||
IEncryptedEventInfo,
|
|
||||||
IImportRoomKeysOpts,
|
|
||||||
IRecoveryKey,
|
|
||||||
} from "./crypto/api";
|
|
||||||
import { EventTimelineSet } from "./models/event-timeline-set";
|
import { EventTimelineSet } from "./models/event-timeline-set";
|
||||||
import { VerificationRequest } from "./crypto/verification/request/VerificationRequest";
|
import { VerificationRequest } from "./crypto/verification/request/VerificationRequest";
|
||||||
import { VerificationBase as Verification } from "./crypto/verification/Base";
|
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 { buildFeatureSupportMap, Feature, ServerSupport } from "./feature";
|
||||||
import { CryptoBackend } from "./common-crypto/CryptoBackend";
|
import { CryptoBackend } from "./common-crypto/CryptoBackend";
|
||||||
import { RUST_SDK_STORE_PREFIX } from "./rust-crypto/constants";
|
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 { DeviceInfoMap } from "./crypto/DeviceList";
|
||||||
import {
|
import {
|
||||||
AddSecretStorageKeyOpts,
|
AddSecretStorageKeyOpts,
|
||||||
@@ -3195,14 +3189,20 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|||||||
* Import a list of room keys previously exported by exportRoomKeys
|
* Import a list of room keys previously exported by exportRoomKeys
|
||||||
*
|
*
|
||||||
* @param keys - a list of session export objects
|
* @param keys - a list of session export objects
|
||||||
|
* @param opts - options object
|
||||||
*
|
*
|
||||||
* @returns a promise which resolves when the keys have been imported
|
* @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> {
|
public importRoomKeys(keys: IMegolmSessionData[], opts?: ImportRoomKeysOpts): Promise<void> {
|
||||||
if (!this.crypto) {
|
if (!this.cryptoBackend) {
|
||||||
throw new Error("End-to-end encryption disabled");
|
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();
|
algorithm.free();
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.importRoomKeys(keys, {
|
await this.getCrypto()?.importRoomKeys(keys, {
|
||||||
progressCallback,
|
progressCallback,
|
||||||
untrusted,
|
untrusted,
|
||||||
source: "backup",
|
source: "backup",
|
||||||
|
|||||||
@@ -82,6 +82,15 @@ export interface CryptoApi {
|
|||||||
*/
|
*/
|
||||||
exportRoomKeys(): Promise<IMegolmSessionData[]>;
|
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.
|
* 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";
|
export * from "./crypto-api/verification";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ import type { AddSecretStorageKeyOpts } from "../secret-storage";
|
|||||||
|
|
||||||
/* re-exports for backwards compatibility. */
|
/* re-exports for backwards compatibility. */
|
||||||
export { CrossSigningKey } from "../crypto-api";
|
export { CrossSigningKey } from "../crypto-api";
|
||||||
|
|
||||||
|
export type {
|
||||||
|
ImportRoomKeyProgressData as IImportOpts,
|
||||||
|
ImportRoomKeysOpts as IImportRoomKeysOpts,
|
||||||
|
} from "../crypto-api";
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
AddSecretStorageKeyOpts as IAddSecretStorageKeyOpts,
|
AddSecretStorageKeyOpts as IAddSecretStorageKeyOpts,
|
||||||
PassphraseInfo as IPassphraseInfo,
|
PassphraseInfo as IPassphraseInfo,
|
||||||
@@ -100,17 +106,3 @@ export interface ICreateSecretStorageOpts {
|
|||||||
*/
|
*/
|
||||||
getKeyBackupPassphrase?: () => Promise<Uint8Array>;
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -35,13 +35,7 @@ import * as algorithms from "./algorithms";
|
|||||||
import { createCryptoStoreCacheCallbacks, CrossSigningInfo, DeviceTrustLevel, UserTrustLevel } from "./CrossSigning";
|
import { createCryptoStoreCacheCallbacks, CrossSigningInfo, DeviceTrustLevel, UserTrustLevel } from "./CrossSigning";
|
||||||
import { EncryptionSetupBuilder } from "./EncryptionSetup";
|
import { EncryptionSetupBuilder } from "./EncryptionSetup";
|
||||||
import { SecretStorage as LegacySecretStorage } from "./SecretStorage";
|
import { SecretStorage as LegacySecretStorage } from "./SecretStorage";
|
||||||
import {
|
import { CrossSigningKey, ICreateSecretStorageOpts, IEncryptedEventInfo, IRecoveryKey } from "./api";
|
||||||
CrossSigningKey,
|
|
||||||
ICreateSecretStorageOpts,
|
|
||||||
IEncryptedEventInfo,
|
|
||||||
IImportRoomKeysOpts,
|
|
||||||
IRecoveryKey,
|
|
||||||
} from "./api";
|
|
||||||
import { OutgoingRoomKeyRequestManager } from "./OutgoingRoomKeyRequestManager";
|
import { OutgoingRoomKeyRequestManager } from "./OutgoingRoomKeyRequestManager";
|
||||||
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store";
|
import { IndexedDBCryptoStore } from "./store/indexeddb-crypto-store";
|
||||||
import { VerificationBase } from "./verification/Base";
|
import { VerificationBase } from "./verification/Base";
|
||||||
@@ -93,7 +87,12 @@ import {
|
|||||||
ServerSideSecretStorageImpl,
|
ServerSideSecretStorageImpl,
|
||||||
} from "../secret-storage";
|
} from "../secret-storage";
|
||||||
import { ISecretRequest } from "./SecretSharing";
|
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 { Device, DeviceMap } from "../models/device";
|
||||||
import { deviceInfoToDevice } from "./device-converter";
|
import { deviceInfoToDevice } from "./device-converter";
|
||||||
|
|
||||||
@@ -2852,7 +2851,7 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
|||||||
* @param keys - a list of session export objects
|
* @param keys - a list of session export objects
|
||||||
* @returns a promise which resolves once the keys have been imported
|
* @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 successes = 0;
|
||||||
let failures = 0;
|
let failures = 0;
|
||||||
const total = keys.length;
|
const total = keys.length;
|
||||||
|
|||||||
@@ -30,7 +30,13 @@ import { RoomEncryptor } from "./RoomEncryptor";
|
|||||||
import { OutgoingRequest, OutgoingRequestProcessor } from "./OutgoingRequestProcessor";
|
import { OutgoingRequest, OutgoingRequestProcessor } from "./OutgoingRequestProcessor";
|
||||||
import { KeyClaimManager } from "./KeyClaimManager";
|
import { KeyClaimManager } from "./KeyClaimManager";
|
||||||
import { MapWithDefault } from "../utils";
|
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 { deviceKeysToDeviceMap, rustDeviceToJsDevice } from "./device-converter";
|
||||||
import { IDownloadKeyResult, IQueryKeysRequest } from "../client";
|
import { IDownloadKeyResult, IQueryKeysRequest } from "../client";
|
||||||
import { Device, DeviceMap } from "../models/device";
|
import { Device, DeviceMap } from "../models/device";
|
||||||
@@ -207,8 +213,22 @@ export class RustCrypto implements CryptoBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async exportRoomKeys(): Promise<IMegolmSessionData[]> {
|
public async exportRoomKeys(): Promise<IMegolmSessionData[]> {
|
||||||
// TODO
|
const raw = await this.olmMachine.exportRoomKeys(() => true);
|
||||||
return [];
|
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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user