You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-25 05:23:13 +03:00
Add CryptoApi.getSecretStorageStatus (#5054)
* Add `CryptoApi.getSecretStorageStatus` `isSecretStorageReady` is a bit of a blunt instrument: it's hard to see from logs *why* the secret storage isn't ready. Add a new method which returns a bit more data. * Update src/rust-crypto/rust-crypto.ts Co-authored-by: Andy Balaam <andy.balaam@matrix.org> --------- Co-authored-by: Andy Balaam <andy.balaam@matrix.org>
This commit is contained in:
committed by
GitHub
parent
977d0322da
commit
b0cbe22f64
@@ -854,9 +854,27 @@ describe("RustCrypto", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("getSecretStorageStatus", async () => {
|
||||||
|
const mockSecretStorage = {
|
||||||
|
getDefaultKeyId: jest.fn().mockResolvedValue("blah"),
|
||||||
|
isStored: jest.fn().mockResolvedValue({ blah: {} }),
|
||||||
|
} as unknown as Mocked<ServerSideSecretStorage>;
|
||||||
|
const rustCrypto = await makeTestRustCrypto(undefined, undefined, undefined, mockSecretStorage);
|
||||||
|
await expect(rustCrypto.getSecretStorageStatus()).resolves.toEqual({
|
||||||
|
defaultKeyId: "blah",
|
||||||
|
ready: true,
|
||||||
|
secretStorageKeyValidityMap: {
|
||||||
|
"m.cross_signing.master": true,
|
||||||
|
"m.cross_signing.self_signing": true,
|
||||||
|
"m.cross_signing.user_signing": true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("isSecretStorageReady", async () => {
|
it("isSecretStorageReady", async () => {
|
||||||
const mockSecretStorage = {
|
const mockSecretStorage = {
|
||||||
getDefaultKeyId: jest.fn().mockResolvedValue(null),
|
getDefaultKeyId: jest.fn().mockResolvedValue(null),
|
||||||
|
isStored: jest.fn().mockResolvedValue(null),
|
||||||
} as unknown as Mocked<ServerSideSecretStorage>;
|
} as unknown as Mocked<ServerSideSecretStorage>;
|
||||||
const rustCrypto = await makeTestRustCrypto(undefined, undefined, undefined, mockSecretStorage);
|
const rustCrypto = await makeTestRustCrypto(undefined, undefined, undefined, mockSecretStorage);
|
||||||
await expect(rustCrypto.isSecretStorageReady()).resolves.toBe(false);
|
await expect(rustCrypto.isSecretStorageReady()).resolves.toBe(false);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import type { ToDeviceBatch, ToDevicePayload } from "../models/ToDeviceMessage.t
|
|||||||
import { type Room } from "../models/room.ts";
|
import { type Room } from "../models/room.ts";
|
||||||
import { type DeviceMap } from "../models/device.ts";
|
import { type DeviceMap } from "../models/device.ts";
|
||||||
import { type UIAuthCallback } from "../interactive-auth.ts";
|
import { type UIAuthCallback } from "../interactive-auth.ts";
|
||||||
import { type PassphraseInfo, type SecretStorageKeyDescription } from "../secret-storage.ts";
|
import { type PassphraseInfo, type SecretStorageKey, type SecretStorageKeyDescription } from "../secret-storage.ts";
|
||||||
import { type VerificationRequest } from "./verification.ts";
|
import { type VerificationRequest } from "./verification.ts";
|
||||||
import {
|
import {
|
||||||
type BackupTrustInfo,
|
type BackupTrustInfo,
|
||||||
@@ -369,6 +369,11 @@ export interface CryptoApi {
|
|||||||
*/
|
*/
|
||||||
isSecretStorageReady(): Promise<boolean>;
|
isSecretStorageReady(): Promise<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inspect the status of secret storage, in more detail than {@link isSecretStorageReady}.
|
||||||
|
*/
|
||||||
|
getSecretStorageStatus(): Promise<SecretStorageStatus>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bootstrap [secret storage](https://spec.matrix.org/v1.12/client-server-api/#storage).
|
* Bootstrap [secret storage](https://spec.matrix.org/v1.12/client-server-api/#storage).
|
||||||
*
|
*
|
||||||
@@ -1148,6 +1153,30 @@ export interface CryptoCallbacks {
|
|||||||
cacheSecretStorageKey?: (keyId: string, keyInfo: SecretStorageKeyDescription, key: Uint8Array) => void;
|
cacheSecretStorageKey?: (keyId: string, keyInfo: SecretStorageKeyDescription, key: Uint8Array) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of a call to {@link CryptoApi.getSecretStorageStatus}.
|
||||||
|
*/
|
||||||
|
export interface SecretStorageStatus {
|
||||||
|
/** Whether secret storage is fully populated. The same as {@link CryptoApi.isSecretStorageReady}. */
|
||||||
|
ready: boolean;
|
||||||
|
|
||||||
|
/** The ID of the current default secret storage key. */
|
||||||
|
defaultKeyId: string | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For each secret that we checked whether it is correctly stored in secret storage with the default secret storage key.
|
||||||
|
*
|
||||||
|
* Note that we will only check that the key backup key is stored if key backup is currently enabled (i.e. that
|
||||||
|
* {@link CryptoApi.getActiveSessionBackupVersion} returns non-null). `m.megolm_backup.v1` will only be present in that case.
|
||||||
|
*
|
||||||
|
* (This is an object rather than a `Map` so that it JSON.stringify()s nicely, since its main purpose is to end up
|
||||||
|
* in logs.)
|
||||||
|
*/
|
||||||
|
secretStorageKeyValidityMap: {
|
||||||
|
[P in SecretStorageKey]?: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parameter of {@link CryptoApi#bootstrapSecretStorage}
|
* Parameter of {@link CryptoApi#bootstrapSecretStorage}
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ import {
|
|||||||
type KeyBackupRestoreOpts,
|
type KeyBackupRestoreOpts,
|
||||||
type KeyBackupRestoreResult,
|
type KeyBackupRestoreResult,
|
||||||
type OwnDeviceKeys,
|
type OwnDeviceKeys,
|
||||||
|
type SecretStorageStatus,
|
||||||
type StartDehydrationOpts,
|
type StartDehydrationOpts,
|
||||||
UserVerificationStatus,
|
UserVerificationStatus,
|
||||||
type VerificationRequest,
|
type VerificationRequest,
|
||||||
@@ -78,7 +79,7 @@ import {
|
|||||||
type ServerSideSecretStorage,
|
type ServerSideSecretStorage,
|
||||||
} from "../secret-storage.ts";
|
} from "../secret-storage.ts";
|
||||||
import { CrossSigningIdentity } from "./CrossSigningIdentity.ts";
|
import { CrossSigningIdentity } from "./CrossSigningIdentity.ts";
|
||||||
import { secretStorageCanAccessSecrets, secretStorageContainsCrossSigningKeys } from "./secret-storage.ts";
|
import { secretStorageContainsCrossSigningKeys } from "./secret-storage.ts";
|
||||||
import { isVerificationEvent, RustVerificationRequest, verificationMethodIdentifierToMethod } from "./verification.ts";
|
import { isVerificationEvent, RustVerificationRequest, verificationMethodIdentifierToMethod } from "./verification.ts";
|
||||||
import { EventType, MsgType } from "../@types/event.ts";
|
import { EventType, MsgType } from "../@types/event.ts";
|
||||||
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
|
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
|
||||||
@@ -827,6 +828,13 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
|
|||||||
* Implementation of {@link CryptoApi#isSecretStorageReady}
|
* Implementation of {@link CryptoApi#isSecretStorageReady}
|
||||||
*/
|
*/
|
||||||
public async isSecretStorageReady(): Promise<boolean> {
|
public async isSecretStorageReady(): Promise<boolean> {
|
||||||
|
return (await this.getSecretStorageStatus()).ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link CryptoApi#getSecretStorageStatus}
|
||||||
|
*/
|
||||||
|
public async getSecretStorageStatus(): Promise<SecretStorageStatus> {
|
||||||
// make sure that the cross-signing keys are stored
|
// make sure that the cross-signing keys are stored
|
||||||
const secretsToCheck: SecretStorageKey[] = [
|
const secretsToCheck: SecretStorageKey[] = [
|
||||||
"m.cross_signing.master",
|
"m.cross_signing.master",
|
||||||
@@ -834,13 +842,32 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
|
|||||||
"m.cross_signing.self_signing",
|
"m.cross_signing.self_signing",
|
||||||
];
|
];
|
||||||
|
|
||||||
// if key backup is active, we also need to check that the backup decryption key is stored
|
// If key backup is active, we also need to check that the backup decryption key is stored
|
||||||
const keyBackupEnabled = (await this.backupManager.getActiveBackupVersion()) != null;
|
const keyBackupEnabled = (await this.backupManager.getActiveBackupVersion()) != null;
|
||||||
if (keyBackupEnabled) {
|
if (keyBackupEnabled) {
|
||||||
secretsToCheck.push("m.megolm_backup.v1");
|
secretsToCheck.push("m.megolm_backup.v1");
|
||||||
}
|
}
|
||||||
|
|
||||||
return secretStorageCanAccessSecrets(this.secretStorage, secretsToCheck);
|
const defaultKeyId = await this.secretStorage.getDefaultKeyId();
|
||||||
|
|
||||||
|
const result: SecretStorageStatus = {
|
||||||
|
// Assume we have all secrets until proven otherwise
|
||||||
|
ready: true,
|
||||||
|
defaultKeyId,
|
||||||
|
secretStorageKeyValidityMap: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const secretName of secretsToCheck) {
|
||||||
|
// Check which keys this particular secret is encrypted with
|
||||||
|
const record = (await this.secretStorage.isStored(secretName)) || {};
|
||||||
|
|
||||||
|
// If it's encrypted with the right key, it is valid
|
||||||
|
const secretStored = !!defaultKeyId && defaultKeyId in record;
|
||||||
|
result.secretStorageKeyValidityMap[secretName] = secretStored;
|
||||||
|
result.ready = result.ready && secretStored;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user