From e5a7339767099329b3b729580c9957cc6ae336e8 Mon Sep 17 00:00:00 2001 From: Jiri Zbytovsky Date: Fri, 24 Jan 2025 19:20:21 +0100 Subject: [PATCH] feat(suite-native): add Check Settings to toggle FW revision check --- suite-native/intl/src/en.ts | 29 +++++ suite-native/module-settings/package.json | 1 + .../src/components/FeaturesSettings.tsx | 14 +++ .../TurnOffFirmwareAuthenticityCheckCard.tsx | 107 ++++++++++++++++ .../src/navigation/SettingsStackNavigator.tsx | 10 ++ .../screens/SettingsDeviceChecksScreen.tsx | 14 +++ ...ffFirmwareAuthenticityCheckModalScreen.tsx | 119 ++++++++++++++++++ suite-native/navigation/src/navigators.ts | 2 + suite-native/navigation/src/routes.ts | 2 + yarn.lock | 1 + 10 files changed, 299 insertions(+) create mode 100644 suite-native/module-settings/src/components/TurnOffFirmwareAuthenticityCheckCard.tsx create mode 100644 suite-native/module-settings/src/screens/SettingsDeviceChecksScreen.tsx create mode 100644 suite-native/module-settings/src/screens/TurnOffFirmwareAuthenticityCheckModalScreen.tsx diff --git a/suite-native/intl/src/en.ts b/suite-native/intl/src/en.ts index 825192413a8..f51bd6c61ad 100644 --- a/suite-native/intl/src/en.ts +++ b/suite-native/intl/src/en.ts @@ -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', @@ -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: { diff --git a/suite-native/module-settings/package.json b/suite-native/module-settings/package.json index 16d3034a7a0..95a8a8f5b31 100644 --- a/suite-native/module-settings/package.json +++ b/suite-native/module-settings/package.json @@ -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" } } diff --git a/suite-native/module-settings/src/components/FeaturesSettings.tsx b/suite-native/module-settings/src/components/FeaturesSettings.tsx index 510ee7e4e8c..5000cc4f8bf 100644 --- a/suite-native/module-settings/src/components/FeaturesSettings.tsx +++ b/suite-native/module-settings/src/components/FeaturesSettings.tsx @@ -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>(); const navigateTo = useSettingsNavigateTo(); @@ -69,6 +71,18 @@ export const FeaturesSettings = () => { isLoading={hasDiscovery} testID="@settings/coin-enabling" /> + {isFwRevisionCheckEnabled && ( + + } + subtitle={ + + } + onPress={() => navigateTo(SettingsStackRoutes.SettingsDeviceChecks)} + /> + )} )} diff --git a/suite-native/module-settings/src/components/TurnOffFirmwareAuthenticityCheckCard.tsx b/suite-native/module-settings/src/components/TurnOffFirmwareAuthenticityCheckCard.tsx new file mode 100644 index 00000000000..4ca006d0cd6 --- /dev/null +++ b/suite-native/module-settings/src/components/TurnOffFirmwareAuthenticityCheckCard.tsx @@ -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 ( + + ); +}; + +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 ( + + ); +}; + +const TurnOffButton = () => { + const navigateTo = useSettingsNavigateTo(); + + const handleButtonPress = () => { + navigateTo(SettingsStackRoutes.TurnOffFirmwareAuthenticityCheckModal); + }; + + return ( + + ); +}; + +export const TurnOffFirmwareAuthenticityCheckCard = () => { + const isFwAuthenticityCheckEnabled = useSelector(selectIsFirmwareAuthenticityCheckEnabled); + + return ( + } + > + + + + + + {isFwAuthenticityCheckEnabled ? ( + <> + + + + ) : ( + <> + + + + )} + + + + ); +}; diff --git a/suite-native/module-settings/src/navigation/SettingsStackNavigator.tsx b/suite-native/module-settings/src/navigation/SettingsStackNavigator.tsx index 2d55e734701..d095f41d7d1 100644 --- a/suite-native/module-settings/src/navigation/SettingsStackNavigator.tsx +++ b/suite-native/module-settings/src/navigation/SettingsStackNavigator.tsx @@ -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(); @@ -53,5 +55,13 @@ export const SettingsStackNavigator = () => ( name={SettingsStackRoutes.SettingsCoinEnabling} component={SettingsCoinEnablingScreen} /> + + ); diff --git a/suite-native/module-settings/src/screens/SettingsDeviceChecksScreen.tsx b/suite-native/module-settings/src/screens/SettingsDeviceChecksScreen.tsx new file mode 100644 index 00000000000..0f02b6de74e --- /dev/null +++ b/suite-native/module-settings/src/screens/SettingsDeviceChecksScreen.tsx @@ -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 ( + }> + + + ); +}; diff --git a/suite-native/module-settings/src/screens/TurnOffFirmwareAuthenticityCheckModalScreen.tsx b/suite-native/module-settings/src/screens/TurnOffFirmwareAuthenticityCheckModalScreen.tsx new file mode 100644 index 00000000000..6248f4a6a56 --- /dev/null +++ b/suite-native/module-settings/src/screens/TurnOffFirmwareAuthenticityCheckModalScreen.tsx @@ -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 = () => ( + + + + + + + + + + + + + + + + + + + + + + +); + +type NavigationProp = StackNavigationProps< + SettingsStackParamList, + SettingsStackRoutes.SettingsDeviceChecks +>; + +export const TurnOffFirmwareAuthenticityCheckModalScreen = () => { + const [isChecked, setIsChecked] = useState(false); + const navigation = useNavigation(); + 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 ( + }> + + + } + subtitle={ + + } + /> + + + + + + + + + + + + + {isChecked && ( + + + + )} + + ); +}; diff --git a/suite-native/navigation/src/navigators.ts b/suite-native/navigation/src/navigators.ts index c42602760c7..5ee31b9f150 100644 --- a/suite-native/navigation/src/navigators.ts +++ b/suite-native/navigation/src/navigators.ts @@ -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 = { diff --git a/suite-native/navigation/src/routes.ts b/suite-native/navigation/src/routes.ts index bf757fea007..e8f24416e53 100644 --- a/suite-native/navigation/src/routes.ts +++ b/suite-native/navigation/src/routes.ts @@ -116,6 +116,8 @@ export enum SettingsStackRoutes { SettingsAbout = 'SettingsAbout', SettingsFAQ = 'SettingsFAQ', SettingsCoinEnabling = 'SettingsCoinEnabling', + SettingsDeviceChecks = 'SettingsDeviceChecks', + TurnOffFirmwareAuthenticityCheckModal = 'TurnOffFirmwareAuthenticityCheckModal', } export enum TradingStackRoutes { diff --git a/yarn.lock b/yarn.lock index 33f9b086fe9..a776e12c902 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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