bip32
https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
RECENT CHANGES:
- (16 Apr 2013) Added private derivation for i ≥ 0x80000000 (less risk of parent private key leakage)
- (30 Apr 2013) Switched from multiplication by IL to addition of IL (faster, easier implementation)
- (25 May 2013) Added test vectors
- (15 Jan 2014) Rename keys with index ≥ 0x80000000 to hardened keys, and add explicit conversion functions.
- (24 Feb 2017) Added test vectors for hardened derivation with leading zeros
BIP: 32 Layer: Applications Title: Hierarchical Deterministic Wallets Author: Pieter Wuille <pieter.wuille@gmail.com> Comments-Summary: No comments yet. Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0032 Status: Final Type: Informational Created: 2012-02-11 License: BSD-2-Clause
BIP 32定义了HD wallet,是一个系统可以从单一个 seed 产生一树状结构储存多组 keypairs(私钥和公钥)。好处是可以方便的备份、转移到其他相容装置(因为都只需要 seed),以及分层的权限控制等。
Abstract
This document describes hierarchical deterministic wallets (or "HD Wallets"): wallets which can be shared partially or entirely with different systems, each with or without the ability to spend coins.
The specification is intended to set a standard for deterministic wallets that can be interchanged between different clients. Although the wallets described here have many features, not all are required by supporting clients.
The specification consists of two parts. In a first part, a system for deriving a tree of keypairs from a single seed is presented. The second part demonstrates how to build a wallet structure on top of such a tree.
这个文档描述了HD Wallets,它能够部分或完整地在不同系统使用,每一个都可以有/没有发送货币的能力。
它的目的就是设置一个能够让HD Wallets在不同的用户间交互的标准。虽然这里描述这个wallet有很多特性,但是并不是所有的都需要提供给用户
该规范包含两部分。第一部分是一个可以从已有的单一个 seed 产生一树状结构储存多组 keypairs(私钥和公钥)的系统。第二部分是阐述如何在该树状结构上创建一个wallet结构。
Motivation
The Bitcoin reference client uses randomly generated keys. In order to avoid the necessity for a backup after every transaction, (by default) 100 keys are cached in a pool of reserve keys. Still, these wallets are not intended to be shared and used on several systems simultaneously. They support hiding their private keys by using the wallet encrypt feature and not sharing the password, but such "neutered" wallets lose the power to generate public keys as well.
Deterministic wallets do not require such frequent backups, and elliptic curve mathematics permit schemes where one can calculate the public keys without revealing the private keys. This permits for example a webshop business to let its webserver generate fresh addresses (public key hashes) for each order or for each customer, without giving the webserver access to the corresponding private keys (which are required for spending the received funds).
However, deterministic wallets typically consist of a single "chain" of keypairs. The fact that there is only one chain means that sharing a wallet happens on an all-or-nothing basis. However, in some cases one only wants some (public) keys to be shared and recoverable. In the example of a webshop, the webserver does not need access to all public keys of the merchant's wallet; only to those addresses which are used to receive customer's payments, and not for example the change addresses that are generated when the merchant spends money. Hierarchical deterministic wallets allow such selective sharing by supporting multiple keypair chains, derived from a single root.
比特币客户端使用随机生成的密钥。为了避免每次交易后都需要备份,(默认情况下)将100个keys缓存在备用keys池中.不过,这些钱包不打算被共享,并同时在多个系统上使用.他们支持使用钱包加密功能隐藏私钥,而不共享密码,但这种“neutered”钱包也失去了生成公钥的能力。
HD钱包不需要如此频繁的备份,而椭圆曲线数学允许在不暴露私钥的情况下计算公钥的方案.例如,webshop业务允许它的webserver为每个订单或每个客户生成新的地址(公钥散列),而不允许webserver访问相应的私钥(用于支出收到的资金)。
然而,HD钱包通常由一个“链”keypairs组成。只有一条链的事实意味着共享一个钱包发生在0或1的基础上,即要么keys都不共享,要么共享全部keys.但是,在某些情况下,您只需要一些(公共)密钥来共享和恢复.比如在webshop的例子中,webserver不需要访问商家钱包的所有公钥;只需要那些用于接收客户付款的地址,而不是商家花钱时生成的更改地址。HD钱包通过支持从单个根派生的多个密钥对链,允许这样的选择性共享。
Specification: Key derivation
Conventions
In the rest of this text we will assume the public key cryptography used in Bitcoin, namely elliptic curve cryptography using the field and curve parameters defined by secp256k1 (http://www.secg.org/sec2-v2.pdf). Variables below are either:
在本文的其余部分中,我们将假设使用的是比特币中使用的公钥密码,即使用secp256k1定义的字段和曲线参数的椭圆曲线密码
- Integers modulo the order of the curve (referred to as n).
- Coordinates of points on the curve.
- Byte sequences.
Addition (+) of two coordinate pair is defined as application of the EC group operation. Concatenation (||) is the operation of appending one byte sequence onto another.
As standard conversion functions, we assume标准转换函数:
- point(p): returns the coordinate pair resulting from EC point multiplication (repeated application of the EC group operation) of the secp256k1 base point with the integer p.返返回由整数p表示的secp256k1基点的EC点乘法(EC组操作的重复应用)产生的坐标对
- ser32(i): serialize a 32-bit unsigned integer i as a 4-byte sequence, most significant byte first.将一个32位无符号整数i序列化为一个4字节的序列,大端存储
- ser256(p): serializes the integer p as a 32-byte sequence, most significant byte first.将整数p序列化为32字节序列,大端存储
- serP(P): serializes the coordinate pair P = (x,y) as a byte sequence using SEC1's compressed form: (0x02 or 0x03) || ser256(x), where the header byte depends on the parity of the omitted y coordinate.使用SEC1的压缩形式(0x02或0x03)|| ser256(x)将坐标对P = (x,y)序列化为字节序列,其中header字节取决于省略的y坐标的奇偶校验。
- parse256(p): interprets a 32-byte sequence as a 256-bit number, most significant byte first.将32字节序列转成256位的数字,大端存储
Extended keys扩展密钥
In what follows, we will define a function that derives a number of child keys from a parent key. In order to prevent these from depending solely on the key itself, we extend both private and public keys first with an extra 256 bits of entropy. This extension, called the chain code, is identical for corresponding private and public keys, and consists of 32 bytes.
We represent an extended private key as (k, c), with k the normal private key, and c the chain code. An extended public key is represented as (K, c), with K = point(k) and c the chain code.
Each extended key has 231 normal child keys, and 231 hardened child keys. Each of these child keys has an index. The normal child keys use indices 0 through 231-1. The hardened child keys use indices 231 through 232-1. To ease notation for hardened key indices, a number iH represents i+231.
在接下来的内容中,我们将定义一个函数,该函数从父键派生许多子键。为了防止它们完全依赖于密钥本身,我们首先使用额外的256位熵来扩展私钥和公钥。这个扩展称为chain code由32个字节组成,对于相应的公私钥,这个chain code是相同的。(链码chain code是用来给这个过程引入确定性随机数据的,使得索引不能充分衍生其他的子密钥。因此,有了子密钥并不能让它发现自己的姊妹密钥,除非你已经有了链码。)
我们将扩展私钥表示为(k, c),普通私钥为k,chain code为c。扩展公钥表示为(K, c),其中K = point(K), c为chain code。
每个扩展密钥有231个普通子keys和231个硬子keys。每个子keys都有一个索引。普通的子keys使用索引0到231-1。硬子keys使用索引231到231-1。为了简化hardened索引的符号,一个数字iH表示i+231。
⚠️什么是hardened:(来自https://blog.csdn.net/m0_37847176/article/details/82011876 )
从扩展公钥衍生一个分支公钥的能力是很重要的,但牵扯一些风险。访问扩展公钥并不能得到访问子私钥的途径。但是,因为扩展公钥包含有链码,如果某一个子私钥被知道或者被泄漏的话,链码就可以被用来衍生所有的其他子私钥。简单地说就是泄露的私钥加上一个母链码就可以暴露所有的子密钥。更糟糕的是,子私钥与母链码可以用来推断母私钥。
为了应对这种风险,HD 钱包使用一种叫做硬化衍生(hardened derivation)的替代 衍生函数。这就“打破”了母公钥以及子链码之间的关系。这个硬化衍生函数使用了母私钥去推导子链码,而不是母公钥。这就在母/子顺序中创造了一道“防火 墙”——有链码但并不能够用来推算子私钥或者姊妹私钥。强化衍生函数看起来几乎与一般的衍生的子私钥相同,不同的是母私钥被用来输入散列函数中而不是母公钥。
简单地来说,如果你想要利用扩展公钥的便捷来衍生公钥的分支而不将你自己暴露在泄露扩展链码的风险下, 你应该使用hardened母私钥来衍生子公钥,而不是从一般的母私钥来衍生。最好的方式是,为了避免了推导出主密钥,主密钥所衍生的第一层级的子密钥最好使用hardened衍生。其索引下标带H,即iH。
Child key derivation (CKD) functions
Given a parent extended key and an index i, it is possible to compute the corresponding child extended key. The algorithm to do so depends on whether the child is a hardened key or not (or, equivalently, whether i ≥ 231), and whether we're talking about private or public keys.
给定父扩展key和索引i,可以计算相应的子扩展key。这个算法,取决于child是否硬key(即是否 索引i≥231),以及我们讨论的是公钥还是私钥。
Private parent key → private child key 父私钥->子私钥
The function CKDpriv((kpar, cpar), i) → (ki, ci) computes a child extended private key from the parent extended private key从父扩展私钥计算子扩展私钥:
- Check whether i ≥ 231 (whether the child is a hardened key).首先检查是否索引i ≥ 231(即是不是hardened key)
- If so (hardened child): let I = HMAC-SHA512(Key = cpar, Data = 0x00 || ser256(kpar) || ser32(i)). (Note: The 0x00 pads the private key to make it 33 bytes long.)如果是,则I = HMAC-SHA512(Key = cpar, Data = 0x00 || ser256(kpar) || ser32(i))(0x00填充私钥,使其长度为33字节)
- If not (normal child): let I = HMAC-SHA512(Key = cpar, Data = serP(point(kpar)) || ser32(i)).如果不是则..
- Split I into two 32-byte sequences, IL and IR. 然后将I分成左右两个32字节的序列
- The returned child key ki is parse256(IL) + kpar (mod n). 返回的子key ki为parse256(IL) + kpar (mod n)
- The returned chain code ci is IR. 返回的chain code ci为右序列IR
- In case parse256(IL) ≥ n or ki = 0, the resulting key is invalid, and one should proceed with the next value for i. (Note: this has probability lower than 1 in 2127.) 要防止parse256(IL)≥n或Ki = 0的情况,因为由此产生的key是无效的,应该计算下一个索引i。(注意:这个概率低于1 /2127)。
The HMAC-SHA512 function is specified in RFC 4231.
Public parent key → public child key 父公钥 → 子公钥
The function CKDpub((Kpar, cpar), i) → (Ki, ci) computes a child extended public key from the parent extended public key. It is only defined for non-hardened child keys.从父扩展公钥计算子扩展公钥。它只定义为非hardened child
- Check whether i ≥ 231 (whether the child is a hardened key).检查是否为hardened child
- If so (hardened child): return failure 是则失败
- If not (normal child): let I = HMAC-SHA512(Key = cpar, Data = serP(Kpar) || ser32(i)). 不是则..
- Split I into two 32-byte sequences, IL and IR. 然后将I分成左右两个32字节的序列
- The returned child key Ki is point(parse256(IL)) + Kpar. 返回的子key ki point(parse256(IL)) + Kpar
- The returned chain code ci is IR. 返回的chain code ci为右序列IR
- In case parse256(IL) ≥ n or Ki is the point at infinity, the resulting key is invalid, and one should proceed with the next value for i.要防止parse256(IL)≥n或Ki点在无穷远处的情况,由此产生的key是无效的,应该继续计算下一个索引i。
Private parent key → public child key 父私钥 → 子公钥
The function N((k, c)) → (K, c) computes the extended public key corresponding to an extended private key (the "neutered" version, as it removes the ability to sign transactions).计算与扩展私钥对应的扩展公钥(“neutered”版本,因为它删除了签署交易的能力)。
- The returned key K is point(k). 返回的密钥K是point(k)
- The returned chain code c is just the passed chain code.返回的链码c只是传递的链码
To compute the public child key of a parent private key:要计算父私钥的公用子密钥
- N(CKDpriv((kpar, cpar), i)) (works always).
- CKDpub(N(kpar, cpar), i) (works only for non-hardened child keys).仅适用于非硬化子密钥
The fact that they are equivalent is what makes non-hardened keys useful (one can derive child public keys of a given parent key without knowing any private key), and also what distinguishes them from hardened keys. The reason for not always using non-hardened keys (which are more useful) is security; see further for more information.
上面两个式子是等价的这一事实使non-hardened keys变得有用(人们可以在不知道任何私钥的情况下派生出给定父键的子公钥),这也使它们区别于hardened keys。不总是使用non-hardened keys(这个十分有用的keys)的原因是因为安全;有关更多信息,请参阅下文。
Public parent key → private child key 父公钥 → 子私钥
This is not possible.
The key tree
The next step is cascading several CKD constructions to build a tree. We start with one root, the master extended key m. By evaluating CKDpriv(m,i) for several values of i, we get a number of level-1 derived nodes. As each of these is again an extended key, CKDpriv can be applied to those as well.
下一步是级联几个CKD结构来构建树。我们从一个根开始,主扩展key m。通过对几个 i 值求 CKDpriv(m,i),我们得到了许多一级派生节点
To shorten notation, we will write CKDpriv(CKDpriv(CKDpriv(m,3H),2),5) as m/3H/2/5. Equivalently for public keys, we write CKDpub(CKDpub(CKDpub(M,3),2),5) as M/3/2/5. This results in the following identities:
为了缩短符号,我们将写入CKDpriv(CKDpriv(m,3H),2),5)作为m / 3H / 2/5。我们写入CKDpub(CKDpub(M,3),2),5)作为M / 3/2/5,其等同于公钥。
- N(m/a/b/c) = N(m/a/b)/c = N(m/a)/b/c = N(m)/a/b/c = M/a/b/c.
- N(m/aH/b/c) = N(m/aH/b)/c = N(m/aH)/b/c.
However, N(m/aH) cannot be rewritten as N(m)/aH, as the latter is not possible.然而,N(m/aH)不能被重写为N(m)/aH,因为后者是不可能的。
Each leaf node in the tree corresponds to an actual key, while the internal nodes correspond to the collections of keys that descend from them. The chain codes of the leaf nodes are ignored, and only their embedded private or public key is relevant. Because of this construction, knowing an extended private key allows reconstruction of all descendant private keys and public keys, and knowing an extended public keys allows reconstruction of all descendant non-hardened public keys.
树中的每个叶节点对应于一个实际的键,而内部节点对应于从它们派生的键集合。忽略叶节点的chain codes,只有它们的嵌入私有或公共密钥是相关的。由于这种结构,知道扩展的私钥可以重建所有后代的私钥和公钥,而知道扩展的公钥可以重建所有后代的non-hardened公钥。
Key identifiers 密钥标识符
Extended keys can be identified by the Hash160 (RIPEMD160 after SHA256) of the serialized ECDSA public key K, ignoring the chain code. This corresponds exactly to the data used in traditional Bitcoin addresses. It is not advised to represent this data in base58 format though, as it may be interpreted as an address that way (and wallet software is not required to accept payment to the chain key itself).
The first 32 bits of the identifier are called the key fingerprint.
扩展密钥可以由序列化的ECDSA公钥K的Hash160 (SHA256之后的RIPEMD160)标识,而不考虑chain code。这与传统比特币地址使用的数据完全一致。不过,不建议以base58格式表示该数据,因为它可能被转成这样格式的地址(钱包软件不需要接受支付给chain key本身的付款)
标识符的前32位称为密钥指纹。
Serialization format序列化格式
Extended public and private keys are serialized as follows就是怎么讲扩展公私钥序列化,下面为每个字节所对应的内容:
- 4 byte: version bytes (mainnet: 0x0488B21E public, 0x0488ADE4 private; testnet: 0x043587CF public, 0x04358394 private)版本字节
- 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 derived keys, .... 深度:主节点为0x00,级别1派生密钥为0x01
- 4 bytes: the fingerprint of the parent's key (0x00000000 if master key) 父密钥的指纹(如果主密钥为0x00000000)
- 4 bytes: child number. This is ser32(i) for i in xi = xpar/i, with xi the key being serialized. (0x00000000 if master key) 子数字。这是对于i在xi = xpar/i中的ser32(i),其中xi是键序列化。 (如果主密钥为0x00000000)
- 32 bytes: the chain code 链码
- 33 bytes: the public key or private key data (serP(K) for public keys, 0x00 || ser256(k) for private keys) 公钥或私钥数据
This 78 byte structure can be encoded like other Bitcoin data in Base58, by first adding 32 checksum bits (derived from the double SHA-256 checksum), and then converting to the Base58 representation. This results in a Base58-encoded string of up to 112 characters. Because of the choice of the version bytes, the Base58 representation will start with "xprv" or "xpub" on mainnet, "tprv" or "tpub" on testnet.
Note that the fingerprint of the parent only serves as a fast way to detect parent and child nodes in software, and software must be willing to deal with collisions. Internally, the full 160-bit identifier could be used.
When importing a serialized extended public key, implementations must verify whether the X coordinate in the public key data corresponds to a point on the curve. If not, the extended public key is invalid.
这个78字节结构可以像其他Base58形式的比特币数据那样进行编码,首先添加32个校验和位(源自double SHA-256校验和),然后转换为Base58表示。这将导致一个由多达112个字符组成的base58编码的字符串。由于选择了版本字节,Base58表示将在mainnet上以“xprv”或“xpub”开始,在testnet上以“tprv”或“tpub”开始。
注意,父节点的指纹只能作为检测软件中父节点和子节点的一种快速方法,软件必须愿意处理冲突。在内部,可以使用完整的160位标识符。
在导入序列化的扩展公钥时,该实现过程必须验证公钥数据中的X坐标是否对应于曲线上的一点。如果没有,扩展的公钥无效。
⚠️extend key的作用:
在产生 child 的过程中最重要的两个参数是 parent private key(再生成parent public key) 和 parent chain code 了。可以在生成child时同时得到该child的extend key,这样下次还想得到一层层同样索引的child时,不用从头一直索引下来得到,而是可以直接使用extend key这个长长的字符串来得到child。
Master key generation主键生成(seed->masterkey,seed一般使用BIP39标准)
The total number of possible extended keypairs is almost 2512, but the produced keys are only 256 bits long, and offer about half of that in terms of security. Therefore, master keys are not generated directly, but instead from a potentially short seed value.
可能扩展的密钥对的总数接近2512个,但是产生的密钥只有256位长,并且将一半的数量用于安全。因此,主键不是直接生成的,而是从潜在的短seed value生成的。
- Generate a seed byte sequence S of a chosen length (between 128 and 512 bits; 256 bits is advised) from a (P)RNG.从(P)RNG中生成一个长度(128至512位之间,建议256位)的种子字节序列S;。
- Calculate I = HMAC-SHA512(Key = "Bitcoin seed", Data = S) 计算I
- Split I into two 32-byte sequences, IL and IR. 然后将I分成左右两个32字节的序列
- Use parse256(IL) as master secret key, and IR as master chain code. 使用parse256(IL)作为主密钥,IR作为chain code
In case IL is 0 or ≥n, the master key is invalid.如果IL is 0 or ≥n,主键无效
该图来自:https://blog.csdn.net/m0_37847176/article/details/82011876
主私钥(m) 可以通过椭圆曲线生成相应的主公钥(M)。 链代码用于从主密钥中创造子密钥的函数中引入熵,即扩展密钥处。
Specification: Wallet structure
The previous sections specified key trees and their nodes. The next step is imposing a wallet structure on this tree. The layout defined in this section is a default only, though clients are encouraged to mimic it for compatibility, even if not all features are supported.
前面的部分指定了关键树及其节点。下一步是在这棵树上安装一个wallet结构。本节中定义的布局仅为默认布局,但鼓励客户为了兼容性而模仿它,即使它不支持所有特性。
The default wallet layout默认wallet布局
An HDW is organized as several 'accounts'. Accounts are numbered, the default account ("") being number 0. Clients are not required to support more than one account - if not, they only use the default account.
HDW被组织为几个“帐户”。帐户即数字编号,默认帐户("")编号为0。客户不需要支持多个帐户——如果不支持,他们只使用默认帐户。
Each account is composed of two keypair chains: an internal and an external one. The external keychain is used to generate new public addresses, while the internal keychain is used for all other operations (change addresses, generation addresses, ..., anything that doesn't need to be communicated). Clients that do not support separate keychains for these should use the external one for everything.
每个帐户由两个密钥对链组成:一个内部密钥对链和一个外部密钥对链。外部密钥链用于生成新的公共地址,而内部密钥链用于所有其他操作(更改地址、生成地址、…,任何不需要沟通的事情)。不支持这种分开的密钥链的客户端应该将外部密钥链用于所有事情。
- m/iH/0/k corresponds to the k'th keypair of the external chain of account number i of the HDW derived from master m.对应于主键m派生的HDW的第i个账号的外部链的第k个密钥对
- m/iH/1/k corresponds to the k'th keypair of the internal chain of account number i of the HDW derived from master m.对应于主键m派生的HDW的第i个账号的内部链的第k个密钥对
Use cases
Full wallet sharing: m主私钥
In cases where two systems need to access a single shared wallet, and both need to be able to perform spendings, one needs to share the master private extended key. Nodes can keep a pool of N look-ahead keys cached for external chains, to watch for incoming payments. The look-ahead for internal chains can be very small, as no gaps are to be expected here. An extra look-ahead could be active for the first unused account's chains - triggering the creation of a new account when used. Note that the name of the account will still need to be entered manually and cannot be synchronized via the block chain.
如果两个系统需要访问一个共享的钱包,并且两个系统都需要能够执行开销,那么需要共享主私有扩展密钥。节点可以为外部链缓存N个向前查找键,以监视传入的付款。内部链的向前查找的可能非常小,因为在这里没有缺口。对于第一个未使用的帐户链,一个额外的向前查找功能可能是活跃的——即在使用时触发一个新帐户的创建。注意,帐户的名称仍然需要手动输入,不能通过块链进行同步
Audits: N(m/*)
In case an auditor needs full access to the list of incoming and outgoing payments, one can share all account public extended keys. This will allow the auditor to see all transactions from and to the wallet, in all accounts, but not a single secret key.
如果审计人员需要能够完全地访问收支付款清单,可以共享所有帐户的公共扩展密钥。这将允许审计人员查看所有账户中来自钱包和到钱包的所有交易,但不查看任何一个秘密密钥。
Per-office balances: m/iH
When a business has several independent offices, they can all use wallets derived from a single master. This will allow the headquarters to maintain a super-wallet that sees all incoming and outgoing transactions of all offices, and even permit moving money between the offices.
当一个公司有几个独立的办公室时,他们都可以使用从一个master那里得到的钱包。这将使总部拥有一个超级钱包,可以看到所有办公室的所有进出交易,甚至允许在办公室之间转移资金。
Recurrent business-to-business transactions: N(m/iH/0)周期性b2b交易
In case two business partners often transfer money, one can use the extended public key for the external chain of a specific account (M/i h/0) as a sort of "super address", allowing frequent transactions that cannot (easily) be associated, but without needing to request a new address for each payment. Such a mechanism could also be used by mining pool operators as variable payout address.
如果两个业务伙伴经常转账,一个可以将特定帐户(M/i h/0)的外部链的扩展公钥用作一种“超级地址”,允许频繁的交易,这些交易不能(容易地)关联,但无需为每次支付请求新的地址。这种机制也可以被挖掘池运营商用作可变支付地址。
Unsecure money receiver: N(m/iH/0)
When an unsecure webserver is used to run an e-commerce site, it needs to know public addresses that are used to receive payments. The webserver only needs to know the public extended key of the external chain of a single account. This means someone illegally obtaining access to the webserver can at most see all incoming payments but will not be able to steal the money, will not (trivially) be able to distinguish outgoing transactions, nor be able to see payments received by other webservers if there are several.
当不安全的web服务器用于运行电子商务网站时,它需要知道用于接收付款的公共地址。web服务器只需要知道单个帐户外部链的公共扩展密钥。这意味着,非法访问webserver的人最多只能看到所有收到的付款,但却无法窃取资金,无法(简单地)区分出传出的交易,也无法看到其他web服务器收到的付款(如果有几个的话)。
Compatibility
To comply with this standard, a client must at least be able to import an extended public or private key, to give access to its direct descendants as wallet keys. The wallet structure (master/account/chain/subchain) presented in the second part of the specification is advisory only, but is suggested as a minimal structure for easy compatibility - even when no separate accounts or distinction between internal and external chains is made. However, implementations may deviate from it for specific needs; more complex applications may call for a more complex tree structure.
为了符合这个标准,客户端必须至少能够导入扩展的公共或私有密钥,以便能够访问其直接后代作为钱包密钥。在规范的第二部分中提出的钱包结构(主/帐户/链/子链)仅仅是建议性的,但是建议作为一种最小的结构,以方便兼容性—即使没有单独的帐户或内部和外部链之间的区别。但是,真正的实现可能因某些特定需要而偏离该结构;更复杂的应用程序可能需要更复杂的树结构。
Security
In addition to the expectations from the EC public-key cryptography itself除了来自椭圆曲线公开密钥加密本身的预期:
- Given a public key K, an attacker cannot find the corresponding private key more efficiently than by solving the EC discrete logarithm problem (assumed to require 2128 group operations).给定一个公钥K,攻击者无法比通过解决EC离散对数问题(假设需要2128
组操作)更有效地找到对应的私钥
the intended security properties of this standard are本标准的预期安全属性为:
- Given a child extended private key (ki,ci) and the integer i, an attacker cannot find the parent private key kpar more efficiently than a 2256 brute force of HMAC-SHA512.给定一个子扩展私钥(ki,ci)和整数i,攻击者无法找到比使用2256的蛮力HMAC-SHA512方法更有效的方法去找到父私钥kpar
- Given any number (2 ≤ N ≤ 232-1) of (index, extended private key) tuples (ij,(kij,cij)), with distinct ij's, determining whether they are derived from a common parent extended private key (i.e., whether there exists a (kpar,cpar) such that for each j in (0..N-1) CKDpriv((kpar,cpar),ij)=(kij,cij)), cannot be done more efficiently than a 2256 brute force of HMAC-SHA512. 给定任意数量(2≤N≤232 - 1)(指数,扩展私钥)的元组(ij,(kij,cij)),与不同的ij,攻击者无法找到比使用2256的蛮力的HMAC-SHA512方法更有效的方法去确定他们是否来自一个共同的父私钥(即对于来自(0..N-1) CKDpriv((kpar,cpar),ij)=(kij,cij)中的每个j是否都存在一个(kpar,cpar)来满足该式子)。
Note however that the following properties does not exist但是请注意,以下属性不存在:
- Given a parent extended public key (Kpar,cpar) and a child public key (Ki), it is hard to find i.给定一个父扩展公钥(Kpar,cpar)和一个子公钥(Ki),很难找到i
- Given a parent extended public key (Kpar,cpar) and a non-hardened child private key (ki), it is hard to find kpar.给定父扩展公钥(Kpar,cpar)和non-hardened子私钥(ki),很难找到kpar。
Implications
Private and public keys must be kept safe as usual. Leaking a private key means access to coins - leaking a public key can mean loss of privacy.
Somewhat more care must be taken regarding extended keys, as these correspond to an entire (sub)tree of keys.
One weakness that may not be immediately obvious, is that knowledge of a parent extended public key plus any non-hardened private key descending from it is equivalent to knowing the parent extended private key (and thus every private and public key descending from it). This means that extended public keys must be treated more carefully than regular public keys. It is also the reason for the existence of hardened keys, and why they are used for the account level in the tree. This way, a leak of account-specific (or below) private key never risks compromising the master or other accounts.
私有和公钥必须像往常一样保持安全。泄漏私钥意味着对硬币的访问——泄漏公钥可能意味着隐私的丧失。
对于扩展键,需要多加注意,因为它们对应于键的整个(子)树。
有一个缺点可能不是很明显,即父扩展公钥的值加上从它生成的任何non-hardened私钥,就相当于知道父扩展私钥(因此每个私钥和公钥都是从它生成的)。这意味着扩展公钥必须比常规公钥更小心地处理。这也是hardened键存在的原因(之前说过为什么没有都使用non-hardened键),也是为什么它们用于树中的帐户级别。通过这种方式,特定账户(或以下)私钥的泄漏永远不会危及主账户或其他账户。
Test Vectors测试向量
Test vector 1
Seed (hex): 000102030405060708090a0b0c0d0e0f
- Chain m 主键
- ext pub: xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8
- ext prv: xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi
- Chain m/0H 第一个账户account
- ext pub: xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw
- ext prv: xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7
- Chain m/0H/1 第一个账户的内部chain
- ext pub: xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ
- ext prv: xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs
- Chain m/0H/1/2H 第一个账户的内部chain的第三个subchain
- ext pub: xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5
- ext prv: xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM
- Chain m/0H/1/2H/2 第一个账户的内部chain的第三个subchain的第三个address
- ext pub: xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV
- ext prv: xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334
- Chain m/0H/1/2H/2/1000000000
- ext pub: xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy
- ext prv: xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76
go运行时遇见问题:
1.
userdeMBP:src user$ go get golang.org/x/crypto/ripemd160 package golang.org/x/crypto/ripemd160: unrecognized import path "golang.org/x/crypto/ripemd160" (https fetch: Get https://golang.org/x/crypto/ripemd160?go-get=1: dial tcp 216.239.37.1:443: i/o timeout)
由于国内网络原因,因此访问https://golang.org/网站会被限制。所以在go get下载其他第三方包的时候,如果这个第三方包又引用了https://golang.org/x/下的包,通常会下载失败,就会报这个错误
参考:https://blog.csdn.net/weixin_42280360/article/details/80649038
2.
userdeMBP:src user$ go build hd-wallet.go # command-line-arguments ./hd-wallet.go:6:1: syntax error: non-declaration statement outside function body
要将运行内容写入func main(){}
3.
userdeMBP:src user$ go build hd-wallet.go # command-line-arguments ./hd-wallet.go:7:8: err declared and not used ./hd-wallet.go:16:2: childprv declared and not used ./hd-wallet.go:20:2: address declared and not used ./hd-wallet.go:25:2: childaddress declared and not used
因为代码中有声明并赋值了err该变量,但是并没有使用,所以使用_来代替它
其他的使用fmt.Println(childaddress)来使用他们
实例:
package main import( "github.com/WeMeetAgain/go-hdwallet" "fmt" ) func main(){ // Generate a random 256 bit seed // seed, _ := hdwallet.GenSeed(256) //fmt.Println(seed) // Create a master private key var str string = "0x000102030405060708090a0b0c0d0e0f" var seed []byte = []byte(str) masterprv := hdwallet.MasterKey(seed) fmt.Println(masterprv) //xprv9s21ZrQH143K3sGikPVJnaJkPsjhuxHxrXVTVB256AnXAFbKstG6pi6nhxZg4VrXCGo9Zrn98bYe6brrmtM5sR1YU8ADbfoHAjVv6R4QXQo // Convert a private key to public key masterpub := masterprv.Pub() fmt.Println(masterpub) //xpub661MyMwAqRbcGMMBrR2K9iFUwuaCKR1pDkR4HZRgeWKW33vURRaMNWRGZFyuKD8NRe17vAsWBwfRSpoghXqXRz8NmkN6bdgdhUbuJEcqSJE // Generate new child key based on private or public key childprv, _ := masterprv.Child(0) childpub, _ := masterpub.Child(0) fmt.Println(childprv) //xprv9uXEX9538uj8iiLcTYzvMMxRv4LCNWpZDNpEGJJZd15CJHoCoYSY7LFhqo5xM4n8Bo9qpo7hoQxWva6rs6CL7nCyYj9boVLSiUPXAeYpZ4R fmt.Println(childpub) //xpub68WavebvyHHRwCR5ZaXviVuAU6AgmyYQabjq4giBBLcBB68MM5knf8aBh584hYmB18yYzkvmrH2pnXmUYdjgborGr3DrgH6zpkcDetpzuNB // Create bitcoin address from public key address := childpub.Address() fmt.Println(address) //17wNBYV1482MeGQorJF7RJkefxbqf7My5w // Convenience string -> string Child and ToAddress functions,使用extend key直接得到上面的child的child walletstring := childpub.String() // String returns the base58-encoded string form of the wallet.以上面的child作为wallet childstring, _ := hdwallet.StringChild(walletstring,0) //return extended key,然后一次得到它的child(0)的extended key childaddress, _ := hdwallet.StringAddress(childstring) //使用extended key直接得到一个child fmt.Println(walletstring) //xpub68WavebvyHHRwCR5ZaXviVuAU6AgmyYQabjq4giBBLcBB68MM5knf8aBh584hYmB18yYzkvmrH2pnXmUYdjgborGr3DrgH6zpkcDetpzuNB fmt.Println(childstring) //xpub6BaCw5HC1yM4H3aC4L4SaZsKVMzXxbfVYiCsoVXz91eFuRnyn6CxJmpNCFqxzFtjSsY9GsdwbNTQZK2mqnsPndwJrtKznLnjSGTqMEAwTQE fmt.Println(childaddress) //1HyLGa9BLV843eQJKTxQb38HZnR9M9WUMx //这里是使用正常的方法,可以看见结果是相同的 chiChildpub,_ := childpub.Child(0) chiAddress := chiChildpub.Address() fmt.Println(chiChildpub) //xpub6BaCw5HC1yM4H3aC4L4SaZsKVMzXxbfVYiCsoVXz91eFuRnyn6CxJmpNCFqxzFtjSsY9GsdwbNTQZK2mqnsPndwJrtKznLnjSGTqMEAwTQE fmt.Println(chiAddress) }