UNPKG

9.22 kBJavaScriptView Raw
1var base58check = require('bs58check')
2var bcrypto = require('./crypto')
3var createHmac = require('create-hmac')
4var typeforce = require('typeforce')
5var types = require('./types')
6var NETWORKS = require('./networks')
7
8var BigInteger = require('bigi')
9var ECPair = require('./ecpair')
10
11var ecurve = require('ecurve')
12var curve = ecurve.getCurveByName('secp256k1')
13
14function 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
26HDNode.HIGHEST_BIT = 0x80000000
27HDNode.LENGTH = 78
28HDNode.MASTER_SECRET = new Buffer('Bitcoin seed')
29
30HDNode.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 // In case IL is 0 or >= n, the master key is invalid
41 // This is handled by the ECPair constructor
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
50HDNode.fromSeedHex = function (hex, network) {
51 return HDNode.fromSeedBuffer(new Buffer(hex, 'hex'), network)
52}
53
54HDNode.fromBase58 = function (string, networks, skipValidation) {
55 var buffer = base58check.decode(string)
56 if (buffer.length !== 78) throw new Error('Invalid buffer length')
57
58 // 4 bytes: version bytes
59 var version = buffer.readUInt32BE(0)
60 var network
61
62 // list of networks?
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 // otherwise, assume a network object (or default to bitcoin)
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 // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ...
80 var depth = buffer[4]
81
82 // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key)
83 var parentFingerprint = buffer.readUInt32BE(5)
84 if (depth === 0) {
85 if (parentFingerprint !== 0x00000000) throw new Error('Invalid parent fingerprint')
86 }
87
88 // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized.
89 // This is encoded in MSB order. (0x00000000 if master key)
90 var index = buffer.readUInt32BE(9)
91 if (depth === 0 && index !== 0) throw new Error('Invalid index')
92
93 // 32 bytes: the chain code
94 var chainCode = buffer.slice(13, 45)
95 var keyPair
96
97 // 33 bytes: private key data (0x00 + k)
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 // 33 bytes: public key data (0x02 + X or 0x03 + X)
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 // Skip validation if requested for efficiency
113 if (!skipValidation) {
114 // Verify that the X coordinate in the public point corresponds to a point on the curve.
115 // If not, the extended public key is invalid.
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
132HDNode.prototype.getAddress = function () {
133 return this.keyPair.getAddress()
134}
135
136HDNode.prototype.getIdentifier = function () {
137 return bcrypto.hash160(this.keyPair.getPublicKeyBuffer())
138}
139
140HDNode.prototype.getFingerprint = function () {
141 return this.getIdentifier().slice(0, 4)
142}
143
144HDNode.prototype.getNetwork = function () {
145 return this.keyPair.getNetwork()
146}
147
148HDNode.prototype.getPublicKeyBuffer = function () {
149 return this.keyPair.getPublicKeyBuffer()
150}
151
152HDNode.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
165HDNode.prototype.sign = function (hash) {
166 return this.keyPair.sign(hash)
167}
168
169HDNode.prototype.verify = function (hash, signature) {
170 return this.keyPair.verify(hash, signature)
171}
172
173HDNode.prototype.toBase58 = function (__isPrivate) {
174 if (__isPrivate !== undefined) throw new TypeError('Unsupported argument in 2.0.0')
175
176 // Version
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 // 4 bytes: version bytes
182 buffer.writeUInt32BE(version, 0)
183
184 // 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ....
185 buffer.writeUInt8(this.depth, 4)
186
187 // 4 bytes: the fingerprint of the parent's key (0x00000000 if master key)
188 buffer.writeUInt32BE(this.parentFingerprint, 5)
189
190 // 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized.
191 // This is encoded in big endian. (0x00000000 if master key)
192 buffer.writeUInt32BE(this.index, 9)
193
194 // 32 bytes: the chain code
195 this.chainCode.copy(buffer, 13)
196
197 // 33 bytes: the public key or private key data
198 if (!this.isNeutered()) {
199 // 0x00 + k for private keys
200 buffer.writeUInt8(0, 45)
201 this.keyPair.d.toBuffer(32).copy(buffer, 46)
202
203 // 33 bytes: the public key
204 } else {
205 // X9.62 encoding for public keys
206 this.keyPair.getPublicKeyBuffer().copy(buffer, 45)
207 }
208
209 return base58check.encode(buffer)
210}
211
212// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#child-key-derivation-ckd-functions
213HDNode.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 // Hardened child
220 if (isHardened) {
221 if (this.isNeutered()) throw new TypeError('Could not derive hardened child key')
222
223 // data = 0x00 || ser256(kpar) || ser32(index)
224 data[0] = 0x00
225 this.keyPair.d.toBuffer(32).copy(data, 1)
226 data.writeUInt32BE(index, 33)
227
228 // Normal child
229 } else {
230 // data = serP(point(kpar)) || ser32(index)
231 // = serP(Kpar) || ser32(index)
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 // In case parse256(IL) >= n, proceed with the next value for i
243 if (pIL.compareTo(curve.n) >= 0) {
244 return this.derive(index + 1)
245 }
246
247 // Private parent key -> private child key
248 var derivedKeyPair
249 if (!this.isNeutered()) {
250 // ki = parse256(IL) + kpar (mod n)
251 var ki = pIL.add(this.keyPair.d).mod(curve.n)
252
253 // In case ki == 0, proceed with the next value for i
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 // Public parent key -> public child key
263 } else {
264 // Ki = point(parse256(IL)) + Kpar
265 // = G*IL + Kpar
266 var Ki = curve.G.multiply(pIL).add(this.keyPair.Q)
267
268 // In case Ki is the point at infinity, proceed with the next value for i
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
286HDNode.prototype.deriveHardened = function (index) {
287 typeforce(types.UInt31, index)
288
289 // Only derives hardened private keys by default
290 return this.derive(index + HDNode.HIGHEST_BIT)
291}
292
293// Private === not neutered
294// Public === neutered
295HDNode.prototype.isNeutered = function () {
296 return !(this.keyPair.d)
297}
298
299HDNode.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
323HDNode.prototype.toString = HDNode.prototype.toBase58
324
325module.exports = HDNode