使用擴展私鑰的 HD 錢包中的密鑰派生與強化派生
我正在閱讀《掌握比特幣》一書,並對第 4 章中的錢包密鑰派生感到困惑。密鑰、地址、錢包。
本書首先提到了一個子私鑰派生,其中子私鑰是從三個輸入中派生的:(父公鑰先前從父私鑰派生,父鏈碼,索引)。
接下來,本書討論了擴展密鑰,並提到了兩種類型:擴展私鑰和擴展公鑰。
擴展公鑰用於從父公鑰導出子公鑰,以避免暴露私鑰,因此更安全。這是擴展公鑰的框圖。
另一方面,書中提到擴展私鑰用於使用父母的私鑰和鏈碼來派生孩子的私鑰。
但是,雖然擴展公鑰不暴露私鑰,但由於暴露了鏈碼,使用起來還是有風險的。如果私鑰洩露,它們可以一起用於派生其他子密鑰。
最後,本書建議使用hardened key,對我來說這與擴展私鑰的描述完全相同。
我的問題第一個問題是,擴展私鑰是否與強化密鑰派生相同?
我的第二個問題是,在為兒童派生私鑰時實際使用了哪種技術,我在問題中提到的第一個技術或使用擴展私鑰的第二個技術對我來說與強化密鑰派生完全相同。
這裡有很多混亂,主要是整個方案的點點滴滴,即分層確定性推導,最後有兩個問題似乎表明它缺少一些要點。第一個問題的答案是否定的。第二個問題更有趣:
讓我們從擴展密鑰開始,特別是 BIP32 密鑰。與私鑰和公鑰一樣,擴展密鑰可以是“私鑰”或“公鑰”。我將兩者都放在引號中,因為這兩種類型的擴展密鑰都包含私人資訊。至少足以跟踪密鑰的使用。這種機制被 PC 上的硬體錢包和“只看”軟體錢包使用。
擴展密鑰只是一些數據的 base58 編碼序列化:
[ magic ][ depth ][ parent fingerprint ][ key index ][ chain code ][ key ]
哪裡
key
可以是公鑰或私鑰。私鑰前面有一個0x00
字節,所以這個 blob 的長度保持不變。擴展密鑰通常是通過“遍歷” some 來派生的path
,這意味著您將從某個父擴展密鑰開始派生,並連續派生具有特定索引的子密鑰,直到您最終在path
. 我將停止在此答案中使用“擴展”。從現在開始,我將把擴展私鑰稱為xprv
,將擴展公鑰稱為xpub
,有時也稱為“密鑰”。非擴展只是“私鑰”或“公鑰”。xprv 或 xpub 的
magic
4 個字節表示它所屬的網路:testnet 或 mainnet(t
或x
分別),以及它的密鑰類型(pub
和prv
分別)。這depth
是一個字節,指示 xpriv 或 xpout 在路徑中的深度,從密鑰00
的深度開始master
,並隨著沿路徑完成更多子密鑰的派生而遞增。請注意,到目前為止,我提到的xprv
和鍵之間的唯一區別是魔術中的或部分。還應該清楚的是 an和可以在相同的路徑中,並且在相同的深度。這意味著對於這樣的一對and ,xpub``prv``pub``xprv``xpub``xprv``xpub``[ key ]
的一部分將在 . 中具有一個 32 字節的私鑰(前加一個00
字節)xprv
,以及一個 33 字節的公鑰,這是您從 . 中的私鑰獲得的公鑰xprv
。
hash160
父母的指紋是父母的公鑰的前 4 個字節。這意味著即使使用父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
意味著該索引處的鍵是 amaster xprv
或master xpub
。小m
意味著這個擴展鍵是 amaster xprv
,而大M
amaster xpub
。按照前面的定義,您可以知道m
是鍵 at 的父級0h
,而鍵 at2h
是它之前的鍵在 index 處的子級1
。為了更容易理解,{a..e}
如果我們的意思是這些是xprv
s 和{A..B}
ifxpub
s,我們將用字母註釋路徑中的不同鍵。m / 0h / 1 / 2h / 2 / 1000000000 m a b c d e
路徑通常以 base10 為索引,但在密鑰本身中,它們以十六進制 (base16) 編碼,因此 a
[ key index ]
始終為 4 個字節,如果需要,可以在前面加上零。主密鑰的depth
和index
都始終為零,因此00
和00000000
,它們可以分別達到最大值FF
和FFFFFFFF
。所以m
anda
是 parent 和 child, andd
也是e
。depth
ofb
是02
,它的索引是00000001
,而ofdepth
是c
,03
而它的索引是80000002
(80000000 + 2)。要派生的最後一個子鍵是e
. 我們可以說我們遵循了一條從 開始的路徑m
,從中我們導出a
索引處的鍵0h
,然後從a``b
我們在索引處派生了密鑰1
.. 等等。但是派生一個新密鑰意味著什麼?擴展密鑰格式中的其餘兩個元素,父密鑰
[ chain code ]
和[ key ]
子密鑰一起使用index
以派生它。這意味著要從 派生c
,我們b
將使用b
’schain code
和’ s 提供一些函式。我們的一個具體例子是:key``c``index``b``c
b : 0488ADE4 02 5C1BD648 00000001 2A7857631386BA23DACAC34180DD1983734E444FDBF774041578E9B6ADB37C19 003C6CB8D0F6A264C91EA8B5030FADAA8E538B020F0A387421A12DE9319DC93368
c : 0488ADE4 03 BEF5A2F9 80000002 04466B9CC8E161E966409CA52986C584F07E9DC81F735DB683C3FF6EC7B1503F 00CBCE0D719ECF7431D88E6A89FA1483E02E35092AF60C042B1DF2FF59FA424DCA
欄位按上述結構排序。在兩者上,
magic
都說xprv
,depth
在父子節點之間遞增,fingerprint
atc
是hash160
您將從 at 的私鑰獲得的公鑰的b
,並且b
’sindex
在範圍的第一個非硬化的一半,而c
’ s 是第二個,硬化的一半。最後,對每個 s 的chain code
和s 進行編碼。key``xprv
派生
chain code
andkey
forc
fromb
是通過一個名為的過程完成的CKDpriv
,這意味著xprv
從父級派生一個子級xprv
。在這個過程中,我們使用了chain code
andkey
fromb
和index
fromc
。重要的一點是:我們只在從它的和導出它c
之後編碼。chain code``key``index
Any
xprv
可用於在 any 處CKDpriv
派生子代。具體作用於輸入的方式取決於孩子是在強化範圍內,還是在非強化範圍內。基本上,一個函式在父母的and和孩子的. 這個hmac函式有兩個值a (不要與我們的出現混淆,將被稱為)和。父級用作,而如果子級索引在強化範圍內,則由父級以私鑰形式組成, 和在公鑰中xprv``index``CKDpriv``index``CKDpriv``HMAC-SHA512``chain code``key``index``key*``key``hkey``text``chain code``hkey``text``key``[0h,2147483647h]
如果索引在非硬化範圍內,則形成。然後將其與孩子的index
.
c
的索引在強化範圍內,因此CKDpriv
的 hmac-sha512 使用輸入執行:HMAC-SHA512( 2A7857631386BA23DACAC34180DD1983734E444FDBF774041578E9B6ADB37C19, 003C6CB8D0F6A264C91EA8B5030FADAA8E538B020F0A387421A12DE9319DC9336880000002 )
它返回一個 64 字節的雜湊:
8F6154A0A82D0F68B9E5B586EA66D951DAAA071BEBD390097CC516285C791A6204466B9CC8E161E966409CA52986C584F07E9DC81F735DB683C3FF6EC7B1503F
這個散列的右半部分的 32 個字節,
04466B9C...C7B1503F
成為孩子的 (c
here)chain code
,而左邊的 32 個字節用於“調整”,意思是“添加 mod n”到父母的密鑰,在這個例子中:8F6154A0A82D0F68B9E5B586EA66D951DAAA071BEBD390097CC516285C791A62 + 3C6CB8D0F6A264C91EA8B5030FADAA8E538B020F0A387421A12DE9319DC93368 = CBCE0D719ECF7431D88E6A89FA1483E02E35092AF60C042B1DF2FF59FA424DCA mod n
- 我沒有
00
在此處的鍵中寫入前置字節,因為這只是添加數字,但是那些零字節對於散列函式非常重要,因此我特意將它們包含在其中。現在我們已經有了
c
’schain code
和key
(以私鑰形式),我們希望實際編碼c
它以使其成為可用的xprv
. 要獲取fingerprint
from ,我們需要知道fromb
的公鑰。由於它是私鑰形式,因此我們必須進行乘法運算:key``b
CBCE0D719ECF7431D88E6A89FA1483E02E35092AF60C042B1DF2FF59FA424DCA * G = 03501E454BF00751F24B1B489AA925215D66AF2234E3891C3B21A52BEDB3CD711C
取
hash160
這個公鑰的 ,返回的雜湊是BEF5A2F9A56A94AAB12459F72AD9CF8CF19C7BBE
。前四個字節是b
指紋:BEF5A2F9
。編碼其餘部分c
很容易。xprv
從我們派生一個孩子的魔法開始xprv
,將深度增加b
一,然後是fingerprint
. Nextc
被index
編碼。我們派生了索引2h
,所以這將是80000002
,然後是我們從中得到的新的chain code
和。key``CKDpriv
這基本上就是硬化派生。父私鑰和鏈碼用於在某個強化索引處派生子密鑰。如果我們想推導
d
怎麼辦?它位於 index2
,因此是非硬化索引。這是第二種情況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
這兩種派生 child
xprv
的方法之間的區別是微妙但重要的。它使能,這是一個從父CKDpub
級派生子級的函式。 工作原理與 ’ 的非硬化推導幾乎相同,但它使用點加法進行推導,因此我們不是將整數相加來製作子私鑰,而是將點相加來製作子公鑰。請注意,在非硬化派生中,我們如何使用 的父節點的公共點,我們使用作為父私鑰的附加值來派生子私鑰,具體來說,我們派生了的私鑰。xpub
xpub``CKDpub``CKDpriv``HMAC-SHA512``tweak``d
要理解
CKDpub
,首先了解另一個稱為 BIP32 的函式會有所幫助Neuter
。其目的是將 a 轉換xprv
為xpub
. 讓我們“執行”Neuter
我們的xprv
d
. 我們將呼叫結果xpub
D
。Neuter
對 a 做了兩件事xprv
: 1. 替換magic
from0488ADE4
to0488B21E
(替換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
andkey
(public key) forCKDpub
,CKDpriv
與非硬化派生相同,但對於CKDpriv
,我們使用以下方式派生子私鑰:tweak + (parent private key) = child private key
因為
CKDpub
我們將使用:tweak*G + (parent public key) = child public key
這行得通,因為
parent public key
is 真的是 just(parent private key)*G
,而且child public key
is just(child private key)*G
。也就是說,如果我們採用CKDpriv
調整方程並將所有元素乘以G
,我們就得到了CKDpub
調整方程。CKDpub
只能派生xpub
非硬化索引範圍內的子鍵。這是因為 parent 中存在的資訊xpub
,特別是 中的公鑰[ key ]
,僅適用於非強化範圍。在CKDpriv
我們可以使用私鑰知道公鑰的地方,我們不能走其他路。HMAC-SHA512
使用公鑰的輪次CKDpriv
適用於非硬化索引範圍。現在我們已經
d
創建了 xpubD
,路徑中的下一個是e
索引為 1000000000 (或3B9ACA00
)的 ‘s,它在非硬化範圍內,所以我們應該能夠從using派生E
孩子。我們從 parent 的 hmac-sha512 as和 parent (公鑰)與 child的 index 連接開始:xpub``D``CKDpub``chain code``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 位輸出與上述機制不同。
由於鏈節點不同,黑客無法利用鏈節點從擴展推導機制推導出私鑰。