Я читаю книгу Mastering Bitcoin и запутался в выводах ключей кошелька в главе 4. Ключи, адреса, кошельки .
В книге сначала упоминается получение закрытого дочернего ключа, где дочерний закрытый ключ получается из трех входных данных: (родительский открытый ключ, ранее полученный из родительского закрытого ключа, кода родительской цепочки, индекса).
Далее в книге обсуждаются расширенные ключи и упоминаются два типа: расширенные закрытые ключи и расширенные открытые ключи.
Расширенные открытые ключи используются для получения дочерних открытых ключей из открытых ключей родителей, чтобы избежать раскрытия закрытых ключей, что делает их более безопасными. Это блок-схема расширенного открытого ключа .
С другой стороны, в книге упоминается, что расширенный закрытый ключ используется для получения закрытого ключа ребенка с использованием закрытого ключа родителя и кода цепочки.
Однако, хотя расширенный открытый ключ не раскрывает закрытый ключ, его использование по-прежнему рискованно, поскольку он раскрывает коды цепочки. В случае утечки закрытого ключа их можно использовать вместе для получения других дочерних ключей.
Наконец, в книге предлагается использовать защищённый ключ , описание которого, по моему мнению, совпадает с описанием расширенного закрытого ключа.
Мой вопрос: первый вопрос: является ли расширенный закрытый ключ тем же самым, что и создание защищенного ключа?
Мой второй вопрос заключается в том, какой метод на самом деле используется при получении закрытого ключа для детей, первый, который я упоминаю в своем вопросе, или второй, использующий расширенный закрытый ключ, который для меня является то же самое, что и получение усиленного ключа.
Здесь много путаницы, в основном фрагменты всей схемы иерархического детерминированного вывода, и, наконец, два вопроса, которые, кажется, указывают на то, что в ней упущен какой-то момент. Ответ на первый вопрос — нет. Второй вопрос более интересен:
Начнем с расширенных ключей, в частности ключей BIP32. Подобно закрытым и открытым ключам, расширенные ключи могут быть либо «закрытыми», либо «открытыми». Я поместил оба в кавычки, потому что оба типа расширенных ключей содержат личную информацию. По крайней мере, достаточно для отслеживания использования ключа. Этот механизм используется аппаратными кошельками и программными кошельками «только для просмотра» на ПК.
Расширенный ключ — это всего лишь сериализация нескольких фрагментов данных в кодировке base58:
[ magic ][ depth ][ parent fingerprint ][ key index ][ chain code ][ key ]
Где key
может быть либо открытый ключ, либо закрытый ключ. Закрытые ключи начинаются с одного 0x00
байта, поэтому длина этого большого двоичного объекта остается неизменной. Расширенный ключ обычно получается путем «обхода» некоторого path
, что означает, что вы должны начать свое получение с некоторого родительского расширенного ключа и последовательно получать дочерние ключи с определенными индексами, пока вы, наконец, не получите окончательный расширенный ключ в файле path
. Я перестану использовать «расширенный» в этом ответе. С этого момента я буду называть расширенный закрытый ключ, xprv
а расширенный открытый ключ — xpub
, а иногда просто «ключи». Нерасширенные - это просто «закрытый ключ» или «открытый ключ».
xprv или xpub - magic
это 4 байта, чтобы указать сеть, к которой он принадлежит: testnet или mainnet ( t
или x
соответственно), и тип ключа ( pub
и prv
соответственно). Это depth
байт, который указывает, насколько глубоко xpriv или xpout находится в пути, начиная с 00
глубины master
ключа и увеличиваясь на единицу по мере того, как вдоль пути выполняется создание дополнительных дочерних ключей. Обратите внимание, что до сих пор единственная разница между ключами xprv
и xpub
, о которой я упоминал, заключалась в части prv
или pub
в магии. Также должно быть ясно, что xprv
и xpub
могут быть на одном пути и на одной глубине. Это означает , что для такой пары xprv
иxpub
[ key ]
часть будет иметь 32-байтовый закрытый ключ (с одним 00
байтом перед ним) в xprv
файле xprv
.
Отпечаток родителя — это первые 4 байта hash160
открытого ключа родителя. Это означает, что даже если родитель xprv
использовался для получения дочернего элемента xprv
, он имел бы то же самое parent fingerprint
, как если бы родитель xpub
использовался для получения дочернего элемента xpub
. Родительско-дочерние отношения между ключами означают, что они являются смежными в пути.
A path
— это n-кортеж индексов, обычно в базе 10, разделенных знаком /
. Диапазон индекса может быть между 0 и 4294967295 (или 2 ^ 32-1), где все [0,2147483647]
следует за неукрепленным производным, а индексы [2147483648,4294967295]
следуют за усиленным производным. Вы можете видеть, что каждая половина диапазона индексов используется для другого метода. Можно сказать, что есть два диапазона. [0,2147483647]
для незащищенных ключей и [0h,2147483647h]
для закаленных ключей. указывает, что индекс h
(назовем его i
) следует рассматривать как i + 2147483648
. Вы, вероятно, с большей вероятностью увидите h
нотацию в виде знака вставки '
, поэтому 1' == 1h
, но я не думаю, что это очень красиво, поэтому я пока ограничусь этим h
.
Пример того, как выглядит путь:
m/0h/1/2h/2/1000000000
Означает m
, что ключ в этом индексе является master xprv
или master xpub
. Маленький m
означает, что этот расширенный ключ является master xprv
, а большой M
— master xpub
. Следуя предыдущим определениям, вы можете сказать, что m
это родитель ключа at 0h
, а ключ at 2h
является дочерним элементом ключа перед ним index 1
. Чтобы упростить понимание, мы будем обозначать разные ключи в пути буквами {a..e}
, если мы подразумеваем, что это xprv
s и {A..B}
если xpub
s.
m / 0h / 1 / 2h / 2 / 1000000000
m a b c d e
Путь обычно задается с индексами в базе 10, но в самом ключе они закодированы в шестнадцатеричном формате (база 16), поэтому a [ key index ]
всегда составляет 4 байта с нулями в начале, если это необходимо. И главного ключа оба всегда равны нулю, поэтому depth
и , и они могут достигать максимума и соответственно. Так и являются родителем и дочерним, и так являются и . of is и его индекс , а of is while его индекс равен (80000000 + 2). Последний дочерний ключ, который нужно получить, это . Можно сказать, что мы следовали по пути, начинающемуся с , из него мы получили ключ с индексом , затем изindex
00
00000000
FF
FFFFFFFF
m
a
d
e
depth
b
02
00000001
depth
c
03
80000002
e
m
a
0h
a
мы получили ключ b
по индексу 1
.. и так далее. Но что значит получить новый ключ?
Оставшиеся два элемента в формате расширенного ключа, родительский [ chain code ]
и [ key ]
используются вместе с дочерним ключом index
для его получения. Это означает, что для наследования c
мы b
передаем некоторой функции b
' chain code
и ' key
и c
's' index
. Конкретным примером нашего b
и c
будет:
b :
0488ADE4
02
5C1BD648
00000001
2A7857631386BA23DACAC34180DD1983734E444FDBF774041578E9B6ADB37C19
003C6CB8D0F6A264C91EA8B5030FADAA8E538B020F0A387421A12DE9319DC93368
c :
0488ADE4
03
BEF5A2F9
80000002
04466B9CC8E161E966409CA52986C584F07E9DC81F735DB683C3FF6EC7B1503F
00CBCE0D719ECF7431D88E6A89FA1483E02E35092AF60C042B1DF2FF59FA424DCA
Поля упорядочены, как в структуре выше. На обоих, magic
говорит xprv
, depth
увеличивается между родительским и дочерним, fingerprint
at c
является hash160
открытым ключом, который вы получили бы из закрытого ключа в b
, а b
s index
находится в первой, незащищенной половине диапазона, в то время как c
' s — вторая, закаленная половина. Наконец, chain code
и key
s каждого из xprv
s кодируются.
Наследование chain code
and key
for c
from b
выполняется с помощью процесса, называемого CKDpriv
, что означает получение дочернего элемента xprv
от родителя xprv
. В этом процессе мы использовали chain code
and key
from b
и index
from c
. Важный момент: мы кодировали только c
после того , как получили его chain code
и key
из того, что будет его index
.
Any xprv
можно использовать с CKDpriv
для получения дочернего элемента xprv
в любом index
. Конкретный способ CKDpriv
воздействия на ввод зависит от того, index
находится ли дочерний элемент в усиленном диапазоне или в незащищенном диапазоне. По сути, CKDpriv
функция запускает HMAC-SHA512
родительские chain code
и key
дочерние index
. Эта функция hmac принимает два значения a key*
(не путать с нашими вхождениями key
, будем обозначаться как hkey
) и text
. Родительский chain code
используется как hkey
, в то время text
как состоит из родительского key
в форме закрытого ключа , если дочерний индекс находится в усиленном диапазоне, [0h,2147483647h]
и в открытом ключеформируются, если индекс находится в незащищенном диапазоне. Затем он объединяется с дочерним index
.
c
index находится в усиленном диапазоне, поэтому CKDpriv
hmac-sha512 запускается с входными данными:
HMAC-SHA512( 2A7857631386BA23DACAC34180DD1983734E444FDBF774041578E9B6ADB37C19,
003C6CB8D0F6A264C91EA8B5030FADAA8E538B020F0A387421A12DE9319DC9336880000002 )
Который возвращает 64-байтовый хеш:
8F6154A0A82D0F68B9E5B586EA66D951DAAA071BEBD390097CC516285C791A6204466B9CC8E161E966409CA52986C584F07E9DC81F735DB683C3FF6EC7B1503F
32 байта в правой половине этого хеша 04466B9C...C7B1503F
становятся дочерними ( c
здесь) chain code
, а 32 байта слева используются для «настройки», что означает просто «добавление mod n» к родительскому ключу, в этом примере:
8F6154A0A82D0F68B9E5B586EA66D951DAAA071BEBD390097CC516285C791A62
+
3C6CB8D0F6A264C91EA8B5030FADAA8E538B020F0A387421A12DE9319DC93368
=
CBCE0D719ECF7431D88E6A89FA1483E02E35092AF60C042B1DF2FF59FA424DCA mod n
00
предваряющие байты в ключах, потому что это просто добавление чисел, но эти нулевые байты очень важны для хеш-функции, поэтому я специально включил их туда.Теперь, когда у нас есть и c
( в форме закрытого ключа), мы хотели бы на самом деле закодировать его, чтобы он был пригодным для использования . Чтобы получить from , нам нужно знать открытый ключ from . Поскольку он находится в форме закрытого ключа, нам придется выполнить умножение:chain code
key
c
xprv
fingerprint
b
key
b
CBCE0D719ECF7431D88E6A89FA1483E02E35092AF60C042B1DF2FF59FA424DCA * G
= 03501E454BF00751F24B1B489AA925215D66AF2234E3891C3B21A52BEDB3CD711C
Возьмите hash160
этот открытый ключ, и возвращенный хэш будет BEF5A2F9A56A94AAB12459F72AD9CF8CF19C7BBE
. Первые четыре байта являются b
отпечатком пользователя: BEF5A2F9
. Кодирование остальной части c
легко. Начните с волшебства xprv
, так как мы получили дочерний элемент xprv
, увеличьте глубину b
на единицу, затем fingerprint
. Следующий c
кодируется index
. Мы вывели index 2h
, так что это будет 80000002
, а затем новый chain code
и key
тот, который мы получили от CKDpriv
.
Это в основном то , что представляет собой закаленная деривация. Закрытый ключ родителя и код цепи используются для получения дочернего ключа в некотором защищенном индексе. Что, если мы хотим вывести d
? Это index 2
, так что это незащищенный индекс. Это второй случай CKDpriv
.
Разница в том, что используется для text
параметра HMAC-SHA512
функции. Вместо того, чтобы использовать key
форму закрытого ключа родителя, мы используем форму открытого ключа, поэтому для получения d
индекса 2
от c
мы сначала находим открытый ключ c
:
CBCE0D719ECF7431D88E6A89FA1483E02E35092AF60C042B1DF2FF59FA424DCA * G
= 0357BFE1E341D01C69FE5654309956CBEA516822FBA8A601743A012A7896EE8DC2
Затем продолжайте выполнять те же шаги, что и выше:
HMAC-SHA512( 04466B9CC8E161E966409CA52986C584F07E9DC81F735DB683C3FF6EC7B1503F,
0357BFE1E341D01C69FE5654309956CBEA516822FBA8A601743A012A7896EE8DC200000002 )
tweak chain code
437984D45C4A2F5840C65B3DC6D7274E2859AD25D092DB032C49AA4D006A426B|CFB71883F01676F587D023CC53A35BC7F88F724B1F8C2892AC1275AC822A3EDD
* обратите внимание, что 00
это не добавляется к text
, так как это открытый ключ.
437984D45C4A2F5840C65B3DC6D7274E2859AD25D092DB032C49AA4D006A426B
+
CBCE0D719ECF7431D88E6A89FA1483E02E35092AF60C042B1DF2FF59FA424DCA
=
0F479245FB19A38A1954C5C7C0EBAB2F9BDFD96A17563EF28A6A4B1A2A764EF4 mod n
hash160( 0357BFE1E341D01C69FE5654309956CBEA516822FBA8A601743A012A7896EE8DC2 )
finger
print
EE7AB90C|DE56A8C0E2BB086AC49748B8DB9DCE72
Остальное легко, и мы можем закодировать:
d :
0488ADE4
04
EE7AB90C
00000002
CFB71883F01676F587D023CC53A35BC7F88F724B1F8C2892AC1275AC822A3EDD
000F479245FB19A38A1954C5C7C0EBAB2F9BDFD96A17563EF28A6A4B1A2A764EF4
Разница между этими двумя методами получения потомков xprv
тонкая, но важная. Это позволяет CKDpub
, которая является функцией для получения потомков xpub
от родителя xpub
. CKDpub
работает почти так же, как CKDpriv
незащищенный вывод , но он выполняет вывод, используя сложение точек, поэтому вместо сложения целых чисел для создания дочерних закрытых ключей мы складываем точки для создания дочерних открытых ключей. Обратите внимание, как в незащищенном производном мы использовали общедоступную точку родителя для HMAC-SHA512
, мы использовали в tweak
качестве добавленного значения к родительскому закрытому ключу для получения дочернего закрытого ключа, в частности, мы получили d
закрытый ключ .
Чтобы понять CKDpub
, полезно сначала узнать о еще одной функции BIP32 под названием Neuter
. Его цель - преобразовать xprv
в xpub
. Давайте «запустим» Neuter
на нашем xprv
d
. Назовем полученный xpub
D
. Neuter
делает две вещи xprv
: 1. Заменяет magic
from 0488ADE4
на 0488B21E
(заменяет xprv
на xpub
) 2. Заменяет закрытый ключ в поле на key
открытую точку того же закрытого ключа
для нашего xprv
d
общедоступная точка:
0F479245FB19A38A1954C5C7C0EBAB2F9BDFD96A17563EF28A6A4B1A2A764EF4 * G
= 02E8445082A72F29B75CA48748A914DF60622A609CACFCE8ED0E35804560741D29
(это обычный процесс закрытого ключа -> открытый ключ)
так что результат:
D:
0488B21E
04
EE7AB90C
00000002
CFB71883F01676F587D023CC53A35BC7F88F724B1F8C2892AC1275AC822A3EDD
02E8445082A72F29B75CA48748A914DF60622A609CACFCE8ED0E35804560741D29
Теперь d
он «кастрирован», D
открытый ключ закодирован, но посмотрите, как chain code
сохраняются depth
, fingerprint
и index
. Он xpub
D
находится в том же месте на пути, что и xprv
d
. Мы будем использовать chain code
and key
(открытый ключ) для CKDpub
, так же, как и CKDpriv
с незащищенной деривацией, но что касается CKDpriv
, мы получили дочерний закрытый ключ, используя:
tweak + (parent private key) = child private key
ибо CKDpub
мы будем использовать:
tweak*G + (parent public key) = child public key
Это работает, потому что parent public key
это действительно справедливо (parent private key)*G
и child public key
справедливо (child private key)*G
. То есть, если мы возьмем CKDpriv
уравнение настройки и умножим все элементы на G
, мы получим именно CKDpub
уравнение настройки. CKDpub
может создавать дочерние xpub
ключи только в диапазоне незащищенных индексов. Это связано с тем, что информация, представленная в родительском элементе xpub
, в частности открытый ключ в элементе [ key ]
, применяется только к незащищенному диапазону. Где CKDpriv
мы могли бы использовать закрытый ключ, чтобы узнать открытый ключ, мы не можем пойти другим путем. HMAC-SHA512
раунд, в котором используются открытые ключи , CKDpriv
относится к диапазону незащищенных индексов.
Теперь, когда мы выполнили стерилизацию d
для создания xpub D
, следующим в пути будет файл e
с индексом 1000000000 (или 3B9ACA00
), который находится в незащищенном диапазоне, поэтому мы должны иметь возможность получить E
дочерний элемент xpub
с D
помощью CKDpub
. Мы начинаем с hmac-sha512 родителя chain code
as hkey
и родителя key
(открытого ключа), объединенного с дочерним E
индексом:
HMAC-SHA512( CFB71883F01676F587D023CC53A35BC7F88F724B1F8C2892AC1275AC822A3EDD,
02E8445082A72F29B75CA48748A914DF60622A609CACFCE8ED0E35804560741D293B9ACA00 )
tweak chain code
37D3E49D8ECB854CC518BBA096F46795A9707860BF0FC95E5B19278C997098D4|C783E67B921D2BEB8F6B389CC646D7263B4145701DADD2161548A8B078E65E9E
Умножьте настройку на генератор, G
чтобы мы могли настроить открытый ключ родителя, используя сложение точек:
37D3E49D8ECB854CC518BBA096F46795A9707860BF0FC95E5B19278C997098D4 * G
= 0327E992F68217BC3E88CFFC3FEAB475880145413CBE008DB22B496DF4E1C3F864 <- tweak*G
Добавьте настройку к родительской точке. Результатом является открытый ключ ребенка:
0327E992F68217BC3E88CFFC3FEAB475880145413CBE008DB22B496DF4E1C3F864
+
02E8445082A72F29B75CA48748A914DF60622A609CACFCE8ED0E35804560741D29
=
022A471424DA5E657499D1FF51CB43C47481A03B1E77F951FE64CEC9F5A48F7011
Получить отпечатки пальцев родителей:
hash160(02E8445082A72F29B75CA48748A914DF60622A609CACFCE8ED0E35804560741D29) = D880D7D8....
Наконец, мы можем закодировать E
:
0488B21E
05
D880D7D8
3B9ACA00
C783E67B921D2BEB8F6B389CC646D7263B4145701DADD2161548A8B078E65E9E
022A471424DA5E657499D1FF51CB43C47481A03B1E77F951FE64CEC9F5A48F7011
Выполнив стерилизацию d
, D
а затем производя E
, мы можем сказать, что наш путь теперь выглядит так:
m / 0h / 1 / 2h / 2 / 1000000000
m / a / b / c / D / E
Или мы можем использовать N()
нотацию (для Neuter), чтобы показать, где CKDpub
она использовалась, но я думаю, что это менее красиво.m / a / b / c / N(d / e)
Итак, чтобы резюмировать ваш вопрос, существует 3 разных метода вывода: два с использованием закрытых ключей и один с использованием открытых ключей:
CKDpriv
получить дочерний xprv
элемент по усиленному индексуCKDpriv
для получения дочернего xprv
элемента по нежесткому индексуCKDpub
получить дочерний xpub
элемент по незащищенному индексуЯ тоже с трудом пытаюсь понять материал там. Вот что я понимаю:
Простое понимание состоит в том, что для получения расширенного закрытого ключа и расширенного получения открытого ключа все они имеют одинаковые входные данные (родительский открытый ключ и узел родительской цепочки). В результате у них будут одинаковые левые 256 бит и правые 256 бит.
Разница в том, что
для получения расширенного закрытого ключа левые 256 бит добавляются к родительскому закрытому ключу для создания дочернего закрытого ключа.
А для получения расширенного открытого ключа левые 256 бит добавляются к родительскому открытому ключу для создания дочернего открытого ключа.
Оба производных расширенного ключа используют свои правые 256-битные выходные данные в качестве узла цепочки, поэтому узел цепочки одинаков для обоих производных.
Для вывода усиленного ключа ввод отличается от вышеприведенного вывода расширенного ключа.
Вместо того, чтобы использовать родительский открытый ключ, он использует родительский закрытый ключ с узлом цепочки в качестве входных данных , что приведет к другим 512-битным выходным данным. Таким образом, правильный 256-битный вывод, который используется в качестве узла цепочки, отличается от описанного выше механизма.
Поскольку узел цепочки отличается, хакеры не могут вывести закрытый ключ, используя узел цепочки из расширенного механизма деривации.
Защищенные ключи, по сути, полностью генерируют новые ключи.
Все преимущество использования иерархических ключей заключается в том, что вы можете генерировать много открытых ключей без доступа к закрытым ключам. Позже вы можете использовать тот же метод, который вы использовали для создания открытых ключей на закрытых ключах, и иметь возможность тратить средства.
Это полезно, например, если вы хотите, чтобы веб-сайт получал средства, но не терял их в случае взлома. Веб-сайт может генерировать новый открытый ключ для каждого клиента, даже не касаясь закрытых ключей, что обеспечивает более надежную защиту средств.
Закаленные ключи разрывают эту связь. Операция с усиленным ключом может изменить закрытый ключ, чтобы создать новый, но НЕ изменить открытый ключ.
Они не очень критичны, так как вы можете просто сгенерировать новый ключ и получить тот же результат, но это более удобно. Можно более-менее игнорировать закалённые ключи и просто продолжать радоваться жизни =).
Магнус
Кайл Грэм
Талис К.
скат
0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2
или 2% bx ec-multiply0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2
Мельк