Skip to content

Commit

Permalink
feat(suite-native): add Device Settings to toggle FW revision check
Browse files Browse the repository at this point in the history
  • Loading branch information
Lemonexe committed Jan 17, 2025
1 parent ce9b3fc commit a91440a
Show file tree
Hide file tree
Showing 13 changed files with 300 additions and 3 deletions.
2 changes: 1 addition & 1 deletion packages/urls/src/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export const HELP_CENTER_EVM_ADDRESS_CHECKSUM: Url =
export const HELP_CENTER_EVM_SEND_TO_CONTRACT_URL =
'https://trezor.io/support/a/where-is-my-ethereum';
export const HELP_CENTER_FIRMWARE_REVISION_CHECK: Url =
'https://trezor.io/learn/a/trezor-firmware-revision-check';
'https://trezor.io/learn/a/trezor-firmware-authenticity-check';
export const HELP_CENTER_REPLACE_BY_FEE: Url =
'https://trezor.io/learn/a/replace-by-fee-rbf-ethereum';

Expand Down
27 changes: 27 additions & 0 deletions suite-native/intl/src/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ export const en = {
},
moduleDeviceSettings: {
title: 'Device settings',
additionalSettings: 'Additional settings',
firmware: {
title: 'Firmware',
version: 'Version',
Expand Down Expand Up @@ -504,6 +505,32 @@ export const en = {
},
},
},
advancedSettings: {
title: 'Advanced settings',
subtitle: 'Very nerdy stuff here',
firmwareAuthenticityCheck: {
title: 'Turn off firmware authenticity check',
subtitle:
'Firmware authenticity check is a crucial security feature. We strongly recommend keeping it turned on.',
buttonTurnOff: 'Turn off',
buttonTurnOn: 'Turn on',
buttonLearnMore: 'Learn more',

turnOffModal: {
title: 'Turn off firmware authenticity check',
content:
'Trezor Support will never ask you to turn off the firmware revision check. This feature is designed to protect your security.',
item1: 'Only if the device has passed the check before',
item1Explanation:
'Using an unverified device could result in the loss of funds.',
item2: 'Only for testing and development',
item2Explanation:
'These security checks should only be disabled for testing and development purposes.',
acknowledgement: 'I’ve read and understood the above',
buttonTurnOff: 'Turn off',
},
},
},
updateHowTo: {
title: 'How to update firmware',
subtitle: 'Follow these steps:',
Expand Down
2 changes: 2 additions & 0 deletions suite-native/module-device-settings/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@
"@suite-native/link": "workspace:*",
"@suite-native/module-authorize-device": "workspace:*",
"@suite-native/navigation": "workspace:*",
"@suite-native/settings": "workspace:*",
"@suite-native/toasts": "workspace:*",
"@trezor/connect": "workspace:*",
"@trezor/device-utils": "workspace:*",
"@trezor/env-utils": "workspace:*",
"@trezor/styles": "workspace:*",
"@trezor/urls": "workspace:*",
"react": "18.2.0",
"react-native": "0.76.1",
"react-redux": "8.0.7"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useNavigation } from '@react-navigation/native';

import { Translation } from '@suite-native/intl';
import {
DeviceAuthenticityStackParamList,
DeviceAuthenticityStackRoutes,
DeviceSettingsStackParamList,
DeviceStackRoutes,
StackToStackCompositeNavigationProps,
} from '@suite-native/navigation';
import { SettingsSection, SettingsSectionItem } from '@suite-native/settings';

type NavigationProp = StackToStackCompositeNavigationProps<
DeviceAuthenticityStackParamList,
DeviceAuthenticityStackRoutes,
DeviceSettingsStackParamList
>;

