1. 首页 > 产业新闻 > 新能源

深入浅出 HD 钱包基本原理

28 日上午 9:00(美国太平洋时间 27 日下午 5:00),ArcBlock Technical Learning Series 第十期“深入浅出 HD 钱包基本原理”, 由 ArcBlock 资深前端工程师 王仕军 讲授。

了解区块链、或者持有数字货(比如比特币和以太坊)的同学可能都知道把数字货币存在自己的钱包,目前市面上的钱包应用非常多,有支持单链的、支持多链的,有手机 APP、有网页、有桌面客户端,还有浏览器插件如 MetaMask。绝大多数钱包应用在创建钱包时千叮咛万嘱咐让你做好备份的助记词是怎么回事儿?为什么这些钱包自称是 HD 钱包?为什么说泄露了助记词就丢失了所有的币?为什么用单个助记词可以产生、控制很多个账户?助记词谈到是怎么产生私钥的?安全性有没有保障?破解的难度谈到有多大?如果你有耐心读完本文,相信这些问题的答案都会了然于胸。废话少说,下面的我们就用层层递进、抽丝剥茧的方式去了解下分层确定性钱包的设计和实现。

分层确定性钱包常被简写成 HD Wallet,简写来源于 Hierarchical Deterministic Wallet,如果要彻底搞清楚什么是 Hierarchical Deterministic Wallet,从语言层面来看这个名词性短语:· Hierarchical 是形容词· Deterministic 是形容词· Wallet 是名词搞清楚每个词语在技术上的含义、设计动机,对于我们理解 HD 钱包非常有帮助。

确切的说,任何区块链钱包里面都没有币,里面有的只是私钥、公钥对,可能有点反常识,但是事实确实如此。钱包可以包含任意数量的私钥、公钥对,其中私钥可以用来签名交易,从而把这个私钥能控制的币花出去,但是币本身是存在区块链大账本上的。把区块链钱包理解为钥匙串可能更形象些,因为钥匙并不是你的资产,而是控制资产的凭据。

在技术性的区块链文章里面,私钥、公钥、地址都是有特定的符号表示的,如下图:

· k,私钥,通常是随机产生,需要绝对保密的· K,公钥,由私钥通过椭圆曲线乘法运算而来,不可倒推出私钥,可以不保密· A,地址,由公钥通过单向哈希运算而来,不可倒推出公钥,是完全公开的

比特币早期的钱包客户端 Satoshi Client 里面会自动随机生成 100 个私钥、公钥对,这些私钥之间完全没有关联,这种钱包也叫做随机钱包(Random Wallet)或者非确定性钱包(Non-Deterministic Wallet),钱包的备份和恢复必须针对每个私钥进行。

如果能随机产生一个种子,然后根据这个种子去生成一系列的私钥、公钥对,这样钱包的备份就会容易很多,因为只需要备份随机的种子就行了,这种根据随机种子按确定规则生成一系列钱包的方式就叫做种子钱包(Seeded Wallet)或确定性钱包(Deterministic Wallet),种子钱包在生成多个私钥时会用到序号作为参数,所以这种钱包也叫线性确定性钱包(Sequential Deterministic Wallet)。

种子钱包解决了备份的问题,但是还是不完美,没有办法把钱包的一部分共享出去给别人管理,但同时自己保有知情权、控制权。社区的智慧是无穷的,分层确定性钱包应运而生:

因为生成的钱包结构是有层次的,所以就被叫做 Hierarchical Deterministic Wallet:· 树状的钱包结构可以让钱包的组织方式更加灵活,或者赋予其现实世界的意义,比如可以用单个 HD 钱包来管理组织的所有资产;· 每个节点都会有私钥、公钥,也可以派生出更多的子节点;· 树状结构中的某个分支及其子树可以根据实际需要共享出去;· 备份和恢复只需要关心主节点;

现如今 HD 钱包俨然已经成为事实上的行业标准,知道 HD 钱包的含义之后,我们来看看他的设计和实现。HD 钱包的想法最早出现在比特币社区,而比特币社区里面提出新功能、流程、改进建议都有标准化的流程,发起者需要用文档的形式把内容书面化,提交给社区去讨论、论证,这种文档就叫做 BIP(Bitcoin Improvement Proposal),比特币社区甚至连 BIP 本身该讲解

