От мнемонической фразы до закрытого ключа: все, что вам нужно знать

От мнемонической фразы до закрытого ключа: все, что вам нужно знать

22 июня 2023 г.

При создании кошелька для любого приложения на основе блокчейна (TrustWallet, MetaMask, Phantom) или криптовалюты первое, с чем вы столкнетесь, — это мнемоническая фраза — набор слов (обычно 12 или 24), который обладает силой восстановить свой кошелек. Но как этот набор слов преобразуется в закрытый ключ, который является настоящим хранителем ваших цифровых активов?

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

Переход состоит из 4 основных этапов:

* Мнемогенерация * Преобразование мнемоники в семя * Преобразование ключа в мастер-ключ * Преобразование главного ключа в дочерний закрытый ключ

Генерация мнемоники

Во-первых, нам нужно получить слова для мнемонической фразы. В этом примере я покажу, как получить фразу из 12 слов, но это также применимо к фразе из 24 слов.

Начнем с генерации случайных 128 бит (16 байт):

let mut bytes = [0u8; 16];
rand::thread_rng().fill(&mut bytes[..]);

Крайне важно генерировать их случайным образом, чтобы никто не мог их сгенерировать повторно.

Далее нам нужно вычислить хэш SHA256 наших байтов:

let mut hasher = Sha256::new();
hasher.update(bytes);
let result = hasher.finalize();

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

После этих шагов мы можем легко получить слова для мнемонической фразы. Нам нужно объединить сгенерированные байты и байты контрольной суммы. Всего это будет 132 бита. Каждая группа из 11 бит (всего 12 групп) представляет собой число от 0 до 2047.

Сопоставьте эти числа со списком английских слов BIP-39, который содержит 2048 слов. :

// word_list contains all BIP-39 words
let chunk_size = 11;
let bits = BitVec::from_bytes(&bytes);
let mut mnemonic = String::new();

for i in (0..bits.len()).step_by(chunk_size) {
    let end = std::cmp::min(i + chunk_size, bits.len());
    let chunk = bits.get(i..end).unwrap();
    let mut value: usize = 0;
    for bit in chunk.iter() {
        value = (value << 1) | (bit as usize);
    }
    mnemonic.push_str(word_list[value]);
    mnemonic.push(' ');
}
mnemonic.pop();

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

Возможно, вас интересует выбор слов. Почему мы используем именно этот список? Можете ли вы использовать свой собственный список слов для хранения приватных ключей? Теоретически да, вы можете использовать любой список из 2047 слов или даже использовать свою любимую книгу в качестве источника слов. Однако список слов BIP-39 является мировым стандартом, и все криптокошельки сопоставляют слова со своими индексами в соответствии с этим списком. Вы можете запомнить индексы из любого списка слов, но в этом случае вам каждый раз нужно будет сопоставлять слова из вашего списка со стандартным.

Конвертация мнемоники в семя

Теперь нам нужно преобразовать нашу фразу из 12 слов в начальное число. В соответствии с BIP-39 следует применять функцию PBKDF2 с HMAC-SHA512. Кроме того, в этот алгоритм обычно добавляют солевую фразу-пароль. Если парольная фраза пуста, то значение соли просто «мнемоника». Использование парольной фразы обеспечивает дополнительный уровень безопасности. Парольную фразу следует держать в секрете, как и мнемонику.

Получение seed из мнемоники и парольной фразы:

let mut pbkdf2_hash = [0u8; 64];
let salt = format!("mnemonic{}", passphrase);
pbkdf2::derive(
    pbkdf2::PBKDF2_HMAC_SHA512,
    std::num::NonZeroU32::new(2048).unwrap(),
    &salt.as_bytes(),
    mnemonic.as_bytes(),
    &mut pbkdf2_hash
);

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

Преобразование исходного кода в мастер-ключ

После того как вы получили начальное число (хранящееся в pbkdf2_hash) из мнемоники с помощью функции PBKDF2 HMAC-SHA512, вы можете использовать ее для создания главного закрытого ключа и кода цепи для иерархического детерминированного (HD) кошелька в соответствии с BIP. -32 стандарт.

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

Стандарт BIP-32 указывает, что начальное число должно обрабатываться с помощью HMAC-SHA512 с использованием «начального числа биткойнов» в качестве ключа для создания главного закрытого ключа и основного кода цепи.

Получение мастер-ключа:

let key = b"Bitcoin seed";

type HmacSha512 = Hmac<Sha512>;
let mut mac = HmacSha512::new_from_slice(key).expect("HmacSha512 error");
mac.update(&pbkdf2_hash);

let result = mac.finalize();
let bytes = result.into_bytes();

let master_private_key = &bytes[0..32];
let chain_code = &bytes[32..64];

Ключевое значение «Семя биткойнов» определено в стандарте BIP-32 (Предложение по улучшению биткойнов 32) для создания иерархических детерминированных кошельков.

Может показаться запутанным, что «начальное число биткойнов» используется даже при получении ключей для других криптовалют, таких как Ethereum. Однако важно помнить, что BIP-32 — это протокол, созданный сообществом биткойнов, и его стандартам следуют многие другие криптовалюты. Использование «начального биткойна» не ограничивает использование только биткойном — это просто значение, используемое криптографической функцией для генерации главного закрытого ключа и кода цепочки.

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

HD Wallet Tree

Преобразование главного ключа в дочерний закрытый ключ

Наконец, мы можем получить закрытые ключи для любого блокчейна из master_key.

Пример биткойна:

let secp = bitcoin::secp256k1::Secp256k1::new();
let master_privkey = ExtendedPrivKey::new_master(Network::Bitcoin, &master_key).unwrap();

let child_number = ChildNumber::from_normal_idx(0).unwrap();
let child_privkey = master_privkey.ckd_priv(&secp, child_number).unwrap();

Пример Эфириума:

let mut child_privkey_index = 0;
let derivation_path = format!("m/44'/60'/0'/0/{}", child_privkey_index);
let master_key = ExtendedPrivKey::derive(&seed, &derivation_path).unwrap();
let child_privkey = SecretKey::parse_slice(&child_key.secret()).unwrap();

В Ethereum каждый master_key напрямую сопоставляется с дочерними ключами.

Обзор

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


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE