feat: disable standard recovery for T1B1 with 12 or 18 words

This commit is contained in:
Vojtěch Tranta
2026-02-16 10:42:14 +01:00
parent e1c3cd9fcb
commit 55f705225d
4 changed files with 69 additions and 6 deletions

View File

@@ -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)
);
};

View File

@@ -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 = () => {
<SelectWordCount
onSelect={number => {
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');
}
}}
/>
</RecoveryStepBox>

View File

@@ -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"
>

View File

@@ -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