@@ -191,11 +215,14 @@ function DeviceManage() {
)}
{isCurrentDevice && (
- {`Session Key: ${mx.getDeviceEd25519Key().match(/.{1,4}/g).join(' ')}`}
+ {`Session Key: ${mx
+ .getDeviceEd25519Key()
+ .match(/.{1,4}/g)
+ .join(' ')}`}
)}
>
- )}
+ }
/>
);
};
@@ -203,16 +230,18 @@ function DeviceManage() {
const unverified = [];
const verified = [];
const noEncryption = [];
- deviceList.sort((a, b) => b.last_seen_ts - a.last_seen_ts).forEach((device) => {
- const isVerified = isCrossVerified(mx, device.device_id);
- if (isVerified === true) {
- verified.push(device);
- } else if (isVerified === false) {
- unverified.push(device);
- } else {
- noEncryption.push(device);
- }
- });
+ deviceList
+ .sort((a, b) => b.last_seen_ts - a.last_seen_ts)
+ .forEach((device) => {
+ const isVerified = isCrossVerified(mx, device.device_id);
+ if (isVerified === true) {
+ verified.push(device);
+ } else if (isVerified === false) {
+ unverified.push(device);
+ } else {
+ noEncryption.push(device);
+ }
+ });
return (
@@ -247,35 +276,37 @@ function DeviceManage() {
/>
)}
- {
- unverified.length > 0
- ? unverified.map((device) => renderDevice(device, false))
- :
No unverified sessions
- }
+ {unverified.length > 0 ? (
+ unverified.map((device) => renderDevice(device, false))
+ ) : (
+
No unverified sessions
+ )}
{noEncryption.length > 0 && (
-
- Sessions without encryption support
- {noEncryption.map((device) => renderDevice(device, null))}
-
+
+ Sessions without encryption support
+ {noEncryption.map((device) => renderDevice(device, null))}
+
)}
Verified sessions
- {
- verified.length > 0
- ? verified.map((device, index) => {
- if (truncated && index >= TRUNCATED_COUNT) return null;
- return renderDevice(device, true);
- })
- : No verified sessions
- }
- { verified.length > TRUNCATED_COUNT && (
+ {verified.length > 0 ? (
+ verified.map((device, index) => {
+ if (truncated && index >= TRUNCATED_COUNT) return null;
+ return renderDevice(device, true);
+ })
+ ) : (
+ No verified sessions
+ )}
+ {verified.length > TRUNCATED_COUNT && (
)}
- { deviceList.length > 0 && (
- Session names are visible to everyone, so do not put any private info here.
+ {deviceList.length > 0 && (
+
+ Session names are visible to everyone, so do not put any private info here.
+
)}
diff --git a/src/app/pages/auth/login/PasswordLoginForm.tsx b/src/app/pages/auth/login/PasswordLoginForm.tsx
index 087d384d5f..90c305d001 100644
--- a/src/app/pages/auth/login/PasswordLoginForm.tsx
+++ b/src/app/pages/auth/login/PasswordLoginForm.tsx
@@ -33,7 +33,7 @@ import {
login,
useLoginComplete,
} from './loginUtil';
-import { PasswordInput } from '../../../components/password-input/PasswordInput';
+import { PasswordInput } from '../../../components/password-input';
import { FieldError } from '../FiledError';
import { getResetPasswordPath } from '../../pathUtils';
import { stopPropagation } from '../../../utils/keyboard';
diff --git a/src/app/pages/auth/register/PasswordRegisterForm.tsx b/src/app/pages/auth/register/PasswordRegisterForm.tsx
index f4439dd6cd..9f17342295 100644
--- a/src/app/pages/auth/register/PasswordRegisterForm.tsx
+++ b/src/app/pages/auth/register/PasswordRegisterForm.tsx
@@ -20,7 +20,7 @@ import {
UIAFlow,
createClient,
} from 'matrix-js-sdk';
-import { PasswordInput } from '../../../components/password-input/PasswordInput';
+import { PasswordInput } from '../../../components/password-input';
import {
getLoginTermUrl,
getUIAFlowForStages,
diff --git a/src/app/pages/auth/reset-password/PasswordResetForm.tsx b/src/app/pages/auth/reset-password/PasswordResetForm.tsx
index 7c71de021d..392f45c076 100644
--- a/src/app/pages/auth/reset-password/PasswordResetForm.tsx
+++ b/src/app/pages/auth/reset-password/PasswordResetForm.tsx
@@ -19,7 +19,7 @@ import { useAutoDiscoveryInfo } from '../../../hooks/useAutoDiscoveryInfo';
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
import { useAuthServer } from '../../../hooks/useAuthServer';
import { usePasswordEmail } from '../../../hooks/usePasswordEmail';
-import { PasswordInput } from '../../../components/password-input/PasswordInput';
+import { PasswordInput } from '../../../components/password-input';
import { ConfirmPasswordMatch } from '../../../components/ConfirmPasswordMatch';
import { FieldError } from '../FiledError';
import { UIAFlowOverlay } from '../../../components/UIAFlowOverlay';
diff --git a/src/app/pages/client/sidebar/SettingsTab.tsx b/src/app/pages/client/sidebar/SettingsTab.tsx
index f19b922112..83cd118ca1 100644
--- a/src/app/pages/client/sidebar/SettingsTab.tsx
+++ b/src/app/pages/client/sidebar/SettingsTab.tsx
@@ -1,6 +1,5 @@
import React, { useState } from 'react';
-import { Modal, Overlay, OverlayBackdrop, OverlayCenter, Text } from 'folds';
-import FocusTrap from 'focus-trap-react';
+import { Text } from 'folds';
import { SidebarItem, SidebarItemTooltip, SidebarAvatar } from '../../../components/sidebar';
import { UserAvatar } from '../../../components/user-avatar';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
@@ -8,8 +7,8 @@ import { getMxIdLocalPart, mxcUrlToHttp } from '../../../utils/matrix';
import { nameInitials } from '../../../utils/common';
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
import { Settings } from '../../../features/settings';
-import { stopPropagation } from '../../../utils/keyboard';
import { useUserProfile } from '../../../hooks/useUserProfile';
+import { Modal500 } from '../../../components/Modal500';
export function SettingsTab() {
const mx = useMatrixClient();
@@ -41,22 +40,9 @@ export function SettingsTab() {
)}
{settings && (
- }>
-
-
-
-
-
-
-
-
+
+
+
)}
);
diff --git a/src/app/pages/client/sidebar/UnverifiedTab.css.ts b/src/app/pages/client/sidebar/UnverifiedTab.css.ts
index e8fe8c51b2..cce17ccb92 100644
--- a/src/app/pages/client/sidebar/UnverifiedTab.css.ts
+++ b/src/app/pages/client/sidebar/UnverifiedTab.css.ts
@@ -22,3 +22,9 @@ export const UnverifiedAvatar = style({
color: color.Critical.OnContainer,
borderColor: color.Critical.ContainerLine,
});
+
+export const UnverifiedOtherAvatar = style({
+ backgroundColor: color.Warning.Container,
+ color: color.Warning.OnContainer,
+ borderColor: color.Warning.ContainerLine,
+});
diff --git a/src/app/pages/client/sidebar/UnverifiedTab.tsx b/src/app/pages/client/sidebar/UnverifiedTab.tsx
index 919c4a708a..b3b92c89a8 100644
--- a/src/app/pages/client/sidebar/UnverifiedTab.tsx
+++ b/src/app/pages/client/sidebar/UnverifiedTab.tsx
@@ -1,49 +1,92 @@
-import React from 'react';
+import React, { useState } from 'react';
import { Badge, color, Icon, Icons, Text } from 'folds';
-import { openSettings } from '../../../../client/action/navigation';
-import { isCrossVerified } from '../../../../util/matrixUtil';
import {
SidebarAvatar,
SidebarItem,
SidebarItemBadge,
SidebarItemTooltip,
} from '../../../components/sidebar';
-import { useDeviceList } from '../../../hooks/useDeviceList';
-import { tabText } from '../../../organisms/settings/Settings';
+import { useDeviceIds, useDeviceList, useSplitCurrentDevice } from '../../../hooks/useDeviceList';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import * as css from './UnverifiedTab.css';
+import {
+ useDeviceVerificationStatus,
+ useUnverifiedDeviceCount,
+ VerificationStatus,
+} from '../../../hooks/useDeviceVerificationStatus';
+import { useCrossSigningActive } from '../../../hooks/useCrossSigning';
+import { Modal500 } from '../../../components/Modal500';
+import { Settings, SettingsPages } from '../../../features/settings';
-export function UnverifiedTab() {
+function UnverifiedIndicator() {
const mx = useMatrixClient();
- const deviceList = useDeviceList();
- const unverified = deviceList?.filter(
- (device) => isCrossVerified(mx, device.device_id) === false
+
+ const crypto = mx.getCrypto();
+ const [devices] = useDeviceList();
+
+ const [currentDevice, otherDevices] = useSplitCurrentDevice(devices);
+
+ const verificationStatus = useDeviceVerificationStatus(
+ crypto,
+ mx.getSafeUserId(),
+ currentDevice?.device_id
+ );
+ const currentVerified = verificationStatus === VerificationStatus.Verified;
+
+ const otherDevicesId = useDeviceIds(otherDevices);
+ const unverifiedDeviceCount = useUnverifiedDeviceCount(
+ crypto,
+ mx.getSafeUserId(),
+ otherDevicesId
);
- if (!unverified?.length) return null;
+ const [settings, setSettings] = useState(false);
+ const closeSettings = () => setSettings(false);
+
+ if (currentVerified && unverifiedDeviceCount === 0) {
+ return null;
+ }
return (
-
+
{(triggerRef) => (
openSettings(tabText.SECURITY)}
+ onClick={() => setSettings(true)}
>
-
+
)}
-
-
- {unverified.length}
-
-
+ {currentVerified && (
+
+
+ {unverifiedDeviceCount}
+
+
+ )}
+ {settings && (
+
+
+
+ )}
);
}
+
+export function UnverifiedTab() {
+ const crossSigningActive = useCrossSigningActive();
+
+ if (!crossSigningActive) return null;
+
+ return ;
+}
diff --git a/src/app/utils/matrix-crypto.ts b/src/app/utils/matrix-crypto.ts
new file mode 100644
index 0000000000..dda402f8f2
--- /dev/null
+++ b/src/app/utils/matrix-crypto.ts
@@ -0,0 +1,14 @@
+import { CryptoApi } from 'matrix-js-sdk/lib/crypto-api';
+
+export const verifiedDevice = async (
+ api: CryptoApi,
+ userId: string,
+ deviceId: string
+): Promise => {
+ const status = await api.getDeviceVerificationStatus(userId, deviceId);
+
+ if (!status) return null;
+
+ const verified = status.crossSigningVerified;
+ return verified;
+};
diff --git a/src/app/utils/matrix.ts b/src/app/utils/matrix.ts
index 66975e7b96..09f7e8f1c8 100644
--- a/src/app/utils/matrix.ts
+++ b/src/app/utils/matrix.ts
@@ -4,6 +4,7 @@ import {
encryptAttachment,
} from 'browser-encrypt-attachment';
import {
+ EventTimeline,
MatrixClient,
MatrixError,
MatrixEvent,
@@ -173,7 +174,7 @@ export const eventWithShortcode = (ev: MatrixEvent) =>
export const getDMRoomFor = (mx: MatrixClient, userId: string): Room | undefined => {
const dmLikeRooms = mx
.getRooms()
- .filter((room) => mx.isRoomEncrypted(room.roomId) && room.getMembers().length <= 2);
+ .filter((room) => room.hasEncryptionStateEvent() && room.getMembers().length <= 2);
return dmLikeRooms.find((room) => room.getMember(userId));
};
@@ -205,7 +206,9 @@ export const guessDmRoomUserId = (room: Room, myUserId: string): string => {
if (member) return member.userId;
// if there are no joined members other than us, use the oldest member
- const member1 = getOldestMember(room.currentState.getMembers());
+ const member1 = getOldestMember(
+ room.getLiveTimeline().getState(EventTimeline.FORWARDS)?.getMembers() ?? []
+ );
return member1?.userId ?? myUserId;
};
diff --git a/src/app/utils/room.ts b/src/app/utils/room.ts
index 5f4615a6ed..36de449397 100644
--- a/src/app/utils/room.ts
+++ b/src/app/utils/room.ts
@@ -30,10 +30,12 @@ export const getStateEvent = (
room: Room,
eventType: StateEvent,
stateKey = ''
-): MatrixEvent | undefined => room.currentState.getStateEvents(eventType, stateKey) ?? undefined;
+): MatrixEvent | undefined =>
+ room.getLiveTimeline().getState(EventTimeline.FORWARDS)?.getStateEvents(eventType, stateKey) ??
+ undefined;
export const getStateEvents = (room: Room, eventType: StateEvent): MatrixEvent[] =>
- room.currentState.getStateEvents(eventType);
+ room.getLiveTimeline().getState(EventTimeline.FORWARDS)?.getStateEvents(eventType) ?? [];
export const getAccountData = (
mx: MatrixClient,
diff --git a/src/client/action/room.js b/src/client/action/room.js
index 48ae7c4764..90b748104d 100644
--- a/src/client/action/room.js
+++ b/src/client/action/room.js
@@ -1,3 +1,4 @@
+import { EventTimeline } from 'matrix-js-sdk';
import { getIdServer } from '../../util/matrixUtil';
/**
@@ -63,7 +64,7 @@ function guessDMRoomTargetId(room, myUserId) {
if (oldestMember) return oldestMember.userId;
// if there are no joined members other than us, use the oldest member
- room.currentState.getMembers().forEach((member) => {
+ room.getLiveTimeline().getState(EventTimeline.FORWARDS)?.getMembers().forEach((member) => {
if (member.userId === myUserId) return;
if (typeof oldestMemberTs === 'undefined' || (member.events.member && member.events.member.getTs() < oldestMemberTs)) {
@@ -250,7 +251,7 @@ async function setPowerLevel(mx, roomId, userId, powerLevel) {
async function setMyRoomNick(mx, roomId, nick) {
const room = mx.getRoom(roomId);
- const mEvent = room.currentState.getStateEvents('m.room.member', mx.getUserId());
+ const mEvent = room.getLiveTimeline().getState(EventTimeline.FORWARDS).getStateEvents('m.room.member', mx.getUserId());
const content = mEvent?.getContent();
if (!content) return;
await mx.sendStateEvent(roomId, 'm.room.member', {
@@ -261,7 +262,7 @@ async function setMyRoomNick(mx, roomId, nick) {
async function setMyRoomAvatar(mx, roomId, mxc) {
const room = mx.getRoom(roomId);
- const mEvent = room.currentState.getStateEvents('m.room.member', mx.getUserId());
+ const mEvent = room.getLiveTimeline().getState(EventTimeline.FORWARDS).getStateEvents('m.room.member', mx.getUserId());
const content = mEvent?.getContent();
if (!content) return;
await mx.sendStateEvent(roomId, 'm.room.member', {
diff --git a/src/client/initMatrix.ts b/src/client/initMatrix.ts
index 52e8317e40..b64fa883aa 100644
--- a/src/client/initMatrix.ts
+++ b/src/client/initMatrix.ts
@@ -1,11 +1,8 @@
import { createClient, MatrixClient, IndexedDBStore, IndexedDBCryptoStore } from 'matrix-js-sdk';
-import Olm from '@matrix-org/olm';
import { logger } from 'matrix-js-sdk/lib/logger';
import { cryptoCallbacks } from './state/secretStorageKeys';
-global.Olm = Olm;
-
if (import.meta.env.PROD) {
logger.disableAll();
}
@@ -24,20 +21,22 @@ export const initClient = async (session: Session): Promise => {
dbName: 'web-sync-store',
});
+ const legacyCryptoStore = new IndexedDBCryptoStore(global.indexedDB, 'crypto-store');
+
const mx = createClient({
baseUrl: session.baseUrl,
accessToken: session.accessToken,
userId: session.userId,
store: indexedDBStore,
- cryptoStore: new IndexedDBCryptoStore(global.indexedDB, 'crypto-store'),
+ cryptoStore: legacyCryptoStore,
deviceId: session.deviceId,
timelineSupport: true,
cryptoCallbacks: cryptoCallbacks as any,
verificationMethods: ['m.sas.v1'],
});
- await mx.initCrypto();
await indexedDBStore.startup();
+ await mx.initRustCrypto();
mx.setGlobalErrorOnUnknownDevices(false);
mx.setMaxListeners(50);
diff --git a/src/types/matrix/accountData.ts b/src/types/matrix/accountData.ts
index 1078cb3562..20ce941911 100644
--- a/src/types/matrix/accountData.ts
+++ b/src/types/matrix/accountData.ts
@@ -9,4 +9,40 @@ export enum AccountDataEvent {
PoniesUserEmotes = 'im.ponies.user_emotes',
PoniesEmoteRooms = 'im.ponies.emote_rooms',
+
+ SecretStorageDefaultKey = 'm.secret_storage.default_key',
+
+ CrossSigningMaster = 'm.cross_signing.master',
+ CrossSigningSelf = 'm.cross_signing.self',
+ CrossSigningUser = 'm.cross_signing.user',
+ MegolmBackupV1 = 'm.megolm_backup.v1',
}
+
+export type SecretStorageDefaultKeyContent = {
+ key: string;
+};
+
+export type SecretStoragePassphraseContent = {
+ algorithm: string;
+ salt: string;
+ iterations: number;
+ bits?: number;
+};
+
+export type SecretStorageKeyContent = {
+ name?: string;
+ algorithm: string;
+ iv?: string;
+ mac?: string;
+ passphrase?: SecretStoragePassphraseContent;
+};
+
+export type SecretContent = {
+ iv: string;
+ ciphertext: string;
+ mac: string;
+};
+
+export type SecretAccountData = {
+ encrypted: Record;
+};