工作也写成了 BIP,定义 BIP 格式、工作流的的元 BIP 见此(https://github/bitcoin/bips/blob/master/bip-0002.mediawiki),而和 HD 钱包紧密关联的几个 BIP 如下:· BIP32: HD 钱包的核心提案,说明了自私钥生成方法以及树壮结构的构造方式;· BIP43: 为 HD 钱包子私钥派生路径增加有广泛共识的段;· BIP44: 确定支持多链 HD 钱包子私钥派生路径的标准格式;我们先来看 BIP32,其中定义了如下两个内容:· 根据父节点公(私)钥匙派生子节点公(私)钥的算法;· 将派生出来的钥匙对组织成树状结构的方法;在 BIP32 中根据父节点去派生子节点的方法被称作 Child Key Derivation Function,简称为 CKD,CKD 根据如下 3 个参数去生成子节点:· 父节点私钥或者公钥(Parent Private/Public Key)· 父节点链码(Parent Chain Code)· 子节点序号(Child Index)为了保证生成过程不可逆,CKD 会用到单向哈希函数 HMAC-SHA512,整个 HD Wallet 树里面的任何节点都可以有私钥、公钥,都具有如下的性质:· 各节点的私钥和随机生成的私钥并没有明显的区分· 节点私钥可以用来推导出节点公钥,进而推导出账户地址· 节点私钥可以用来签名交易· 至于节点之间的父子、兄弟关系在 HD 钱包之外完全是无感的

根据父节点私钥(Parent Private Key)生成子节点私钥(Child Private Key)的流程如下图:

· 根据父节点私钥和椭圆曲线乘法推导出父节点公钥(Parent Public Key);· 把父节点公钥、父节点链码、子节点序号作为参数求 HMAC-SHA512 得到 512 位输出;· 把步骤 2 的输出拆分为两个等长的 256 位串,分别标记为 L、R;· 把步骤 3 的输出 L 和父节点私钥做运算得到子节点私钥(Child Private Key);· 把步骤 3 的输出 R 当做子节点链码(Child Chain Code);子节点私钥、子节点链码可以作为输入传给 CKD,就可以生成孙节点,以及任意深度的节点。子私钥生成函数在 BIP32 中被标记为:

根据父节点公钥生成子节点公钥的流程如下图:

· 把父节点公钥、父节点链码、子节点序号作为参数求 HMAC-SHA512 得到 512 位输出;· 把步骤 1 的输出拆分为两个等长的 256 位串,分别标记为 L、R;· 把步骤 2 的输出 L 和父节点公钥做运算得到子节点公钥(Child Public Key);· 把步骤 2 的输出 R 当做子节点链码(Child Chain Code);子私钥生成函数在 BIP32 中被标记为:

节点私钥和子节点公钥的生成过程如果用 JS 代码实现,核心逻辑如下:

HDKey.prototype.deriveChild=function(index) { var indexBuffer=Buffer.allocUnsafe(4); indexBuffer.writeUInt32BE(index, 0); var data=Buffer.concat([this.publicKey, indexBuffer]); var I=crypto.createHmac(‘sha512’, this.chainCode).update(data).digest(); var IL=I.slice(0, 32); var IR=I.slice(32); var hd=new HDKey(this.versions); if (this.privateKey) { hd.privateKey=secp256k1.privateKeyTweakAdd(this.privateKey, IL); } else { hd.publicKey=secp256k1.publicKeyTweakAdd(this.publicKey, IL, true); } hd.chainCode=IR; hd.index=index; return hd;};

如果你读到这里可能心里已经产生 N 多疑问:Chain Code 到底是什么东西?引入它有什么好处?每个父节点到底能生成多少个子节点呢?既然确定性钱包是从种子开始的,上面只是提到了从父节点开始生成子节点,怎么和种子关联上?请继续往下读。

钱包安全的核心在私钥,而公钥则比较容易被找到,如果子节点生成过程只依赖父节点公钥和子节点序号,那么黑客拿到父节点公钥之后就能复原出所有子节点、孙节点的公钥,这样就会破坏隐私性,CKD 里面引入的 Chain Code 则是在整个子节点派生过程中引入确定的随机数,为 HD 钱包的隐私性增加了一重保障。

因为在子节点生成过程中会同时用到父节点公钥和父节点链码,BIP32 里面约定把两者拼接再做特定结构编码产生的结果叫做 Extended Key,也叫做可扩展的钥匙,顾名思义就是根据 Extended Key 我们就可以开始派生子节点。父节点公钥、私钥和链码结合产生的 Extended Key 分别是:· Extended Private Key=Private Key + Chain Code, 标记为 xpriv,可用于派生子节点私钥和公钥· Extended Public Key=Public Key + Chain Code, 标记为 xpub,只能用于派生出子节点公钥因为从 Extended Key 可以解出父节点私钥、公钥和链码,可以说 Extended Key 代表了 HD 钱包中某个分支、子树的根或者起点,也正是因为这种特性,对 Extended Key 的数据保密要格外小心。

定义清楚 CKD 之后我们该从哪里开始生成节点呢?必须得有个主节点,主节点的生成有两种可能的方案:· 随机生成 512 位的随机数开始,拆分为两个 256 位的数字,分别作为主节点私钥和主节点链码,而后递归的生成子节点,虽然这种方式生成的随机数有 2^512 个,但是在生成主节点私钥的时候只用到了 256 位,实际上主节点私钥的可能取值就缩小到 2^256 个;· 随机的生成特定位数的随机数,位数越大越好,然后将该随机数进行 HMAC-SHA256 计算,得到 512 位的哈希,将其拆分为主节点私钥和链码,根据单项哈希函数的性质,只要随机数种子不同,得到的哈希值也会不同,私钥也会不同,这样生成 HD 钱包主节点的私钥就可以有更大的值域空间、更好的随机性;第 2 中方案的流程可以用下图来表示(其中主节点私钥在 BIP32 中被称为 Master Private Key,主节点链码被称为 Master Chain Code):

因为区块链钱包里面保存的私钥能转移用户的资产,对安全性再怎么强调都不为过,对于上面的子节点私钥和公钥生成函数是否足够安全呢?我们设想下面的的场景:

· 如果黑客知道了父节点的公钥和链码,那么他可以生成所有子节点、孙节点的公钥、地址,这样会严重破坏 HD 钱包的隐私性;· 如果黑客在上面的基础上知道了某个孙节点的私钥,那么所有重孙节点的私钥都能被推导出来,父节点的私钥也可能被推导出来,这样整个 HD 钱包就沦陷了;如果安全问题是没有办法彻底避免的,讲解在某个子节点私钥泄露的时候把破坏性降到最低呢?这就需要对 CKD 函数稍作改进,在 BIP32 中称之为 安全增强的子私钥派生函数,记为 HCKD(Hardened Child Key Derivation),原来的 CKD 函数(Normal Child Key Derivation)和安全增强的 CKD 函数流程对比如下图:

安全增强的 CKD 函数产生的节点属性称呼也响应的发生变化:· 增强的子节点私钥:Hardened Child Private Key· 增强的子节点公钥:Hardened Child Public Key,只能根据增强的子节点私钥推导而来不同点在于,安全增强的 CKD 函数中子节点私钥的生成不再使用父节点公钥,而是直接使用父节点私钥,因为相比私钥而言公钥更容易被黑客截获,这样必须在有父节点私钥的情况下才能推导出子节点私钥,只靠父节点的公钥和链码不能推导出增强的子节点公钥。这样子节点之间的兄弟关系就不那么容易被获悉,而即使某个增强子节点私钥泄露,也不会影响到父节点。BIP32 约定 CKD 函数的节点序号取值范围在 0 ~ 2^31 之间,而 HCKD 的节点序号在 2^31 ~ 2^32 之间,这样每个节点就可以生成 2^32 个子节点。

理论上 HD 钱包中任何节点的生成都会有路径,因为我们能找到从主节点到该节点的不同深度各节点的序号,这样我们就可以用统一的路径符号来标记每个节点,在做节点派生的时候也只需要声明路径即可。举几个常见派生过程和对应的派生路径:· CKDpriv(CKDpriv(CKDpriv(m,3),2),5)=> m/3/2/5· CKDpriv(CKDpriv(CKDpriv(m,3H),2H),5H)=> m/3’/2’/5’· CKDpub(CKDpub(CKDpub(m,0),0),0)=> M/0/0/0其中 m 表示私钥,而 M 表示公钥。m/3/2/5 表示从主节点派生出来的第 4 个子节点的第 3 个孙节点的第 6 个重孙节点,派生过程中使用的是 CKD 函数,而 m/3’/2’/5′ 则表示派生过程中使用的是 HCKD 函数。知道每个节点的派生路径之后,通过合并路径相同前缀的方法,不难得到如下的树状 HD 钱包节点结构图:

显然,BIP32 的在钱包安全性、易用性方面做了比较不错的平衡,但是不同的钱包应用开发者可以自定义自己的节点结构,这就很容易导致没有办法 100% 保证在使用了 HD 钱包 A 的用户能将自己的种子导入到 HD 钱包 B 中还能正常工作;也没有办法保证 HD 钱包能支持多个链的私钥管理。因为这个原因,比特币社区在 BIP32 的基础上提出了比较范的 BIP43 和比较具体的 BIP44,两者的目的在于就 HD 钱包子节点派生路径的模式、每段的含义上做出具体的规定,形成共识,事实上现如今的 HD 钱包都遵循了 BIP32 和 BIP44 的规定,也只有遵循了这两个规范的钱包应用才是大概率完全兼容的。春秋战国时期的中国不同小国的文字、马车轮距不同导致了较高的社会交易成本,秦始皇统一六国之后实施了“车同轨、书同文”的政策,BIP44 之于 BIP32 的作用和 “车同轨、书同文” 政策的效果非常类似,也正是他俩的结合才让 BIP 钱包成了事实上的行业标准。

BIP44 的内容相比 BIP32 就简单很多,里面规定了子节点派生路径的范式:m / purpose’ / coin_type’ / account’ / chain / address_index示例如下:m/44’/60’/0’/0/0每个段的含义分别是:· CKD: m: 使用 CKDpriv, M 则表示使用 CKDPub· Purpose: 44′ , hardened, 遵循哪个规范, 44 意味着 BIP44· Coin: 60′, hardened, 60 指代以太坊, 完整的链代码(https://github/satoshilabs/slips/blob/master/slip-0044.md)· Account: 0′ , hardened, 账户编号· Chain: 0 , 对于非比特币路径都是 0· Index: 0, 具体的账户节点

本文采摘于网络,不代表本站立场,转载联系作者并注明出处:http://www.fjxmta.com/chanye/xinnengyuan/57789.html

联系我们

在线咨询:点击这里给我发消息

微信号:wx123456