HD錢包(Hierarchical Deterministic walle,簡稱 HD錢包)是目前常用的層級確定性錢包,能夠通過助記詞推導出一些列密鑰,可以方便用戶備份錢包。
經過一段時間對 HD 錢包的了解,希望通過本篇文章可以為大家梳理一下 HD 錢包的推導密鑰流程。首先說明一下 HD錢包
推導密鑰的主要流程為:
1. 獲取助記詞
2. 根據助記詞推導種子
3. 根據種子推導主密鑰
4. 根據父密鑰推導子密鑰。(主密鑰是首個父密鑰)
5. ... (按照第 4 步依次推導)
標准規則
HD 錢包的私鑰推導流程主要借助於幾個標准規則,有 BIP32,BIP39,BIP44。
BIP32
:該標准定義了,通過一個種子來維護多個私鑰的樹狀結構方式。HD錢包就是依據該標准。
BIP39
:定義了錢包的助記詞和種子的生成規則。
BIP44
:在 BIP32 的基礎上,給予樹狀結構的每一層一個特殊的意義。可以讓統一種子支援多個幣種,多個賬戶。
算法與函數
推導過程中使用到的算法,簡單了解一下。
SHA256
:獲取熵校驗和的算法
PBKDF2
:根據助記詞推算種子的算法。其內部使用了 HMAC-SHA512
算法(2048 次循環)。
HMAC-SHA512
:生成種子時使用,以及推導主密鑰,由父密鑰推導子密鑰時使用。
橢圓曲線算法
:私鑰推導公鑰的算法。
CKD函數
(child key derivation):從父密鑰推導普通子密鑰時使用的函數。
HKD函數
(hardened key derivation formula):由父密鑰推導增強子密鑰時的函數。
1. 獲取助記詞
獲取助記詞是 HD錢包
的使用的第一步,有了助記詞才能推導種子以及推導后面的密鑰操作。
生成助記詞的主要步驟為:
1. 生成一個長度為 128 ~ 256 位的隨機序列(后面稱為熵;
- 熵的取值長度需要為 32 的整數倍的值。所以取值可能為:【128, 160, 192, 224, 256】;
- 目前我們 APP 使用的是 128 位。
2. 獲取熵的校驗和;
- 取熵哈希后的前 n 位作為校驗和(n = 熵長度/32)。
- 校驗和的理論值為【4,5,6,7,8】,因為熵的取值范圍為 128 ~ 256。
-
3. 生成新的序列;
- 新的序列方式為 = 熵 + 校驗和
-
4. 獲得 m 個 11 位二進制數。
- 將新生成的序列,按照 11 位進行平分。
- 為什么是 11 位?助記詞庫總共有 2048 個單詞,2^11 正好是 2048。所以用 11 位二進制數剛好可以將所有的助記詞定位。
- 新序列的長度取值范圍為:【128+4,160+5,192+6,224+7,256+8】即【132,165,198,231,264】
- m 的取值范圍為:【12,15,18,21,24】
-
5. 獲得 m 個助記詞;
- 根據第四步得到的 m 個 11位二進制數,去助記詞表定位每一個助記詞。
熵、校驗和、助記詞個數關系如下表所示
熵(bits) | 校驗和(bits) | 熵+校驗和(bits) | 助記詞個數 |
---|---|---|---|
128 | 4 | 132 | 12 |
160 | 5 | 165 | 15 |
192 | 6 | 198 | 18 |
224 | 7 | 231 | 21 |
256 | 8 | 264 | 24 |
簡單說流程為:
1. 生成隨機序列熵。(128 ~ 256 位隨機序列,而且為 32 的倍數)
2. 生成新的隨機序列。 (熵 + 哈希(熵)/32)
3. 獲取 m 個助記詞對應序號。m = (熵 + 哈希(熵)/32) / 11
4. 分別獲取對應的助記詞。
獲取助記詞流程圖如下:
2. 根據助記詞推導種子(seed)
了解了第一步,就知道如何生成助記詞,有了助記詞就可以進行推導種子了。
通過助記詞生成種子,需要使用 PBKDF2算法
來生成。PBKDF2算法
具體如下:
參數 1:mnemonic: 助記詞
參數 2:salt: 鹽,目的是為了增加算法難度
具體算法為:
采用 HMAC-SHA512 算法,進行 2048 次哈希來延伸助記詞和鹽參數。
產生一個 512 位的最終輸出,這個 512 位輸出就是種子(seed)
seed生成算法如圖所示:
- ⑦:就是助記詞。PBKDF2 的算法參數
- ⑧:salt。PBKDF2 算法參數,目的是為了增加算法難度。
- ⑨:使用sha512算法,進行 2048 次 hash
- 結果:產成一個 512 bit 的種子。
3. 根據種子推導主密鑰和主鏈碼
經過前面幾步,了解到了如何獲取助記詞,以及如何通過助記詞推導種子流程。接下來開始推導主密鑰。
推導主密鑰的方法為,從種子通過 HMAC-SHA512
算法計算一次,就會生成一個 512 位的序列。將其從中間分開,左側 256 位為主私鑰
,右側 256 位為主鏈碼
。
- 主私鑰:可以推導主公鑰,也可以進行推導子密鑰等
- 鏈碼:用作推導子密鑰的熵。
推導主密鑰流程圖如下:
4. 根據父密鑰推導子密鑰
通過前面幾步,已經了解到從如何生成助記詞到如何創建主密鑰,接下來就需要了解如何推導子密鑰。
子密鑰是通過父密鑰來進行推導的,當然首個父密鑰就是主密鑰
。
推導子密鑰的方法將分三步來學習
- 子私鑰推導方式
- 子公鑰推導方式
- 增強子密鑰推導方式
4.1 子私鑰推導
子私鑰的推導方式,可以使用 CKD (child key derivation) 函數從父密鑰
推導出子密鑰。
CKD 函數為:CDKpriv((Kpar, Cpar), i) → (Ki, Ci)
具體推導步驟為:
- 通過
父私鑰
通過橢圓曲線算法推導出父公鑰
- 通過
父公鑰
、父鏈碼
、索引序號
通過HMAC-SHA512
函數進行一次算法。得到子私鑰
和子鏈碼
- 通過
子私鑰
通過橢圓曲線算法推導出子公鑰
。
索引序號決定了可以推導出子密鑰的個數,其取值范圍為:0 ~ 2^32 。其中索引號 0 ~ 2^31 - 1 推導出子密鑰的過程被稱為正常衍生
,索引號 2^31 ~ 2^32 - 1 推導出子密鑰的過程被稱為增強衍生
為什么引入鏈碼?
錢包安全的核心在私鑰,而公鑰則比較容易被找到,如果子節點生成過程只依賴父節點公鑰和子節點序號,那么黑客拿到父節點公鑰之后就能復原出所有子節點、孫節點的公鑰,這樣就會破壞隱私性,CKD 里面引入的 Chain Code 則是在整個子節點派生過程中引入確定的隨機數,為 HD 錢包的隱私性增加了一重保障。
流程圖如下:
4.2 擴展密鑰
在該推導過程中,父密鑰和鏈碼結合起來統稱為擴展密鑰
。即 256位的父密鑰+256的鏈碼結合
父私鑰和鏈碼結合起來稱為擴展私鑰
,擴展私鑰可用來推導子私鑰
,子私鑰可以繼續推導子公鑰
父公鑰和鏈碼結合起來稱為擴展公鑰
,擴展公鑰可用來推導子公鑰
擴展密鑰使用 Base58Check 來編碼。擴展私鑰編碼后前綴為xprv
,擴展公鑰編碼后前綴為xpub
。
4.3 子公鑰推導
通過 4.1 可以通過 CKD 函數配合父私鑰和鏈碼以及索引號推導出子私鑰,然后通過子私鑰推導子公鑰。
但是層級確定性錢包的特點就是可以不通過私鑰而直接通過公鑰派生出子公鑰的能力,可以增強其安全性。所以就有了兩種推導子公鑰的方式:1、通過子私鑰推導。2、通過父公鑰推導
子公鑰的推導過程和子私鑰推導過程流程基本一樣,差異之處為:
- 將私鑰推導過程替換為公鑰
- 使用子公鑰推導出其子鏈碼
子公鑰推導流程如下圖所示:
4.4 普通子密鑰鑰推導的安全問題
通過擴展公鑰推導出子公鑰的能力很重要,但是也會有一些風險。比如並不能得到訪問子私鑰的途徑。擴展公鑰擁有鏈碼,如果子私鑰被泄露,鏈碼就可以用來衍生其他所有的子密鑰。也就是說泄露了父鏈碼
和子私鑰
就會暴露所有的子密鑰,而且,子私鑰
和父鏈碼
可以推導出父私鑰
。如下圖:
4.4.1 疑問:怎么由子私鑰+父鏈碼推導出父私鑰?
在 CKD 的算法的源碼中,通過以上得知,父密鑰推導子密鑰有兩種方法:1、父私鑰 → 子私鑰,2、父公鑰 → 子公鑰。這兩種方法得到的子鏈碼是相同的。
其算法過程為
- 父公鑰 + 鏈碼 經過 HAMC-SHA512 算法得到 512 位結果。
其中對 512 結果進行從中間分開。左側256位和右側256位 - 子私鑰 = 左256位 + 父私鑰,子公鑰 = 左256位 + 父公鑰。
- 子鏈碼 = 右側 256 位
當某一個環節的子私鑰
泄露后。在加上擴展公鑰
近乎是公開的,是可以拿到的,通過擴展公鑰,可以得到完整的512位結果,就可以得到左256位。結合上述公式,也就可以從子私鑰推導出父私鑰了。流程為:
- 拿到擴展公鑰。(公開的)
- 進行 HAMC-SHA512 得到 512 位。
- 取左 256 位。
- 子私鑰泄露 !!!!!
- 父私鑰 = 子私鑰 - 左 256 位
4.5 增強子密鑰推導
為此HD 錢包使用了新的推導函數 HDK( hardened key derivation formula )增強密鑰推導。HDK 中通過父私鑰
去推導子鏈碼
,而不是父公鑰
。
流程圖如下:

HD 路徑 | 密鑰描述 |
---|---|
m/0 | 從主私鑰(m) 推導出的第一代的第一個子私鑰 |
m/0/0 | 從第一代子密鑰m(0)推導出的第二代第一個孫私鑰 |
m/0'/0 | 從第一代增強密鑰 (m/0')推導出得第二代第一個孫密鑰 |
m/1/0 | 從第一代的第二個子密鑰推導出的第二代第一個孫密鑰 |
BIP44 指定了 5 個預定義樹狀層級結構:
m / purpose' / coin_type' / account' / change / address_index
- m: 固定為主密鑰
- purpose: 固定 44' (或者 0x8000002C)貌似是代表 BIP44 的意思。
- coin_type: 代表幣種。比如:0代表 BTC,60 代表 ETH,194 代表 EOS。
- account: 代表這個幣的索引賬戶。從 0 開始
- change: 常量 0 或 1。
- 0 用於外部鏈。外部鏈:用於在錢包外可見的地址。(比如收款等,也是我們經常使用的)
- 1 用於內部鏈。內部鏈:用於在錢包外部不可見的地址,用於返回交易變更。
- address_index:地址索引號。代表生成的第幾個地址。官方建議,不要超過 20 個地址,第 0 個沒使用不建議使用第1個。
通過BIP44標准制定的路徑為:
例如:BTC:m / 44' / 0' / a' / 0 / n
例如:ETH:m / 44' / 60' / a' / 0 / n
例如:EOS:m / 44' / 194' / a'/ 0 / n
其中 a 就代表的是賬戶,n 代表的是第幾個索引值。