Skip to content

Commit

Permalink
feat(suite-native): add Check Settings to toggle FW revision check
Browse files Browse the repository at this point in the history
  • Loading branch information
Lemonexe committed Jan 30, 2025
1 parent a80b424 commit e5a7339
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 0 deletions.
29 changes: 29 additions & 0 deletions suite-native/intl/src/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,10 @@ export const en = {
title: 'Enabled coins',
subtitle: 'Manage coins that you use',
},
deviceChecks: {
title: 'Device checks',
subtitle: 'Authenticity and security checks',
},
},
support: {
title: 'Support',
Expand Down Expand Up @@ -758,6 +762,31 @@ export const en = {
defaultPassphrase: 'Passphrase wallet #{index}',
},
},
deviceChecks: {
title: 'Device checks',
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',
},
},
},
},
moduleOnboarding: {
welcomeScreen: {
Expand Down
1 change: 1 addition & 0 deletions suite-native/module-settings/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"jotai": "1.9.1",
"react": "18.2.0",
"react-native": "0.76.1",
"react-native-reanimated": "^3.16.7",
"react-redux": "8.0.7"
}
}
14 changes: 14 additions & 0 deletions suite-native/module-settings/src/components/FeaturesSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { isDevButtonVisibleAtom } from './ProductionDebug';
export const FeaturesSettings = () => {
const isDevButtonVisible = useAtomValue(isDevButtonVisibleAtom);
const isUsbDeviceConnectFeatureEnabled = useFeatureFlag(FeatureFlag.IsDeviceConnectEnabled);
const isFwRevisionCheckEnabled = useFeatureFlag(FeatureFlag.IsFwRevisionCheckEnabled);

const navigation = useNavigation<StackNavigationProps<RootStackParamList, RootStackRoutes>>();
const navigateTo = useSettingsNavigateTo();

Expand Down Expand Up @@ -69,6 +71,18 @@ export const FeaturesSettings = () => {
isLoading={hasDiscovery}
testID="@settings/coin-enabling"
/>
{isFwRevisionCheckEnabled && (
<SettingsSectionItem
iconName="trezorDevices"
title={
<Translation id="moduleSettings.items.features.deviceChecks.title" />
}
subtitle={
<Translation id="moduleSettings.items.features.deviceChecks.subtitle" />
}
onPress={() => navigateTo(SettingsStackRoutes.SettingsDeviceChecks)}
/>
)}
</>
)}
</SettingsSection>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { useDispatch, useSelector } from 'react-redux';

import {
selectIsFirmwareAuthenticityCheckEnabled,
setCheckFirmwareAuthenticity,
SettingsCardWithIconLayout,
} from '@suite-native/settings';
import { Translation } from '@suite-native/intl';
import { Button, HStack, Text, VStack } from '@suite-native/atoms';
import { SettingsStackRoutes } from '@suite-native/navigation';
import { useOpenLink } from '@suite-native/link';
import { useToast } from '@suite-native/toasts';
import { prepareNativeStyle, useNativeStyles } from '@trezor/styles';

import { useSettingsNavigateTo } from '../navigation/useSettingsNavigateTo';

// TODO this page is for desktop; await creation of new page tailored to the suite-native UX
const HELP_CENTER_FIRMWARE_REVISION_CHECK =
'https://trezor.io/learn/a/trezor-firmware-authenticity-check';

const fullWidthButtonStyle = prepareNativeStyle(() => ({ flex: 1 }));

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

return (
<Button
size="small"
onPress={handleButtonPress}
colorScheme="tertiaryElevation0"
style={applyStyle(fullWidthButtonStyle)}
>
<Translation id="moduleSettings.deviceChecks.firmwareAuthenticityCheck.buttonLearnMore" />
</Button>
);
};

const TurnOnButton = () => {
const { applyStyle } = useNativeStyles();
const dispatch = useDispatch();
const { showToast } = useToast();
const handleButtonPress = () => {
dispatch(setCheckFirmwareAuthenticity(true));
showToast({
variant: 'default',
message: 'Authenticity check turned on',
icon: 'check',
});
};

return (
<Button
size="small"
onPress={handleButtonPress}
colorScheme="primary"
style={applyStyle(fullWidthButtonStyle)}
>
<Translation id="moduleSettings.deviceChecks.firmwareAuthenticityCheck.buttonTurnOn" />
</Button>
);
};

