October 4, 2022

Мнемоническая фраза и пути деривации

Всем привет, эта статья будет посвящена понятию мнемонической фразы, я постараюсь рассказать о том, почему и как 12 слов английского языка превращаются в множество кошельков почти любой сети.

Мнемоническая фраза

Мнемоническая фраза - набор слов, который обеспечивает доступ к активам вашего кошелька и является его резервной копией. Фраза формируется из слов алфавита BIP-39, обычно она содержит 12 или 24 слова, но возможно добавление кодовой фразы, при которой фраза будет содержать 13 или 25 слов(об этом ниже).


Количество возможных вариантов мнемонических фраз для определённого количества слов.

Как мы видим, 12 слов уже имеют столь огромное количество комбинаций, что возможность её получения методом перебора просто отпадает.

Кодовая фраза

Кодовой фразой называют дополнительное слово к уже сгенерированной мнемонической фразе, которое пользователь может выбрать сам. Единственное ограничение - 100 символов ASCII. Также фраза является чувствительной к регистру.

Данную технологию поддерживает большинство аппаратных кошельков, такие как: Ledger, Trezor, Coldcard и другие.

Этот код позволяет ещё сильнее обезопасить ваши активы, чтобы при краже основной мнемонической фразы, злоумышленнику пришлось бы перебирать и кодовую, чтобы получить доступ к заветному кошельку.


Кодовой фразой может являться ваше имя, кличка питомца или что либо ещё, но в таком случае перебрать это проблем не составит. Тут нам и пригодятся хэш-функции, давайте подумаем, а что если нам всё также запоминать лишь наше имя, но при этом в качестве кодовой фразы использовать не само имя, а его хэш, полученный из любой хэш-функции?


Условно, при использовании функции sha-256 из LogisticNode мы получим нечто вроде 663225c0ece3e98375738f0772dca57008565f21dc384c95f13233b3d7d8aeba, перебор этого выражения невозможен для современных компьютеров, как и попытки угадать изначальную фразу вместе с выбранной хэш функцией.

Выходит, что имея сид фразу с этим "паролем", вы сможете обезопасить себя от большинства технических атак. Так как даже при краже сид фразы, злоумышленник просто не сможет получить или каким-либо образом подобным подобрать кодовую фразу, а вы в это время просто помните начальное слово(LogisticNode) и хэш-функцию(sha-256), которую вы использовали для генерации хэша, который и является нашим "паролем".

С мнемонической фразой закончили, теперь перейдём к тому как из 12 слов получаются приватные ключи.

Seed фраза и мастер-ключ

Первое, что нужно понять, - определение термина HD wallet.

HD(Hierarchical deterministic) wallet - иерархический детерминированный кошелёк, ниже описано что означают эти страшные слова.

«Иерархический» аспект кошелька возникает из-за того, что каждый дочерний ключ может генерировать свои собственные дочерние ключи, которые могут генерировать свои собственные, и так далее и тому подобное, создавая при этом некое подобие дерева и как следствие систему иерархии.

«Детерминированная» часть означает, что одна и та же мнемоническая фраза будет генерировать одно и то же дерево с одними и теми же ключами одним и тем же способом.

Далее, посмотрим на картинку со всеми этапами преобразований.

Мнемоническая фраза -> seed фраза -> мастер-ключ

Рассмотрим её по пунктам:

Seed фраза

Seed фраза - преобразованная версия мнемонической фразы. Используется стандарт формирования ключа PBKDF2 вместе с хэш-функцией HMAC-SHA512.

В качестве сообщения(пароля) используется мнемоническая фраза, в качестве salt используется мнемоническая фраза + кодовая фраза(то самое 13, 25 слово). По умолчанию кодовое слово является пустым.

