Користувач втратив доступ до гаманця через баг у генераторі seed-фрази: Math.random() на React Native дав передбачувану послідовність. Конкуренти не перевіряли джерело ентропії — результат: дублікати сидів і втрата коштів. Такі інциденти — звичайна справа в криптовалютних додатках. Одна така помилка може коштувати користувачам мільйони доларів, а відновлення коштів — майже нереальне завдання. Наша команда 5+ років впроваджує BIP-39 на iOS, Android та крос-платформі. Понад 20 проєктів, жодної колізії.
Корінь проблеми — небезпечний CSPRNG. Навіть досвідчені розробники плутають SecureRandom зі звичайним Random. Псевдовипадкові числа (PRNG) типу arc4random дають лише 32 біти ентропії — цього недостатньо для BIP-39. Потрібен криптостійкий генератор, сертифікований за стандартами NIST.
Генерація: де помиляються
Найнебезпечніша помилка — використання псевдовипадкового джерела. Math.random() у JavaScript не криптографічно випадковий. Date.now() як сид — катастрофа. Для BIP-39 потрібно рівно 128 біт (12 слів) або 256 біт (24 слова) криптографічно випадкової ентропії.
Нижче — порівняння джерел ентропії:
| Платформа | Правильний генератор | Небезпечна альтернатива |
|---|---|---|
| iOS | SecRandomCopyBytes |
arc4random (тільки 32 біти) |
| Android | SecureRandom |
Random (псевдовипадковий) |
| React Native | crypto.getRandomValues (через polyfill) |
Math.random |
Для порівняння: використання SecureRandom замість Random збільшує простір ключів у 10^38 разів, роблячи атаку перебором практично неможливою.
На 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 працюють на небезпечному джерелі.
Чому важливо використовувати криптографічний генератор випадкових чисел?
Використання небезпечного random може призвести до колізій (два гаманці з однаковою seed-фразою) або передбачуваності. CSPRNG забезпечує 128 біт ентропії, що робить атаку перебором неможливою в осяжному майбутньому. Економія на генераторі обертається ризиком втрати коштів. Наші інженери завжди перевіряють генератор на відповідність стандартам NIST.
Відновлення та сумісність
Відновлення – це введення 12/24 слів → ті ж адреси. Баги тут зазвичай у нормалізації тексту: зайві пробіли, unicode пробіли (NBSP замість звичайного), регістр. bip39.validateMnemonic() має повернути false для таких випадків, але UI повинен нормалізувати введення до перевірки – trim() та replace(\s+/g, ' ') обов'язкові.
Друге джерело несумісності – passphrase. BIP-39 дозволяє опціональну passphrase («25-те слово»). MetaMask її ігнорує (пустий рядок). Trezor підтримує. Якщо ваш гаманець тихо передає пусту passphrase, а користувач відновлюється на пристрої, де вказав passphrase – адреси будуть іншими. Потрібно явно запитувати при відновленні.
Як відновити seed-фразу без помилок сумісності?
Ми застосовуємо наступний покроковий алгоритм:
- Нормалізація введення (trim, lowercase, одиничні пробіли).
- Прямий запит passphrase в окремому полі.
- Перевірка контрольної суми (слово-код).
- Тестування на офіційних векторах BIP-39.
- Підтримка 12 і 24 слів.
Типові помилки при генерації seed-фрази
- Використання
Date.now()як seed - Відсутність нормалізації введення при відновленні
- Ігнорування passphrase
- Неперевірка контрольної суми
- Копіювання seed-фрази в clipboard без очищення
UX для підтвердження seed-фрази
Показувати слова групами по 4, не всі одразу. Після показу – верифікація: випадкові 3–4 слова в довільному порядку, користувач тапає у правильній послідовності. Не давати копіювати через clipboard за замовчуванням – буфер обміну читають інші додатки. Якщо копіювання дозволено, очищати clipboard через 60 секунд через UIApplication/ClipboardManager.
На Android 10+ ClipboardManager.clearPrimaryClip() – публічний API. На iOS до 16 прямого очищення немає, встановлюємо в clipboard пустий рядок.
Процес роботи та що входить
Реалізація включає кілька етапів. Спочатку — аудит поточної системи сидів та джерел ентропії. Потім проектування безпечного коду генерації, розробка UI для показу та верифікації слів, механізм відновлення з нормалізацією. Після — тестування на офіційних векторах BIP-39 та інтеграція passphrase (опціонально). Вкінці — документація та гайд для розробника.
| Етап | Опис |
|---|---|
| Аудит | Перевірка існуючого коду на вразливості |
| Реалізація | Написання коду генерації та відновлення |
| UI | Інтерфейс показу та верифікації seed-фрази |
| Тестування | Прогін на офіційних тест-векторах BIP-39 |
| Інтеграція | Підключення passphrase та сумісність |
Терміни та гарантії
Базова реалізація — від 3 до 5 днів. Розширена (з passphrase, сумісність з конкретним гаманцем) — від 5 до 7 днів. Ми даємо гарантію коректної генерації та відновлення згідно з BIP-39 specification. Щоб уникнути типових помилок на старті, замовте аудит вашої поточної реалізації. Отримайте консультацію інженера — зв'яжіться з нами.