const TurnOffButton = () => {
const navigateTo = useSettingsNavigateTo();

const handleButtonPress = () => {
navigateTo(SettingsStackRoutes.TurnOffFirmwareAuthenticityCheckModal);
};

return (
<Button size="small" onPress={handleButtonPress} colorScheme="redElevation0">
<Translation id="moduleSettings.deviceChecks.firmwareAuthenticityCheck.buttonTurnOff" />
</Button>
);
};

export const TurnOffFirmwareAuthenticityCheckCard = () => {
const isFwAuthenticityCheckEnabled = useSelector(selectIsFirmwareAuthenticityCheckEnabled);

return (
<SettingsCardWithIconLayout
icon="shieldCheck"
title={<Translation id="moduleSettings.deviceChecks.firmwareAuthenticityCheck.title" />}
>
<VStack spacing="sp16">
<Text variant="hint" color="textSubdued">
<Translation id="moduleSettings.deviceChecks.firmwareAuthenticityCheck.subtitle" />
</Text>
<HStack spacing="sp8">
{isFwAuthenticityCheckEnabled ? (
<>
<TurnOffButton />
<LearnMoreButton />
</>
) : (
<>
<LearnMoreButton />
<TurnOnButton />
</>
)}
</HStack>
</VStack>
</SettingsCardWithIconLayout>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { SettingsViewOnly } from '../screens/SettingsViewOnly';
import { SettingsAboutUsScreen } from '../screens/SettingsAboutUsScreen';
import { SettingsFAQScreen } from '../screens/SettingsFAQScreen';
import { SettingsCoinEnablingScreen } from '../screens/SettingsCoinEnablingScreen';
import { SettingsDeviceChecksScreen } from '../screens/SettingsDeviceChecksScreen';
import { TurnOffFirmwareAuthenticityCheckModalScreen } from '../screens/TurnOffFirmwareAuthenticityCheckModalScreen';

const SettingsStack = createNativeStackNavigator<SettingsStackParamList>();

Expand Down Expand Up @@ -53,5 +55,13 @@ export const SettingsStackNavigator = () => (
name={SettingsStackRoutes.SettingsCoinEnabling}
component={SettingsCoinEnablingScreen}
/>
<SettingsStack.Screen
name={SettingsStackRoutes.SettingsDeviceChecks}
component={SettingsDeviceChecksScreen}
/>
<SettingsStack.Screen
name={SettingsStackRoutes.TurnOffFirmwareAuthenticityCheckModal}
component={TurnOffFirmwareAuthenticityCheckModalScreen}
/>
</SettingsStack.Navigator>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Screen, ScreenHeader } from '@suite-native/navigation';
import { useTranslate } from '@suite-native/intl';

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

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

return (
<Screen header={<ScreenHeader content={translate('moduleSettings.deviceChecks.title')} />}>
<TurnOffFirmwareAuthenticityCheckCard />
</Screen>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { useState } from 'react';
import { useDispatch } from 'react-redux';
import { Pressable } from 'react-native';
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';

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

import {
Screen,
ScreenHeader,
SettingsStackParamList,
SettingsStackRoutes,
StackNavigationProps,
} from '@suite-native/navigation';
import {
Button,
Card,
CheckBox,
HStack,
IconListItem,
Text,
TitleHeader,
VStack,
} from '@suite-native/atoms';
import { Translation } from '@suite-native/intl';
import { setCheckFirmwareAuthenticity } from '@suite-native/settings';
import { useToast } from '@suite-native/toasts';

const CHECKBOX_ANIMATION_DURATION = 200; // same as in useAccordionAnimation

const InformativeList = () => (
<VStack spacing="sp24">
<IconListItem icon="warning" variant="yellow" iconSize="large" verticalAlign="flex-start">
<VStack spacing="sp4">
<Text variant="highlight">
<Translation id="moduleSettings.deviceChecks.firmwareAuthenticityCheck.turnOffModal.item1" />
</Text>
<Text variant="hint" color="textSubdued">
<Translation id="moduleSettings.deviceChecks.firmwareAuthenticityCheck.turnOffModal.item1Explanation" />
</Text>
</VStack>
</IconListItem>
<IconListItem icon="code" variant="yellow" iconSize="large" verticalAlign="flex-start">
<VStack spacing="sp4">
<Text variant="highlight">
<Translation id="moduleSettings.deviceChecks.firmwareAuthenticityCheck.turnOffModal.item2" />
</Text>
<Text variant="hint" color="textSubdued">
<Translation id="moduleSettings.deviceChecks.firmwareAuthenticityCheck.turnOffModal.item2Explanation" />
</Text>
</VStack>
</IconListItem>
</VStack>
);

type NavigationProp = StackNavigationProps<
SettingsStackParamList,
SettingsStackRoutes.SettingsDeviceChecks
>;

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

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

const handleButtonPress = () => {
dispatch(setCheckFirmwareAuthenticity(false));
if (navigation.canGoBack()) {
navigation.goBack();
} else {
navigation.navigate(SettingsStackRoutes.SettingsDeviceChecks);
}
showToast({
variant: 'default',
message: 'Authenticity check turned off',
icon: 'check',
});
};

return (
<Screen header={<ScreenHeader closeActionType="close" />}>
<VStack spacing="sp32" flex={1}>
<TitleHeader
titleVariant="titleMedium"
title={
<Translation id="moduleSettings.deviceChecks.firmwareAuthenticityCheck.turnOffModal.title" />
}
subtitle={
<Translation id="moduleSettings.deviceChecks.firmwareAuthenticityCheck.turnOffModal.content" />
}
/>
<InformativeList />
<Pressable onPress={handleCheckboxPress}>
<Card>
<HStack spacing="sp16">
<CheckBox isChecked={isChecked} onChange={handleCheckboxPress} />
<Text variant="highlight">
<Translation id="moduleSettings.deviceChecks.firmwareAuthenticityCheck.turnOffModal.acknowledgement" />
</Text>
</HStack>
</Card>
</Pressable>
</VStack>
{isChecked && (
<Animated.View
entering={FadeIn.duration(CHECKBOX_ANIMATION_DURATION)}
exiting={FadeOut.duration(CHECKBOX_ANIMATION_DURATION)}
>
<Button colorScheme="yellowBold" onPress={handleButtonPress}>
<Translation id="moduleSettings.deviceChecks.firmwareAuthenticityCheck.turnOffModal.buttonTurnOff" />
</Button>
</Animated.View>
)}
</Screen>
);
};
2 changes: 2 additions & 0 deletions suite-native/navigation/src/navigators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ export type SettingsStackParamList = {
[SettingsStackRoutes.SettingsAbout]: undefined;
[SettingsStackRoutes.SettingsFAQ]: undefined;
[SettingsStackRoutes.SettingsCoinEnabling]: undefined;
[SettingsStackRoutes.SettingsDeviceChecks]: undefined;
[SettingsStackRoutes.TurnOffFirmwareAuthenticityCheckModal]: undefined;
};

export type ReceiveStackParamList = {
Expand Down
2 changes: 2 additions & 0 deletions suite-native/navigation/src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ export enum SettingsStackRoutes {
SettingsAbout = 'SettingsAbout',
SettingsFAQ = 'SettingsFAQ',
SettingsCoinEnabling = 'SettingsCoinEnabling',
SettingsDeviceChecks = 'SettingsDeviceChecks',
TurnOffFirmwareAuthenticityCheckModal = 'TurnOffFirmwareAuthenticityCheckModal',
}

export enum TradingStackRoutes {
Expand Down
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10908,6 +10908,7 @@ __metadata:
jotai: "npm:1.9.1"
react: "npm:18.2.0"
react-native: "npm:0.76.1"
react-native-reanimated: "npm:^3.16.7"
react-redux: "npm:8.0.7"
languageName: unknown
linkType: soft
Expand Down

0 comments on commit e5a7339

Please sign in to comment.