Генерация безопасной BIP-39 сид-фразы с помощью кубиков (d6): пошаговое руководство, безопасность и формулы 🎲🔐
Как надежно сгенерировать BIP-39 сид-фразу с помощью кубиков (d6) — инструкция, формулы и скрипты
Вступление — зачем генерировать сид-фразу с помощью кубиков
Генерация сид-фразы офлайн — один из самых надёжных способов получить истинно случайную фразу, не доверяя программному обеспечению или интернет-источникам. Метод с кубиками (d6) позволяет получить энтропию от физических явлений — человеческих бросков — и при корректной процедуре получить полноценную 12-словную BIP-39 фразу с полной 128-битной энтропией.
В этой статье — полная инструкция: сколько бросков нужно, как перевести последовательность бросков в ровные 128 бит без биаса (rejection sampling), как получить контрольную сумму согласно BIP-39 и преобразовать всё это в 12 слов. В конце — скрипт для красиво отформатированного показа формул в браузере (MathJax) и офлайн-скрипт (Python) для запуска на air-gapped машине.
Краткий план (если спешишь)
- Нужно 128 бит энтропии → 12 слов BIP-39.
- d6 даёт ≈ log2(6) ≈ 2.585 bits на бросок → нужно 50 бросков (50 × log2(6) ≈ 128.25).
- Интерпретируем 50 бросков как 50-значное base-6 число V.
- Делаем rejection sampling: принимаем V только если V < T (см. формулы), иначе повторяем 50 бросков.
- Берём E = V mod 2^128 → это ровные 128 бит; добавляем 4-битную контрольную сумму SHA-256 → 132 бита → 12 × 11-бит индексов → слова BIP-39.
Теория и формулы (коротко и математически)
Основные обозначения и формулы, которые мы будем использовать — в удобном виде (формулы красиво отрисованы с помощью MathJax внизу страницы):
- Количество бросков: \(n = 50\).
- Каждый бросок: \(r_i \in \{1,2,3,4,5,6\}\), \(i=1..50\).
- Base-6 цифра: \(d_i = r_i — 1 \in \{0,\dots,5\}\).
- Целое число V (base-6 → integer):
\[
V = \sum_{i=1}^{50} d_i \cdot 6^{50-i}
\] - Rejection sampling:
\[
K = \left\lfloor \dfrac{6^{50}}{2^{128}} \right\rfloor,\quad T = K \cdot 2^{128}.
\] Принимаем V только если \(V < T\). Иначе — бросаем заново. - Энтропия (128 бит):
\[
E = V \bmod 2^{128}.
\] - Контрольная сумма (4 бита):
\[
CS = \mathrm{first\_bits}_4(\mathrm{SHA256}(E)).
\] - Полная последовательность: \(132\) бита \(= \mathrm{ENT}\,||\,\mathrm{CS}\). Разбиваем на 12 групп по 11 бит и получаем индексы \(i_j\in[0,2047]\) для слов BIP-39.
Подробный пошаговый алгоритм
Шаг 0 — подготовка
- Возьми один кубик d6 (или несколько — не имеет принципиального значения), чистый лист / карандаш и устройство для оффлайн-вычислений (air-gapped ноутбук) с Python, если будешь проверять/обрабатывать данные.
- Отключи интернет на устройстве, если собираешься обрабатывать результаты на компьютере; лучше запускать проверочный скрипт на полностью оффлайн машине.
Шаг 1 — делаем броски
- Сделай ровно 50 бросков кубиком.
- Записывай каждый результат в порядке: \(r_1, r_2, \dots, r_{50}\). Рекомендуется считать \(r_1\) как самый значимый разряд (MSD), \(r_{50}\) — как младший (LSD).
Шаг 2 — перевод в base-6 целое V
Для каждого броска вычисляем цифру \(d_i = r_i — 1\) (диапазон 0..5). Затем:
Шаг 3 — rejection sampling (убираем bias)
Вычисляем:
Принимаем V только если \(V < T\). Если \(V \ge T\) — весь набор из 50 бросков отвергается и процесс начинается заново (еще 50 бросков).
Практическое замечание: вероятность принять V ≈ 84.2% → редкие перебросы.
Шаг 4 — получение 128 бит энтропии
`E` — 128-битное целое. Запиши его в 16 байт (big-endian) — это твоя ENT-последовательность.
Шаг 5 — контрольная сумма и 12 слов
- Вычисли \(h = \mathrm{SHA256}(E)\).
- Возьми первые 4 бита \(CS = h[0]_{\text{bits}}[:4]\).
- Составь полный битстрим: `ENT (128 бит)` || `CS (4 бита)` → 132 бита.
- Разбей на 12 групп по 11 бит → индексы 0..2047 → смотри BIP-39 wordlist → получишь 12 слов.
Безопасность и практические советы
- Полностью офлайн: броски делай в месте без камер; при обработке результатов используй машину без подключения к интернету.
- Запись: запиши фразу на бумаге и (ещё лучше) на металле. Сделай 2–3 копии и храни в разнесённых безопасных местах (банк-ячейка, дом, доверенное лицо).
- Не фотографируй и не сохраняй цифровые копии.
- Проверка: проверь фразу, восстановив её в офлайн-кошельке и сверив публичные адреса (тестовая транзакция с малой суммой — опция).
- Добавь BIP-39 passphrase</strong (опционально): дополнительный секрет повышает безопасность, но его нужно хранить также надежно.
Оффлайн-скрипт (Python) — принимает 50 бросков и выдаёт 12 слов
Запусти на air-gapped машине. Требуется файл bip39.txt (2048 слов, по одному на строку). Скрипт проверяет rejection sampling и выводит фразу.
# bip39_dice.py — запускать офлайн (Python 3.8+)
import hashlib
import sys
# Вставьте 50 чисел 1..6 (по порядку бросков), либо вводите через stdin/файл
# Пример: rolls = [1,4,6,2,...] длина 50
rolls = []
print("Введите 50 бросков (через пробел или по одному, enter для следующего):")
while len(rolls) < 50:
try:
parts = input().strip().split()
for p in parts:
if p:
v = int(p)
if 1 <= v <= 6: rolls.append(v) else: print("Число должно быть 1..6") except EOFError: break if len(rolls) != 50: print("Нужно ровно 50 бросков. Ввод окончен.") sys.exit(1) # base-6 -> V
V = 0
for r in rolls:
V = V * 6 + (r - 1)
# rejection sampling
K = (6**50) // (2**128)
T = K * (2**128)
if V >= T:
print("Набор отвергнут (V >= T). Повторите 50 бросков.")
sys.exit(2)
E = V % (2**128)
entropy_bytes = E.to_bytes(16, byteorder='big')
# checksum (4 bits)
h = hashlib.sha256(entropy_bytes).digest()
cs_bits = bin(h[0])[2:].zfill(8)[:4]
ent_bits = bin(int.from_bytes(entropy_bytes, 'big'))[2:].zfill(128)
full_bits = ent_bits + cs_bits
# split into 12 indices
indexes = [int(full_bits[i*11:(i+1)*11], 2) for i in range(12)]
# load bip39 wordlist (bip39.txt — 2048 слов)
with open('bip39.txt', 'r', encoding='utf-8') as f:
words = [w.strip() for w in f.readlines()]
if len(words) != 2048:
print("Ошибка: bip39.txt должен содержать 2048 слов.")
sys.exit(3)
phrase = ' '.join(words[idx] for idx in indexes)
print("\\nBIP-39 phrase:")
print(phrase)
Скрипт для красивого отображения формул (MathJax)
Ниже — фрагмент HTML/JS, который можно вставить в страницу (без <head> и <body> по твоему запросу). Он подключает MathJax и аккуратно отображает формулы, включая те, что использованы в статье. Если хочешь использовать офлайн, замени подключение MathJax на локальную копию (или пропусти подключение и используй серверную рендеринговую утилиту).
<!-- Вставь этот блок в HTML (без head/body по соглашению) -->
<div id="math-container">
<p>Основные формулы: </p>
<div>V = \sum_{i=1}^{50} d_i \cdot 6^{50-i}</div>
<div>K = \left\lfloor \dfrac{6^{50}}{2^{128}} \right\rfloor, \quad T = K \cdot 2^{128}</div>
<div>E = V \bmod 2^{128}</div>
</div>
<!-- MathJax CDN (можно заменить на локальную копию) -->
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<script>
window.MathJax = {
tex: { inlineMath: [['$','$'], ['\\(','\\)']] },
svg: { fontCache: 'global' }
};
// Необязательный: плавная анимация при рендере
document.addEventListener('DOMContentLoaded', function () {
if (window.MathJax && MathJax.typesetPromise) {
MathJax.typesetPromise();
}
});
</script>
Этот блок автоматически отрисует LaTeX-формулы и будет корректно работать в современных браузерах. Если хочешь, заменю подключение на KaTeX (более лёгкий, но требует CSS) или подготовлю offline-bundle MathJax для запуска без интернета.
Примеры и контрольные проверки
- После генерации фразы импортируй её в офлайн-кошелёк (на той же air-gapped машине) и проверь первый адрес. Сравни с адресом, который выдаст любой стандартный BIP-39 инструмент с тем же derivation path.
- Рекомендация: сначала протестируй процесс с небольшими суммами (0.0001 …) чтобы убедиться, что всё работает как нужно.
Частые вопросы (FAQ)
Q: Сколько раз процедура может отвергнуть набор бросков?
A: Вероятность отклонения ≈ 15.8% для 50 бросков. В среднем нужен ≈1.19 попытки — то есть редко требуется повтор.
Q: Можно ли использовать другие кубики (d20, монеты)?
A: Да. Формула и число бросков меняются в зависимости от логарифма базы: для d20 (base-20) потребуется меньше бросков, для монеты (d2) — больше. Всегда используйте rejection sampling при несоответствии base^n ≠ 2^128.
Q: Можно ли сократить количество бросков до 48 или 49?
A: Теоретически — да, но тогда вероятность отклонения и bias меняются. Практически 50 — удобное и безопасное решение для d6.
Заключение
Генерация BIP-39 сид-фразы с помощью кубиков — надёжный офлайн-метод для тех, кто хочет исключить зависимость от программных источников энтропии. Главное — соблюдать алгоритм (50 бросков, base-6 → V, rejection sampling, мод 2^128, добавление контрольной суммы и перевод в 12 слов). Храни фразу офлайн и добавь passphrase при необходимости.