Skip to content

Commit db7e3e3

Browse files
authored
Add disableKeyStorage() to crypto API (#4742)
* Add disableKeyStorage() to crypto API As an all-in-one method for deleting all server side key storage on the user's account (as the doc hopefully explains). * Add test * const * Can't be disabled here
1 parent 3e51271 commit db7e3e3

File tree

3 files changed

+84
-11
lines changed

3 files changed

+84
-11
lines changed

spec/unit/rust-crypto/rust-crypto.spec.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2318,6 +2318,43 @@ describe("RustCrypto", () => {
23182318
expect(dehydratedDeviceIsDeleted).toBeTruthy();
23192319
});
23202320
});
2321+
2322+
describe("disableKeyStorage", () => {
2323+
it("should disable key storage", async () => {
2324+
const secretStorage = {
2325+
getDefaultKeyId: jest.fn().mockResolvedValue("bloop"),
2326+
setDefaultKeyId: jest.fn(),
2327+
store: jest.fn(),
2328+
} as unknown as ServerSideSecretStorage;
2329+
2330+
fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA);
2331+
2332+
let backupIsDeleted = false;
2333+
fetchMock.delete("path:/_matrix/client/v3/room_keys/version/1", () => {
2334+
backupIsDeleted = true;
2335+
return {};
2336+
});
2337+
2338+
let dehydratedDeviceIsDeleted = false;
2339+
fetchMock.delete("path:/_matrix/client/unstable/org.matrix.msc3814.v1/dehydrated_device", () => {
2340+
dehydratedDeviceIsDeleted = true;
2341+
return { device_id: "ADEVICEID" };
2342+
});
2343+
2344+
const rustCrypto = await makeTestRustCrypto(makeMatrixHttpApi(), undefined, undefined, secretStorage);
2345+
await rustCrypto.disableKeyStorage();
2346+
2347+
expect(secretStorage.store).toHaveBeenCalledWith("m.cross_signing.master", null);
2348+
expect(secretStorage.store).toHaveBeenCalledWith("m.cross_signing.self_signing", null);
2349+
expect(secretStorage.store).toHaveBeenCalledWith("m.cross_signing.user_signing", null);
2350+
expect(secretStorage.store).toHaveBeenCalledWith("m.megolm_backup.v1", null);
2351+
expect(secretStorage.store).toHaveBeenCalledWith("m.secret_storage.key.bloop", null);
2352+
expect(secretStorage.setDefaultKeyId).toHaveBeenCalledWith(null);
2353+
2354+
expect(backupIsDeleted).toBeTruthy();
2355+
expect(dehydratedDeviceIsDeleted).toBeTruthy();
2356+
});
2357+
});
23212358
});
23222359

23232360
/** Build a MatrixHttpApi instance */

src/crypto-api/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,16 @@ export interface CryptoApi {
629629
*/
630630
resetKeyBackup(): Promise<void>;
631631

632+
/**
633+
* Disables server-side key storage and deletes server-side backups.
634+
* * Deletes the current key backup version, if any (but not any previous versions).
635+
* * Disables 4S, deleting the info for the default key, the default key pointer itself and any
636+
* known 4S data (cross-signing keys and the megolm key backup key).
637+
* * Deletes any dehydrated devices.
638+
* * Sets the "m.org.matrix.custom.backup_disabled" account data flag to indicate that the user has disabled backups.
639+
*/
640+
disableKeyStorage(): Promise<void>;
641+
632642
/**
633643
* Deletes the given key backup.
634644
*

src/rust-crypto/rust-crypto.ts

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,24 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
12761276
this.checkKeyBackupAndEnable();
12771277
}
12781278

1279+
/**
1280+
* Implementation of {@link CryptoApi#disableKeyStorage}.
1281+
*/
1282+
public async disableKeyStorage(): Promise<void> {
1283+
// Get the key backup version we're using
1284+
const info = await this.getKeyBackupInfo();
1285+
if (info?.version) {
1286+
await this.deleteKeyBackupVersion(info.version);
1287+
} else {
1288+
logger.error("Can't delete key backup version: no version available");
1289+
}
1290+
1291+
// also turn off 4S, since this is also storing keys on the server.
1292+
await this.deleteSecretStorage();
1293+
1294+
await this.dehydratedDeviceManager.delete();
1295+
}
1296+
12791297
/**
12801298
* Signs the given object with the current device and current identity (if available).
12811299
* As defined in {@link https://spec.matrix.org/v1.8/appendices/#signing-json | Signing JSON}.
@@ -1447,17 +1465,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
14471465
// Disable backup, and delete all the backups from the server
14481466
await this.backupManager.deleteAllKeyBackupVersions();
14491467

1450-
// Remove the stored secrets in the secret storage
1451-
await this.secretStorage.store("m.cross_signing.master", null);
1452-
await this.secretStorage.store("m.cross_signing.self_signing", null);
1453-
await this.secretStorage.store("m.cross_signing.user_signing", null);
1454-
await this.secretStorage.store("m.megolm_backup.v1", null);
1455-
1456-
// Remove the recovery key
1457-
const defaultKeyId = await this.secretStorage.getDefaultKeyId();
1458-
if (defaultKeyId) await this.secretStorage.store(`m.secret_storage.key.${defaultKeyId}`, null);
1459-
// Disable the recovery key and the secret storage
1460-
await this.secretStorage.setDefaultKeyId(null);
1468+
this.deleteSecretStorage();
14611469

14621470
// Reset the cross-signing keys
14631471
await this.crossSigningIdentity.bootstrapCrossSigning({
@@ -1471,6 +1479,24 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
14711479
this.logger.debug("resetEncryption: ended");
14721480
}
14731481

1482+
/**
1483+
* Removes the secret storage key, default key pointer and all (known) secret storage data
1484+
* from the user's account data
1485+
*/
1486+
private async deleteSecretStorage(): Promise<void> {
1487+
// Remove the stored secrets in the secret storage
1488+
await this.secretStorage.store("m.cross_signing.master", null);
1489+
await this.secretStorage.store("m.cross_signing.self_signing", null);
1490+
await this.secretStorage.store("m.cross_signing.user_signing", null);
1491+
await this.secretStorage.store("m.megolm_backup.v1", null);
1492+
1493+
// Remove the recovery key
1494+
const defaultKeyId = await this.secretStorage.getDefaultKeyId();
1495+
if (defaultKeyId) await this.secretStorage.store(`m.secret_storage.key.${defaultKeyId}`, null);
1496+
// Disable the recovery key and the secret storage
1497+
await this.secretStorage.setDefaultKeyId(null);
1498+
}
1499+
14741500
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
14751501
//
14761502
// SyncCryptoCallbacks implementation

0 commit comments

Comments
 (0)