# BIP39、BIP44、BIP32 协议
## 流程
![](../../image/wallet/wallet-BIP.png)
## BIP39
熵每次都可以得到不同的助记词
```math
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 |
### 创建助记词
1. 创建一个128到256位的随机序列(熵)。
2. 提出SHA256哈希前几位(熵长/ 32),就可以创造一个随机序列的校验和。
3. 将校验和添加到随机序列的末尾。
4. 将序列划分为包含11位的不同部分。
5. 将每个包含11位部分的值与一个已经预先定义2048个单词的字典做对应。
6. 生成的有顺序的单词组就是助记码。
### 助记词生成种子(seed)
创建助记词之后的7-9步是:
7. PBKDF2密钥延伸函数的第一个参数是从步骤6生成的助记符。
8. PBKDF2密钥延伸函数的第二个参数是盐。 由字符串常数“助记词”与可选的用户提供的密码字符串连接组成。
9. PBKDF2使用HMAC-SHA512算法,使用2048次哈希来延伸助记符和盐参数,产生一个512位的值作为其最终输出。 这个512位的值就是种子。
图5-7显示了从助记词如何生成种子
## BIP32
### 分层路径
### 从 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 钱包的隐私性增加了一重保障。
#### 正常衍生子密钥
#### 强化衍生子密钥
- 图上标识了 索引号码 根据 正常衍生 和 硬化衍生不同,索引的范围不同,对于正常衍生的索引号范围为 [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)(仅适用于非硬化子密钥)。
它们等价的事实是使非硬化密钥有用(可以在不知道任何私钥的情况下导出给定父密钥的子公钥),以及它们与硬密钥的区别。 不总是使用非硬化键(更有用)的原因是安全性; 后面可以了解更详细的信息。
### 参考如下:
[基于 BIP-32 和 BIP-39 规范生成 HD 钱包(分层确定性钱包)](https://stevenocean.github.io/2018/09/23/generate-hd-wallet-by-bip39.html)
[分层确定性钱包 HD Wallet 剖析:设计和实现](https://www.arcblock.io/zh/post/2018/12/01/hd-wallets-design-and-implementation)
## BIP44
BIP32路径中的5个层次定义:
```
m / purpose' / coin_type' / account' / change / address_index
```
路径中的撇号表示使用了经过硬化(hardern)处理的BIP32派生。
1. purporse': 固定值44'(或0x8000002C), 代表是BIP44
2. coin_type': 这个代表的是币种, 可以兼容很多种币, 比如BTC是0', ETH是60'
硬化的派生在这个级别上使用。(所以右上角有 ' 标记)币种,代表一个主节点(种子)可用于无限数量的独立加密币,如比特币等。此级别为每个加密币创建一个单独的子树,避免重用已经在其它链上存在的地址。开发人员可以为他们的项目注册未使用的号码
3. Account: 一类coin下能够有多个账户,就相当于你的人民币会存在多张银行卡 以顺序递增的方式从索引0开始编号。这个数字在BIP32派生中用作子索引。
4. Change: 表明为外部链0或内部链1:常量0用于外部链,常量1用于内部链(也称为更改地址)。外部链用于在钱包外部可见的地址(例如用于接收付款)。内部链用于不在钱包外部可见的地址,用于返回交易更改.**一个是用来接收地址一个是用来创造找零地址**
5. Index: 被HD钱包衍生的真正可用的地址是第四层级的子级,就是第五层级的树的“address_index”。比如,第三个层级的主账户如M/44'/0'/1'收到比特币真正支付的地址是 M/44'/0'/0'/0/2
最后我们简要介绍一下 BIP44 中的账户发现算法:
1. 推导出第一个账户节点(生成编号初始值设为 0)
2. 推导出这个账户的外部链
3. 依次扫描外部链上的地址,如果连续二十个地址都没有交易记录,停止扫描
4. 如果外部链上没有发现交易,退出
5. 如果存在交易,将生成编号的值增加 1,跳转到第一步
一句话概括下BIP44就是:给BIP32的分层路径定义规范
参考如下:
[Ethereum HD Wallet(虚拟货币钱包)-BIP32、BIP39、BIP44](https://www.cnblogs.com/wanghui-garcia/p/9970735.html)
[理解开发HD 钱包涉及的BIP32、BIP44、BIP39](https://learnblockchain.cn/2018/09/28/hdwallet/)