Skip to content

Commit

Permalink
feat(devtools): Add crypto information in devtools
Browse files Browse the repository at this point in the history
  • Loading branch information
florianduros committed Jan 22, 2025
1 parent ad01218 commit d52c2f2
Show file tree
Hide file tree
Showing 5 changed files with 331 additions and 0 deletions.
1 change: 1 addition & 0 deletions res/css/_components.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@
@import "./views/dialogs/_ConfirmUserActionDialog.pcss";
@import "./views/dialogs/_CreateRoomDialog.pcss";
@import "./views/dialogs/_CreateSubspaceDialog.pcss";
@import "./views/dialogs/_Crypto.pcss";
@import "./views/dialogs/_DeactivateAccountDialog.pcss";
@import "./views/dialogs/_DevtoolsDialog.pcss";
@import "./views/dialogs/_ExportDialog.pcss";
Expand Down
22 changes: 22 additions & 0 deletions res/css/views/dialogs/_Crypto.pcss
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/

.mx_Crypto {
.mx_KeyStorage,
.mx_CrossSigning {
margin: var(--cpd-space-4x) 0;
text-align: left;

thead {
font: var(--cpd-font-heading-sm-semibold);
}

th {
padding-right: var(--cpd-space-2x);
}
}
}
2 changes: 2 additions & 0 deletions src/components/views/dialogs/DevtoolsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { SettingLevel } from "../../../settings/SettingLevel";
import ServerInfo from "./devtools/ServerInfo";
import CopyableText from "../elements/CopyableText";
import RoomNotifications from "./devtools/RoomNotifications";
import { Crypto } from "./devtools/Crypto";

enum Category {
Room,
Expand All @@ -49,6 +50,7 @@ const Tools: Record<Category, [label: TranslationKey, tool: Tool][]> = {
[_td("devtools|explore_account_data"), AccountDataExplorer],
[_td("devtools|settings_explorer"), SettingExplorer],
[_td("devtools|server_info"), ServerInfo],
[_td("devtools|crypto|title"), Crypto],
],
};

Expand Down
264 changes: 264 additions & 0 deletions src/components/views/dialogs/devtools/Crypto.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/

import React, { JSX } from "react";
import { InlineSpinner } from "@vector-im/compound-web";

import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext";
import BaseTool from "./BaseTool";
import { useAsyncMemo } from "../../../../hooks/useAsyncMemo";
import { _t } from "../../../../languageHandler";

interface KeyBackupProps {
onBack(): void;
}

export function Crypto({ onBack }: KeyBackupProps): JSX.Element {
return (
<BaseTool onBack={onBack} className="mx_Crypto">
<KeyStorage />
<CrossSigning />
</BaseTool>
);
}

/**
* A component that displays information about the key storage.
*/
function KeyStorage(): JSX.Element {
const matrixClient = useMatrixClientContext();
const keyStorageData = useAsyncMemo(async () => {
const crypto = matrixClient.getCrypto();
if (!crypto) return null;

const backupInfo = await crypto.getKeyBackupInfo();
const backupKeyStored = Boolean(await matrixClient.isKeyBackupKeyStored());
const backupKeyFromCache = await crypto.getSessionBackupPrivateKey();
const backupKeyCached = Boolean(backupKeyFromCache);
const backupKeyWellFormed = backupKeyFromCache instanceof Uint8Array;
const activeBackupVersion = await crypto.getActiveSessionBackupVersion();
const secretStorageKeyInAccount = await matrixClient.secretStorage.hasKey();
const secretStorageReady = await crypto.isSecretStorageReady();

return {
backupInfo,
backupKeyStored,
backupKeyCached,
backupKeyWellFormed,
activeBackupVersion,
secretStorageKeyInAccount,
secretStorageReady,
};
}, [matrixClient]);

if (keyStorageData === undefined) return <InlineSpinner aria-label={_t("common|loading")} />;
if (keyStorageData === null) return <span>{_t("devtools|crypto|crypto_not_available")}</span>;

const {
backupInfo,
backupKeyStored,
backupKeyCached,
backupKeyWellFormed,
activeBackupVersion,
secretStorageKeyInAccount,
secretStorageReady,
} = keyStorageData;

return (
<table className="mx_KeyStorage">
<thead>{_t("devtools|crypto|key_storage")}</thead>
<tbody>
<tr>
<th scope="row">{_t("devtools|crypto|key_backup_latest_version")}</th>
<td>
{backupInfo
? `${backupInfo.version} (${_t("settings|security|key_backup_algorithm")} ${backupInfo.algorithm})`
: _t("devtools|crypto|key_backup_inactive_warning")}
</td>
</tr>
<tr>
<th scope="row">{_t("devtools|crypto|backup_key_stored_status")}</th>
<td>
{backupKeyStored
? _t("devtools|crypto|backup_key_stored")
: _t("devtools|crypto|backup_key_not_stored")}
</td>
</tr>
<tr>
<th scope="row">{_t("devtools|crypto|key_backup_active_version")}</th>
<td>
{activeBackupVersion === null
? _t("devtools|crypto|key_backup_active_version_none")
: activeBackupVersion}
</td>
</tr>
<tr>
<th scope="row">{_t("devtools|crypto|backup_key_cached_status")}</th>
<td>
{`${
backupKeyCached
? _t("devtools|crypto|backup_key_cached")
: _t("devtools|crypto|not_found_locally")
},
${
backupKeyWellFormed
? _t("devtools|crypto|backup_key_well_formed")
: _t("devtools|crypto|backup_key_unexpected_type")
}`}
</td>
</tr>
<tr>
<th scope="row">{_t("devtools|crypto|4s_public_key_status")}</th>
<td>
{secretStorageKeyInAccount
? _t("devtools|crypto|4s_public_key_in_account_data")
: _t("devtools|crypto|4s_public_key_not_in_account_data")}
</td>
</tr>
<tr>
<th scope="row">{_t("devtools|crypto|secret_storage_status")}</th>
<td>
{secretStorageReady
? _t("devtools|crypto|secret_storage_ready")
: _t("devtools|crypto|secret_storage_not_ready")}
</td>
</tr>
</tbody>
</table>
);
}

