Skip to content

Commit

Permalink
chore(suite): consolidate entropy check selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
komret committed Feb 4, 2025
1 parent 1b0bcbe commit a515484
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 46 deletions.
21 changes: 6 additions & 15 deletions packages/suite/src/components/suite/Preloader/Preloader.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { FC, PropsWithChildren, useEffect } from 'react';

import { Feature, selectIsFeatureDisabled } from '@suite-common/message-system';
import {
selectIsEntropyCheckFailed,
selectIsFirmwareAuthenticityCheckDismissed,
selectSelectedDevice,
} from '@suite-common/wallet-core';
Expand All @@ -12,7 +10,7 @@ import { useGuideKeyboard } from 'src/hooks/guide';
import { useDispatch, useSelector } from 'src/hooks/suite';
import { useWindowVisibility } from 'src/hooks/suite/useWindowVisibility';
import {
selectIsEntropyCheckEnabled,
selectIsEntropyCheckEnabledAndFailed,
selectIsFirmwareAuthenticityCheckEnabledAndHardFailed,
selectIsLoggedOut,
selectIsTransportInitialized,
Expand Down Expand Up @@ -63,18 +61,14 @@ export const Preloader = ({ children }: PropsWithChildren) => {
const isLoggedOut = useSelector(selectIsLoggedOut);
const selectedDevice = useSelector(selectSelectedDevice);
const { initialRun, viewOnlyPromoClosed } = useSelector(selectSuiteFlags);
const isFirmwareCheckFailed = useSelector(
const isFirmwareCheckEnabledAndFailed = useSelector(
selectIsFirmwareAuthenticityCheckEnabledAndHardFailed,
);
const isFirmwareAuthenticityCheckDismissed = useSelector(
selectIsFirmwareAuthenticityCheckDismissed,
);
const isEntropyCheckEnabled = useSelector(selectIsEntropyCheckEnabled);
// Entropy check won't be performed if disabled but we also have to check it here to avoid showing the UI when the failed state is stored in database.
const isEntropyCheckDisabledByMessageSystem = useSelector(state =>
selectIsFeatureDisabled(state, Feature.entropyCheck),
);
const isEntropyCheckFailed = useSelector(selectIsEntropyCheckFailed);
// Entropy check won't be performed if disabled but we must also check it here to avoid showing the UI when the failed state is stored in database.
const isEntropyCheckEnabledAndFailed = useSelector(selectIsEntropyCheckEnabledAndFailed);

// report firmware authenticity failures even when the UI is disabled
useReportDeviceCompromised();
Expand Down Expand Up @@ -102,16 +96,13 @@ export const Preloader = ({ children }: PropsWithChildren) => {
return <InitialLoading timeout={90} />;
}

const isEntropyCheckEnabledAndFailed =
isEntropyCheckEnabled && !isEntropyCheckDisabledByMessageSystem && isEntropyCheckFailed;

if (
(router.route?.app === undefined ||
!ROUTES_TO_SKIP_FIRMWARE_CHECK.includes(router.route?.app)) &&
((!isFirmwareAuthenticityCheckDismissed && isFirmwareCheckFailed) ||
((!isFirmwareAuthenticityCheckDismissed && isFirmwareCheckEnabledAndFailed) ||
isEntropyCheckEnabledAndFailed)
) {
return <DeviceCompromised isEntropyCheckFailed={isEntropyCheckEnabledAndFailed} />;
return <DeviceCompromised />;
}

if (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,34 @@ import { useDevice, useDispatch, useSelector } from 'src/hooks/suite';
import {
selectFirmwareHashCheckErrorIfEnabled,
selectFirmwareRevisionCheckErrorIfEnabled,
selectIsEntropyCheckEnabledAndFailed,
} from 'src/reducers/suite/suiteReducer';

import { SecurityCheckFail, SecurityCheckFailProps } from './SecurityCheckFail';
import { hardFailureChecklistItems, softFailureChecklistItems } from './checklistItems';

const useSecurityCheckFailProps = (
isEntropyCheckFailed: boolean,
): Partial<SecurityCheckFailProps> => {
const useSecurityCheckFailProps = (): SecurityCheckFailProps => {
const { device } = useDevice();
const revisionCheckError = useSelector(selectFirmwareRevisionCheckErrorIfEnabled);
const hashCheckError = useSelector(selectFirmwareHashCheckErrorIfEnabled);
const isEntropyCheckFailed = useSelector(selectIsEntropyCheckEnabledAndFailed);
const dispatch = useDispatch();

// Let user access the wallet if it may have been initiated before so that they can access the funds and send them to safety.
const goToSuite = () => {
// Condition to satisfy TypeScript, device.id is always defined at this point.
if (device?.id) {
dispatch(deviceActions.dismissFirmwareAuthenticityCheck(device.id));
}
};

if (isEntropyCheckFailed) {
return {
heading: 'TR_DEVICE_COMPROMISED_HEADING',
text: 'TR_DEVICE_COMPROMISED_ENTROPY_CHECK_TEXT',
checklistItems: hardFailureChecklistItems,
goBack: undefined,
supportUrl: TREZOR_SUPPORT_URL, // TODO: add specific URL when it is created
};
}
// revision check has precedence over hash check, because it does not have the ambiguous other-error state
Expand All @@ -31,6 +43,8 @@ const useSecurityCheckFailProps = (
heading: 'TR_DEVICE_COMPROMISED_HEADING',
text: 'TR_DEVICE_COMPROMISED_FW_REVISION_CHECK_TEXT',
checklistItems: hardFailureChecklistItems,
goBack: goToSuite,
supportUrl: TREZOR_SUPPORT_FW_REVISION_CHECK_FAILED_URL,
};
}
// hash check other-error shall display softer wording than standard hash check errors
Expand All @@ -40,50 +54,31 @@ const useSecurityCheckFailProps = (
text: 'TR_FAILED_VERIFY_DEVICE_TEXT',
checklistItems: softFailureChecklistItems,
supportButtonVariant: 'warning',
goBack: goToSuite,
supportUrl: TREZOR_SUPPORT_FW_REVISION_CHECK_FAILED_URL,
};
}
if (hashCheckError !== null) {
return {
heading: 'TR_DEVICE_COMPROMISED_HEADING',
text: 'TR_DEVICE_COMPROMISED_FW_HASH_CHECK_TEXT',
checklistItems: hardFailureChecklistItems,
goBack: goToSuite,
supportUrl: TREZOR_SUPPORT_FW_REVISION_CHECK_FAILED_URL,
};
}

// should not happen, but default props will be used with no problem
return {};
};

type DeviceCompromisedProps = {
isEntropyCheckFailed: boolean;
return { supportUrl: TREZOR_SUPPORT_FW_REVISION_CHECK_FAILED_URL };
};

export const DeviceCompromised = ({ isEntropyCheckFailed }: DeviceCompromisedProps) => {
const dispatch = useDispatch();
const { device } = useDevice();

const securityCheckFailProps = useSecurityCheckFailProps(isEntropyCheckFailed);

const goToSuite = () => {
// Condition to satisfy TypeScript, device.id is always defined at this point.
if (device?.id) {
dispatch(deviceActions.dismissFirmwareAuthenticityCheck(device.id));
}
};
export const DeviceCompromised = () => {
const securityCheckFailProps = useSecurityCheckFailProps();

return (
<WelcomeLayout>
<Card data-testid="@device-compromised">
<SecurityCheckFail
// Only let user access the wallet if it may have been initiated before so that they can access the funds and send them to safety.
goBack={isEntropyCheckFailed ? undefined : goToSuite}
supportUrl={
isEntropyCheckFailed
? TREZOR_SUPPORT_URL // TODO: add specific URL when it is created
: TREZOR_SUPPORT_FW_REVISION_CHECK_FAILED_URL
}
{...securityCheckFailProps}
/>
<SecurityCheckFail {...securityCheckFailProps} />
</Card>
</WelcomeLayout>
);
Expand Down
21 changes: 20 additions & 1 deletion packages/suite/src/reducers/suite/suiteReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { Feature, selectIsFeatureDisabled } from '@suite-common/message-system';
import { isDeviceAcquired } from '@suite-common/suite-utils';
import type { InvityServerEnvironment } from '@suite-common/trading';
import { NetworkSymbol } from '@suite-common/wallet-config';
import { DeviceRootState, discoveryActions, selectSelectedDevice } from '@suite-common/wallet-core';
import {
DeviceRootState,
discoveryActions,
selectIsEntropyCheckFailed,
selectSelectedDevice,
} from '@suite-common/wallet-core';
import { AddressDisplayOptions, WalletType } from '@suite-common/wallet-types';
import { ConnectSettings, InstallerInfo, TRANSPORT, TransportInfo } from '@trezor/connect';
import { isWeb } from '@trezor/env-utils';
Expand Down Expand Up @@ -577,4 +582,18 @@ export const selectIsFirmwareAuthenticityCheckEnabledAndHardFailed = (state: App
return isRevisionHardError || isHashHardError;
};

/**
* Return true if entropy check has failed and is not disabled via settings nor message system.
*/
export const selectIsEntropyCheckEnabledAndFailed = (state: AppState) => {
const isEntropyCheckEnabled = selectIsEntropyCheckEnabled(state);
const isEntropyCheckDisabledByMessageSystem = selectIsFeatureDisabled(
state,
Feature.entropyCheck,
);
const isEntropyCheckFailed = selectIsEntropyCheckFailed(state);

return isEntropyCheckEnabled && !isEntropyCheckDisabledByMessageSystem && isEntropyCheckFailed;
};

export default suiteReducer;

0 comments on commit a515484

Please sign in to comment.