4. BIP39、BIP44、BIP32 协议¶
4.1. HD 钱包导入流程¶
4.2. BIP39¶
熵每次都可以得到不同的助记词
CS = ENT /32
checksum = SHA256(entropy)
MS = (ENT + CS) / 11
ENT:熵长度。CS:校验长度。MS:助记词长度。checksum:校验和
校验和: 对熵取SHA256哈希结果前CS位
ENT | CS | ENT+CS | MS |
---|---|---|---|
128 | 4 | 132 | 12 |
160 | 5 | 165 | 15 |
192 | 6 | 198 | 18 |
224 | 7 | 231 | 21 |
256 | 8 | 264 | 24 |
4.2.1. 创建助记词¶
创建一个128到256位的随机序列(熵)。
提出SHA256哈希前几位(熵长/ 32),就可以创造一个随机序列的校验和。
将校验和添加到随机序列的末尾。
将序列划分为包含11位的不同部分。
将每个包含11位部分的值与一个已经预先定义2048个单词的字典做对应。
生成的有顺序的单词组就是助记码。
4.2.2. 助记词生成种子(seed)¶
创建助记词之后的7-9步是:
PBKDF2密钥延伸函数的第一个参数是从步骤6生成的助记符。
PBKDF2密钥延伸函数的第二个参数是盐。 由字符串常数“助记词”与可选的用户提供的密码字符串连接组成。
PBKDF2使用HMAC-SHA512算法,使用2048次哈希来延伸助记符和盐参数,产生一个512位的值作为其最终输出。 这个512位的值就是种子。
图5-7显示了从助记词如何生成种子
4.3. BIP32¶
4.3.1. 分层路径¶
4.3.2. 从 Seed 创建 HDWallet¶
HD钱包从单个根种子(root seed)中创建,为128到256位的随机数。常见的通过助记词可以确定种子Root Seed。
HD钱包的所有的确定性都衍生自这个根种子。任何兼容HD 钱包的根种子也可重新创造整个HD钱包。
子私钥推导 HD 钱包使用 CKD(child key derivation) 函数从父密钥(parent keys)推导子密钥(child keys),CKD 由下列三个要素做单向散列哈希(one way hash function) 。父密钥 (没有压缩过的椭圆曲线推导的私钥或公钥 ECDSA uncompressed key)
链码作为熵 (chain code 256 bits)
子代索引序号 (index 32 bits)
钱包安全的核心在私钥,而公钥则比较容易被找到,如果子节点生成过程只依赖父节点公钥和子节点序号,那么黑客拿到父节点公钥之后就能复原出所有子节点、孙节点的公钥,这样就会破坏隐私性,CKD 里面引入的 Chain Code 则是在整个子节点派生过程中引入确定的随机数,为 HD 钱包的隐私性增加了一重保障。
4.3.2.1. 正常衍生子密钥¶
4.3.2.2. 强化衍生子密钥¶
图上标识了 索引号码 根据 正常衍生 和 硬化衍生不同,索引的范围不同,对于正常衍生的索引号范围为 [0x0, 0x7FFFFFFF],而硬化衍生的索引号范围为 [0x80000000, 0xFFFFFFFF]
硬化衍生的索引号太长,一般为了便于阅读,都是会将索引号右上角加上撇号,譬如:0x80000000 记为 0’,0x80000001 记为 1’,以此类推.
针对 扩展密钥 的学习,可以看到有三种生成规则,分别如下:
Private parent key -> private child key
即,从 父私钥 和 父链码 计算生成 子私钥 和 子链码。用公式表示就是:
CKDpriv((kpar, cpar), i) → (ki, ci)
检查 是否 i ≥ 2^31(子私钥)。
如果是(硬化的子密钥):让I= HMAC-SHA512(Key = cpar,Data = 0x00 || ser256(kpar)|| ser32(i))(注意:0x00将私钥补齐到33字节长。)
如果不是(普通的普通子密钥):让I= HMAC-SHA512(Key = cpar,Data = serP(point(kpar))|| ser32(i))。将I分为两个32字节序列,IL和IR。
返回的子密钥ki是parse256(IL)+ kpar(mod n)。
返回的链码ci是IR。
如果parse256(IL)≥n或ki = 0,则生成的密钥无效,并且应继续下一个i值。 (注:概率低于1/2127)
Public parent key -> public child key
即,从 父公钥 和 父链码 计算生成 子公钥 和 子链码。公式表示如下:
CKDpub((Kpar, cpar), i) → (Ki, ci)
检查是否 i ≥ 2^31 (子密钥是否是硬化密钥)
如果是(硬化子密钥):返回失败
如果不是(普通子密钥):让I= HMAC-SHA512(Key = cpar, Data = serP(Kpar) || ser32(i)).将I分为两个32字节序列,IL和IR。
返回的子密钥Ki是point(parse256(IL))+ Kpar。
返回的链码ci是IR。
如果parse256(IL)≥n或Ki是无限远的点,则生成的密钥无效,并且应继续下一个i值。
Private parent key -> public child key
即,从 父私钥 和 父链码 计算生成 子公钥 和 子链码。公式表示如下:
N((k, c)) → (K, c)
返回的密钥K是point(k)。
返回的链码c只是传递的链码。
要计算父私钥的公用子密钥:
N(CKDpriv((kpar,cpar),i))(总是工作)。
CKDpub(N(kpar,cpar),i)(仅适用于非硬化子密钥)。
它们等价的事实是使非硬化密钥有用(可以在不知道任何私钥的情况下导出给定父密钥的子公钥),以及它们与硬密钥的区别。 不总是使用非硬化键(更有用)的原因是安全性; 后面可以了解更详细的信息。
4.4. BIP44¶
BIP32路径中的5个层次定义:
m / purpose' / coin_type' / account' / change / address_index
路径中的撇号表示使用了经过硬化(hardern)处理的BIP32派生。
purporse': 固定值44'(或0x8000002C), 代表是BIP44
coin_type': 这个代表的是币种, 可以兼容很多种币, 比如BTC是0', ETH是60'
硬化的派生在这个级别上使用。(所以右上角有 ' 标记)币种,代表一个主节点(种子)可用于无限数量的独立加密币,如比特币等。此级别为每个加密币创建一个单独的子树,避免重用已经在其它链上存在的地址。开发人员可以为他们的项目注册未使用的号码Account: 一类coin下能够有多个账户,就相当于你的人民币会存在多张银行卡 以顺序递增的方式从索引0开始编号。这个数字在BIP32派生中用作子索引。
Change: 表明为外部链0或内部链1:常量0用于外部链,常量1用于内部链(也称为更改地址)。外部链用于在钱包外部可见的地址(例如用于接收付款)。内部链用于不在钱包外部可见的地址,用于返回交易更改.一个是用来接收地址一个是用来创造找零地址
Index: 被HD钱包衍生的真正可用的地址是第四层级的子级,就是第五层级的树的“address_index”。比如,第三个层级的主账户如M/44'/0'/1'收到比特币真正支付的地址是 M/44'/0'/0'/0/2
最后我们简要介绍一下 BIP44 中的账户发现算法:
推导出第一个账户节点(生成编号初始值设为 0)
推导出这个账户的外部链
依次扫描外部链上的地址,如果连续二十个地址都没有交易记录,停止扫描
如果外部链上没有发现交易,退出
如果存在交易,将生成编号的值增加 1,跳转到第一步
一句话概括下BIP44就是:给BIP32的分层路径定义规范
4.5. reference¶
Ethereum HD Wallet(虚拟货币钱包)-BIP32、BIP39、BIP44
理解开发HD 钱包涉及的BIP32、BIP44、BIP39
基于 BIP-32 和 BIP-39 规范生成 HD 钱包(分层确定性钱包)
分层确定性钱包 HD Wallet 剖析:设计和实现