type CrossSigningData = {
crossSigningPublicKeysOnDevice: boolean;
crossSigningPrivateKeysInStorage: boolean;
masterPrivateKeyCached: boolean;
selfSigningPrivateKeyCached: boolean;
userSigningPrivateKeyCached: boolean;
homeserverSupportsCrossSigning: boolean;
crossSigningReady: boolean;
};

/**
* A component that displays information about cross-signing.
*/
function CrossSigning(): JSX.Element {
const matrixClient = useMatrixClientContext();
const crossSigningData = useAsyncMemo<CrossSigningData | null>(async () => {
const crypto = matrixClient.getCrypto();
if (!crypto) return null;

const crossSigningStatus = await crypto.getCrossSigningStatus();
const crossSigningPublicKeysOnDevice = crossSigningStatus.publicKeysOnDevice;
const crossSigningPrivateKeysInStorage = crossSigningStatus.privateKeysInSecretStorage;
const masterPrivateKeyCached = crossSigningStatus.privateKeysCachedLocally.masterKey;
const selfSigningPrivateKeyCached = crossSigningStatus.privateKeysCachedLocally.selfSigningKey;
const userSigningPrivateKeyCached = crossSigningStatus.privateKeysCachedLocally.userSigningKey;
const homeserverSupportsCrossSigning =
await matrixClient.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing");
const crossSigningReady = await crypto.isCrossSigningReady();

return {
crossSigningPublicKeysOnDevice,
crossSigningPrivateKeysInStorage,
masterPrivateKeyCached,
selfSigningPrivateKeyCached,
userSigningPrivateKeyCached,
homeserverSupportsCrossSigning,
crossSigningReady,
};
}, [matrixClient]);

if (crossSigningData === undefined) return <InlineSpinner aria-label={_t("common|loading")} />;
if (crossSigningData === null) return <span>{_t("devtools|crypto|crypto_not_available")}</span>;

const {
crossSigningPublicKeysOnDevice,
crossSigningPrivateKeysInStorage,
masterPrivateKeyCached,
selfSigningPrivateKeyCached,
userSigningPrivateKeyCached,
homeserverSupportsCrossSigning,
} = crossSigningData;

return (
<table className="mx_CrossSigning">
<thead>{_t("devtools|crypto|cross_signing")}</thead>
<tbody>
<tr>
<th scope="row">{_t("devtools|crypto|cross_signing_status")}</th>
<td>{getCrossSigningStatus(crossSigningData)}</td>
</tr>
<tr>
<th scope="row">{_t("devtools|crypto|cross_signing_public_keys_on_device_status")}</th>
<td>
{crossSigningPublicKeysOnDevice
? _t("devtools|crypto|cross_signing_public_keys_on_device")
: _t("devtools|crypto|not_found")}
</td>
</tr>
<tr>
<th scope="row">{_t("devtools|crypto|cross_signing_private_keys_in_storage_status")}</th>
<td>
{crossSigningPrivateKeysInStorage
? _t("devtools|crypto|cross_signing_private_keys_in_storage")
: _t("devtools|crypto|cross_signing_private_keys_not_in_storage")}
</td>
</tr>
<tr>
<th scope="row">{_t("devtools|crypto|master_private_key_cached_status")}</th>
<td>
{masterPrivateKeyCached
? _t("devtools|crypto|cross_signing_cached")
: _t("devtools|crypto|not_found_locally")}
</td>
</tr>
<tr>
<th scope="row">{_t("devtools|crypto|self_signing_private_key_cached_status")}</th>
<td>
{selfSigningPrivateKeyCached
? _t("devtools|crypto|cross_signing_cached")
: _t("devtools|crypto|not_found_locally")}
</td>
</tr>
<tr>
<th scope="row">{_t("devtools|crypto|user_signing_private_key_cached_status")}</th>
<td>
{userSigningPrivateKeyCached
? _t("devtools|crypto|cross_signing_cached")
: _t("devtools|crypto|not_found_locally")}
</td>
</tr>
<tr>
<th scope="row">{_t("devtools|crypto|homeserver_supports_cross_signing_status")}</th>
<td>
{homeserverSupportsCrossSigning
? _t("devtools|crypto|homeserver_supports_cross_signing")
: _t("devtools|crypto|not_found")}
</td>
</tr>
</tbody>
</table>
);
}