Есть ещё несколько важных параметров:

  • Количество итераций: 2048(Данные параметр обеспечивает безопасность шифрования, при желании количество можно увеличить в целях безопасности. Так как по стандарту число установлено 2048, вы можете его увеличить, тогда seed будет другой и как следствие всё что ниже, а простые кошельки всё также будут использовать 2048 итераций и давать доступ совсем к другим кошелькам. При этом уменьшать их не следует, так как это не безопасно.
  • Длина ключа: 64(Seed фраза для наших задач должна содержать 64 байта)

Таким образом, используя данный код на питоне мы можем выполнить необходимые преобразования и получить сид фразу:

import hashlib
import hmac
import unicodedata
from typing import AnyStr
from binascii import (hexlify, unhexlify)
PBKDF2_ROUNDS = 2048

def normalize_string(txt: AnyStr) -> str:
    if isinstance(txt, bytes):
            utxt = txt.decode("utf8")
    elif isinstance(txt, str):
            utxt = txt
    else:
            raise TypeError("String value expected")
    return unicodedata.normalize("NFKD", utxt)

def to_seed(mnemonic: str, passphrase: str = "") -> bytes:
    mnemonic = normalize_string(mnemonic)
    passphrase = normalize_string(passphrase)    
    passphrase = "mnemonic" + passphrase   
    mnemonic_bytes = mnemonic.encode("utf-8")   
    passphrase_bytes = passphrase.encode("utf-8")    
    stretched = hashlib.pbkdf2_hmac(
            "sha512", mnemonic_bytes, passphrase_bytes, PBKDF2_ROUNDS
    )    
    return stretched[:64] # Непреобразованный вид, для преобразования в читаемый используйте функцию hexlify

Мастер-ключ

Мастер ключ(Extended master key) - первый ключ, который вы получите при вставлении seed фразы в HMAC-SHA512.

В качестве ключа при использовании хэш функции используется просто строка "Bitcoin seed".

HMAC функция возвращает 64 байта данных, которые невозможно предсказать. Далее их разделяют на две части, первая из которых является нашим расширенным приватным ключом(Xprv).

  • Левая сторона является обычным приватным ключом.
  • Правая сторона будет так называемым Chain code, это просто случайные 32 байта данных, которые в дальнейшем будут использоваться для генерации дочерних ключей.

Расширенный приватный ключ

Расширенный приватный ключ(Extended private key) - простой private key в сочетании с Chain code.

Расширенный приватный ключ наглядно.

Из расширенного приватного ключа также можно сгенерировать расширенный публичный ключ(Xpub), просто вычислив публичный ключ из приватного и добавив к нему Chain code.

Мы получили все ключи, поэтому можно переходить к описанию того, как из них создаются все дочерние связки ключей.

Генерация дочерних ключей

Благодаря Xprv и Xpub мы можем создавать огромное множество дочерних ключей, а если точнее - вплоть до числа 2**32.

  • Расширенный приватный ключ может генерировать оба вида дочерних ключей - приватный и публичный.
  • Расширенный публичный ключ может генерировать только публичные ключи тех же кошельков, что и у приватного. (Это может быть полезно, когда по какой-то причине вы хотите дать возможность получить все адреса ваших кошельков, но не давать приватные ключи от них)

В целях безопасности, вы можете создать защищённые(Hardened) дочерние ключи. В сравнении с обычными ключами, имея Xpub не получится получить их публичный ключ, что может требоваться в некоторых задачах для усиления безопасности.

Также, из каждого дочернего приватного ключа(Normal, Hardened) и дочернего публичного ключа также можно генерировать до 2**32 дочерних, выглядит это как-то так(Пример для Normal, можете прочитать полностью в англоязычной статье):

Генерация дочерних ключей из дочернего приватного ключа(Normal private key)

Наконец мы закончили о ключах и теперь можно приступить к теме сильно проще - путям деривации.

Пути деривации

Пути деривации - это часть HD wallets, которая определяется неким стандартом(В нашем случае BIP44) и описывает то, как получать ключи в дереве.

Всего есть 5 уровней, которые входят в стандарт BIP32:

m / purpose' / coin_type' / account' / change / address_index

На изображении показано, как это выглядит графически.

Графическое изображение структуры путей деривации.

Опишем каждую часть по порядку.

Purpose

Purpose - константна со значением 44' (или 0x8000002C), она указывает на спецификацию, которую использует наше дерево для генерации ключей.

Но есть и исключения, вы задумывались почему в биткоине есть какие-то Legacy, Segwit и Taproot адреса? Как раз из-за purpose. У Legacy он 44, Segwit имеет номер 84, а Taproot - 86(Следовательно, и адреса у них разнятся).

Coin type

Coin type - это криптовалюта, ключи которой мы хотим создать. Рассмотрим номера самых популярных монеток.

  • 0' = Bitcoin
  • 1' = Bitcoin (Testnet)
  • 2' = Litecoin
  • 3' = Dogecoin
  • 60' = Ethereum

Полный список вы можете посмотреть по ссылке, каждая монета, у которой поддерживается мнемоническая фраза(то есть почти каждая) имеет свой номер в этом списке.

Account

Account - простое значение, которое позволяет отделать множества "счетов".

По умолчанию это значение установлено как 0', при желании вы можете использовать номер 1 для одних задач, а номер 2 для других. Таким образом одна мнемоническая фраза может использоваться для разных задач, а этот номер может быть очень большим числом, чтобы опять обезопасить себя при потере мнемонической фразы, ведь перебирать значения этого поля и проверять кошельки в каждом - требует слишком большие мощности.

Вы заметили, сколько возможностей обезопасить свой кошелёк уже было описано? Именно поэтому я занялся написанием этой статьи.

Change

Change - значение, которое не используется в большинстве современных криптовалют.

Всего есть 2 значения:

  • Receiving = 0 - Адреса, которые мы передаём людям для получения транзакций.
  • Change = 1 - Адреса, используемые для отправки остатка себе.

Последнее - механика работы биткоина, когда в каждой транзакции участвуют все средства, просто введённая сумма отправляется получателю, а остаток(Весь баланс-отправленная сумма), отправляется себе.

И наконец, последний уровень - index.

Index

Index - это просто номера кошельков, которые используется как child index в стандарте BIP32.

Номера от 0 до 2**32, именно столько кошельков может создать мнемоническая фраза с 1 номером в Account, и даже так подобное число просто невероятно огромно.

Конец

Спасибо за прочтение статьи, надеюсь вам было интересна, а главное полезна описанная мной информация.

Автор статьи - https://t.me/leviathan_hell.

Котик.