1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | 'use strict'
|
19 |
|
20 | import { Bn } from './bn'
|
21 | import { Bw } from './bw'
|
22 | import { Base58Check } from './base-58-check'
|
23 | import { Constants } from './constants'
|
24 | import { Hash } from './hash'
|
25 | import { Point } from './point'
|
26 | import { PrivKey as PrivKeyClass } from './priv-key'
|
27 | import { PubKey } from './pub-key'
|
28 | import { Random } from './random'
|
29 | import { Struct } from './struct'
|
30 | import { Workers } from './workers'
|
31 |
|
32 | class 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 |
|
79 |
|
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 |
|
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 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
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 |
|
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 |
|
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 |
|
362 |
|
363 |
|
364 |
|
365 |
|
366 |
|
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 |
|
404 |
|
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 |
|
424 | Bip32.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 |
|
448 | Bip32.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 |
|
472 | export { Bip32 }
|