function getCrossSigningStatus({
homeserverSupportsCrossSigning,
crossSigningReady,
crossSigningPrivateKeysInStorage,
}: CrossSigningData): string {
if (!homeserverSupportsCrossSigning) {
return _t("devtools|crypto|cross_signing_unsupported");
} else if (crossSigningReady && crossSigningPrivateKeysInStorage) {
return _t("devtools|crypto|cross_signing_ready");
} else if (crossSigningReady && !crossSigningPrivateKeysInStorage) {
return _t("devtools|crypto|cross_signing_ready_no_backup");
} else if (crossSigningPrivateKeysInStorage) {
return _t("devtools|crypto|cross_signing_untrusted");
} else {
return _t("devtools|crypto|cross_signing_not_ready");
}
}
42 changes: 42 additions & 0 deletions src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,48 @@
"category_room": "Room",
"caution_colon": "Caution:",
"client_versions": "Client Versions",
"crypto": {
"4s_public_key_in_account_data": "in account data",
"4s_public_key_not_in_account_data": "not found",
"4s_public_key_status": "Secret storage public key:",
"backup_key_cached": "cached locally",
"backup_key_cached_status": "Backup key cached:",
"backup_key_not_stored": "not stored",
"backup_key_stored": "in secret storage",
"backup_key_stored_status": "Backup key stored:",
"backup_key_unexpected_type": "unexpected type",
"backup_key_well_formed": "well formed",
"cross_signing": "Cross-signing",
"cross_signing_cached": "cached locally",
"cross_signing_not_ready": "Cross-signing is not set up.",
"cross_signing_private_keys_in_storage": "in secret storage",
"cross_signing_private_keys_in_storage_status": "Cross-signing private keys:",
"cross_signing_private_keys_not_in_storage": "not found in storage",
"cross_signing_public_keys_on_device": "in memory",
"cross_signing_public_keys_on_device_status": "Cross-signing public keys:",
"cross_signing_ready": "Cross-signing is ready for use.",
"cross_signing_ready_no_backup": "Cross-signing is ready but keys are not backed up.",
"cross_signing_status": "Cross-signing status:",
"cross_signing_unsupported": "Your homeserver does not support cross-signing.",
"cross_signing_untrusted": "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.",
"crypto_not_available": "Cryptographic module is not available",
"homeserver_supports_cross_signing": "exists",
"homeserver_supports_cross_signing_status": "Homeserver feature support:",
"key_backup_active_version": "Active backup version:",
"key_backup_active_version_none": "None",
"key_backup_inactive_warning": "Your keys are not being backed up from this session.",
"key_backup_latest_version": "Latest backup version on server:",
"key_storage": "Key Storage",
"master_private_key_cached_status": "Master private key:",
"not_found": "not found",
"not_found_locally": "not found locally",
"secret_storage_not_ready": "not ready",
"secret_storage_ready": "ready",
"secret_storage_status": "Secret storage:",
"self_signing_private_key_cached_status": "Self signing private key:",
"title": "Crypto",
"user_signing_private_key_cached_status": "User signing private key:"
},
"developer_mode": "Developer mode",
"developer_tools": "Developer Tools",
"edit_setting": "Edit setting",
Expand Down

0 comments on commit d52c2f2

Please sign in to comment.