/*
* 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, { type 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";
import Modal from "../../../../Modal";
import { ManualDeviceKeyVerificationDialog } from "../ManualDeviceKeyVerificationDialog";
interface KeyBackupProps {
/**
* Callback to invoke when the back button is clicked.
*/
onBack(): void;
}
/**
* A component that displays information about the key storage and cross-signing.
*/
export function Crypto({ onBack }: KeyBackupProps): JSX.Element {
const matrixClient = useMatrixClientContext();
return (
{matrixClient.getCrypto() ? (
<>
>
) : (
{_t("devtools|crypto|crypto_not_available")}
)}
);
}
/**
* A component that displays information about the key storage.
*/
function KeyStorage(): JSX.Element {
const matrixClient = useMatrixClientContext();
const keyStorageData = useAsyncMemo(async () => {
const crypto = matrixClient.getCrypto()!;
// Get all the key storage data that we will display
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]);
// Show a spinner while loading
if (keyStorageData === undefined) return ;
const {
backupInfo,
backupKeyStored,
backupKeyCached,
backupKeyWellFormed,
activeBackupVersion,
secretStorageKeyInAccount,
secretStorageReady,
} = keyStorageData;
return (
{_t("devtools|crypto|key_storage")}
| {_t("devtools|crypto|key_backup_latest_version")} |
{backupInfo
? `${backupInfo.version} (${_t("settings|security|key_backup_algorithm")} ${backupInfo.algorithm})`
: _t("devtools|crypto|key_backup_inactive_warning")}
|
| {_t("devtools|crypto|backup_key_stored_status")} |
{backupKeyStored
? _t("devtools|crypto|backup_key_stored")
: _t("devtools|crypto|backup_key_not_stored")}
|
| {_t("devtools|crypto|key_backup_active_version")} |
{activeBackupVersion === null
? _t("devtools|crypto|key_backup_active_version_none")
: activeBackupVersion}
|
| {_t("devtools|crypto|backup_key_cached_status")} |
{`${
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")
}`}
|
| {_t("devtools|crypto|4s_public_key_status")} |
{secretStorageKeyInAccount
? _t("devtools|crypto|4s_public_key_in_account_data")
: _t("devtools|crypto|4s_public_key_not_in_account_data")}
|
| {_t("devtools|crypto|secret_storage_status")} |
{secretStorageReady
? _t("devtools|crypto|secret_storage_ready")
: _t("devtools|crypto|secret_storage_not_ready")}
|
);
}
/**
* A component that displays information about cross-signing.
*/
function CrossSigning(): JSX.Element {
const matrixClient = useMatrixClientContext();
const crossSigningData = useAsyncMemo(async () => {
const crypto = matrixClient.getCrypto()!;
// Get all the cross-signing data that we will display
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 crossSigningReady = await crypto.isCrossSigningReady();
return {
crossSigningPublicKeysOnDevice,
crossSigningPrivateKeysInStorage,
masterPrivateKeyCached,
selfSigningPrivateKeyCached,
userSigningPrivateKeyCached,
crossSigningReady,
};
}, [matrixClient]);
// Show a spinner while loading
if (crossSigningData === undefined) return ;
const {
crossSigningPublicKeysOnDevice,
crossSigningPrivateKeysInStorage,
masterPrivateKeyCached,
selfSigningPrivateKeyCached,
userSigningPrivateKeyCached,
crossSigningReady,
} = crossSigningData;
return (
{_t("devtools|crypto|cross_signing")}
| {_t("devtools|crypto|cross_signing_status")} |
{getCrossSigningStatus(crossSigningReady, crossSigningPrivateKeysInStorage)} |
| {_t("devtools|crypto|cross_signing_public_keys_on_device_status")} |
{crossSigningPublicKeysOnDevice
? _t("devtools|crypto|cross_signing_public_keys_on_device")
: _t("devtools|crypto|not_found")}
|
| {_t("devtools|crypto|cross_signing_private_keys_in_storage_status")} |
{crossSigningPrivateKeysInStorage
? _t("devtools|crypto|cross_signing_private_keys_in_storage")
: _t("devtools|crypto|cross_signing_private_keys_not_in_storage")}
|
| {_t("devtools|crypto|master_private_key_cached_status")} |
{masterPrivateKeyCached
? _t("devtools|crypto|cross_signing_cached")
: _t("devtools|crypto|not_found_locally")}
|
| {_t("devtools|crypto|self_signing_private_key_cached_status")} |
{selfSigningPrivateKeyCached
? _t("devtools|crypto|cross_signing_cached")
: _t("devtools|crypto|not_found_locally")}
|
| {_t("devtools|crypto|user_signing_private_key_cached_status")} |
{userSigningPrivateKeyCached
? _t("devtools|crypto|cross_signing_cached")
: _t("devtools|crypto|not_found_locally")}
|
);
}
/**
* Get the cross-signing status.
* @param crossSigningReady Whether cross-signing is ready.
* @param crossSigningPrivateKeysInStorage Whether cross-signing private keys are in secret storage.
*/
function getCrossSigningStatus(crossSigningReady: boolean, crossSigningPrivateKeysInStorage: boolean): string {
if (crossSigningReady) {
return crossSigningPrivateKeysInStorage
? _t("devtools|crypto|cross_signing_ready")
: _t("devtools|crypto|cross_signing_untrusted");
}
if (crossSigningPrivateKeysInStorage) {
return _t("devtools|crypto|cross_signing_not_ready");
}
return _t("devtools|crypto|cross_signing_not_ready");
}
/**
* A component that displays information about the current session.
*/
function Session(): JSX.Element {
const matrixClient = useMatrixClientContext();
const sessionData = useAsyncMemo(async () => {
const crypto = matrixClient.getCrypto()!;
const keys = await crypto.getOwnDeviceKeys();
return {
fingerprint: keys.ed25519,
deviceId: matrixClient.deviceId,
};
}, [matrixClient]);
// Show a spinner while loading
if (sessionData === undefined) {
return ;
}
return (
{_t("devtools|crypto|session")}
| {_t("devtools|crypto|device_id")} |
{sessionData.deviceId} |
| {_t("devtools|crypto|session_fingerprint")} |
{sessionData.fingerprint} |
);
}