export const DeviceAdvancedSettingsSection = () => {
const navigation = useNavigation<NavigationProp>();

const handleAdvancedSettingsPress = () => {
navigation.navigate(DeviceStackRoutes.AdvancedSettings);
};

return (
<SettingsSection title={<Translation id="moduleDeviceSettings.additionalSettings" />}>
<SettingsSectionItem
iconName="wrench"
title={<Translation id="moduleDeviceSettings.advancedSettings.title" />}
subtitle={<Translation id="moduleDeviceSettings.advancedSettings.subtitle" />}
onPress={handleAdvancedSettingsPress}
></SettingsSectionItem>
</SettingsSection>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { useDispatch, useSelector } from 'react-redux';

import { useNavigation } from '@react-navigation/native';

import {
selectIsFirmwareAuthenticityCheckDisabled,
setCheckFirmwareAuthenticity,
} from '@suite-native/settings';
import { Translation } from '@suite-native/intl';
import { Button, HStack, Text, VStack } from '@suite-native/atoms';
import {
DeviceSettingsStackParamList,
DeviceStackRoutes,
StackNavigationProps,
} from '@suite-native/navigation';
import { useOpenLink } from '@suite-native/link';
import { HELP_CENTER_FIRMWARE_REVISION_CHECK } from '@trezor/urls';

import { DeviceSettingsCardLayout } from './DeviceSettingsCardLayout';

const LearnMoreButton = () => {
const openLink = useOpenLink();
const handleButtonPress = () => openLink(HELP_CENTER_FIRMWARE_REVISION_CHECK);

return (
<Button onPress={handleButtonPress} colorScheme="tertiaryElevation0">
<Translation id="moduleDeviceSettings.advancedSettings.firmwareAuthenticityCheck.buttonLearnMore" />
</Button>
);
};

const TurnOnButton = () => {
const dispatch = useDispatch();
const handleButtonPress = () => {
dispatch(setCheckFirmwareAuthenticity(false));
};

return (
<Button onPress={handleButtonPress} colorScheme="primary">
<Translation id="moduleDeviceSettings.advancedSettings.firmwareAuthenticityCheck.buttonTurnOn" />
</Button>
);
};

type NavigationProp = StackNavigationProps<
DeviceSettingsStackParamList,
DeviceStackRoutes.TurnOffFirmwareAuthencityCheck
>;

const TurnOffButton = () => {
const navigation = useNavigation<NavigationProp>();
const handleButtonPress = () => {
navigation.navigate(DeviceStackRoutes.TurnOffFirmwareAuthencityCheck);
};

return (
<Button onPress={handleButtonPress} colorScheme="redElevation0">
<Translation id="moduleDeviceSettings.advancedSettings.firmwareAuthenticityCheck.buttonTurnOff" />
</Button>
);
};

export const TurnOffFirmwareAuthencityCheckCard = () => {
const isFwAuthenticityCheckDisabled = useSelector(selectIsFirmwareAuthenticityCheckDisabled);

return (
<DeviceSettingsCardLayout
icon="shieldCheck"
title={
<Translation id="moduleDeviceSettings.advancedSettings.firmwareAuthenticityCheck.title" />
}
>
<VStack spacing="sp16">
<Text variant="body" color="textSubdued">
<Translation id="moduleDeviceSettings.advancedSettings.firmwareAuthenticityCheck.subtitle" />
</Text>
<HStack spacing="sp8">
{isFwAuthenticityCheckDisabled ? <TurnOnButton /> : <TurnOffButton />}
<LearnMoreButton />
</HStack>
</VStack>
</DeviceSettingsCardLayout>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { DeviceAuthenticityStackNavigator } from './DeviceAuthenticityStackNavig
import { DevicePinProtectionStackNavigator } from './DevicePinProtectionStackNavigator';
import { FirmwareUpdateScreen } from '../screens/FirmwareUpdateScreen/FirmwareUpdateScreen';
import { ContinueOnTrezorScreen } from '../screens/ContinueOnTrezorScreen';
import { DeviceAdvancedSettingsScreen } from '../screens/DeviceAdvancedSettingsScreen';
import { TurnOffFirmwareAuthencityCheckModalScreen } from '../screens/TurnOffFirmwareAuthencityCheckModalScreen';
const DeviceSettingsStack = createNativeStackNavigator<DeviceSettingsStackParamList>();

export const DeviceSettingsStackNavigator = () => (
Expand Down Expand Up @@ -43,5 +45,13 @@ export const DeviceSettingsStackNavigator = () => (
name={DeviceStackRoutes.FirmwareUpdateInProgress}
component={FirmwareUpdateInProgressScreen}
/>
<DeviceSettingsStack.Screen
name={DeviceStackRoutes.AdvancedSettings}
component={DeviceAdvancedSettingsScreen}
/>
<DeviceSettingsStack.Screen
name={DeviceStackRoutes.TurnOffFirmwareAuthencityCheck}
component={TurnOffFirmwareAuthencityCheckModalScreen}
/>
</DeviceSettingsStack.Navigator>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Screen, ScreenHeader } from '@suite-native/navigation';
import { useTranslate } from '@suite-native/intl';

import { TurnOffFirmwareAuthencityCheckCard } from '../components/TurnOffFirmwareAuthencityCheckCard';

export const DeviceAdvancedSettingsScreen = () => {
const { translate } = useTranslate();

return (
<Screen
header={
<ScreenHeader content={translate('moduleDeviceSettings.advancedSettings.title')} />
}
>
<TurnOffFirmwareAuthencityCheckCard />
</Screen>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ import { useSelector } from 'react-redux';

import { SUPPORTS_DEVICE_AUTHENTICITY_CHECK } from '@suite-common/suite-constants';
import {
selectSelectedDevice,
selectDeviceModel,
selectDeviceReleaseInfo,
selectSelectedDevice,
} from '@suite-common/wallet-core';
import { Button, Text, VStack } from '@suite-native/atoms';
import { DeviceImage } from '@suite-native/device';
import { Translation, useTranslate } from '@suite-native/intl';
import { Screen, ScreenHeader } from '@suite-native/navigation';
import { FeatureFlag, useFeatureFlag } from '@suite-native/feature-flags';

import { DeviceAuthenticityCard } from '../components/DeviceAuthenticityCard';
import { DeviceAdvancedSettingsSection } from '../components/DeviceAdvancedSettingsSection';
import { DeviceFirmwareCard } from '../components/DeviceFirmwareCard';
import { DevicePinProtectionCard } from '../components/DevicePinProtectionCard';
import { HowToUpdateBottomSheet } from '../components/HowToUpdateBottomSheet';
Expand All @@ -28,6 +30,8 @@ export const DeviceSettingsModalScreen = () => {

const isUpgradable = deviceReleaseInfo?.isNewer ?? false;

const isFwRevisionCheckEnabled = useFeatureFlag(FeatureFlag.IsFwRevisionCheckEnabled);

if (!device || !deviceModel) {
return null;
}
Expand Down Expand Up @@ -56,6 +60,7 @@ export const DeviceSettingsModalScreen = () => {
<Translation id="moduleDeviceSettings.updateHowTo.title" />
</Button>
)}
{isFwRevisionCheckEnabled && <DeviceAdvancedSettingsSection />}
</VStack>
<HowToUpdateBottomSheet
isVisible={isUpdateSheetOpen}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { useState } from 'react';
import { useDispatch } from 'react-redux';
import { TouchableOpacity } from 'react-native';

import { useNavigation } from '@react-navigation/native';

import {
DeviceSettingsStackParamList,
DeviceStackRoutes,
Screen,
ScreenHeader,
StackNavigationProps,
} from '@suite-native/navigation';
import {
Box,
Button,
CheckBox,
IconListItem,
Text,
HStack,
VStack,
Card,
} from '@suite-native/atoms';
import { Translation } from '@suite-native/intl';
import { setCheckFirmwareAuthenticity } from '@suite-native/settings';

type NavigationProp = StackNavigationProps<
DeviceSettingsStackParamList,
DeviceStackRoutes.AdvancedSettings
>;

export const TurnOffFirmwareAuthencityCheckModalScreen = () => {
const [isChecked, setIsChecked] = useState(false);
const navigation = useNavigation<NavigationProp>();
const dispatch = useDispatch();

const handleCheckboxPress = () => setIsChecked(prev => !prev);

const handleButtonPress = () => {
dispatch(setCheckFirmwareAuthenticity(true));
if (navigation.canGoBack()) {
navigation.goBack();
} else {
navigation.navigate(DeviceStackRoutes.AdvancedSettings);
}
};

return (
<Screen header={<ScreenHeader closeActionType="close" />}>
<VStack spacing="sp32" flex={1}>
<Box>
<Text variant="titleMedium">
<Translation id="moduleDeviceSettings.advancedSettings.firmwareAuthenticityCheck.turnOffModal.title" />
</Text>
<Text variant="body">
<Translation id="moduleDeviceSettings.advancedSettings.firmwareAuthenticityCheck.turnOffModal.content" />
</Text>
</Box>
<VStack spacing="sp24">
<IconListItem
icon="warning"
variant="yellow"
iconSize="large"
verticalAlign="flex-start"
>
<Text variant="highlight">
<Translation id="moduleDeviceSettings.advancedSettings.firmwareAuthenticityCheck.turnOffModal.item1" />
{'\n'}
</Text>
<Text>
<Translation id="moduleDeviceSettings.advancedSettings.firmwareAuthenticityCheck.turnOffModal.item1Explanation" />
</Text>
</IconListItem>
<IconListItem
icon="code"
variant="yellow"
iconSize="large"
verticalAlign="flex-start"
>
<Text variant="highlight">
<Translation id="moduleDeviceSettings.advancedSettings.firmwareAuthenticityCheck.turnOffModal.item2" />
{'\n'}
</Text>
<Text>
<Translation id="moduleDeviceSettings.advancedSettings.firmwareAuthenticityCheck.turnOffModal.item2Explanation" />
</Text>
</IconListItem>
</VStack>
<TouchableOpacity onPress={handleCheckboxPress}>
<Card>
<HStack spacing="sp16">
<CheckBox isChecked={isChecked} onChange={handleCheckboxPress} />
<Text variant="highlight">
<Translation id="moduleDeviceSettings.advancedSettings.firmwareAuthenticityCheck.turnOffModal.acknowledgement" />
</Text>
</HStack>
</Card>
</TouchableOpacity>
</VStack>
{isChecked && (
<Button colorScheme="yellowBold" onPress={handleButtonPress}>
<Translation id="moduleDeviceSettings.advancedSettings.firmwareAuthenticityCheck.turnOffModal.buttonTurnOff" />
</Button>
)}
</Screen>
);
};
4 changes: 3 additions & 1 deletion suite-native/module-device-settings/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
{ "path": "../link" },
{ "path": "../module-authorize-device" },
{ "path": "../navigation" },
{ "path": "../settings" },
{ "path": "../toasts" },
{ "path": "../../packages/connect" },
{ "path": "../../packages/device-utils" },
{ "path": "../../packages/env-utils" },
{ "path": "../../packages/styles" }
{ "path": "../../packages/styles" },
{ "path": "../../packages/urls" }
]
}
2 changes: 2 additions & 0 deletions suite-native/navigation/src/navigators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ export type DeviceSettingsStackParamList = {
[DeviceStackRoutes.FirmwareUpdate]: undefined;
[DeviceStackRoutes.FirmwareUpdateInProgress]: undefined;
[DeviceStackRoutes.ContinueOnTrezor]: undefined;
[DeviceStackRoutes.AdvancedSettings]: undefined;
[DeviceStackRoutes.TurnOffFirmwareAuthencityCheck]: undefined;
};

export type DevicePinProtectionStackParamList = {
Expand Down
Loading

0 comments on commit a91440a

Please sign in to comment.