Реалізація генерації та відновлення seed-фрази в мобільному криптокошельку
Seed-фраза — єдина точка відновлення всього кошелька. Якщо мнемоніка згенерована з поганою ентропією або відновлення дає різні адреси — користувач втрачає доступ до коштів. Обидва сценарії трапляються в production.
Генерація: де ошибаються
Найопаснішша помилка — використання псевдовипадкового джерела. Math.random() в JavaScript не криптографічно випадкова. Date.now() як seed — катастрофа. Для BIP-39 потрібно рівно 128 бітів (12 слів) або 256 бітів (24 слова) криптографічно випадкової ентропії.
На iOS правильний шлях — SecRandomCopyBytes:
var entropy = Data(count: 16) // 128 бітів для 12 слів
let result = entropy.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, 16, $0.baseAddress!)
}
guard result == errSecSuccess else { throw WalletError.entropyGeneration }
На Android — SecureRandom з java.security, не Random:
val entropy = ByteArray(16)
SecureRandom().nextBytes(entropy)
React Native: react-native-get-random-values полифилит crypto.getRandomValues(), що використовує нативний CSPRNG. Без цього пакета @noble/hashes та @scure/bip39 працюють на небезпечному джерелі.
Відновлення та сумісність
Відновлення — введення 12/24 слів → ті ж адреси. Баги звичайно у нормалізації тексту: лишні пробіли, unicode пробіли (NBSP замість звичайного), регістр. bip39.validateMnemonic() повинна повернути false для таких, але UI повинна нормалізувати введення перед перевіркою — trim() та replace(/\s+/g, ' ') обов'язкові.
Друге джерело несумісності — passphrase. BIP-39 дозволяє опціональну passphrase ("25-е слово"). MetaMask її ігнорує (пуста строка). Trezor підтримує. Якщо ваш кошелек тихо передає пусту passphrase, а користувач відновлюється на пристрої, де вказав passphrase — адреси будуть різними. Потрібно явно запитати при відновленні.
UX для підтвердження seed
Показуйте слова групами по 4, не всі одразу. Після показу — верифікація: випадкові 3–4 слова в довільному порядку, користувач тапає у правильній послідовності. Не дозволяйте копіювання через clipboard за замовчуванням — clipboard читають інші програми. Якщо копіювання дозволено, чистіть clipboard через 60 секунд через UIApplication/ClipboardManager.
На Android 10+ ClipboardManager.clearPrimaryClip() — публічний API. На iOS < 16 прямої очистки немає, встановлюємо у clipboard пусту строку.
Процес
Реалізація, тестування на BIP-39 офіційних векторах, UI підтвердження seed, відновлення з нормалізацією введення. Часова шкала — 3–5 днів. Якщо потрібна підтримка passphrase та сумісність з конкретним зовнішнім кошельком — додайте день на тестування.







