forked from BitGo/BitGoJS
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbitcoin.js
More file actions
174 lines (148 loc) · 5.08 KB
/
Copy pathbitcoin.js
File metadata and controls
174 lines (148 loc) · 5.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
const common = require('./common');
const bitcoin = require('bitgo-utxo-lib');
const ecurve = require('ecurve');
const curve = ecurve.getCurveByName('secp256k1');
const BigInteger = require('bigi');
const createHmac = require('create-hmac');
const HDNode = bitcoin.HDNode;
let secp256k1;
try {
secp256k1 = require('secp256k1');
} catch (err) {
console.log('running without secp256k1 acceleration');
}
// Check for IE, and disable secp256k1, due to:
// https://github.com/indutny/bn.js/issues/133
const isIE = (({}).constructor.name === undefined);
if (isIE) {
secp256k1 = undefined;
}
bitcoin.getNetwork = function(network) {
network = network || common.getNetwork();
return bitcoin.networks[network];
};
bitcoin.makeRandomKey = function() {
return bitcoin.ECPair.makeRandom({ network: bitcoin.getNetwork() });
};
HDNode.prototype.getKey = function(network) {
network = network || bitcoin.getNetwork();
const k = this.keyPair;
const result = new bitcoin.ECPair(k.d, k.d ? null : k.Q, { network: network, compressed: k.compressed });
// Creating Q from d takes ~25ms, so if it's not created, use native bindings to pre-compute
if (!result.__Q && secp256k1) {
result.__Q = ecurve.Point.decodeFrom(curve, secp256k1.publicKeyCreate(k.d.toBuffer(32), false));
}
return result;
};
/**
* Derive a child HDNode from a parent HDNode and index. Uses secp256k1 to speed
* up public key derivations by 100x vs. bitcoinjs-lib implementation.
*
* @param {HDNode} hdnode parent HDNode
* @param {Number} index child index
* @returns {HDNode} derived HDNode
*/
const deriveFast = function(hdnode, index) {
// no fast path for private key derivations -- delegate to standard method
if (!secp256k1 || hdnode.keyPair.d) {
return hdnode.derive(index);
}
const isHardened = index >= bitcoin.HDNode.HIGHEST_BIT;
if (isHardened) {
throw new Error('cannot derive hardened key from public key');
}
const indexBuffer = new Buffer(4);
indexBuffer.writeUInt32BE(index, 0);
// data = serP(point(kpar)) || ser32(index)
// = serP(Kpar) || ser32(index)
const data = Buffer.concat([
hdnode.keyPair.getPublicKeyBuffer(),
indexBuffer
]);
const I = createHmac('sha512', hdnode.chainCode).update(data).digest();
const IL = I.slice(0, 32);
const IR = I.slice(32);
const pIL = BigInteger.fromBuffer(IL);
// In case parse256(IL) >= n, proceed with the next value for i
if (pIL.compareTo(curve.n) >= 0) {
return deriveFast(hdnode, index + 1);
}
// Private parent key -> private child key
// Ki = point(parse256(IL)) + Kpar
// = G*IL + Kpar
// The expensive op is the point multiply -- use secp256k1 lib to do that
const Ki = ecurve.Point.decodeFrom(curve, secp256k1.publicKeyCreate(IL, false)).add(hdnode.keyPair.Q);
// In case Ki is the point at infinity, proceed with the next value for i
if (curve.isInfinity(Ki)) {
return deriveFast(hdnode, index + 1);
}
const keyPair = new bitcoin.ECPair(null, Ki, { network: hdnode.keyPair.network });
const hd = new bitcoin.HDNode(keyPair, IR);
hd.depth = hdnode.depth + 1;
hd.index = index;
hd.parentFingerprint = hdnode.getFingerprint().readUInt32BE(0);
return hd;
};
if (secp256k1) {
bitcoin.ECPair.prototype.sign = function(hash) {
if (!this.d) {
throw new Error('Missing private key');
}
const sig = secp256k1.sign(hash, this.d.toBuffer(32)).signature;
return bitcoin.ECSignature.fromDER(secp256k1.signatureExport(sig));
};
bitcoin.ECPair.prototype.verify = function(hash, signature) {
signature = new bitcoin.ECSignature(signature.r, signature.s);
signature = secp256k1.signatureNormalize(secp256k1.signatureImport(signature.toDER()));
return secp256k1.verify(hash, signature, this.getPublicKeyBuffer());
};
}
/**
* Derive a BIP32 path, given a root key
* We cache keys at each level of hierarchy we derive, to avoid re-deriving (approx 25ms per derivation)
* @param rootKey key to derive off
* @param path the path, e.g. 'm/0/0/0/1'
* @returns {*} the derived hd key
*/
bitcoin.hdPath = function(rootKey) {
const cache = {};
const derive = function(path) {
const components = path.split('/').filter(function(c) {
return c !== '';
});
// strip any extraneous / characters
path = components.join('/');
if (cache[path]) {
return cache[path];
}
const len = components.length;
if (len === 0 || len === 1 && components[0] === 'm') {
return rootKey;
}
const parentPath = components.slice(0, len - 1).join('/');
const parentKey = derive(parentPath);
const el = components[len - 1];
let hardened = false;
if (el[el.length - 1] === "'") {
hardened = true;
}
const index = parseInt(el, 10);
let derived;
if (hardened) {
derived = parentKey.deriveHardened(index);
} else {
derived = deriveFast(parentKey, index);
}
cache[path] = derived;
return derived;
};
const deriveKey = function(path) {
const hdNode = this.derive(path);
return hdNode.keyPair;
};
return {
derive: derive,
deriveKey: deriveKey
};
};
module.exports = bitcoin;