-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(devtools): Add crypto information in devtools
- Loading branch information
1 parent
ad01218
commit d52c2f2
Showing
5 changed files
with
331 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters