UNPKG

24.5 kBJavaScriptView Raw
1"use strict";
2var __assign = (this && this.__assign) || function () {
3 __assign = Object.assign || function(t) {
4 for (var s, i = 1, n = arguments.length; i < n; i++) {
5 s = arguments[i];
6 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7 t[p] = s[p];
8 }
9 return t;
10 };
11 return __assign.apply(this, arguments);
12};
13var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14 if (k2 === undefined) k2 = k;
15 Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
16}) : (function(o, m, k, k2) {
17 if (k2 === undefined) k2 = k;
18 o[k2] = m[k];
19}));
20var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21 Object.defineProperty(o, "default", { enumerable: true, value: v });
22}) : function(o, v) {
23 o["default"] = v;
24});
25var __importStar = (this && this.__importStar) || function (mod) {
26 if (mod && mod.__esModule) return mod;
27 var result = {};
28 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
29 __setModuleDefault(result, mod);
30 return result;
31};
32var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
33 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
34 return new (P || (P = Promise))(function (resolve, reject) {
35 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
36 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
37 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
38 step((generator = generator.apply(thisArg, _arguments || [])).next());
39 });
40};
41var __generator = (this && this.__generator) || function (thisArg, body) {
42 var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
43 return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
44 function verb(n) { return function (v) { return step([n, v]); }; }
45 function step(op) {
46 if (f) throw new TypeError("Generator is already executing.");
47 while (_) try {
48 if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
49 if (y = 0, t) op = [op[0] & 2, t.value];
50 switch (op[0]) {
51 case 0: case 1: t = op; break;
52 case 4: _.label++; return { value: op[1], done: false };
53 case 5: _.label++; y = op[1]; op = [0]; continue;
54 case 7: op = _.ops.pop(); _.trys.pop(); continue;
55 default:
56 if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
57 if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
58 if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
59 if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
60 if (t[2]) _.ops.pop();
61 _.trys.pop(); continue;
62 }
63 op = body.call(thisArg, _);
64 } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
65 if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
66 }
67};
68var __importDefault = (this && this.__importDefault) || function (mod) {
69 return (mod && mod.__esModule) ? mod : { "default": mod };
70};
71Object.defineProperty(exports, "__esModule", { value: true });
72exports.thirdparty = exports.hdkey = void 0;
73var crypto = __importStar(require("crypto"));
74var ethereumjs_util_1 = require("ethereumjs-util");
75var scrypt_js_1 = require("scrypt-js");
76var hdkey_1 = require("./hdkey");
77Object.defineProperty(exports, "hdkey", { enumerable: true, get: function () { return __importDefault(hdkey_1).default; } });
78var thirdparty_1 = require("./thirdparty");
79Object.defineProperty(exports, "thirdparty", { enumerable: true, get: function () { return __importDefault(thirdparty_1).default; } });
80var bs58check = require('bs58check');
81var randomBytes = require('randombytes');
82var uuidv4 = require('uuid').v4;
83function validateHexString(paramName, str, length) {
84 if (str.toLowerCase().startsWith('0x')) {
85 str = str.slice(2);
86 }
87 if (!str && !length) {
88 return str;
89 }
90 if (length % 2) {
91 throw new Error("Invalid length argument, must be an even number");
92 }
93 if (typeof length === 'number' && str.length !== length) {
94 throw new Error("Invalid " + paramName + ", string must be " + length + " hex characters");
95 }
96 if (!/^([0-9a-f]{2})+$/i.test(str)) {
97 var howMany = typeof length === 'number' ? length : 'empty or a non-zero even number of';
98 throw new Error("Invalid " + paramName + ", string must be " + howMany + " hex characters");
99 }
100 return str;
101}
102function validateBuffer(paramName, buff, length) {
103 if (!Buffer.isBuffer(buff)) {
104 var howManyHex = typeof length === 'number' ? "" + length * 2 : 'empty or a non-zero even number of';
105 var howManyBytes = typeof length === 'number' ? " (" + length + " bytes)" : '';
106 throw new Error("Invalid " + paramName + ", must be a string (" + howManyHex + " hex characters) or buffer" + howManyBytes);
107 }
108 if (typeof length === 'number' && buff.length !== length) {
109 throw new Error("Invalid " + paramName + ", buffer must be " + length + " bytes");
110 }
111 return buff;
112}
113function mergeToV3ParamsWithDefaults(params) {
114 var v3Defaults = {
115 cipher: 'aes-128-ctr',
116 kdf: 'scrypt',
117 salt: randomBytes(32),
118 iv: randomBytes(16),
119 uuid: randomBytes(16),
120 dklen: 32,
121 c: 262144,
122 n: 262144,
123 r: 8,
124 p: 1,
125 };
126 if (!params) {
127 return v3Defaults;
128 }
129 if (typeof params.salt === 'string') {
130 params.salt = Buffer.from(validateHexString('salt', params.salt), 'hex');
131 }
132 if (typeof params.iv === 'string') {
133 params.iv = Buffer.from(validateHexString('iv', params.iv, 32), 'hex');
134 }
135 if (typeof params.uuid === 'string') {
136 params.uuid = Buffer.from(validateHexString('uuid', params.uuid, 32), 'hex');
137 }
138 if (params.salt) {
139 validateBuffer('salt', params.salt);
140 }
141 if (params.iv) {
142 validateBuffer('iv', params.iv, 16);
143 }
144 if (params.uuid) {
145 validateBuffer('uuid', params.uuid, 16);
146 }
147 return __assign(__assign({}, v3Defaults), params);
148}
149function kdfParamsForPBKDF(opts) {
150 return {
151 dklen: opts.dklen,
152 salt: opts.salt,
153 c: opts.c,
154 prf: 'hmac-sha256',
155 };
156}
157function kdfParamsForScrypt(opts) {
158 return {
159 dklen: opts.dklen,
160 salt: opts.salt,
161 n: opts.n,
162 r: opts.r,
163 p: opts.p,
164 };
165}
166// wallet implementation
167var Wallet = /** @class */ (function () {
168 function Wallet(privateKey, publicKey) {
169 if (publicKey === void 0) { publicKey = undefined; }
170 this.privateKey = privateKey;
171 this.publicKey = publicKey;
172 if (privateKey && publicKey) {
173 throw new Error('Cannot supply both a private and a public key to the constructor');
174 }
175 if (privateKey && !(0, ethereumjs_util_1.isValidPrivate)(privateKey)) {
176 throw new Error('Private key does not satisfy the curve requirements (ie. it is invalid)');
177 }
178 if (publicKey && !(0, ethereumjs_util_1.isValidPublic)(publicKey)) {
179 throw new Error('Invalid public key');
180 }
181 }
182 // static methods
183 /**
184 * Create an instance based on a new random key.
185 *
186 * @param icapDirect setting this to `true` will generate an address suitable for the `ICAP Direct mode`
187 */
188 Wallet.generate = function (icapDirect) {
189 if (icapDirect === void 0) { icapDirect = false; }
190 if (icapDirect) {
191 var max = new ethereumjs_util_1.BN('088f924eeceeda7fe92e1f5b0fffffffffffffff', 16);
192 while (true) {
193 var privateKey = randomBytes(32);
194 if (new ethereumjs_util_1.BN((0, ethereumjs_util_1.privateToAddress)(privateKey)).lte(max)) {
195 return new Wallet(privateKey);
196 }
197 }
198 }
199 else {
200 return new Wallet(randomBytes(32));
201 }
202 };
203 /**
204 * Create an instance where the address is valid against the supplied pattern (**this will be very slow**)
205 */
206 Wallet.generateVanityAddress = function (pattern) {
207 if (!(pattern instanceof RegExp)) {
208 pattern = new RegExp(pattern);
209 }
210 while (true) {
211 var privateKey = randomBytes(32);
212 var address = (0, ethereumjs_util_1.privateToAddress)(privateKey);
213 if (pattern.test(address.toString('hex'))) {
214 return new Wallet(privateKey);
215 }
216 }
217 };
218 /**
219 * Create an instance based on a public key (certain methods will not be available)
220 *
221 * This method only accepts uncompressed Ethereum-style public keys, unless
222 * the `nonStrict` flag is set to true.
223 */
224 Wallet.fromPublicKey = function (publicKey, nonStrict) {
225 if (nonStrict === void 0) { nonStrict = false; }
226 if (nonStrict) {
227 publicKey = (0, ethereumjs_util_1.importPublic)(publicKey);
228 }
229 return new Wallet(undefined, publicKey);
230 };
231 /**
232 * Create an instance based on a BIP32 extended public key (xpub)
233 */
234 Wallet.fromExtendedPublicKey = function (extendedPublicKey) {
235 if (extendedPublicKey.slice(0, 4) !== 'xpub') {
236 throw new Error('Not an extended public key');
237 }
238 var publicKey = bs58check.decode(extendedPublicKey).slice(45);
239 // Convert to an Ethereum public key
240 return Wallet.fromPublicKey(publicKey, true);
241 };
242 /**
243 * Create an instance based on a raw private key
244 */
245 Wallet.fromPrivateKey = function (privateKey) {
246 return new Wallet(privateKey);
247 };
248 /**
249 * Create an instance based on a BIP32 extended private key (xprv)
250 */
251 Wallet.fromExtendedPrivateKey = function (extendedPrivateKey) {
252 if (extendedPrivateKey.slice(0, 4) !== 'xprv') {
253 throw new Error('Not an extended private key');
254 }
255 var tmp = bs58check.decode(extendedPrivateKey);
256 if (tmp[45] !== 0) {
257 throw new Error('Invalid extended private key');
258 }
259 return Wallet.fromPrivateKey(tmp.slice(46));
260 };
261 /**
262 * Import a wallet (Version 1 of the Ethereum wallet format).
263 *
264 * @param input A JSON serialized string, or an object representing V1 Keystore.
265 * @param password The keystore password.
266 */
267 Wallet.fromV1 = function (input, password) {
268 return __awaiter(this, void 0, void 0, function () {
269 var json, kdfparams, derivedKey, ciphertext, mac, decipher, seed;
270 return __generator(this, function (_a) {
271 switch (_a.label) {
272 case 0:
273 json = typeof input === 'object' ? input : JSON.parse(input);
274 if (json.Version !== '1') {
275 throw new Error('Not a V1 Wallet');
276 }
277 if (json.Crypto.KeyHeader.Kdf !== 'scrypt') {
278 throw new Error('Unsupported key derivation scheme');
279 }
280 kdfparams = json.Crypto.KeyHeader.KdfParams;
281 return [4 /*yield*/, (0, scrypt_js_1.scrypt)(Buffer.from(password), Buffer.from(json.Crypto.Salt, 'hex'), kdfparams.N, kdfparams.R, kdfparams.P, kdfparams.DkLen)];
282 case 1:
283 derivedKey = _a.sent();
284 ciphertext = Buffer.from(json.Crypto.CipherText, 'hex');
285 mac = (0, ethereumjs_util_1.keccak256)(Buffer.concat([derivedKey.slice(16, 32), ciphertext]));
286 if (mac.toString('hex') !== json.Crypto.MAC) {
287 throw new Error('Key derivation failed - possibly wrong passphrase');
288 }
289 decipher = crypto.createDecipheriv('aes-128-cbc', (0, ethereumjs_util_1.keccak256)(derivedKey.slice(0, 16)).slice(0, 16), Buffer.from(json.Crypto.IV, 'hex'));
290 seed = runCipherBuffer(decipher, ciphertext);
291 return [2 /*return*/, new Wallet(seed)];
292 }
293 });
294 });
295 };
296 /**
297 * Import a wallet (Version 3 of the Ethereum wallet format). Set `nonStrict` true to accept files with mixed-caps.
298 *
299 * @param input A JSON serialized string, or an object representing V3 Keystore.
300 * @param password The keystore password.
301 */
302 Wallet.fromV3 = function (input, password, nonStrict) {
303 if (nonStrict === void 0) { nonStrict = false; }
304 return __awaiter(this, void 0, void 0, function () {
305 var json, derivedKey, kdfparams, ciphertext, mac, decipher, seed;
306 return __generator(this, function (_a) {
307 switch (_a.label) {
308 case 0:
309 json = typeof input === 'object' ? input : JSON.parse(nonStrict ? input.toLowerCase() : input);
310 if (json.version !== 3) {
311 throw new Error('Not a V3 wallet');
312 }
313 if (!(json.crypto.kdf === 'scrypt')) return [3 /*break*/, 2];
314 kdfparams = json.crypto.kdfparams;
315 return [4 /*yield*/, (0, scrypt_js_1.scrypt)(Buffer.from(password), Buffer.from(kdfparams.salt, 'hex'), kdfparams.n, kdfparams.r, kdfparams.p, kdfparams.dklen)];
316 case 1:
317 // FIXME: support progress reporting callback
318 derivedKey = _a.sent();
319 return [3 /*break*/, 3];
320 case 2:
321 if (json.crypto.kdf === 'pbkdf2') {
322 kdfparams = json.crypto.kdfparams;
323 if (kdfparams.prf !== 'hmac-sha256') {
324 throw new Error('Unsupported parameters to PBKDF2');
325 }
326 derivedKey = crypto.pbkdf2Sync(Buffer.from(password), Buffer.from(kdfparams.salt, 'hex'), kdfparams.c, kdfparams.dklen, 'sha256');
327 }
328 else {
329 throw new Error('Unsupported key derivation scheme');
330 }
331 _a.label = 3;
332 case 3:
333 ciphertext = Buffer.from(json.crypto.ciphertext, 'hex');
334 mac = (0, ethereumjs_util_1.keccak256)(Buffer.concat([Buffer.from(derivedKey.slice(16, 32)), ciphertext]));
335 if (mac.toString('hex') !== json.crypto.mac) {
336 throw new Error('Key derivation failed - possibly wrong passphrase');
337 }
338 decipher = crypto.createDecipheriv(json.crypto.cipher, derivedKey.slice(0, 16), Buffer.from(json.crypto.cipherparams.iv, 'hex'));
339 seed = runCipherBuffer(decipher, ciphertext);
340 return [2 /*return*/, new Wallet(seed)];
341 }
342 });
343 });
344 };
345 /*
346 * Import an Ethereum Pre Sale wallet.
347 * Based on https://github.com/ethereum/pyethsaletool/blob/master/pyethsaletool.py
348 * JSON fields: encseed, ethaddr, btcaddr, email
349 *
350 * @param input A JSON serialized string, or an object representing EthSale Keystore.
351 * @param password The keystore password.
352 */
353 Wallet.fromEthSale = function (input, password) {
354 var json = typeof input === 'object' ? input : JSON.parse(input);
355 var encseed = Buffer.from(json.encseed, 'hex');
356 // key derivation
357 var derivedKey = crypto.pbkdf2Sync(password, password, 2000, 32, 'sha256').slice(0, 16);
358 // seed decoding (IV is first 16 bytes)
359 // NOTE: crypto (derived from openssl) when used with aes-*-cbc will handle PKCS#7 padding internally
360 // see also http://stackoverflow.com/a/31614770/4964819
361 var decipher = crypto.createDecipheriv('aes-128-cbc', derivedKey, encseed.slice(0, 16));
362 var seed = runCipherBuffer(decipher, encseed.slice(16));
363 var wallet = new Wallet((0, ethereumjs_util_1.keccak256)(seed));
364 if (wallet.getAddress().toString('hex') !== json.ethaddr) {
365 throw new Error('Decoded key mismatch - possibly wrong passphrase');
366 }
367 return wallet;
368 };
369 Object.defineProperty(Wallet.prototype, "pubKey", {
370 // private getters
371 /**
372 * Returns the wallet's public key.
373 */
374 get: function () {
375 if (!keyExists(this.publicKey)) {
376 this.publicKey = (0, ethereumjs_util_1.privateToPublic)(this.privateKey);
377 }
378 return this.publicKey;
379 },
380 enumerable: false,
381 configurable: true
382 });
383 Object.defineProperty(Wallet.prototype, "privKey", {
384 /**
385 * Returns the wallet's private key.
386 */
387 get: function () {
388 if (!keyExists(this.privateKey)) {
389 throw new Error('This is a public key only wallet');
390 }
391 return this.privateKey;
392 },
393 enumerable: false,
394 configurable: true
395 });
396 // public instance methods
397 /**
398 * Returns the wallet's private key.
399 *
400 */
401 // tslint:disable-next-line
402 Wallet.prototype.getPrivateKey = function () {
403 return this.privKey;
404 };
405 Wallet.prototype.getPrivateKeyString = function () {
406 return (0, ethereumjs_util_1.bufferToHex)(this.privKey);
407 };
408 /**
409 * Returns the wallet's public key.
410 */
411 // tslint:disable-next-line
412 Wallet.prototype.getPublicKey = function () {
413 return this.pubKey;
414 };
415 /**
416 * Returns the wallet's public key as a "0x" prefixed hex string
417 */
418 Wallet.prototype.getPublicKeyString = function () {
419 return (0, ethereumjs_util_1.bufferToHex)(this.getPublicKey());
420 };
421 /**
422 * Returns the wallet's address.
423 */
424 Wallet.prototype.getAddress = function () {
425 return (0, ethereumjs_util_1.publicToAddress)(this.pubKey);
426 };
427 /**
428 * Returns the wallet's address as a "0x" prefixed hex string
429 */
430 Wallet.prototype.getAddressString = function () {
431 return (0, ethereumjs_util_1.bufferToHex)(this.getAddress());
432 };
433 /**
434 * Returns the wallet's private key as a "0x" prefixed hex string checksummed
435 * according to [EIP 55](https://github.com/ethereum/EIPs/issues/55).
436 */
437 Wallet.prototype.getChecksumAddressString = function () {
438 return (0, ethereumjs_util_1.toChecksumAddress)(this.getAddressString());
439 };
440 /**
441 * Returns an Etherem Version 3 Keystore Format object representing the wallet
442 *
443 * @param password The password used to encrypt the Keystore.
444 * @param opts The options for the keystore. See [its spec](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) for more info.
445 */
446 Wallet.prototype.toV3 = function (password, opts) {
447 return __awaiter(this, void 0, void 0, function () {
448 var v3Params, kdfParams, derivedKey, _a, cipher, ciphertext, mac;
449 return __generator(this, function (_b) {
450 switch (_b.label) {
451 case 0:
452 if (!keyExists(this.privateKey)) {
453 throw new Error('This is a public key only wallet');
454 }
455 v3Params = mergeToV3ParamsWithDefaults(opts);
456 _a = v3Params.kdf;
457 switch (_a) {
458 case "pbkdf2" /* PBKDF */: return [3 /*break*/, 1];
459 case "scrypt" /* Scrypt */: return [3 /*break*/, 2];
460 }
461 return [3 /*break*/, 4];
462 case 1:
463 kdfParams = kdfParamsForPBKDF(v3Params);
464 derivedKey = crypto.pbkdf2Sync(Buffer.from(password), kdfParams.salt, kdfParams.c, kdfParams.dklen, 'sha256');
465 return [3 /*break*/, 5];
466 case 2:
467 kdfParams = kdfParamsForScrypt(v3Params);
468 return [4 /*yield*/, (0, scrypt_js_1.scrypt)(Buffer.from(password), kdfParams.salt, kdfParams.n, kdfParams.r, kdfParams.p, kdfParams.dklen)];
469 case 3:
470 // FIXME: support progress reporting callback
471 derivedKey = _b.sent();
472 return [3 /*break*/, 5];
473 case 4: throw new Error('Unsupported kdf');
474 case 5:
475 cipher = crypto.createCipheriv(v3Params.cipher, derivedKey.slice(0, 16), v3Params.iv);
476 if (!cipher) {
477 throw new Error('Unsupported cipher');
478 }
479 ciphertext = runCipherBuffer(cipher, this.privKey);
480 mac = (0, ethereumjs_util_1.keccak256)(Buffer.concat([Buffer.from(derivedKey.slice(16, 32)), Buffer.from(ciphertext)]));
481 return [2 /*return*/, {
482 version: 3,
483 id: uuidv4({ random: v3Params.uuid }),
484 // @ts-ignore - the official V3 keystore spec omits the address key
485 address: this.getAddress().toString('hex'),
486 crypto: {
487 ciphertext: ciphertext.toString('hex'),
488 cipherparams: { iv: v3Params.iv.toString('hex') },
489 cipher: v3Params.cipher,
490 kdf: v3Params.kdf,
491 kdfparams: __assign(__assign({}, kdfParams), { salt: kdfParams.salt.toString('hex') }),
492 mac: mac.toString('hex'),
493 },
494 }];
495 }
496 });
497 });
498 };
499 /**
500 * Return the suggested filename for V3 keystores.
501 */
502 Wallet.prototype.getV3Filename = function (timestamp) {
503 /*
504 * We want a timestamp like 2016-03-15T17-11-33.007598288Z. Date formatting
505 * is a pain in Javascript, everbody knows that. We could use moment.js,
506 * but decide to do it manually in order to save space.
507 *
508 * toJSON() returns a pretty close version, so let's use it. It is not UTC though,
509 * but does it really matter?
510 *
511 * Alternative manual way with padding and Date fields: http://stackoverflow.com/a/7244288/4964819
512 *
513 */
514 var ts = timestamp ? new Date(timestamp) : new Date();
515 return ['UTC--', ts.toJSON().replace(/:/g, '-'), '--', this.getAddress().toString('hex')].join('');
516 };
517 Wallet.prototype.toV3String = function (password, opts) {
518 return __awaiter(this, void 0, void 0, function () {
519 var _a, _b;
520 return __generator(this, function (_c) {
521 switch (_c.label) {
522 case 0:
523 _b = (_a = JSON).stringify;
524 return [4 /*yield*/, this.toV3(password, opts)];
525 case 1: return [2 /*return*/, _b.apply(_a, [_c.sent()])];
526 }
527 });
528 });
529 };
530 return Wallet;
531}());
532exports.default = Wallet;
533// helpers
534function runCipherBuffer(cipher, data) {
535 return Buffer.concat([cipher.update(data), cipher.final()]);
536}
537function keyExists(k) {
538 return k !== undefined && k !== null;
539}
540//# sourceMappingURL=index.js.map
\No newline at end of file