Schedule dehydration on reload if the dehydration key is already cached locally (#29021)

* Schedule dehydration on reload

* fix test and use the right function to check dehydration is enabled

* use dehydration helper function when scheduling dehydration on restart

* fix test by passing in client object
This commit is contained in:
Hubert Chathi
2025-01-31 18:29:59 +00:00
committed by GitHub
parent b64471e4f6
commit 4cba79ddcc
5 changed files with 48 additions and 9 deletions
+15 -1
View File
@@ -41,6 +41,7 @@ import PlatformPeg from "./PlatformPeg";
import { formatList } from "./utils/FormattingUtils"; import { formatList } from "./utils/FormattingUtils";
import SdkConfig from "./SdkConfig"; import SdkConfig from "./SdkConfig";
import { setDeviceIsolationMode } from "./settings/controllers/DeviceIsolationModeController.ts"; import { setDeviceIsolationMode } from "./settings/controllers/DeviceIsolationModeController.ts";
import { initialiseDehydration } from "./utils/device/dehydration";
export interface IMatrixClientCreds { export interface IMatrixClientCreds {
homeserverUrl: string; homeserverUrl: string;
@@ -340,7 +341,20 @@ class MatrixClientPegClass implements IMatrixClientPeg {
setDeviceIsolationMode(this.matrixClient, SettingsStore.getValue("feature_exclude_insecure_devices")); setDeviceIsolationMode(this.matrixClient, SettingsStore.getValue("feature_exclude_insecure_devices"));
// TODO: device dehydration and whathaveyou // Start dehydration. This code is only for the case where the client
// gets restarted, so we only do this if we already have the dehydration
// key cached, and we don't have to try to rehydrate a device. If this
// is a new login, we will start dehydration after Secret Storage is
// unlocked.
try {
await initialiseDehydration({ onlyIfKeyCached: true, rehydrate: false }, this.matrixClient);
} catch (e) {
// We may get an error dehydrating, such as if cross-signing and
// SSSS are not set up yet. Just log the error and continue.
// If SSSS gets set up later, we will re-try dehydration.
console.log("Error starting device dehydration", e);
}
return; return;
} }
@@ -332,7 +332,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
setupNewKeyBackup: !backupInfo, setupNewKeyBackup: !backupInfo,
}); });
} }
await initialiseDehydration(true); await initialiseDehydration({ createNewKey: true });
this.setState({ this.setState({
phase: Phase.Stored, phase: Phase.Stored,
+9 -7
View File
@@ -7,8 +7,9 @@ Please see LICENSE files in the repository root for full details.
*/ */
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { CryptoApi } from "matrix-js-sdk/src/crypto-api"; import { CryptoApi, StartDehydrationOpts } from "matrix-js-sdk/src/crypto-api";
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
import { MatrixClientPeg } from "../../MatrixClientPeg"; import { MatrixClientPeg } from "../../MatrixClientPeg";
/** /**
@@ -21,14 +22,14 @@ import { MatrixClientPeg } from "../../MatrixClientPeg";
* *
* Dehydration can currently only be enabled by setting a flag in the .well-known file. * Dehydration can currently only be enabled by setting a flag in the .well-known file.
*/ */
async function deviceDehydrationEnabled(crypto: CryptoApi | undefined): Promise<boolean> { async function deviceDehydrationEnabled(client: MatrixClient, crypto: CryptoApi | undefined): Promise<boolean> {
if (!crypto) { if (!crypto) {
return false; return false;
} }
if (!(await crypto.isDehydrationSupported())) { if (!(await crypto.isDehydrationSupported())) {
return false; return false;
} }
const wellknown = await MatrixClientPeg.safeGet().waitForClientWellKnown(); const wellknown = await client.waitForClientWellKnown();
return !!wellknown?.["org.matrix.msc3814"]; return !!wellknown?.["org.matrix.msc3814"];
} }
@@ -40,10 +41,11 @@ async function deviceDehydrationEnabled(crypto: CryptoApi | undefined): Promise<
* @param createNewKey: force a new dehydration key to be created, even if one * @param createNewKey: force a new dehydration key to be created, even if one
* already exists. This is used when we reset secret storage. * already exists. This is used when we reset secret storage.
*/ */
export async function initialiseDehydration(createNewKey: boolean = false): Promise<void> { export async function initialiseDehydration(opts: StartDehydrationOpts = {}, client?: MatrixClient): Promise<void> {
const crypto = MatrixClientPeg.safeGet().getCrypto(); client = client || MatrixClientPeg.safeGet();
if (await deviceDehydrationEnabled(crypto)) { const crypto = client.getCrypto();
if (await deviceDehydrationEnabled(client, crypto)) {
logger.log("Device dehydration enabled"); logger.log("Device dehydration enabled");
await crypto!.startDehydration(createNewKey); await crypto!.startDehydration(opts);
} }
} }
+21
View File
@@ -86,6 +86,27 @@ describe("MatrixClientPeg", () => {
expect(mockInitRustCrypto).toHaveBeenCalledWith({ storageKey: cryptoStoreKey }); expect(mockInitRustCrypto).toHaveBeenCalledWith({ storageKey: cryptoStoreKey });
}); });
it("should try to start dehydration if dehydration is enabled", async () => {
const mockInitRustCrypto = jest.spyOn(testPeg.safeGet(), "initRustCrypto").mockResolvedValue(undefined);
const mockStartDehydration = jest.fn();
jest.spyOn(testPeg.safeGet(), "getCrypto").mockReturnValue({
isDehydrationSupported: jest.fn().mockResolvedValue(true),
startDehydration: mockStartDehydration,
setDeviceIsolationMode: jest.fn(),
} as any);
jest.spyOn(testPeg.safeGet(), "waitForClientWellKnown").mockResolvedValue({
"m.homeserver": {
base_url: "http://example.com",
},
"org.matrix.msc3814": true,
} as any);
const cryptoStoreKey = new Uint8Array([1, 2, 3, 4]);
await testPeg.start({ rustCryptoStoreKey: cryptoStoreKey });
expect(mockInitRustCrypto).toHaveBeenCalledWith({ storageKey: cryptoStoreKey });
expect(mockStartDehydration).toHaveBeenCalledWith({ onlyIfKeyCached: true, rehydrate: false });
});
it("Should migrate existing login", async () => { it("Should migrate existing login", async () => {
const mockInitRustCrypto = jest.spyOn(testPeg.safeGet(), "initRustCrypto").mockResolvedValue(undefined); const mockInitRustCrypto = jest.spyOn(testPeg.safeGet(), "initRustCrypto").mockResolvedValue(undefined);
@@ -128,6 +128,7 @@ describe("<MatrixChat />", () => {
getCrypto: jest.fn().mockReturnValue({ getCrypto: jest.fn().mockReturnValue({
getVerificationRequestsToDeviceInProgress: jest.fn().mockReturnValue([]), getVerificationRequestsToDeviceInProgress: jest.fn().mockReturnValue([]),
isCrossSigningReady: jest.fn().mockReturnValue(false), isCrossSigningReady: jest.fn().mockReturnValue(false),
isDehydrationSupported: jest.fn().mockReturnValue(false),
getUserDeviceInfo: jest.fn().mockReturnValue(new Map()), getUserDeviceInfo: jest.fn().mockReturnValue(new Map()),
getUserVerificationStatus: jest.fn().mockResolvedValue(new UserVerificationStatus(false, false, false)), getUserVerificationStatus: jest.fn().mockResolvedValue(new UserVerificationStatus(false, false, false)),
getVersion: jest.fn().mockReturnValue("1"), getVersion: jest.fn().mockReturnValue("1"),
@@ -1005,6 +1006,7 @@ describe("<MatrixChat />", () => {
resetKeyBackup: jest.fn(), resetKeyBackup: jest.fn(),
isEncryptionEnabledInRoom: jest.fn().mockResolvedValue(false), isEncryptionEnabledInRoom: jest.fn().mockResolvedValue(false),
checkKeyBackupAndEnable: jest.fn().mockResolvedValue(null), checkKeyBackupAndEnable: jest.fn().mockResolvedValue(null),
isDehydrationSupported: jest.fn().mockReturnValue(false),
}; };
loginClient.getCrypto.mockReturnValue(mockCrypto as any); loginClient.getCrypto.mockReturnValue(mockCrypto as any);
}); });