UNPKG

12.9 kBJavaScriptView Raw
1/**
2 * Bip32: HD Wallets
3 * =================
4 *
5 * Bip32 is hierarchical deterministic wallets. The standard way to use this is:
6 * const bip32 = new Bip32().fromRandom()
7 * const bip32 = new Bip32().fromSeed(buf)
8 * const bip32 = new Bip32().fromString(string)
9 * const xprv = bip32.toString()
10 * const xpub = bip32.toPublic().toString()
11 *
12 * This code was originally copied from here:
13 *
14 * https://github.com/sarchar/brainwallet.github.com
15 *
16 * It has faced mostly cosmetic alterations since it was copied.
17 */
18'use strict'
19
20import { Bn } from './bn'
21import { Bw } from './bw'
22import { Base58Check } from './base-58-check'
23import { Constants } from './constants'
24import { Hash } from './hash'
25import { Point } from './point'
26import { PrivKey as PrivKeyClass } from './priv-key'
27import { PubKey } from './pub-key'
28import { Random } from './random'
29import { Struct } from './struct'
30import { Workers } from './workers'
31
32class Bip32 extends Struct {
33 constructor (
34 versionBytesNum,
35 depth,
36 parentFingerPrint,
37 childIndex,
38 chainCode,
39 privKey,
40 pubKey,
41 constants = null,
42 PrivKey = PrivKeyClass
43 ) {
44 super({
45 versionBytesNum,
46 depth,
47 parentFingerPrint,
48 childIndex,
49 chainCode,
50 privKey,
51 pubKey
52 })
53 constants = constants || Constants.Default.Bip32
54 this.Constants = constants
55 this.PrivKey = PrivKey
56 }
57
58 fromRandom () {
59 this.versionBytesNum = this.Constants.privKey
60 this.depth = 0x00
61 this.parentFingerPrint = Buffer.from([0, 0, 0, 0])
62 this.childIndex = 0
63 this.chainCode = Random.getRandomBuffer(32)
64 this.privKey = new this.PrivKey().fromRandom()
65 this.pubKey = new PubKey().fromPrivKey(this.privKey)
66 return this
67 }
68
69 static fromRandom () {
70 return new this().fromRandom()
71 }
72
73 fromString (str) {
74 return this.fromBuffer(Base58Check.decode(str))
75 }
76
77 /**
78 * Use workers to convert a bip32 string into a bip32 object without
79 * blocking.
80 */
81 async asyncFromString (str) {
82 const args = [str]
83 const workersResult = await Workers.asyncObjectMethod(
84 this,
85 'fromString',
86 args
87 )
88 return this.fromFastBuffer(workersResult.resbuf)
89 }
90
91 fromSeed (bytes) {
92 if (!Buffer.isBuffer(bytes)) {
93 throw new Error('bytes must be a buffer')
94 }
95 if (bytes.length < 128 / 8) {
96 throw new Error('Need more than 128 bits of entropy')
97 }
98 if (bytes.length > 512 / 8) {
99 throw new Error('More than 512 bits of entropy is nonstandard')
100 }
101 const hash = Hash.sha512Hmac(bytes, Buffer.from('Bitcoin seed'))
102
103 this.depth = 0x00
104 this.parentFingerPrint = Buffer.from([0, 0, 0, 0])
105 this.childIndex = 0
106 this.chainCode = hash.slice(32, 64)
107 this.versionBytesNum = this.Constants.privKey
108 this.privKey = new this.PrivKey().fromBn(Bn().fromBuffer(hash.slice(0, 32)))
109 this.pubKey = new PubKey().fromPrivKey(this.privKey)
110
111 return this
112 }
113
114 static fromSeed (bytes) {
115 return new this().fromSeed(bytes)
116 }
117
118 async asyncFromSeed (bytes) {
119 const workersResult = await Workers.asyncObjectMethod(this, 'fromSeed', [
120 bytes
121 ])
122 return this.fromFastBuffer(workersResult.resbuf)
123 }
124
125 static asyncFromSeed (bytes) {
126 return new this().asyncFromSeed(bytes)
127 }
128
129 fromBuffer (buf) {
130 // Both pub and private extended keys are 78 buf
131 if (buf.length !== 78) {
132 throw new Error('incorrect bip32 data length')
133 }
134
135 this.versionBytesNum = buf.slice(0, 4).readUInt32BE(0)
136 this.depth = buf.slice(4, 5).readUInt8(0)
137 this.parentFingerPrint = buf.slice(5, 9)
138 this.childIndex = buf.slice(9, 13).readUInt32BE(0)
139 this.chainCode = buf.slice(13, 45)
140 const keyBytes = buf.slice(45, 78)
141
142 const isPrivate = this.versionBytesNum === this.Constants.privKey
143 const isPublic = this.versionBytesNum === this.Constants.pubKey
144
145 if (isPrivate && keyBytes[0] === 0) {
146 this.privKey = new this.PrivKey().fromBn(
147 Bn().fromBuffer(keyBytes.slice(1, 33))
148 )
149 this.pubKey = new PubKey().fromPrivKey(this.privKey)
150 } else if (isPublic && (keyBytes[0] === 0x02 || keyBytes[0] === 0x03)) {
151 this.pubKey = new PubKey().fromDer(keyBytes)
152 } else {
153 throw new Error('Invalid key')
154 }
155
156 return this
157 }
158
159 /**
160 * This is a faster version of .fromBuffer that reads in the output from
161 * .toFastBuffer rather than from .toBuffer. .toFastBuffer outputs almost the
162 * same thing as .toBuffer, except the public key is uncompressed. That makes
163 * it larger, but also means that point multiplication doesn't have to be
164 * used to derive the y value. So reading it in is faster. The only thing we
165 * have to do is explicitely set the "compressed" value of public key to true
166 * after reading it in. That is because although .toFastBuffer and
167 * .fromFastBuffer transmit the public key in uncompressed form, we want it
168 * to be set to compressed when stored in memory.
169 */
170 fromFastBuffer (buf) {
171 if (buf.length === 0) {
172 return this
173 }
174 if (buf.length !== 78 && buf.length !== 78 + 33) {
175 throw new Error('incorrect bip32 fastBuffer data length: ' + buf.length)
176 }
177
178 this.versionBytesNum = buf.slice(0, 4).readUInt32BE(0)
179 this.depth = buf.slice(4, 5).readUInt8(0)
180 this.parentFingerPrint = buf.slice(5, 9)
181 this.childIndex = buf.slice(9, 13).readUInt32BE(0)
182 this.chainCode = buf.slice(13, 45)
183
184 const keyBytes = buf.slice(45, buf.length)
185
186 const isPrivate = this.versionBytesNum === this.Constants.privKey
187 const isPublic = this.versionBytesNum === this.Constants.pubKey
188
189 if (isPrivate && keyBytes[0] === 0 && buf.length === 78) {
190 this.privKey = new this.PrivKey().fromBn(
191 Bn().fromBuffer(keyBytes.slice(1, 33))
192 )
193 this.pubKey = new PubKey().fromPrivKey(this.privKey)
194 } else if (isPublic && buf.length === 78 + 33) {
195 this.pubKey = new PubKey().fromFastBuffer(keyBytes)
196 this.pubKey.compressed = true
197 } else {
198 throw new Error('Invalid key')
199 }
200
201 return this
202 }
203
204 derive (path) {
205 const e = path.split('/')
206
207 if (path === 'm') {
208 return this
209 }
210
211 let bip32 = this
212 for (const i in e) {
213 const c = e[i]
214
215 if (i === '0') {
216 if (c !== 'm') throw new Error('invalid path')
217 continue
218 }
219
220 if (
221 parseInt(c.replace("'", ''), 10).toString() !== c.replace("'", '')
222 ) {
223 throw new Error('invalid path')
224 }
225
226 const usePrivate = c.length > 1 && c[c.length - 1] === "'"
227 let childIndex =
228 parseInt(usePrivate ? c.slice(0, c.length - 1) : c, 10) & 0x7fffffff
229
230 if (usePrivate) {
231 childIndex += 0x80000000
232 }
233
234 bip32 = bip32.deriveChild(childIndex)
235 }
236
237 return bip32
238 }
239
240 async asyncDerive (path) {
241 const workersResult = await Workers.asyncObjectMethod(this, 'derive', [
242 path
243 ])
244 return new this.constructor().fromFastBuffer(workersResult.resbuf)
245 }
246
247 deriveChild (i) {
248 if (typeof i !== 'number') {
249 throw new Error('i must be a number')
250 }
251
252 let ib = []
253 ib.push((i >> 24) & 0xff)
254 ib.push((i >> 16) & 0xff)
255 ib.push((i >> 8) & 0xff)
256 ib.push(i & 0xff)
257 ib = Buffer.from(ib)
258
259 const usePrivate = (i & 0x80000000) !== 0
260
261 const isPrivate = this.versionBytesNum === this.Constants.privKey
262
263 if (usePrivate && (!this.privKey || !isPrivate)) {
264 throw new Error('Cannot do private key derivation without private key')
265 }
266
267 let ret = null
268 if (this.privKey) {
269 let data = null
270
271 if (usePrivate) {
272 data = Buffer.concat([
273 Buffer.from([0]),
274 this.privKey.bn.toBuffer({ size: 32 }),
275 ib
276 ])
277 } else {
278 data = Buffer.concat([this.pubKey.toBuffer({ size: 32 }), ib])
279 }
280
281 const hash = Hash.sha512Hmac(data, this.chainCode)
282 const il = Bn().fromBuffer(hash.slice(0, 32), { size: 32 })
283 const ir = hash.slice(32, 64)
284
285 // ki = IL + kpar (mod n).
286 const k = il.add(this.privKey.bn).mod(Point.getN())
287
288 ret = new this.constructor()
289 ret.chainCode = ir
290
291 ret.privKey = new this.PrivKey().fromBn(k)
292 ret.pubKey = new PubKey().fromPrivKey(ret.privKey)
293 } else {
294 const data = Buffer.concat([this.pubKey.toBuffer(), ib])
295 const hash = Hash.sha512Hmac(data, this.chainCode)
296 const il = Bn().fromBuffer(hash.slice(0, 32))
297 const ir = hash.slice(32, 64)
298
299 // Ki = (IL + kpar)*G = IL*G + Kpar
300 const ilG = Point.getG().mul(il)
301 const Kpar = this.pubKey.point
302 const Ki = ilG.add(Kpar)
303 const newpub = new PubKey()
304 newpub.point = Ki
305
306 ret = new this.constructor()
307 ret.chainCode = ir
308
309 ret.pubKey = newpub
310 }
311
312 ret.childIndex = i
313 const pubKeyhash = Hash.sha256Ripemd160(this.pubKey.toBuffer())
314 ret.parentFingerPrint = pubKeyhash.slice(0, 4)
315 ret.versionBytesNum = this.versionBytesNum
316 ret.depth = this.depth + 1
317
318 return ret
319 }
320
321 toPublic () {
322 const bip32 = new this.constructor().fromObject(this)
323 bip32.versionBytesNum = this.Constants.pubKey
324 bip32.privKey = undefined
325 return bip32
326 }
327
328 toBuffer () {
329 const isPrivate = this.versionBytesNum === this.Constants.privKey
330 const isPublic = this.versionBytesNum === this.Constants.pubKey
331 if (isPrivate) {
332 return new Bw()
333 .writeUInt32BE(this.versionBytesNum)
334 .writeUInt8(this.depth)
335 .write(this.parentFingerPrint)
336 .writeUInt32BE(this.childIndex)
337 .write(this.chainCode)
338 .writeUInt8(0)
339 .write(this.privKey.bn.toBuffer({ size: 32 }))
340 .toBuffer()
341 } else if (isPublic) {
342 if (this.pubKey.compressed === false) {
343 throw new Error(
344 'cannot convert bip32 to buffer if pubKey is not compressed'
345 )
346 }
347 return new Bw()
348 .writeUInt32BE(this.versionBytesNum)
349 .writeUInt8(this.depth)
350 .write(this.parentFingerPrint)
351 .writeUInt32BE(this.childIndex)
352 .write(this.chainCode)
353 .write(this.pubKey.toBuffer())
354 .toBuffer()
355 } else {
356 throw new Error('bip32: invalid versionBytesNum byte')
357 }
358 }
359
360 /**
361 * This is the "fast" analog of toBuffer. It is almost the same as toBuffer,
362 * and in fact is actually not any faster. The only difference is that it
363 * adds an uncompressed rather than compressed public key to the output. This
364 * is so that .fromFastBufer can read in the public key without having to do
365 * fancy, slow point multiplication to derive the y value of the public key.
366 * Thus, although .toFastBuffer is not any faster, .fromFastBuffer is faster.
367 */
368 toFastBuffer () {
369 if (!this.versionBytesNum) {
370 return Buffer.alloc(0)
371 }
372 const isPrivate = this.versionBytesNum === this.Constants.privKey
373 const isPublic = this.versionBytesNum === this.Constants.pubKey
374 if (isPrivate) {
375 return new Bw()
376 .writeUInt32BE(this.versionBytesNum)
377 .writeUInt8(this.depth)
378 .write(this.parentFingerPrint)
379 .writeUInt32BE(this.childIndex)
380 .write(this.chainCode)
381 .writeUInt8(0)
382 .write(this.privKey.bn.toBuffer({ size: 32 }))
383 .toBuffer()
384 } else if (isPublic) {
385 return new Bw()
386 .writeUInt32BE(this.versionBytesNum)
387 .writeUInt8(this.depth)
388 .write(this.parentFingerPrint)
389 .writeUInt32BE(this.childIndex)
390 .write(this.chainCode)
391 .write(this.pubKey.toFastBuffer())
392 .toBuffer()
393 } else {
394 throw new Error('bip32: invalid versionBytesNum byte')
395 }
396 }
397
398 toString () {
399 return Base58Check.encode(this.toBuffer())
400 }
401
402 /**
403 * Use workers to convert a bip32 object into a bip32 string without
404 * blocking.
405 */
406 async asyncToString () {
407 const workersResult = await Workers.asyncObjectMethod(this, 'toString', [])
408 return JSON.parse(workersResult.resbuf.toString())
409 }
410
411 toJSON () {
412 return this.toFastHex()
413 }
414
415 fromJSON (json) {
416 return this.fromFastHex(json)
417 }
418
419 isPrivate () {
420 return this.versionBytesNum === this.Constants.privKey
421 }
422}
423
424Bip32.Mainnet = class extends Bip32 {
425 constructor (
426 versionBytesNum,
427 depth,
428 parentFingerPrint,
429 childIndex,
430 chainCode,
431 privKey,
432 pubKey
433 ) {
434 super(
435 versionBytesNum,
436 depth,
437 parentFingerPrint,
438 childIndex,
439 chainCode,
440 privKey,
441 pubKey,
442 Constants.Mainnet.Bip32,
443 PrivKeyClass.Mainnet
444 )
445 }
446}
447
448Bip32.Testnet = class extends Bip32 {
449 constructor (
450 versionBytesNum,
451 depth,
452 parentFingerPrint,
453 childIndex,
454 chainCode,
455 privKey,
456 pubKey
457 ) {
458 super(
459 versionBytesNum,
460 depth,
461 parentFingerPrint,
462 childIndex,
463 chainCode,
464 privKey,
465 pubKey,
466 Constants.Testnet.Bip32,
467 PrivKeyClass.Testnet
468 )
469 }
470}
471
472export { Bip32 }