1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const crypto = require("./crypto");
|
4 | const testecc_1 = require("./testecc");
|
5 | const bs58check = require('bs58check');
|
6 | const typeforce = require('typeforce');
|
7 | const wif = require('wif');
|
8 | function BIP32Factory(ecc) {
|
9 | testecc_1.testEcc(ecc);
|
10 | const UINT256_TYPE = typeforce.BufferN(32);
|
11 | const NETWORK_TYPE = typeforce.compile({
|
12 | wif: typeforce.UInt8,
|
13 | bip32: {
|
14 | public: typeforce.UInt32,
|
15 | private: typeforce.UInt32,
|
16 | },
|
17 | });
|
18 | const BITCOIN = {
|
19 | messagePrefix: '\x18Bitcoin Signed Message:\n',
|
20 | bech32: 'bc',
|
21 | bip32: {
|
22 | public: 0x0488b21e,
|
23 | private: 0x0488ade4,
|
24 | },
|
25 | pubKeyHash: 0x00,
|
26 | scriptHash: 0x05,
|
27 | wif: 0x80,
|
28 | };
|
29 | const HIGHEST_BIT = 0x80000000;
|
30 | const UINT31_MAX = Math.pow(2, 31) - 1;
|
31 | function BIP32Path(value) {
|
32 | return (typeforce.String(value) && value.match(/^(m\/)?(\d+'?\/)*\d+'?$/) !== null);
|
33 | }
|
34 | function UInt31(value) {
|
35 | return typeforce.UInt32(value) && value <= UINT31_MAX;
|
36 | }
|
37 | function toXOnly(pubKey) {
|
38 | return pubKey.length === 32 ? pubKey : pubKey.slice(1, 33);
|
39 | }
|
40 | class Bip32Signer {
|
41 | constructor(__D, __Q) {
|
42 | this.__D = __D;
|
43 | this.__Q = __Q;
|
44 | this.lowR = false;
|
45 | }
|
46 | get publicKey() {
|
47 | if (this.__Q === undefined)
|
48 | this.__Q = Buffer.from(ecc.pointFromScalar(this.__D, true));
|
49 | return this.__Q;
|
50 | }
|
51 | get privateKey() {
|
52 | return this.__D;
|
53 | }
|
54 | sign(hash, lowR) {
|
55 | if (!this.privateKey)
|
56 | throw new Error('Missing private key');
|
57 | if (lowR === undefined)
|
58 | lowR = this.lowR;
|
59 | if (lowR === false) {
|
60 | return Buffer.from(ecc.sign(hash, this.privateKey));
|
61 | }
|
62 | else {
|
63 | let sig = Buffer.from(ecc.sign(hash, this.privateKey));
|
64 | const extraData = Buffer.alloc(32, 0);
|
65 | let counter = 0;
|
66 |
|
67 |
|
68 | while (sig[0] > 0x7f) {
|
69 | counter++;
|
70 | extraData.writeUIntLE(counter, 0, 6);
|
71 | sig = Buffer.from(ecc.sign(hash, this.privateKey, extraData));
|
72 | }
|
73 | return sig;
|
74 | }
|
75 | }
|
76 | signSchnorr(hash) {
|
77 | if (!this.privateKey)
|
78 | throw new Error('Missing private key');
|
79 | if (!ecc.signSchnorr)
|
80 | throw new Error('signSchnorr not supported by ecc library');
|
81 | return Buffer.from(ecc.signSchnorr(hash, this.privateKey));
|
82 | }
|
83 | verify(hash, signature) {
|
84 | return ecc.verify(hash, this.publicKey, signature);
|
85 | }
|
86 | verifySchnorr(hash, signature) {
|
87 | if (!ecc.verifySchnorr)
|
88 | throw new Error('verifySchnorr not supported by ecc library');
|
89 | return ecc.verifySchnorr(hash, this.publicKey.subarray(1, 33), signature);
|
90 | }
|
91 | }
|
92 | class BIP32 extends Bip32Signer {
|
93 | constructor(__D, __Q, chainCode, network, __DEPTH = 0, __INDEX = 0, __PARENT_FINGERPRINT = 0x00000000) {
|
94 | super(__D, __Q);
|
95 | this.chainCode = chainCode;
|
96 | this.network = network;
|
97 | this.__DEPTH = __DEPTH;
|
98 | this.__INDEX = __INDEX;
|
99 | this.__PARENT_FINGERPRINT = __PARENT_FINGERPRINT;
|
100 | typeforce(NETWORK_TYPE, network);
|
101 | }
|
102 | get depth() {
|
103 | return this.__DEPTH;
|
104 | }
|
105 | get index() {
|
106 | return this.__INDEX;
|
107 | }
|
108 | get parentFingerprint() {
|
109 | return this.__PARENT_FINGERPRINT;
|
110 | }
|
111 | get identifier() {
|
112 | return crypto.hash160(this.publicKey);
|
113 | }
|
114 | get fingerprint() {
|
115 | return this.identifier.slice(0, 4);
|
116 | }
|
117 | get compressed() {
|
118 | return true;
|
119 | }
|
120 |
|
121 |
|
122 | isNeutered() {
|
123 | return this.__D === undefined;
|
124 | }
|
125 | neutered() {
|
126 | return fromPublicKeyLocal(this.publicKey, this.chainCode, this.network, this.depth, this.index, this.parentFingerprint);
|
127 | }
|
128 | toBase58() {
|
129 | const network = this.network;
|
130 | const version = !this.isNeutered()
|
131 | ? network.bip32.private
|
132 | : network.bip32.public;
|
133 | const buffer = Buffer.allocUnsafe(78);
|
134 |
|
135 | buffer.writeUInt32BE(version, 0);
|
136 |
|
137 | buffer.writeUInt8(this.depth, 4);
|
138 |
|
139 | buffer.writeUInt32BE(this.parentFingerprint, 5);
|
140 |
|
141 |
|
142 | buffer.writeUInt32BE(this.index, 9);
|
143 |
|
144 | this.chainCode.copy(buffer, 13);
|
145 |
|
146 | if (!this.isNeutered()) {
|
147 |
|
148 | buffer.writeUInt8(0, 45);
|
149 | this.privateKey.copy(buffer, 46);
|
150 |
|
151 | }
|
152 | else {
|
153 |
|
154 | this.publicKey.copy(buffer, 45);
|
155 | }
|
156 | return bs58check.encode(buffer);
|
157 | }
|
158 | toWIF() {
|
159 | if (!this.privateKey)
|
160 | throw new TypeError('Missing private key');
|
161 | return wif.encode(this.network.wif, this.privateKey, true);
|
162 | }
|
163 |
|
164 | derive(index) {
|
165 | typeforce(typeforce.UInt32, index);
|
166 | const isHardened = index >= HIGHEST_BIT;
|
167 | const data = Buffer.allocUnsafe(37);
|
168 |
|
169 | if (isHardened) {
|
170 | if (this.isNeutered())
|
171 | throw new TypeError('Missing private key for hardened child key');
|
172 |
|
173 | data[0] = 0x00;
|
174 | this.privateKey.copy(data, 1);
|
175 | data.writeUInt32BE(index, 33);
|
176 |
|
177 | }
|
178 | else {
|
179 |
|
180 |
|
181 | this.publicKey.copy(data, 0);
|
182 | data.writeUInt32BE(index, 33);
|
183 | }
|
184 | const I = crypto.hmacSHA512(this.chainCode, data);
|
185 | const IL = I.slice(0, 32);
|
186 | const IR = I.slice(32);
|
187 |
|
188 | if (!ecc.isPrivate(IL))
|
189 | return this.derive(index + 1);
|
190 |
|
191 | let hd;
|
192 | if (!this.isNeutered()) {
|
193 |
|
194 | const ki = Buffer.from(ecc.privateAdd(this.privateKey, IL));
|
195 |
|
196 | if (ki == null)
|
197 | return this.derive(index + 1);
|
198 | hd = fromPrivateKeyLocal(ki, IR, this.network, this.depth + 1, index, this.fingerprint.readUInt32BE(0));
|
199 |
|
200 | }
|
201 | else {
|
202 |
|
203 |
|
204 | const Ki = Buffer.from(ecc.pointAddScalar(this.publicKey, IL, true));
|
205 |
|
206 | if (Ki === null)
|
207 | return this.derive(index + 1);
|
208 | hd = fromPublicKeyLocal(Ki, IR, this.network, this.depth + 1, index, this.fingerprint.readUInt32BE(0));
|
209 | }
|
210 | return hd;
|
211 | }
|
212 | deriveHardened(index) {
|
213 | typeforce(UInt31, index);
|
214 |
|
215 | return this.derive(index + HIGHEST_BIT);
|
216 | }
|
217 | derivePath(path) {
|
218 | typeforce(BIP32Path, path);
|
219 | let splitPath = path.split('/');
|
220 | if (splitPath[0] === 'm') {
|
221 | if (this.parentFingerprint)
|
222 | throw new TypeError('Expected master, got child');
|
223 | splitPath = splitPath.slice(1);
|
224 | }
|
225 | return splitPath.reduce((prevHd, indexStr) => {
|
226 | let index;
|
227 | if (indexStr.slice(-1) === `'`) {
|
228 | index = parseInt(indexStr.slice(0, -1), 10);
|
229 | return prevHd.deriveHardened(index);
|
230 | }
|
231 | else {
|
232 | index = parseInt(indexStr, 10);
|
233 | return prevHd.derive(index);
|
234 | }
|
235 | }, this);
|
236 | }
|
237 | tweak(t) {
|
238 | if (this.privateKey)
|
239 | return this.tweakFromPrivateKey(t);
|
240 | return this.tweakFromPublicKey(t);
|
241 | }
|
242 | tweakFromPublicKey(t) {
|
243 | const xOnlyPubKey = toXOnly(this.publicKey);
|
244 | const tweakedPublicKey = ecc.xOnlyPointAddTweak(xOnlyPubKey, t);
|
245 | if (!tweakedPublicKey || tweakedPublicKey.xOnlyPubkey === null)
|
246 | throw new Error('Cannot tweak public key!');
|
247 | const parityByte = Buffer.from([
|
248 | tweakedPublicKey.parity === 0 ? 0x02 : 0x03,
|
249 | ]);
|
250 | const tweakedPublicKeyCompresed = Buffer.concat([
|
251 | parityByte,
|
252 | tweakedPublicKey.xOnlyPubkey,
|
253 | ]);
|
254 | return new Bip32Signer(undefined, tweakedPublicKeyCompresed);
|
255 | }
|
256 | tweakFromPrivateKey(t) {
|
257 | const hasOddY = this.publicKey[0] === 3 ||
|
258 | (this.publicKey[0] === 4 && (this.publicKey[64] & 1) === 1);
|
259 | const privateKey = hasOddY
|
260 | ? ecc.privateNegate(this.privateKey)
|
261 | : this.privateKey;
|
262 | const tweakedPrivateKey = ecc.privateAdd(privateKey, t);
|
263 | if (!tweakedPrivateKey)
|
264 | throw new Error('Invalid tweaked private key!');
|
265 | return new Bip32Signer(Buffer.from(tweakedPrivateKey), undefined);
|
266 | }
|
267 | }
|
268 | function fromBase58(inString, network) {
|
269 | const buffer = bs58check.decode(inString);
|
270 | if (buffer.length !== 78)
|
271 | throw new TypeError('Invalid buffer length');
|
272 | network = network || BITCOIN;
|
273 |
|
274 | const version = buffer.readUInt32BE(0);
|
275 | if (version !== network.bip32.private && version !== network.bip32.public)
|
276 | throw new TypeError('Invalid network version');
|
277 |
|
278 | const depth = buffer[4];
|
279 |
|
280 | const parentFingerprint = buffer.readUInt32BE(5);
|
281 | if (depth === 0) {
|
282 | if (parentFingerprint !== 0x00000000)
|
283 | throw new TypeError('Invalid parent fingerprint');
|
284 | }
|
285 |
|
286 |
|
287 | const index = buffer.readUInt32BE(9);
|
288 | if (depth === 0 && index !== 0)
|
289 | throw new TypeError('Invalid index');
|
290 |
|
291 | const chainCode = buffer.slice(13, 45);
|
292 | let hd;
|
293 |
|
294 | if (version === network.bip32.private) {
|
295 | if (buffer.readUInt8(45) !== 0x00)
|
296 | throw new TypeError('Invalid private key');
|
297 | const k = buffer.slice(46, 78);
|
298 | hd = fromPrivateKeyLocal(k, chainCode, network, depth, index, parentFingerprint);
|
299 |
|
300 | }
|
301 | else {
|
302 | const X = buffer.slice(45, 78);
|
303 | hd = fromPublicKeyLocal(X, chainCode, network, depth, index, parentFingerprint);
|
304 | }
|
305 | return hd;
|
306 | }
|
307 | function fromPrivateKey(privateKey, chainCode, network) {
|
308 | return fromPrivateKeyLocal(privateKey, chainCode, network);
|
309 | }
|
310 | function fromPrivateKeyLocal(privateKey, chainCode, network, depth, index, parentFingerprint) {
|
311 | typeforce({
|
312 | privateKey: UINT256_TYPE,
|
313 | chainCode: UINT256_TYPE,
|
314 | }, { privateKey, chainCode });
|
315 | network = network || BITCOIN;
|
316 | if (!ecc.isPrivate(privateKey))
|
317 | throw new TypeError('Private key not in range [1, n)');
|
318 | return new BIP32(privateKey, undefined, chainCode, network, depth, index, parentFingerprint);
|
319 | }
|
320 | function fromPublicKey(publicKey, chainCode, network) {
|
321 | return fromPublicKeyLocal(publicKey, chainCode, network);
|
322 | }
|
323 | function fromPublicKeyLocal(publicKey, chainCode, network, depth, index, parentFingerprint) {
|
324 | typeforce({
|
325 | publicKey: typeforce.BufferN(33),
|
326 | chainCode: UINT256_TYPE,
|
327 | }, { publicKey, chainCode });
|
328 | network = network || BITCOIN;
|
329 |
|
330 | if (!ecc.isPoint(publicKey))
|
331 | throw new TypeError('Point is not on the curve');
|
332 | return new BIP32(undefined, publicKey, chainCode, network, depth, index, parentFingerprint);
|
333 | }
|
334 | function fromSeed(seed, network) {
|
335 | typeforce(typeforce.Buffer, seed);
|
336 | if (seed.length < 16)
|
337 | throw new TypeError('Seed should be at least 128 bits');
|
338 | if (seed.length > 64)
|
339 | throw new TypeError('Seed should be at most 512 bits');
|
340 | network = network || BITCOIN;
|
341 | const I = crypto.hmacSHA512(Buffer.from('Bitcoin seed', 'utf8'), seed);
|
342 | const IL = I.slice(0, 32);
|
343 | const IR = I.slice(32);
|
344 | return fromPrivateKey(IL, IR, network);
|
345 | }
|
346 | return {
|
347 | fromSeed,
|
348 | fromBase58,
|
349 | fromPublicKey,
|
350 | fromPrivateKey,
|
351 | };
|
352 | }
|
353 | exports.BIP32Factory = BIP32Factory;
|