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
Add CryptoApi.resetEncryption (#4614)
* feat(crypto api): Add `CryptoApi#resetEncryption` * docs(crypto api): Review changes * test(crypto api): Cleaner way to handle key backup removal
This commit is contained in:
@@ -61,6 +61,7 @@ import {
|
|||||||
EventShieldReason,
|
EventShieldReason,
|
||||||
ImportRoomKeysOpts,
|
ImportRoomKeysOpts,
|
||||||
KeyBackupCheck,
|
KeyBackupCheck,
|
||||||
|
KeyBackupInfo,
|
||||||
VerificationRequest,
|
VerificationRequest,
|
||||||
} from "../../../src/crypto-api";
|
} from "../../../src/crypto-api";
|
||||||
import * as testData from "../../test-utils/test-data";
|
import * as testData from "../../test-utils/test-data";
|
||||||
@@ -72,6 +73,7 @@ import { Curve25519AuthData } from "../../../src/crypto-api/keybackup";
|
|||||||
import encryptAESSecretStorageItem from "../../../src/utils/encryptAESSecretStorageItem.ts";
|
import encryptAESSecretStorageItem from "../../../src/utils/encryptAESSecretStorageItem.ts";
|
||||||
import { CryptoStore, SecretStorePrivateKeys } from "../../../src/crypto/store/base";
|
import { CryptoStore, SecretStorePrivateKeys } from "../../../src/crypto/store/base";
|
||||||
import { CryptoEvent } from "../../../src/crypto-api/index.ts";
|
import { CryptoEvent } from "../../../src/crypto-api/index.ts";
|
||||||
|
import { RustBackupManager } from "../../../src/rust-crypto/backup.ts";
|
||||||
|
|
||||||
const TEST_USER = "@alice:example.com";
|
const TEST_USER = "@alice:example.com";
|
||||||
const TEST_DEVICE_ID = "TEST_DEVICE";
|
const TEST_DEVICE_ID = "TEST_DEVICE";
|
||||||
@@ -1879,6 +1881,74 @@ describe("RustCrypto", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("resetEncryption", () => {
|
||||||
|
let secretStorage: ServerSideSecretStorage;
|
||||||
|
beforeEach(() => {
|
||||||
|
secretStorage = {
|
||||||
|
setDefaultKeyId: jest.fn(),
|
||||||
|
hasKey: jest.fn().mockResolvedValue(false),
|
||||||
|
getKey: jest.fn().mockResolvedValue(null),
|
||||||
|
} as unknown as ServerSideSecretStorage;
|
||||||
|
|
||||||
|
fetchMock.post("path:/_matrix/client/v3/keys/upload", { one_time_key_counts: {} });
|
||||||
|
fetchMock.post("path:/_matrix/client/v3/keys/signatures/upload", {});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reset should reset 4S, backup and cross-signing", async () => {
|
||||||
|
// We don't have a key backup
|
||||||
|
fetchMock.get("path:/_matrix/client/v3/room_keys/version", {});
|
||||||
|
|
||||||
|
const rustCrypto = await makeTestRustCrypto(makeMatrixHttpApi(), undefined, undefined, secretStorage);
|
||||||
|
|
||||||
|
const authUploadDeviceSigningKeys = jest.fn();
|
||||||
|
await rustCrypto.resetEncryption(authUploadDeviceSigningKeys);
|
||||||
|
|
||||||
|
// The default key id should be deleted
|
||||||
|
expect(secretStorage.setDefaultKeyId).toHaveBeenCalledWith(null);
|
||||||
|
expect(await rustCrypto.getActiveSessionBackupVersion()).toBeNull();
|
||||||
|
// The new cross signing keys should be uploaded
|
||||||
|
expect(authUploadDeviceSigningKeys).toHaveBeenCalledWith(expect.any(Function));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("key backup should be re-enabled after reset", async () => {
|
||||||
|
// When we will delete the key backup
|
||||||
|
let backupIsDeleted = false;
|
||||||
|
fetchMock.delete("path:/_matrix/client/v3/room_keys/version/1", () => {
|
||||||
|
backupIsDeleted = true;
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
// If the backup is deleted, we will return an empty object
|
||||||
|
fetchMock.get("path:/_matrix/client/v3/room_keys/version", () => {
|
||||||
|
return backupIsDeleted ? {} : testData.SIGNED_BACKUP_DATA;
|
||||||
|
});
|
||||||
|
|
||||||
|
// We consider the key backup as trusted
|
||||||
|
jest.spyOn(RustBackupManager.prototype, "isKeyBackupTrusted").mockResolvedValue({
|
||||||
|
trusted: true,
|
||||||
|
matchesDecryptionKey: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const rustCrypto = await makeTestRustCrypto(makeMatrixHttpApi(), undefined, undefined, secretStorage);
|
||||||
|
// We have a key backup
|
||||||
|
expect(await rustCrypto.getActiveSessionBackupVersion()).not.toBeNull();
|
||||||
|
|
||||||
|
// A new key backup should be created after the reset
|
||||||
|
let newKeyBackupInfo!: KeyBackupInfo;
|
||||||
|
fetchMock.post("path:/_matrix/client/v3/room_keys/version", (res, options) => {
|
||||||
|
newKeyBackupInfo = JSON.parse(options.body as string);
|
||||||
|
return { version: "2" };
|
||||||
|
});
|
||||||
|
|
||||||
|
const authUploadDeviceSigningKeys = jest.fn();
|
||||||
|
await rustCrypto.resetEncryption(authUploadDeviceSigningKeys);
|
||||||
|
|
||||||
|
// A new key backup should be created
|
||||||
|
expect(newKeyBackupInfo.auth_data).toBeTruthy();
|
||||||
|
// The new cross signing keys should be uploaded
|
||||||
|
expect(authUploadDeviceSigningKeys).toHaveBeenCalledWith(expect.any(Function));
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Build a MatrixHttpApi instance */
|
/** Build a MatrixHttpApi instance */
|
||||||
|
|||||||
@@ -396,6 +396,19 @@ export interface CryptoApi {
|
|||||||
payload: ToDevicePayload,
|
payload: ToDevicePayload,
|
||||||
): Promise<ToDeviceBatch>;
|
): Promise<ToDeviceBatch>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the encryption of the user by going through the following steps:
|
||||||
|
* - Disable backing up room keys and delete any existing backups.
|
||||||
|
* - Remove the default secret storage key from the account data (ie: the recovery key).
|
||||||
|
* - Reset the cross-signing keys.
|
||||||
|
* - Re-enable backing up room keys if enabled before.
|
||||||
|
*
|
||||||
|
* @param authUploadDeviceSigningKeys - Callback to authenticate the upload of device signing keys.
|
||||||
|
* Used when resetting the cross signing keys.
|
||||||
|
* See {@link BootstrapCrossSigningOpts#authUploadDeviceSigningKeys}.
|
||||||
|
*/
|
||||||
|
resetEncryption(authUploadDeviceSigningKeys: UIAuthCallback<void>): Promise<void>;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Device/User verification
|
// Device/User verification
|
||||||
|
|||||||
@@ -4340,6 +4340,13 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
|
|||||||
): Promise<KeyBackupRestoreResult> {
|
): Promise<KeyBackupRestoreResult> {
|
||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stub function -- resetEncryption is not implemented here, so throw error
|
||||||
|
*/
|
||||||
|
public resetEncryption(): Promise<void> {
|
||||||
|
throw new Error("Not implemented");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ import { PerSessionKeyBackupDownloader } from "./PerSessionKeyBackupDownloader.t
|
|||||||
import { DehydratedDeviceManager } from "./DehydratedDeviceManager.ts";
|
import { DehydratedDeviceManager } from "./DehydratedDeviceManager.ts";
|
||||||
import { VerificationMethod } from "../types.ts";
|
import { VerificationMethod } from "../types.ts";
|
||||||
import { keyFromAuthData } from "../common-crypto/key-passphrase.ts";
|
import { keyFromAuthData } from "../common-crypto/key-passphrase.ts";
|
||||||
|
import { UIAuthCallback } from "../interactive-auth.ts";
|
||||||
|
|
||||||
const ALL_VERIFICATION_METHODS = [
|
const ALL_VERIFICATION_METHODS = [
|
||||||
VerificationMethod.Sas,
|
VerificationMethod.Sas,
|
||||||
@@ -1472,6 +1473,30 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
|
|||||||
return batch;
|
return batch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link CryptoApi#resetEncryption}.
|
||||||
|
*/
|
||||||
|
public async resetEncryption(authUploadDeviceSigningKeys: UIAuthCallback<void>): Promise<void> {
|
||||||
|
const backupEnabled = (await this.backupManager.getActiveBackupVersion()) !== null;
|
||||||
|
|
||||||
|
// Disable backup, and delete all the backups from the server
|
||||||
|
await this.backupManager.deleteAllKeyBackupVersions();
|
||||||
|
|
||||||
|
// Disable the recovery key and the secret storage
|
||||||
|
await this.secretStorage.setDefaultKeyId(null);
|
||||||
|
|
||||||
|
// Reset the cross-signing keys
|
||||||
|
await this.crossSigningIdentity.bootstrapCrossSigning({
|
||||||
|
setupNewCrossSigning: true,
|
||||||
|
authUploadDeviceSigningKeys,
|
||||||
|
});
|
||||||
|
|
||||||
|
// If key backup was enabled, we create a new backup
|
||||||
|
if (backupEnabled) {
|
||||||
|
await this.resetKeyBackup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// SyncCryptoCallbacks implementation
|
// SyncCryptoCallbacks implementation
|
||||||
|
|||||||
Reference in New Issue
Block a user