Skip to content

Commit c17f47a

Browse files
committed
feat(devtools): Add key storage information in devtools
1 parent ad01218 commit c17f47a

File tree

5 files changed

+179
-0
lines changed

5 files changed

+179
-0
lines changed

res/css/_components.pcss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@
134134
@import "./views/dialogs/_ConfirmUserActionDialog.pcss";
135135
@import "./views/dialogs/_CreateRoomDialog.pcss";
136136
@import "./views/dialogs/_CreateSubspaceDialog.pcss";
137+
@import "./views/dialogs/_Crypto.pcss";
137138
@import "./views/dialogs/_DeactivateAccountDialog.pcss";
138139
@import "./views/dialogs/_DevtoolsDialog.pcss";
139140
@import "./views/dialogs/_ExportDialog.pcss";

res/css/views/dialogs/_Crypto.pcss

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
.mx_Crypto {
9+
.mx_KeyStorage {
10+
margin: var(--cpd-space-4x) 0;
11+
text-align: left;
12+
13+
thead {
14+
font: var(--cpd-font-heading-sm-semibold);
15+
}
16+
17+
th {
18+
padding-right: var(--cpd-space-2x);
19+
}
20+
}
21+
}

src/components/views/dialogs/DevtoolsDialog.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { SettingLevel } from "../../../settings/SettingLevel";
2424
import ServerInfo from "./devtools/ServerInfo";
2525
import CopyableText from "../elements/CopyableText";
2626
import RoomNotifications from "./devtools/RoomNotifications";
27+
import { Crypto } from "./devtools/Crypto";
2728

