From 55f705225defad7d8e59e35dfb3db6ade1d103f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Tranta?= Date: Mon, 16 Feb 2026 10:42:14 +0100 Subject: [PATCH] feat: disable standard recovery for T1B1 with 12 or 18 words --- packages/suite/src/utils/suite/recovery.ts | 32 +++++++++++++++++++ .../onboarding/steps/RecoveryStep/index.tsx | 17 +++++++++- packages/suite/src/views/recovery/index.tsx | 17 +++++++++- .../t1b1/t1b1-recovery-advanced.test.ts | 9 +++--- 4 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 packages/suite/src/utils/suite/recovery.ts diff --git a/packages/suite/src/utils/suite/recovery.ts b/packages/suite/src/utils/suite/recovery.ts new file mode 100644 index 0000000000..1292d80764 --- /dev/null +++ b/packages/suite/src/utils/suite/recovery.ts @@ -0,0 +1,32 @@ +import { DeviceModelInternal } from '@trezor/device-utils'; + +import { RecoveryType, WordCount } from 'src/types/recovery'; + +const WORD_COUNT_12 = 12 as const; +const WORD_COUNT_18 = 18 as const; + +/** + * Determines if a specific recovery type should be disabled for a given device and word count. + * + * For Trezor Model One (T1B1): + * - Standard recovery is disabled for 12 and 18-word seeds (lower security) + * - Standard recovery is enabled for 24-word seeds (sufficient entropy) + * - Advanced recovery is always available for all word counts + */ +export const isStandardRecoveryDisabled = ( + deviceModelInternal: DeviceModelInternal, + wordCount: WordCount, + recoveryType: RecoveryType, +): boolean => { + // Advanced recovery is never disabled + if (recoveryType === 'advanced') { + return false; + } + + // Only disable Standard recovery for T1B1 with 12 or 18-word seeds + return ( + recoveryType === 'standard' && + deviceModelInternal === DeviceModelInternal.T1B1 && + (wordCount === WORD_COUNT_12 || wordCount === WORD_COUNT_18) + ); +}; diff --git a/packages/suite/src/views/onboarding/steps/RecoveryStep/index.tsx b/packages/suite/src/views/onboarding/steps/RecoveryStep/index.tsx index e8920d6a41..06a4e18682 100644 --- a/packages/suite/src/views/onboarding/steps/RecoveryStep/index.tsx +++ b/packages/suite/src/views/onboarding/steps/RecoveryStep/index.tsx @@ -7,6 +7,7 @@ import { goToNextStep, updateAnalytics } from 'src/actions/onboarding/onboarding import { OnboardingCard } from 'src/components/onboarding/OnboardingCard/OnboardingCard'; import { SelectRecoveryType, SelectRecoveryWord, SelectWordCount } from 'src/components/recovery'; import { useDispatch, useRecovery, useSelector } from 'src/hooks/suite'; +import { isStandardRecoveryDisabled } from 'src/utils/suite/recovery'; import RecoveryStepBox from './RecoveryStepBox'; @@ -46,7 +47,21 @@ export const RecoveryStep = () => { { setWordsCount(number); - setStatus('select-recovery-type'); + // For T1B1 with 12 or 18 words, skip recovery type selection and use Advanced recovery + // For 24 words, show the recovery type selection + const shouldSkipSelection = isStandardRecoveryDisabled( + deviceModelInternal, + number, + 'standard', + ); + + if (shouldSkipSelection) { + setAdvancedRecovery(true); + dispatch(updateAnalytics({ recoveryType: 'advanced' })); + recoverDevice(); + } else { + setStatus('select-recovery-type'); + } }} /> diff --git a/packages/suite/src/views/recovery/index.tsx b/packages/suite/src/views/recovery/index.tsx index e415c843be..fb721cab69 100644 --- a/packages/suite/src/views/recovery/index.tsx +++ b/packages/suite/src/views/recovery/index.tsx @@ -22,6 +22,7 @@ import { Loading, PinMatrix, WordInputAdvanced } from 'src/components/suite'; import { useDevice, useDispatch, useSelector } from 'src/hooks/suite'; import type { RecoveryType, WordCount } from 'src/types/recovery'; import type { ForegroundAppProps } from 'src/types/suite'; +import { isStandardRecoveryDisabled } from 'src/utils/suite/recovery'; import { EnterOnDeviceStep } from './steps/EnterOnDeviceStep'; import { InitialStep } from './steps/InitialStep'; @@ -193,7 +194,21 @@ export const Recovery = ({ onCancel }: ForegroundAppProps) => { if (!wordCount) return; dispatch(setWordsCount(wordCount)); - dispatch(setStatus('select-recovery-type')); + + // For T1B1 with 12 or 18 words, skip recovery type selection and use Advanced recovery + // For 24 words, show the recovery type selection + const shouldSkipSelection = isStandardRecoveryDisabled( + deviceModelInternal, + wordCount, + 'standard', + ); + + if (shouldSkipSelection) { + dispatch(setAdvancedRecovery(true)); + dispatch(checkSeed()); + } else { + dispatch(setStatus('select-recovery-type')); + } }} data-testid="@recovery/continue-button" > diff --git a/suite/e2e/tests/onboarding/t1b1/t1b1-recovery-advanced.test.ts b/suite/e2e/tests/onboarding/t1b1/t1b1-recovery-advanced.test.ts index e3d03ff2a7..b86d7b9125 100644 --- a/suite/e2e/tests/onboarding/t1b1/t1b1-recovery-advanced.test.ts +++ b/suite/e2e/tests/onboarding/t1b1/t1b1-recovery-advanced.test.ts @@ -48,10 +48,11 @@ test.describe('Onboarding - recover wallet T1B1', { tag: ['@firmware-ready', '@T await device.powerOn(); }); - await test.step('Retry recovery with basic type', async () => { + await test.step('Retry recovery with 12 words (automatically uses advanced recovery)', async () => { await onboardingPage.retryRecoveryButton.click({ timeout: 15_000 }); + // For T1B1 with 12 words, Standard recovery is disabled, so it automatically uses Advanced recovery await recoveryModal.selectWordCount(12); - await recoveryModal.selectRecoveryButton('standard').click(); + // No recovery type selection needed - it goes directly to advanced recovery // Emulator isn't sometimes ready to accept confirm right away. Retry approach doesn't work. await page.waitForTimeout(500); }); @@ -61,8 +62,8 @@ test.describe('Onboarding - recover wallet T1B1', { tag: ['@firmware-ready', '@T await device.pressYes(); }); - await test.step('Ensure input field for basic recovery is visible', async () => { - await expect(page.getByTestId('@word-input-select/input')).toBeVisible(); + await test.step('Ensure input field for advanced recovery is visible', async () => { + await expect(recoveryModal.wordInputAtIndex(1)).toBeVisible(); }); // Note: Completion of reading device data requires support in trezor-user-env