Wallet

使用擴展私鑰的 HD 錢包中的密鑰派生與強化派生

  • January 25, 2019

我正在閱讀《掌握比特幣》一書,並對第 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 的magic4 個字節表示它所屬的網路:testnet 或 mainnet(tx分別),以及它的密鑰類型(pubprv分別)。這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。鍵之間的父子關係意味著它們在路徑中是相鄰的。

Apath是索引的 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 xprvmaster xpub。小m意味著這個擴展鍵是 a master xprv,而大Ma master xpub。按照前面的定義,您可以知道m是鍵 at 的父級0h,而鍵 at2h是它之前的鍵在 index 處的子級1。為了更容易理解,{a..e}如果我們的意思是這些是xprvs 和{A..B}if xpubs,我們將用字母註釋路徑中的不同鍵。

m / 0h / 1 / 2h / 2 / 1000000000
m   a    b   c    d   e

路徑通常以 base10 為索引,但在密鑰本身中,它們以十六進制 (base16) 編碼,因此 a[ key index ]始終為 4 個字節,如果需要,可以在前面加上零。主密鑰的depthindex都始終為零,因此0000000000,它們可以分別達到最大值FFFFFFFFFF。所以manda是 parent 和 child, andd也是edepthofb02,它的索引是00000001,而ofdepthc03而它的索引是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都說xprvdepth在父子節點之間遞增,fingerprintatchash160您將從 at 的私鑰獲得的公鑰的b,並且b’sindex在範圍的第一個非硬化的一半,而c’ s 是第二個,硬化的一半。最後,對每個 s 的chain code和s 進行編碼。key``xprv

派生chain codeand keyfor cfromb是通過一個名為的過程完成的CKDpriv,這意味著xprv從父級派生一個子級xprv。在這個過程中,我們使用了chain codeand keyfrombindexfrom c。重要的一點是:我們只在從它的和導出它c 之後編碼。chain code``key``index

Anyxprv可用於在 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成為孩子的 ( chere) chain code,而左邊的 32 個字節用於“調整”,意思是“添加 mod n”到父母的密鑰,在這個例子中:

 8F6154A0A82D0F68B9E5B586EA66D951DAAA071BEBD390097CC516285C791A62
+
 3C6CB8D0F6A264C91EA8B5030FADAA8E538B020F0A387421A12DE9319DC93368
=
 CBCE0D719ECF7431D88E6A89FA1483E02E35092AF60C042B1DF2FF59FA424DCA   mod n
  • 我沒有00在此處的鍵中寫入前置字節,因為這只是添加數字,但是那些零字節對於散列函式非常重要,因此我特意將它們包含在其中。

現在我們已經有了c’schain codekey(以私鑰形式),我們希望實際編碼c它以使其成為可用的xprv. 要獲取fingerprintfrom ,我們需要知道fromb的公鑰。由於它是私鑰形式,因此我們必須進行乘法運算:key``b

CBCE0D719ECF7431D88E6A89FA1483E02E35092AF60C042B1DF2FF59FA424DCA * G
= 03501E454BF00751F24B1B489AA925215D66AF2234E3891C3B21A52BEDB3CD711C

hash160這個公鑰的 ,返回的雜湊是BEF5A2F9A56A94AAB12459F72AD9CF8CF19C7BBE。前四個字節是b指紋:BEF5A2F9。編碼其餘部分c很容易。xprv從我們派生一個孩子的魔法開始xprv,將深度增加b一,然後是fingerprint. Nextcindex編碼。我們派生了索引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

這兩種派生 childxprv的方法之間的區別是微妙但重要的。它使能,這是一個從CKDpub級派生子級的函式。 工作原理與 ’ 的非硬化推導幾乎相同,但它使用點加法進行推導,因此我們不是將整數相加來製作子私鑰,而是將點相加來製作子公鑰。請注意,在非硬化派生中,我們如何使用 的父節點的公共點,我們使用作為父私鑰的附加值來派生子私鑰,具體來說,我們派生了的私鑰。xpub xpub``CKDpub``CKDpriv``HMAC-SHA512``tweak``d

要理解CKDpub,首先了解另一個稱為 BIP32 的函式會有所幫助Neuter。其目的是將 a 轉換xprvxpub. 讓我們“執行”Neuter我們的xprv d. 我們將呼叫結果xpub DNeuter對 a 做了兩件事xprv: 1. 替換magicfrom 0488ADE4to 0488B21E(替換xprvxpub) 2.key用相同私鑰的公共點替換欄位中的私鑰

對於我們xprv d來說,公共點是:

0F479245FB19A38A1954C5C7C0EBAB2F9BDFD96A17563EF28A6A4B1A2A764EF4 * G
= 02E8445082A72F29B75CA48748A914DF60622A609CACFCE8ED0E35804560741D29

(這只是私鑰->公鑰的正常過程)

所以結果是:

D:

0488B21E
04
EE7AB90C
00000002
CFB71883F01676F587D023CC53A35BC7F88F724B1F8C2892AC1275AC822A3EDD
02E8445082A72F29B75CA48748A914DF60622A609CACFCE8ED0E35804560741D29

現在d是“絕育”的,D已對公鑰進行了編碼,但請查看chain codedepth和如何持久化fingerprintindexxpub D位於路徑中的相同位置xprv d。我們將使用chain codeand key(public key) for CKDpubCKDpriv與非硬化派生相同,但對於CKDpriv,我們使用以下方式派生子私鑰:

tweak + (parent private key) = child private key

因為CKDpub我們將使用:

tweak*G + (parent public key) = child public key

這行得通,因為parent public keyis 真的是 just (parent private key)*G,而且child public keyis just (child private key)*G。也就是說,如果我們採用CKDpriv調整方程並將所有元素乘以G,我們就得到了CKDpub調整方程。 CKDpub只能派生xpub非硬化索引範圍內的子鍵。這是因為 parent 中存在的資訊xpub,特別是 中的公鑰[ key ],僅適用於非強化範圍。在CKDpriv我們可以使用私鑰知道公鑰的地方,我們不能走其他路。HMAC-SHA512使用公鑰的輪次CKDpriv適用於非硬化索引範圍。

現在我們已經d創建了 xpub D,路徑中的下一個是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 種不同的派生方法,兩種使用私鑰,一種使用公鑰:

  1. CKDpriv``xprv在硬化索引處派生一個孩子
  2. CKDpriv``xprv在非硬索引處派生一個孩子
  3. CKDpub``xpub在非硬化索引處派生一個孩子

我也很難理解那裡的東西。這是我的理解:

一個簡單的見解是,對於擴展私鑰推導和擴展公鑰推導,它們都具有相同的輸入(父公鑰和父鏈節點)。結果,它們將具有相同的左 256 位和右 256 位。

不同的是,

對於擴展的私鑰推導,左邊的 256 位被添加到父私鑰以產生子私鑰。

而對於擴展公鑰推導,左邊的 256 位被添加到父公鑰以產生子公鑰。

兩種擴展密鑰推導都使用其右 256 位輸出作為鏈節點,因此鏈節點對於兩種推導都是相同的。

對於硬化密鑰派生,輸入與上述擴展密鑰派生不同。

它不使用父公鑰,而是使用帶有鏈節點的父私鑰作為輸入,這將產生不同的 512 位輸出。所以作為鏈節點的右 256 位輸出與上述機制不同。

由於鏈節點不同,黑客無法利用鏈節點從擴展推導機制推導出私鑰。

引用自:https://bitcoin.stackexchange.com/questions/62533