2829
enum Category {
2930
Room,
@@ -49,6 +50,7 @@ const Tools: Record<Category, [label: TranslationKey, tool: Tool][]> = {
4950
[_td("devtools|explore_account_data"), AccountDataExplorer],
5051
[_td("devtools|settings_explorer"), SettingExplorer],
5152
[_td("devtools|server_info"), ServerInfo],
53+
[_td("devtools|crypto|title"), Crypto],
5254
],
5355
};
5456

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
import React, { JSX } from "react";
9+
import { InlineSpinner } from "@vector-im/compound-web";
10+
11+
import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext";
12+
import BaseTool from "./BaseTool";
13+
import { useAsyncMemo } from "../../../../hooks/useAsyncMemo";
14+
import { _t } from "../../../../languageHandler";
15+
16+
interface KeyBackupProps {
17+
onBack(): void;
18+
}
19+
20+
export function Crypto({ onBack }: KeyBackupProps): JSX.Element {
21+
return (
22+
<BaseTool onBack={onBack} className="mx_Crypto">
23+
<KeyStorage />
24+
</BaseTool>
25+
);
26+
}
27+
28+
/**
29+
* A component that displays information about the key storage.
30+
*/
31+
function KeyStorage(): JSX.Element {
32+
const matrixClient = useMatrixClientContext();
33+
const keyStorageData = useAsyncMemo(async () => {
34+
const crypto = matrixClient.getCrypto();
35+
if (!crypto) return null;
36+
37+
const backupInfo = await crypto.getKeyBackupInfo();
38+
const backupKeyStored = Boolean(await matrixClient.isKeyBackupKeyStored());
39+
const backupKeyFromCache = await crypto.getSessionBackupPrivateKey();
40+
const backupKeyCached = Boolean(backupKeyFromCache);
41+
const backupKeyWellFormed = backupKeyFromCache instanceof Uint8Array;
42+
const activeBackupVersion = await crypto.getActiveSessionBackupVersion();
43+
const secretStorageKeyInAccount = await matrixClient.secretStorage.hasKey();
44+
const secretStorageReady = await crypto.isSecretStorageReady();
45+
46+
return {
47+
backupInfo,
48+
backupKeyStored,
49+
backupKeyCached,
50+
backupKeyWellFormed,
51+
activeBackupVersion,
52+
secretStorageKeyInAccount,
53+
secretStorageReady,
54+
};
55+
}, [matrixClient]);
56+
57+
if (keyStorageData === undefined) return <InlineSpinner aria-label={_t("common|loading")} />;
58+
if (keyStorageData === null) return <span>{_t("devtools|crypto|crypto_not_available")}</span>;
59+
60+
const {
61+
backupInfo,
62+
backupKeyStored,
63+
backupKeyCached,
64+
backupKeyWellFormed,
65+
activeBackupVersion,
66+
secretStorageKeyInAccount,
67+
secretStorageReady,
68+
} = keyStorageData;
69+
70+
return (
71+
<table className="mx_KeyStorage">
72+
<thead>{_t("devtools|crypto|key_storage")}</thead>
73+
<tbody>
74+
<tr>
75+
<th scope="row">{_t("devtools|crypto|key_backup_latest_version")}</th>
76+
<td>
77+
{backupInfo
78+
? `${backupInfo.version} (${_t("settings|security|key_backup_algorithm")}) ${backupInfo.algorithm}`
79+
: _t("devtools|crypto|key_backup_inactive_warning")}
80+
</td>
81+
</tr>
82+
<tr>
83+
<th scope="row">{_t("devtools|crypto|backup_key_stored_status")}</th>
84+
<td>
85+
{backupKeyStored
86+
? _t("devtools|crypto|backup_key_stored")
87+
: _t("devtools|crypto|backup_key_not_stored")}
88+
</td>
89+
</tr>
90+
<tr>
91+
<th scope="row">{_t("devtools|crypto|key_backup_active_version")}</th>
92+
<td>
93+
{activeBackupVersion === null
94+
? _t("devtools|crypto|key_backup_active_version_none")
95+
: activeBackupVersion}
96+
</td>
97+
</tr>
98+
<tr>
99+
<th scope="row">{_t("devtools|crypto|backup_key_cached_status")}</th>
100+
<td>
101+
{`${
102+
backupKeyCached
103+
? _t("devtools|crypto|backup_key_cached")
104+
: _t("devtools|crypto|backup_key_not_cached")
105+
},
106+
${
107+
backupKeyWellFormed
108+
? _t("devtools|crypto|backup_key_well_formed")
109+
: _t("devtools|crypto|backup_key_unexpected_type")
110+
}`}
111+
</td>
112+
</tr>
113+
<tr>
114+
<th scope="row">{_t("devtools|crypto|4s_public_key_status")}</th>
115+
<td>
116+
{secretStorageKeyInAccount
117+
? _t("devtools|crypto|4s_public_key_in_account_data")
118+
: _t("devtools|crypto|4s_public_key_not_in_account_data")}
119+
</td>
120+
</tr>
121+
<tr>
122+
<th scope="row">{_t("devtools|crypto|secret_storage_status")}</th>
123+
<td>
124+
{secretStorageReady
125+
? _t("devtools|crypto|secret_storage_ready")
126+
: _t("devtools|crypto|secret_storage_not_ready")}
127+
</td>
128+
</tr>
129+
</tbody>
130+
</table>
131+
);
132+
}

src/i18n/strings/en_EN.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,29 @@
734734
"category_room": "Room",
735735
"caution_colon": "Caution:",
736736
"client_versions": "Client Versions",
737+
"crypto": {
738+
"4s_public_key_in_account_data": "in account data",
739+
"4s_public_key_not_in_account_data": "not found",
740+
"4s_public_key_status": "Secret storage public key:",
741+
"backup_key_cached": "cached locally",
742+
"backup_key_cached_status": "Backup key cached:",
743+
"backup_key_not_cached": "not found locally",
744+
"backup_key_not_stored": "not stored",
745+
"backup_key_stored": "in secret storage",
746+
"backup_key_stored_status": "Backup key stored:",
747+
"backup_key_unexpected_type": "unexpected type",
748+
"backup_key_well_formed": "well formed",
749+
"crypto_not_available": "Cryptographic module is not available",
750+
"key_backup_active_version": "Active backup version:",
751+
"key_backup_active_version_none": "None",
752+
"key_backup_inactive_warning": "Your keys are not being backed up from this session.",
753+
"key_backup_latest_version": "Latest backup version on server:",
754+
"key_storage": "Key Storage",
755+
"secret_storage_not_ready": "not ready",
756+
"secret_storage_ready": "ready",
757+
"secret_storage_status": "Secret storage:",
758+
"title": "Crypto"
759+
},
737760
"developer_mode": "Developer mode",
738761
"developer_tools": "Developer Tools",
739762
"edit_setting": "Edit setting",

0 commit comments

Comments
 (0)