From 4029eeeafe993a65fa82417d9a5baae8148ab50f Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Tue, 14 Jan 2025 10:24:42 +0100 Subject: [PATCH] Add `CryptoApi#resetEncryption` --- src/@types/event.ts | 2 +- src/crypto-api/index.ts | 13 +++++++++++++ src/crypto/index.ts | 7 +++++++ src/rust-crypto/rust-crypto.ts | 23 +++++++++++++++++++++++ src/secret-storage.ts | 21 +++++++++++++++------ 5 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/@types/event.ts b/src/@types/event.ts index 8815c6f900..70060f6d1f 100644 --- a/src/@types/event.ts +++ b/src/@types/event.ts @@ -373,7 +373,7 @@ export interface AccountDataEvents extends SecretStorageAccountDataEvents { [EventType.PushRules]: IPushRules; [EventType.Direct]: { [userId: string]: string[] }; [EventType.IgnoredUserList]: { [userId: string]: {} }; - "m.secret_storage.default_key": { key: string }; + "m.secret_storage.default_key": { key: string } | {}; "m.identity_server": { base_url: string | null }; [key: `${typeof LOCAL_NOTIFICATION_SETTINGS_PREFIX.name}.${string}`]: LocalNotificationSettings; [key: `m.secret_storage.key.${string}`]: SecretStorageKeyDescription; diff --git a/src/crypto-api/index.ts b/src/crypto-api/index.ts index 4a78069677..81ed30c595 100644 --- a/src/crypto-api/index.ts +++ b/src/crypto-api/index.ts @@ -396,6 +396,19 @@ export interface CryptoApi { payload: ToDevicePayload, ): Promise; + /** + * Reset the encryption of the account by going through the following steps: + * - Disable backing up room keys and delete any existing backup. + * - 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): Promise; + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Device/User verification diff --git a/src/crypto/index.ts b/src/crypto/index.ts index 3af8d86436..8981da5639 100644 --- a/src/crypto/index.ts +++ b/src/crypto/index.ts @@ -4340,6 +4340,13 @@ export class Crypto extends TypedEventEmitter { throw new Error("Not implemented"); } + + /** + * Stub function -- resetEncryption is not implemented here, so throw error + */ + public resetEncryption(): Promise { + throw new Error("Not implemented"); + } } /** diff --git a/src/rust-crypto/rust-crypto.ts b/src/rust-crypto/rust-crypto.ts index 02fe6270a1..f1bd03bd90 100644 --- a/src/rust-crypto/rust-crypto.ts +++ b/src/rust-crypto/rust-crypto.ts @@ -88,6 +88,7 @@ import { PerSessionKeyBackupDownloader } from "./PerSessionKeyBackupDownloader.t import { DehydratedDeviceManager } from "./DehydratedDeviceManager.ts"; import { VerificationMethod } from "../types.ts"; import { keyFromAuthData } from "../common-crypto/key-passphrase.ts"; +import { UIAuthCallback } from "../interactive-auth.ts"; const ALL_VERIFICATION_METHODS = [ VerificationMethod.Sas, @@ -1472,6 +1473,28 @@ export class RustCrypto extends TypedEventEmitter): Promise { + const backupEnabled = (await this.backupManager.getActiveBackupVersion()) !== null; + + // Delete all the backup + 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, + }); + + // Enable the key backup if it was enabled before + if (backupEnabled) await this.backupManager.checkKeyBackupAndEnable(true); + } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SyncCryptoCallbacks implementation diff --git a/src/secret-storage.ts b/src/secret-storage.ts index 2d6cd8a279..ae9f4a21f2 100644 --- a/src/secret-storage.ts +++ b/src/secret-storage.ts @@ -315,10 +315,12 @@ export interface ServerSideSecretStorage { /** * Set the default key ID for encrypting secrets. + * If keyId is `null`, the default key id value in the account data will be set to an empty object. + * This is considered as "disabling" the default key. * * @param keyId - The new default key ID */ - setDefaultKeyId(keyId: string): Promise; + setDefaultKeyId(keyId: string | null): Promise; } /** @@ -352,8 +354,8 @@ export class ServerSideSecretStorageImpl implements ServerSideSecretStorage { */ public async getDefaultKeyId(): Promise { const defaultKey = await this.accountDataAdapter.getAccountDataFromServer("m.secret_storage.default_key"); - if (!defaultKey) return null; - return defaultKey.key ?? null; + if (!defaultKey || !Object.keys(defaultKey).length) return null; + return (defaultKey as { key: string }).key ?? null; } /** @@ -361,17 +363,24 @@ export class ServerSideSecretStorageImpl implements ServerSideSecretStorage { * * @param keyId - The new default key ID */ - public setDefaultKeyId(keyId: string): Promise { + public setDefaultKeyId(keyId: string | null): Promise { return new Promise((resolve, reject) => { const listener = (ev: MatrixEvent): void => { - if (ev.getType() === "m.secret_storage.default_key" && ev.getContent().key === keyId) { + const content = ev.getContent(); + const isSameKey = keyId === null ? !Object.keys(content).length : content.key === keyId; + if (ev.getType() === "m.secret_storage.default_key" && isSameKey) { this.accountDataAdapter.removeListener(ClientEvent.AccountData, listener); resolve(); } }; this.accountDataAdapter.on(ClientEvent.AccountData, listener); - this.accountDataAdapter.setAccountData("m.secret_storage.default_key", { key: keyId }).catch((e) => { + // The spec says that the key should be an object with a `key` property + // https://spec.matrix.org/v1.13/client-server-api/#key-storage + // To delete the default key, we send an empty object like the rust sdk does + // (see https://docs.rs/matrix-sdk/latest/matrix_sdk/encryption/recovery/struct.Recovery.html#method.reset_identity) + const newValue = keyId === null ? {} : { key: keyId }; + this.accountDataAdapter.setAccountData("m.secret_storage.default_key", newValue).catch((e) => { this.accountDataAdapter.removeListener(ClientEvent.AccountData, listener); reject(e); });