Skip to content

Commit d4bb489

Browse files
committed
Initial commit
0 parents  commit d4bb489

File tree

9 files changed

+971
-0
lines changed

9 files changed

+971
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
.nyc_output
3+
coverage

.travis.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
sudo: false
2+
language: node_js
3+
node_js:
4+
- "4"
5+
- "5"
6+
- "6"
7+
- "7"
8+
- "8"
9+
- "9"
10+
matrix:
11+
include:
12+
- node_js: "8"
13+
env: TEST_SUITE=standard
14+
- node_js: "8"
15+
env: TEST_SUITE=coverage
16+
env:
17+
- TEST_SUITE=unit
18+
script: npm run-script $TEST_SUITE

LICENSE

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Copyright (c) 2017, Daniel Cousens
2+
3+
Permission to use, copy, modify, and/or distribute this software for any
4+
purpose with or without fee is hereby granted, provided that the above
5+
copyright notice and this permission notice appear in all copies.
6+
7+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# bip32
2+
[![Build Status](https://travis-ci.org/bitcoinjs/bip32.png?branch=master)](https://travis-ci.org/bitcoinjs/bip32)
3+
[![NPM](https://img.shields.io/npm/v/bip32.svg)](https://www.npmjs.org/package/bip32)
4+
5+
[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
6+
7+
8+
## Example
9+
``` javascript
10+
let bip32 = require('bip32')
11+
let node = bip32.fromBase58('xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi')
12+
13+
let child = node.derivePath('m/0/0')
14+
// ...
15+
```
16+
17+
## LICENSE [ISC](LICENSE)

crypto.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
let createHash = require('create-hash')
2+
let createHmac = require('create-hmac')
3+
4+
function hash160 (buffer) {
5+
return createHash('rmd160').update(
6+
createHash('sha256').update(buffer).digest()
7+
).digest()
8+
}
9+
10+
function hmacSHA512 (key, data) {
11+
return createHmac('sha512', key).update(data).digest()
12+
}
13+
14+
module.exports = { hash160, hmacSHA512 }

index.js

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
let Buffer = require('safe-buffer').Buffer
2+
let bs58check = require('bs58check')
3+
let crypto = require('./crypto')
4+
let ecc = require('tiny-secp256k1')
5+
let typeforce = require('typeforce')
6+
let wif = require('wif')
7+
8+
let NETWORK_TYPE = typeforce.compile({
9+
wif: typeforce.UInt8,
10+
bip32: {
11+
public: typeforce.UInt32,
12+
private: typeforce.UInt32
13+
}
14+
})
15+
16+
let BITCOIN = {
17+
bip32: {
18+
public: 0x0488b21e,
19+
private: 0x0488ade4
20+
},
21+
wif: 0x80
22+
}
23+
24+
function BIP32 (d, Q, chainCode, network) {
25+
this.d = d || null
26+
this.Q = Q || ecc.pointFromScalar(d, true)
27+
if (this.Q === null) throw new TypeError('Bad public key')
28+
29+
this.chainCode = chainCode
30+
this.depth = 0
31+
this.index = 0
32+
this.network = network
33+
this.parentFingerprint = 0x00000000
34+
}
35+
36+
function fromSeed (seed, network) {
37+
typeforce(typeforce.Buffer, seed)
38+
if (seed.length < 16) throw new TypeError('Seed should be at least 128 bits')
39+
if (seed.length > 64) throw new TypeError('Seed should be at most 512 bits')
40+
if (network) typeforce(NETWORK_TYPE, network)
41+
network = network || BITCOIN
42+
43+
let I = crypto.hmacSHA512('Bitcoin seed', seed)
44+
let IL = I.slice(0, 32)
45+
let IR = I.slice(32)
46+
47+
// if IL is 0 or >= n, the master key is invalid
48+
if (!ecc.isPrivate(IL)) throw new TypeError('Private key not in range [1, n)')
49+
50+
return new BIP32(IL, null, IR, network)
51+
}
52+
53+
function fromBase58 (string, network) {
54+
let buffer = bs58check.decode(string)
55+
if (buffer.length !== 78) throw new TypeError('Invalid buffer length')
56+
if (network) typeforce(NETWORK_TYPE, network)
57+
network = network || BITCOIN
58+
59+
// 4 bytes: version bytes
60+
let version = buffer.readUInt32BE(0)
61+
if (version !== network.bip32.private &&
62+
version !== network.bip32.public) throw new TypeError('Invalid network version')
63+
64+
// 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ...
65+
let depth = buffer[4]
66+
67+
// 4 bytes: the fingerprint of the parent's key (0x00000000 if master key)
68+
let parentFingerprint = buffer.readUInt32BE(5)
69+
if (depth === 0) {
70+
if (parentFingerprint !== 0x00000000) throw new TypeError('Invalid parent fingerprint')
71+
}
72+
73+
// 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized.
74+
// This is encoded in MSB order. (0x00000000 if master key)
75+
let index = buffer.readUInt32BE(9)
76+
if (depth === 0 && index !== 0) throw new TypeError('Invalid index')
77+
78+
// 32 bytes: the chain code
79+
let chainCode = buffer.slice(13, 45)
80+
let hd
81+
82+
// 33 bytes: private key data (0x00 + k)
83+
if (version === network.bip32.private) {
84+
if (buffer.readUInt8(45) !== 0x00) throw new TypeError('Invalid private key')
85+
let k = buffer.slice(46, 78)
86+
87+
// if IL is 0 or >= n, the master key is invalid
88+
if (!ecc.isPrivate(k)) throw new TypeError('Private key not in range [1, n)')
89+
hd = new BIP32(k, null, chainCode, network)
90+
91+
// 33 bytes: public key data (0x02 + X or 0x03 + X)
92+
} else {
93+
let X = buffer.slice(45, 78)
94+
95+
// verify the X coordinate is a point on the curve
96+
if (!ecc.isPoint(X)) throw new TypeError('Point is not on the curve')
97+
hd = new BIP32(null, X, chainCode, network)
98+
}
99+
100+
hd.depth = depth
101+
hd.index = index
102+
hd.parentFingerprint = parentFingerprint
103+
return hd
104+
}
105+
106+
BIP32.prototype.getIdentifier = function () {
107+
return crypto.hash160(this.Q)
108+
}
109+
110+
BIP32.prototype.getFingerprint = function () {
111+
return this.getIdentifier().slice(0, 4)
112+
}
113+
114+
BIP32.prototype.getNetwork = function () {
115+
return this.network
116+
}
117+
118+
BIP32.prototype.getPublicKey = function () {
119+
return this.Q
120+
}
121+
122+
BIP32.prototype.neutered = function () {
123+
let neutered = new BIP32(null, this.Q, this.chainCode, this.network)
124+
neutered.depth = this.depth
125+
neutered.index = this.index
126+
neutered.parentFingerprint = this.parentFingerprint
127+
return neutered
128+
}
129+
130+
BIP32.prototype.toBase58 = function () {
131+
let network = this.network
132+
let version = (!this.isNeutered()) ? network.bip32.private : network.bip32.public
133+
let buffer = Buffer.allocUnsafe(78)
134+
135+
// 4 bytes: version bytes
136+
buffer.writeUInt32BE(version, 0)
137+
138+
// 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 descendants, ....
139+
buffer.writeUInt8(this.depth, 4)
140+
141+
// 4 bytes: the fingerprint of the parent's key (0x00000000 if master key)
142+
buffer.writeUInt32BE(this.parentFingerprint, 5)
143+
144+
// 4 bytes: child number. This is the number i in xi = xpar/i, with xi the key being serialized.
145+
// This is encoded in big endian. (0x00000000 if master key)
146+
buffer.writeUInt32BE(this.index, 9)
147+
148+
// 32 bytes: the chain code
149+
this.chainCode.copy(buffer, 13)
150+
151+
// 33 bytes: the public key or private key data
152+
if (!this.isNeutered()) {
153+
// 0x00 + k for private keys
154+
buffer.writeUInt8(0, 45)
155+
this.d.copy(buffer, 46)
156+
157+
// 33 bytes: the public key
158+
} else {
159+
// X9.62 encoding for public keys
160+
this.Q.copy(buffer, 45)
161+
}
162+
163+
return bs58check.encode(buffer)
164+
}
165+
166+
BIP32.prototype.toWIF = function () {
167+
if (!this.d) throw new TypeError('Missing private key')
168+
return wif.encode(this.network.wif, this.d, true)
169+
}
170+
171+
let HIGHEST_BIT = 0x80000000
172+
173+
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#child-key-derivation-ckd-functions
174+
BIP32.prototype.derive = function (index) {
175+
typeforce(typeforce.UInt32, index)
176+
177+
let isHardened = index >= HIGHEST_BIT
178+
let data = Buffer.allocUnsafe(37)
179+
180+
// Hardened child
181+
if (isHardened) {
182+
if (this.isNeutered()) throw new TypeError('Missing private key for hardened child key')
183+
184+
// data = 0x00 || ser256(kpar) || ser32(index)
185+
data[0] = 0x00
186+
this.d.copy(data, 1)
187+
data.writeUInt32BE(index, 33)
188+
189+
// Normal child
190+
} else {
191+
// data = serP(point(kpar)) || ser32(index)
192+
// = serP(Kpar) || ser32(index)
193+
this.Q.copy(data, 0)
194+
data.writeUInt32BE(index, 33)
195+
}
196+
197+
let I = crypto.hmacSHA512(this.chainCode, data)
198+
let IL = I.slice(0, 32)
199+
let IR = I.slice(32)
200+
201+
// if parse256(IL) >= n, proceed with the next value for i
202+
if (!ecc.isPrivate(IL)) return this.derive(index + 1)
203+
204+
// Private parent key -> private child key
205+
let hd
206+
if (!this.isNeutered()) {
207+
// ki = parse256(IL) + kpar (mod n)
208+
let ki = ecc.privateAdd(this.d, IL)
209+
210+
// In case ki == 0, proceed with the next value for i
211+
if (ki == null) return this.derive(index + 1)
212+
213+
hd = new BIP32(ki, null, IR, this.network)
214+
215+
// Public parent key -> public child key
216+
} else {
217+
// Ki = point(parse256(IL)) + Kpar
218+
// = G*IL + Kpar
219+
let Ki = ecc.pointAddScalar(this.Q, IL, true)
220+
221+
// In case Ki is the point at infinity, proceed with the next value for i
222+
if (Ki === null) return this.derive(index + 1)
223+
224+
hd = new BIP32(null, Ki, IR, this.network)
225+
}
226+
227+
hd.depth = this.depth + 1
228+
hd.index = index
229+
hd.parentFingerprint = this.getFingerprint().readUInt32BE(0)
230+
return hd
231+
}
232+
233+
let UINT31_MAX = Math.pow(2, 31) - 1
234+
function UInt31 (value) {
235+
return typeforce.UInt32(value) && value <= UINT31_MAX
236+
}
237+
238+
BIP32.prototype.deriveHardened = function (index) {
239+
typeforce(UInt31, index)
240+
241+
// Only derives hardened private keys by default
242+
return this.derive(index + HIGHEST_BIT)
243+
}
244+
245+
// Private === not neutered
246+
// Public === neutered
247+
BIP32.prototype.isNeutered = function () {
248+
return this.d === null
249+
}
250+
251+
function isBIP32Path (value) {
252+
return typeforce.String(value) && value.match(/^(m\/)?(\d+'?\/)*\d+'?$/)
253+
}
254+
255+
BIP32.prototype.derivePath = function (path) {
256+
typeforce(isBIP32Path, path)
257+
258+
let splitPath = path.split('/')
259+
if (splitPath[0] === 'm') {
260+
if (this.parentFingerprint) throw new TypeError('Expected master, got child')
261+
262+
splitPath = splitPath.slice(1)
263+
}
264+
265+
return splitPath.reduce(function (prevHd, indexStr) {
266+
let index
267+
if (indexStr.slice(-1) === "'") {
268+
index = parseInt(indexStr.slice(0, -1), 10)
269+
return prevHd.deriveHardened(index)
270+
} else {
271+
index = parseInt(indexStr, 10)
272+
return prevHd.derive(index)
273+
}
274+
}, this)
275+
}
276+
277+
BIP32.prototype.sign = function (hash) {
278+
return ecc.sign(hash, this.d)
279+
}
280+
281+
BIP32.prototype.verify = function (hash, signature) {
282+
return ecc.verify(hash, signature, this.Q)
283+
}
284+
285+
module.exports = {
286+
fromBase58,
287+
fromSeed
288+
}

0 commit comments

Comments
 (0)