HD錢包從單個根種子(root seed)中創建,為128到256位的隨機數。
HD錢包的所有的確定性都衍生自這個根種子。任何兼容HD 錢包的根種子也可重新創造整個HD錢包。所以簡單的轉移HD錢包的根種子就讓HD錢包中所包含的成千上百萬的密鑰被復制,儲存導出以及導入。
下圖展示創建主密鑰以及HD錢包的主鏈代碼的過程。
根種子輸入到HMAC-SHA512算法中就可以得到一個可用來創造主私鑰(m)(master private key(m) )和主鏈代碼(a master chain code)的哈希。主私鑰(m)之后可以通過使用我們在本章先前看到的那個普通橢圓曲線m * G過程生來成相對應的主公鑰(M)。 鏈代碼用于從母密鑰中創造子密鑰的那個函數中引入熵。如下一節所示。
1. 私有子密鑰的衍生
分層確定性錢包使用CKD(child key derivation)函數去從母密鑰衍生出子密鑰。
子密鑰衍生函數是基于單項哈希函數。這個函數結合了:
一個母私鑰或者公共鑰匙(ECDSA未壓縮鍵)
一個叫做鏈碼(256 bits)的種子
一個索引號(32 bits)
鏈碼是用來給這個過程引入確定性隨機數據的,使得索引不能充分衍生其他的子密鑰。因此,有了子密鑰并不能讓它發現自己的姊妹密鑰,除非你已經有了鏈碼。最初的鏈碼種子(在密碼樹的根部)是用隨機數據構成的,隨后鏈碼從各自的母鏈碼中衍生出來。
這三個項目(母私鑰,鏈碼,索引)相結合并散列可以生成子密鑰,如下。
母公共鑰匙——鏈碼——以及索引號合并在一起并且用HMAC-SHA512函數散列之后可以產生512位的散列。所得的散列可被拆分為兩部分。散列右半部分的256位產出可以給子鏈當鏈碼。左半部分256位散列以及索引碼被加載在母私鑰上來衍生子私鑰。在圖2中,我們看到這個說明——索引集被設為0去生產母密鑰的第0個子密鑰(第一個通過索 引)。
改變索引可以讓我們延長母密鑰以及創造序列中的其他子密鑰。比如子0,子1,子2等等。每一個母密鑰可以有2,147,483,647 (2^31) 個子密鑰。2^31是整個2^32范圍可用的一半,因為另一半是為特定類型的推導而保留的,我們將在本章稍后討論。
向密碼樹下一層重復這個過程,每個子密鑰可以依次成為母密鑰繼續創造它自己的子密鑰,直到無限代。
2. 使用衍生的子密鑰
子私鑰不能從非確定性(隨機)密鑰中被區分出來。因為衍生函數是單向的,所以子密鑰不能被用來發現他們的母密鑰。子密鑰也不能用來發現他們的相同層級的姊妹密鑰。如果你有第n個子密鑰,你不能發現它前面的(第n-1)或者 后面的子密鑰(n+1)或者在同一順序中的其他子密鑰。只有母密鑰以及鏈碼才能得到所有的子密鑰。沒有子鏈碼的話,子密鑰也不能用來衍生出任何孫密鑰。你需要同時有子密鑰以及對應的鏈碼才能創建一個新的分支來衍生出孫密鑰。
那子私鑰自己可被用做什么呢?它可以用來做公鑰和比特幣地址。之后它就可以被用在那個地址來簽署交易和支付任何東西。
提示子私鑰、對應的公鑰以及比特幣地址都不能從隨機創造的密鑰和地址中被區分出來。事實是它們所在的序列,在創造他們的HD錢包函數之外是不可見的。一旦被創造出來,它們就和“正常”密鑰一樣運行了。
3. 擴展密鑰
正如我們之前看到的,密鑰衍生函數可以被用來創造密鑰樹上任何層級的子密鑰。這只需要三個輸入量:一個密鑰,一個鏈碼以及想要的子密鑰的索引。密鑰以及鏈碼這兩個重要的部分被結合之后,就叫做擴展密鑰(extended key)。術語“extended key”也被認為是“可擴展的密鑰”,因為這種密鑰可以用來衍生子密鑰。
擴展密鑰可以簡單地被儲存并且表示為簡單的將256位密鑰與256位鏈碼所并聯的512位序列。有兩種擴展密鑰。擴展的私鑰是私鑰以及鏈碼的結合。它可被用來衍生子私鑰(子私鑰可以衍生子公鑰)。公鑰以及鏈碼組成擴展公鑰,它可以用來擴展子公鑰,見“生成公鑰”章節。
想象一個擴展密鑰作為HD錢包中密鑰樹結構的一個分支的根。你可以衍生出這個分支的剩下所有部分。擴展私鑰可以創建一個完整的分支,而擴展公鑰只能夠創造一個公鑰的分支。
提示一個擴展密鑰包括一個私鑰(或者公鑰)以及一個鏈碼。一個擴展密鑰可以創造出子密鑰并且能創造出密鑰樹結構中的整個分支。分享擴展密鑰就可以訪問整個分支。
擴展密鑰通過Base58Check來編碼,從而能輕易地在不同的BIP-32兼容錢包之間導入導出。擴展密鑰編碼用的 Base58Check使用特殊的版本號,這導致在Base58編碼字符中,出現前綴“xprv”和“xpub”。這種前綴可以讓編碼更易被識別。因為擴展密鑰是512或者513位,所以它比我們之前所看到的Base58Check編碼串更長一些。
以下面的擴展私鑰為例,其使用的是Base58Check編碼:
xprv9tyUQV64JT5qs3RSTJkXCWKMyUgoQp7F3hA1xzG6ZGu6u6Q9VMNjGr67Lctvy5P8oyaYAL9CAWrUE9i6GoNMKUga5biW6Hx4tws2six3b9c
這是上面擴展私鑰對應的擴展公鑰,同樣使用Base58Check編碼:
xpub67xpozcx8pe95XVuZLHXZeG6XWXHpGq6Qv5cmNfi7cS5mtjJ2tgypeQbBs2UAR6KECeeMVKZBPLrtJunSDMstweyLXhRgPxdp14sk9tJPW9
4. 公共子密鑰推導
正如之前提到的,分層確定性錢包的一個很有用的特點就是可以不通過私鑰而直接從公共母密鑰派生出公共子密鑰的能 力。這就給了我們兩種衍生子公鑰的方法:或者通過子私鑰,再或者就是直接通過母公鑰。
因此,擴展密鑰可以在HD錢包結構的分支中,被用來衍生所有的公鑰(且只有公鑰)。
這種快捷方式可以用來創造非常保密的只有公鑰配置。在配置中,服務器或者應用程序不管有沒有私鑰,都可以有擴展公鑰的副本。這種配置可以創造出無限數量的公鑰以及比特幣地址。但是發送到這個地址里的任何比特幣都不能使用。與此同時,在另一種更保險的服務器上,擴展私鑰可以衍生出所有的對應的可簽署交易以及花錢的私鑰。
這種方案的常見應用是安裝擴展公鑰電商的網絡服務器上。網絡服務器可以使用這個公鑰衍生函數去給每一筆交易(比如客戶的購物車)創造一個新的比特幣地址。但為了避免被偷,網絡服務商不會有任何私鑰。沒有HD錢包的話,唯一的方法就是在不同的安全服務器上創造成千上萬個比特幣地址,之后就提前上傳到電商服務器上。這種方法比較繁瑣而且要求持續的維護來確保電商服務器不“用光”公鑰。
這種解決方案的另一種常見的應用是冷藏或者硬件錢包。在這種情況下,擴展的私鑰可以被儲存在紙質錢包中或者硬件設備中(比如 Trezor 硬件錢包),與此同時擴展公鑰可以在線保存。使用者可以根據意愿創造“接收”地址而私鑰可以安全地在線下被保存。為了支付資金,使用者可以使用擴展的私鑰離線簽署比特幣客戶或者通過硬件錢包設備(比如 Trezor)簽署交易。圖3闡述了擴展母公鑰來衍生子公鑰的傳遞機制。
5. 在網店中使用擴展公鑰(xpub)
繼續Gabriel網店的故事,讓我們看看Gabriel是如何使用HD錢包。
Gabriel在一個網絡上的托管服務器上建立一個簡單的WordPress頁面,作為他的網上商店。它的網店非常簡單,只有幾個頁面和一張帶有一個比特幣地址的訂單。
Gabriel使用他的Trezor設備生成的第一個比特幣地址作為他的商店的主要比特幣地址。這樣,所有收到的付款都將支付給他的Trezor硬件錢包所控制的地址。
客戶可以使用表格提交訂單,并向Gabriel發布的比特幣地址發送付款,觸發一封電子郵件,其中包含Gabriel的訂單詳細信息。每周只幾個訂單,這個系統運行得很好。
然而,這個小型網絡商店變得相當成功,并吸引了很多來自當地社區的訂單。Gabriel很快就不堪重負。由于所有訂單都支付相同的地址,因此很難正確匹配訂單和交易,特別是當同一數量的多個訂單緊密相連時。
HD錢包可以在不知道私鑰的情況下獲取公共子密鑰,該能力為Gabriel提供了更好的解決方案。 Gabriel可以在他的網站上加載一個擴展公鑰(xpub),這可以用于為每個客戶訂單導出唯一的地址。Gabriel可以花費他在Trezor里資金,但加載在網站上的xpub只能生成地址并收到資金。HD錢包的這個功能非常安全。 Gabriel的網站不包含任何私鑰,因此不需要高級別的安全性。
為了導出xpub,Gabriel將基于Web的軟件與Trezor硬件錢包配合使用。必須插入Trezor設備才能導出公鑰。請注意,硬件錢包永遠不會導出私鑰,這些密鑰始終保留在設備上。下圖顯示了Gabriel用于導出xpub的Web界面。
Gabriel將xpub復制到他的網店的比特幣購物軟件中。 他使用的軟件是Mycelium Gear,它是一個網店的開源插件,用于各種網絡托管和內容平臺。 Mycelium Gear使用xpub為每次購買生成一個唯一的地址。
6. 硬化子密鑰的衍生
從擴展公鑰衍生一個分支公鑰的能力是很重要的,但牽扯一些風險。訪問擴展公鑰并不能得到訪問子私鑰的途徑。但是,因為擴展公鑰包含有鏈碼,如果子私鑰被知道或者被泄漏的話,鏈碼就可以被用來衍生所有的其他子私鑰。簡單地泄露的私鑰以及一個母鏈碼,可以暴露所有的子密鑰。更糟糕的是,子私鑰與母鏈碼可以用來推斷母私鑰。
為了應對這種風險,HD錢包使用一種叫做硬化衍生(hardened derivation)的替代衍生函數。這就“打破”了母公鑰以及子鏈碼之間的關系。這個硬化衍生函數使用了母私鑰去推導子鏈碼,而不是母公鑰。這就在母/子順序中創造了一道“防火墻”——有鏈碼但并不能夠用來推算子鏈碼或者姊妹私鑰。強化衍生函數看起來幾乎與一般的衍生的子私鑰相同,不同的是母私鑰被用來輸入散列函數中而不是母公鑰,如圖所示。
當強化私鑰衍生函數被使用時,得到的子私鑰以及鏈碼與使用一般衍生函數所得到的結果完全不同。得到的密鑰“分支”可以被用來生產不易被攻擊的擴展公鑰,因為它所含的鏈碼不能被用來開發或者暴露任何私鑰。強化衍生也因此被用在上一層級,使用擴展公鑰的密鑰樹中創造“間隙”。
簡單地來說,如果你想要利用擴展公鑰的便捷來衍生公鑰的分支而不將你自己暴露在泄露擴展鏈碼的風險下, 你應該從強化母私鑰衍生公鑰,而不是從一般的母私鑰來衍生。最好的方式是,為了避免了推到出主密鑰,主密鑰所衍生的第一層級的子密鑰最好使用強化衍生。
7. 正常衍生和強化衍生的索引號碼
用在衍生函數中的索引號碼是32位的整數。為了區分密鑰是從正常衍生函數中衍生出來還是從強化衍生函數中產出,這個索引號被分為兩個范圍。索引號在0和2^31–1(0x0 to 0x7FFFFFFF)之間的是只被用在常規衍生。索引號在2^31和2^32– 1(0x80000000 to 0xFFFFFFFF)之間的只被用在強化衍生。因此,索引號小于2^31就意味著子密鑰是常規的,而大于或者等于2^31的子密鑰就是強化型的。
為了讓索引號碼更容易被閱讀和展示,強化子密鑰的索引號碼是從0開始展示的,但是右上角有一個小撇號。第一個常規子密鑰因此被表述為0,但是第一個強化子密鑰(索引號為0x80000000)就被表示為0‘。第二個強化密鑰依序有了索 引號0x80000001,且被顯示為1’,以此類推。當你看到HD錢包索引號i‘,這就意味著 2^31+i。
8. HD錢包密鑰識別符(路徑)
HD錢包中的密鑰是用“路徑”命名的,且每個級別之間用斜杠(/)字符來表示。由主私鑰衍生出的私鑰起始以“m”打頭。由主公鑰衍生的公鑰起始以“M“打頭。因此,母密鑰生成的第一個子私鑰是m/0。第一個公鑰是M/0。第一個子密鑰的子密鑰就是m/0/1,以此類推。
密鑰的“祖先”是從右向左讀,直到你達到了衍生出的它的主密鑰。舉個例子,標識符m/x/y/z描述的是子密鑰m/x/y的第z個子密鑰。而子密鑰m/x/y又是m/x的第y個子密鑰。m/x又是m的第x個子密鑰。
9. HD錢包樹狀結構的導航
HD錢包樹狀結構提供了極大的靈活性。每一個母擴展密鑰有40億個子密鑰:20億個常規子密鑰和20億個強化子密鑰。 而每個子密鑰又會有40億個子密鑰并且以此類推。只要你愿意,這個樹結構可以無限類推到無窮代。但是,又由于有了這個靈活性,對無限的樹狀結構進行導航就變得異常困難。尤其是對于在不同的HD錢包之間進行轉移交易,因為內部組織到內部分支以及亞分支的可能性是無窮的。
兩個比特幣改進建議(BIPs)提供了這個復雜問題的解決辦法——通過創建幾個HD錢包樹的提議標準。BIP-43提出使用第一個強化子索引作為特殊的標識符表示樹狀結構的“purpose”。基于BIP-43,HD錢包應該使用且只用第一層級的樹的分支,而且有索引號碼去識別結構并且有命名空間來定義剩余的樹的目的地。舉個例子,HD錢包只使用分支m/i’/是 為了表明那個被索引號“i”定義的特殊為目地。
在BIP-43標準下,為了延長的那個特殊規范,BIP-44提議了多賬戶結構作為“purpose”。所有遵循BIP-44的HD錢包依據只使用樹的第一個分支的要求而被定義:m/44‘/。 BIP-44指定了包含5個預定義樹狀層級的結構:
m / purpose’ / coin_type‘ / account’ / change / address_index
第一層的purpose總是被設定為44‘。
第二層的“coin_type”特指幣種并且允許多元貨幣HD錢包中的貨幣在第二個層級下有自己的亞樹狀結構。目前有三種貨幣被定義:Bitcoin is m/44’/0‘、Bitcoin Testnet is m/44’/1‘,以及 Litecoin is m/44’/2‘。
樹的第三層級是“account”,這可以允許使用者為了會計或者組織目的,而去再細分他們的錢包到獨立的邏輯性亞賬戶。 舉個例子,一個HD錢包可能包含兩個比特幣“賬戶”:m/44’/0‘/0’ 和 m/44‘/0’/1‘。每個賬戶都是它自己亞樹的根。
第四層級就是“change”。每一個HD錢包有兩個亞樹,一個是用來接收地址一個是用來創造找零地址。注意無論先前的層級是否使用強化衍生,這一層級使用的都是常規衍生。這是為了允許這一層級的樹可以在不安全環境下,輸出擴展公鑰。
被HD錢包衍生的可用的地址是第四層級的子級,就是第五層級的樹的“address_index”。比如,第三個層級的主賬戶收到比特幣支付的地址就是 M/44’/0‘/0’/0/2。表5-7展示了更多的例子。
評論
查看更多