Skip to content

Commit aacab8e

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

File tree

5 files changed

+176
-0
lines changed

5 files changed

+176
-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: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
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+
function KeyStorage(): JSX.Element {
29+
const matrixClient = useMatrixClientContext();
30+
const keyStorageData = useAsyncMemo(async () => {
31+
const crypto = matrixClient.getCrypto();
32+
if (!crypto) return null;
33+
34+
const backupInfo = await crypto.getKeyBackupInfo();
35+
const backupKeyStored = Boolean(await matrixClient.isKeyBackupKeyStored());
36+
const backupKeyFromCache = await crypto.getSessionBackupPrivateKey();
37+
const backupKeyCached = Boolean(backupKeyFromCache);
38+
const backupKeyWellFormed = backupKeyFromCache instanceof Uint8Array;
39+
const activeBackupVersion = await crypto.getActiveSessionBackupVersion();
40+
const secretStorageKeyInAccount = await matrixClient.secretStorage.hasKey();
41+
const secretStorageReady = await crypto.isSecretStorageReady();
42+
43+
return {
44+
backupInfo,
45+
backupKeyStored,
46+
backupKeyCached,
47+
backupKeyWellFormed,
48+
activeBackupVersion,
49+
secretStorageKeyInAccount,
50+
secretStorageReady,
51+
};
52+
}, [matrixClient]);
53+
54+
if (keyStorageData === undefined) return <InlineSpinner aria-label={_t("common|loading")} />;
55+
if (keyStorageData === null) return <span>{_t("devtools|crypto|crypto_not_available")}</span>;
56+
57+
const {
58+
backupInfo,
59+
backupKeyStored,
60+
backupKeyCached,
61+
backupKeyWellFormed,
62+
activeBackupVersion,
63+
secretStorageKeyInAccount,
64+
secretStorageReady,
65+
} = keyStorageData;
66+
67+
return (
68+
<table className="mx_KeyStorage">
69+
<thead>{_t("devtools|crypto|key_storage")}</thead>
70+
<tbody>
71+
<tr>
72+
<th scope="row">{_t("devtools|crypto|key_backup_latest_version")}</th>
73+
<td>
74+
{backupInfo
75+
? `${backupInfo.version} (${_t("settings|security|key_backup_algorithm")}) ${backupInfo.algorithm}`
76+
: _t("devtools|crypto|key_backup_inactive_warning")}
77+
</td>
78+
</tr>
79+
<tr>
80+
<th scope="row">{_t("devtools|crypto|backup_key_stored_status")}</th>
81+
<td>
82+
{backupKeyStored
83+
? _t("devtools|crypto|backup_key_stored")
84+
: _t("devtools|crypto|backup_key_not_stored")}
85+
</td>
86+
</tr>
87+
<tr>
88+
<th scope="row">{_t("devtools|crypto|key_backup_active_version")}</th>
89+
<td>
90+
{activeBackupVersion === null
91+
? _t("devtools|crypto|key_backup_active_version_none")
92+
: activeBackupVersion}
93+
</td>
94+
</tr>
95+
<tr>
96+
<th scope="row">{_t("devtools|crypto|backup_key_cached_status")}</th>
97+
<td>
98+
{`${
99+
backupKeyCached
100+
? _t("devtools|crypto|backup_key_cached")
101+
: _t("devtools|crypto|backup_key_not_cached")
102+
},
103+
${
104+
backupKeyWellFormed
105+
? _t("devtools|crypto|backup_key_well_formed")
106+
: _t("devtools|crypto|backup_key_unexpected_type")
107+
}`}
108+
</td>
109+
</tr>
110+
<tr>
111+
<th scope="row">{_t("devtools|crypto|4s_public_key_status")}</th>
112+
<td>
113+
{secretStorageKeyInAccount
114+
? _t("devtools|crypto|4s_public_key_in_account_data")
115+
: _t("devtools|crypto|4s_public_key_not_in_account_data")}
116+
</td>
117+
</tr>
118+
<tr>
119+
<th scope="row">{_t("devtools|crypto|secret_storage_status")}</th>
120+
<td>
121+
{secretStorageReady
122+
? _t("devtools|crypto|secret_storage_ready")
123+
: _t("devtools|crypto|secret_storage_not_ready")}
124+
</td>
125+
</tr>
126+
</tbody>
127+
</table>
128+
);
129+
}

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)