1 | var base58check = require('bs58check')
|
2 | var bcrypto = require('./crypto')
|
3 | var createHmac = require('create-hmac')
|
4 | var typeforce = require('typeforce')
|
5 | var types = require('./types')
|
6 | var NETWORKS = require('./networks')
|
7 |
|
8 | var BigInteger = require('bigi')
|
9 | var ECPair = require('./ecpair')
|
10 |
|
11 | var ecurve = require('ecurve')
|
12 | var curve = ecurve.getCurveByName('secp256k1')
|
13 |
|
14 | function HDNode (keyPair, chainCode) {
|
15 | typeforce(types.tuple('ECPair', types.Buffer256bit), arguments)
|
16 |
|
17 | if (!keyPair.compressed) throw new TypeError('BIP32 only allows compressed keyPairs')
|
18 |
|
19 | this.keyPair = keyPair
|
20 | this.chainCode = chainCode
|
21 | this.depth = 0
|
22 | this.index = 0
|
23 | this.parentFingerprint = 0x00000000
|
24 | }
|
25 |
|
26 | HDNode.HIGHEST_BIT = 0x80000000
|
27 | HDNode.LENGTH = 78
|
28 | HDNode.MASTER_SECRET = new Buffer('Bitcoin seed')
|
29 |
|
30 | HDNode.fromSeedBuffer = function (seed, network) {
|
31 | typeforce(types.tuple(types.Buffer, types.maybe(types.Network)), arguments)
|
32 |
|
33 | if (seed.length < 16) throw new TypeError('Seed should be at least 128 bits')
|
34 | if (seed.length > 64) throw new TypeError('Seed should be at most 512 bits')
|
35 |
|
36 | var I = createHmac('sha512', HDNode.MASTER_SECRET).update(seed).digest()
|
37 | var IL = I.slice(0, 32)
|
38 | var IR = I.slice(32)
|
39 |
|
40 |
|
41 |
|
42 | var pIL = BigInteger.fromBuffer(IL)
|
43 | var keyPair = new ECPair(pIL, null, {
|
44 | network: network
|
45 | })
|
46 |
|
47 | return new HDNode(keyPair, IR)
|
48 | }
|
49 |
|
50 | HDNode.fromSeedHex = function (hex, network) {
|
51 | return HDNode.fromSeedBuffer(new Buffer(hex, 'hex'), network)
|
52 | }
|
53 |
|
54 | HDNode.fromBase58 = function (string, networks, skipValidation) {
|
55 | var buffer = base58check.decode(string)
|
56 | if (buffer.length !== 78) throw new Error('Invalid buffer length')
|
57 |
|
58 |
|
59 | var version = buffer.readUInt32BE(0)
|
60 | var network
|
61 |
|
62 |
|
63 | if (Array.isArray(networks)) {
|
64 | network = networks.filter(function (network) {
|
65 | return version === network.bip32.private ||
|
66 | version === network.bip32.public
|
67 | }).pop()
|
68 |
|
69 | if (!network) throw new Error('Unknown network version')
|
70 |
|
71 |
|
72 | } else {
|
73 | network = networks || NETWORKS.bitcoin
|
74 | }
|
75 |
|
76 | if (version !== network.bip32.private &&
|
77 | version !== network.bip32.public) throw new Error('Invalid network version')
|
78 |
|
79 |
|
80 | var depth = buffer[4]
|
81 |
|
82 |
|
83 | var parentFingerprint = buffer.readUInt32BE(5)
|
84 | if (depth === 0) {
|
85 | if (parentFingerprint !== 0x00000000) throw new Error('Invalid parent fingerprint')
|
86 | }
|
87 |
|
88 |
|
89 |
|
90 | var index = buffer.readUInt32BE(9)
|
91 | if (depth === 0 && index !== 0) throw new Error('Invalid index')
|
92 |
|
93 |
|
94 | var chainCode = buffer.slice(13, 45)
|
95 | var keyPair
|
96 |
|
97 |
|
98 | if (version === network.bip32.private) {
|
99 | if (buffer.readUInt8(45) !== 0x00) throw new Error('Invalid private key')
|
100 |
|
101 | var d = BigInteger.fromBuffer(buffer.slice(46, 78))
|
102 |
|
103 | keyPair = new ECPair(d, null, {
|
104 | network: network
|
105 | })
|
106 |
|
107 |
|
108 | } else {
|
109 | var Q = ecurve.Point.decodeFrom(curve, buffer.slice(45, 78))
|
110 | if (!Q.compressed) throw new Error('Invalid public key')
|
111 |
|
112 |
|
113 | if (!skipValidation) {
|
114 |
|
115 |
|
116 | curve.validate(Q)
|
117 | }
|
118 |
|
119 | keyPair = new ECPair(null, Q, {
|
120 | network: network
|
121 | })
|
122 | }
|
123 |
|
124 | var hd = new HDNode(keyPair, chainCode)
|
125 | hd.depth = depth
|
126 | hd.index = index
|
127 | hd.parentFingerprint = parentFingerprint
|
128 |
|
129 | return hd
|
130 | }
|
131 |
|
132 | HDNode.prototype.getAddress = function () {
|
133 | return this.keyPair.getAddress()
|
134 | }
|
135 |
|
136 | HDNode.prototype.getIdentifier = function () {
|
137 | return bcrypto.hash160(this.keyPair.getPublicKeyBuffer())
|
138 | }
|
139 |
|
140 | HDNode.prototype.getFingerprint = function () {
|
141 | return this.getIdentifier().slice(0, 4)
|
142 | }
|
143 |
|
144 | HDNode.prototype.getNetwork = function () {
|
145 | return this.keyPair.getNetwork()
|
146 | }
|
147 |
|
148 | HDNode.prototype.getPublicKeyBuffer = function () {
|
149 | return this.keyPair.getPublicKeyBuffer()
|
150 | }
|
151 |
|
152 | HDNode.prototype.neutered = function () {
|
153 | var neuteredKeyPair = new ECPair(null, this.keyPair.Q, {
|
154 | network: this.keyPair.network
|
155 | })
|
156 |
|
157 | var neutered = new HDNode(neuteredKeyPair, this.chainCode)
|
158 | neutered.depth = this.depth
|
159 | neutered.index = this.index
|
160 | neutered.parentFingerprint = this.parentFingerprint
|
161 |
|
162 | return neutered
|
163 | }
|
164 |
|
165 | HDNode.prototype.sign = function (hash) {
|
166 | return this.keyPair.sign(hash)
|
167 | }
|
168 |
|
169 | HDNode.prototype.verify = function (hash, signature) {
|
170 | return this.keyPair.verify(hash, signature)
|
171 | }
|
172 |
|
173 | HDNode.prototype.toBase58 = function (__isPrivate) {
|
174 | if (__isPrivate !== undefined) throw new TypeError('Unsupported argument in 2.0.0')
|
175 |
|
176 |
|
177 | var network = this.keyPair.network
|
178 | var version = (!this.isNeutered()) ? network.bip32.private : network.bip32.public
|
179 | var buffer = new Buffer(78)
|
180 |
|
181 |
|
182 | buffer.writeUInt32BE(version, 0)
|
183 |
|
184 |
|
185 | buffer.writeUInt8(this.depth, 4)
|
186 |
|
187 |
|
188 | buffer.writeUInt32BE(this.parentFingerprint, 5)
|
189 |
|
190 |
|
191 |
|
192 | buffer.writeUInt32BE(this.index, 9)
|
193 |
|
194 |
|
195 | this.chainCode.copy(buffer, 13)
|
196 |
|
197 |
|
198 | if (!this.isNeutered()) {
|
199 |
|
200 | buffer.writeUInt8(0, 45)
|
201 | this.keyPair.d.toBuffer(32).copy(buffer, 46)
|
202 |
|
203 |
|
204 | } else {
|
205 |
|
206 | this.keyPair.getPublicKeyBuffer().copy(buffer, 45)
|
207 | }
|
208 |
|
209 | return base58check.encode(buffer)
|
210 | }
|
211 |
|
212 |
|
213 | HDNode.prototype.derive = function (index) {
|
214 | typeforce(types.UInt32, index)
|
215 |
|
216 | var isHardened = index >= HDNode.HIGHEST_BIT
|
217 | var data = new Buffer(37)
|
218 |
|
219 |
|
220 | if (isHardened) {
|
221 | if (this.isNeutered()) throw new TypeError('Could not derive hardened child key')
|
222 |
|
223 |
|
224 | data[0] = 0x00
|
225 | this.keyPair.d.toBuffer(32).copy(data, 1)
|
226 | data.writeUInt32BE(index, 33)
|
227 |
|
228 |
|
229 | } else {
|
230 |
|
231 |
|
232 | this.keyPair.getPublicKeyBuffer().copy(data, 0)
|
233 | data.writeUInt32BE(index, 33)
|
234 | }
|
235 |
|
236 | var I = createHmac('sha512', this.chainCode).update(data).digest()
|
237 | var IL = I.slice(0, 32)
|
238 | var IR = I.slice(32)
|
239 |
|
240 | var pIL = BigInteger.fromBuffer(IL)
|
241 |
|
242 |
|
243 | if (pIL.compareTo(curve.n) >= 0) {
|
244 | return this.derive(index + 1)
|
245 | }
|
246 |
|
247 |
|
248 | var derivedKeyPair
|
249 | if (!this.isNeutered()) {
|
250 |
|
251 | var ki = pIL.add(this.keyPair.d).mod(curve.n)
|
252 |
|
253 |
|
254 | if (ki.signum() === 0) {
|
255 | return this.derive(index + 1)
|
256 | }
|
257 |
|
258 | derivedKeyPair = new ECPair(ki, null, {
|
259 | network: this.keyPair.network
|
260 | })
|
261 |
|
262 |
|
263 | } else {
|
264 |
|
265 |
|
266 | var Ki = curve.G.multiply(pIL).add(this.keyPair.Q)
|
267 |
|
268 |
|
269 | if (curve.isInfinity(Ki)) {
|
270 | return this.derive(index + 1)
|
271 | }
|
272 |
|
273 | derivedKeyPair = new ECPair(null, Ki, {
|
274 | network: this.keyPair.network
|
275 | })
|
276 | }
|
277 |
|
278 | var hd = new HDNode(derivedKeyPair, IR)
|
279 | hd.depth = this.depth + 1
|
280 | hd.index = index
|
281 | hd.parentFingerprint = this.getFingerprint().readUInt32BE(0)
|
282 |
|
283 | return hd
|
284 | }
|
285 |
|
286 | HDNode.prototype.deriveHardened = function (index) {
|
287 | typeforce(types.UInt31, index)
|
288 |
|
289 |
|
290 | return this.derive(index + HDNode.HIGHEST_BIT)
|
291 | }
|
292 |
|
293 |
|
294 |
|
295 | HDNode.prototype.isNeutered = function () {
|
296 | return !(this.keyPair.d)
|
297 | }
|
298 |
|
299 | HDNode.prototype.derivePath = function (path) {
|
300 | typeforce(types.BIP32Path, path)
|
301 |
|
302 | var splitPath = path.split('/')
|
303 | if (splitPath[0] === 'm') {
|
304 | if (this.parentFingerprint) {
|
305 | throw new Error('Not a master node')
|
306 | }
|
307 |
|
308 | splitPath = splitPath.slice(1)
|
309 | }
|
310 |
|
311 | return splitPath.reduce(function (prevHd, indexStr) {
|
312 | var index
|
313 | if (indexStr.slice(-1) === "'") {
|
314 | index = parseInt(indexStr.slice(0, -1), 10)
|
315 | return prevHd.deriveHardened(index)
|
316 | } else {
|
317 | index = parseInt(indexStr, 10)
|
318 | return prevHd.derive(index)
|
319 | }
|
320 | }, this)
|
321 | }
|
322 |
|
323 | HDNode.prototype.toString = HDNode.prototype.toBase58
|
324 |
|
325 | module.exports = HDNode
|