UNPKG

9.48 kBJavaScriptView Raw
1"use strict";
2var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3 if (k2 === undefined) k2 = k;
4 Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5}) : (function(o, m, k, k2) {
6 if (k2 === undefined) k2 = k;
7 o[k2] = m[k];
8}));
9var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10 Object.defineProperty(o, "default", { enumerable: true, value: v });
11}) : function(o, v) {
12 o["default"] = v;
13});
14var __importStar = (this && this.__importStar) || function (mod) {
15 if (mod && mod.__esModule) return mod;
16 var result = {};
17 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18 __setModuleDefault(result, mod);
19 return result;
20};
21var __importDefault = (this && this.__importDefault) || function (mod) {
22 return (mod && mod.__esModule) ? mod : { "default": mod };
23};
24Object.defineProperty(exports, "__esModule", { value: true });
25exports.fromQuorumWallet = exports.fromKryptoKit = exports.fromEtherCamp = exports.fromEtherWallet = void 0;
26const crypto = __importStar(require("crypto"));
27const ethereumjs_util_1 = require("ethereumjs-util");
28const scrypt_js_1 = require("scrypt-js");
29const index_1 = __importDefault(require("./index"));
30const utf8 = require('utf8');
31const aesjs = require('aes-js');
32function runCipherBuffer(cipher, data) {
33 return Buffer.concat([cipher.update(data), cipher.final()]);
34}
35const evpKdfDefaults = {
36 count: 1,
37 keysize: 16,
38 ivsize: 16,
39 digest: 'md5',
40};
41function mergeEvpKdfOptsWithDefaults(opts) {
42 if (!opts) {
43 return evpKdfDefaults;
44 }
45 return {
46 count: opts.count || evpKdfDefaults.count,
47 keysize: opts.keysize || evpKdfDefaults.keysize,
48 ivsize: opts.ivsize || evpKdfDefaults.ivsize,
49 digest: opts.digest || evpKdfDefaults.digest,
50 };
51}
52/*
53 * opts:
54 * - digest - digest algorithm, defaults to md5
55 * - count - hash iterations
56 * - keysize - desired key size
57 * - ivsize - desired IV size
58 *
59 * Algorithm form https://www.openssl.org/docs/manmaster/crypto/EVP_BytesToKey.html
60 *
61 * FIXME: not optimised at all
62 */
63function evp_kdf(data, salt, opts) {
64 const params = mergeEvpKdfOptsWithDefaults(opts);
65 // A single EVP iteration, returns `D_i`, where block equlas to `D_(i-1)`
66 function iter(block) {
67 let hash = crypto.createHash(params.digest);
68 hash.update(block);
69 hash.update(data);
70 hash.update(salt);
71 block = hash.digest();
72 for (let i = 1, len = params.count; i < len; i++) {
73 hash = crypto.createHash(params.digest);
74 hash.update(block);
75 block = hash.digest();
76 }
77 return block;
78 }
79 const ret = [];
80 let i = 0;
81 while (Buffer.concat(ret).length < params.keysize + params.ivsize) {
82 ret[i] = iter(i === 0 ? Buffer.alloc(0) : ret[i - 1]);
83 i++;
84 }
85 const tmp = Buffer.concat(ret);
86 return {
87 key: tmp.slice(0, params.keysize),
88 iv: tmp.slice(params.keysize, params.keysize + params.ivsize),
89 };
90}
91// http://stackoverflow.com/questions/25288311/cryptojs-aes-pattern-always-ends-with
92function decodeCryptojsSalt(input) {
93 const ciphertext = Buffer.from(input, 'base64');
94 if (ciphertext.slice(0, 8).toString() === 'Salted__') {
95 return {
96 salt: ciphertext.slice(8, 16),
97 ciphertext: ciphertext.slice(16),
98 };
99 }
100 return { ciphertext };
101}
102/*
103 * Third Party API: Import a wallet generated by EtherWallet
104 * This wallet format is created by https://github.com/SilentCicero/ethereumjs-accounts
105 * and used on https://www.myetherwallet.com/
106 */
107function fromEtherWallet(input, password) {
108 const json = typeof input === 'object' ? input : JSON.parse(input);
109 let privateKey;
110 if (!json.locked) {
111 if (json.private.length !== 64) {
112 throw new Error('Invalid private key length');
113 }
114 privateKey = Buffer.from(json.private, 'hex');
115 }
116 else {
117 if (typeof password !== 'string') {
118 throw new Error('Password required');
119 }
120 if (password.length < 7) {
121 throw new Error('Password must be at least 7 characters');
122 }
123 // the "encrypted" version has the low 4 bytes
124 // of the hash of the address appended
125 const hash = json.encrypted ? json.private.slice(0, 128) : json.private;
126 // decode openssl ciphertext + salt encoding
127 const cipher = decodeCryptojsSalt(hash);
128 if (!cipher.salt) {
129 throw new Error('Unsupported EtherWallet key format');
130 }
131 // derive key/iv using OpenSSL EVP as implemented in CryptoJS
132 const evp = evp_kdf(Buffer.from(password), cipher.salt, { keysize: 32, ivsize: 16 });
133 const decipher = crypto.createDecipheriv('aes-256-cbc', evp.key, evp.iv);
134 privateKey = runCipherBuffer(decipher, Buffer.from(cipher.ciphertext));
135 // NOTE: yes, they've run it through UTF8
136 privateKey = Buffer.from(utf8.decode(privateKey.toString()), 'hex');
137 }
138 const wallet = new index_1.default(privateKey);
139 if (wallet.getAddressString() !== json.address) {
140 throw new Error('Invalid private key or address');
141 }
142 return wallet;
143}
144exports.fromEtherWallet = fromEtherWallet;
145/**
146 * Third Party API: Import a brain wallet used by Ether.Camp
147 */
148function fromEtherCamp(passphrase) {
149 return new index_1.default((0, ethereumjs_util_1.keccak256)(Buffer.from(passphrase)));
150}
151exports.fromEtherCamp = fromEtherCamp;
152/**
153 * Third Party API: Import a wallet from a KryptoKit seed
154 */
155async function fromKryptoKit(entropy, password) {
156 function kryptoKitBrokenScryptSeed(buf) {
157 // js-scrypt calls `Buffer.from(String(salt), 'utf8')` on the seed even though it is a buffer
158 //
159 // The `buffer`` implementation used does the below transformation (doesn't matches the current version):
160 // https://github.com/feross/buffer/blob/67c61181b938b17d10dbfc0a545f713b8bd59de8/index.js
161 function decodeUtf8Char(str) {
162 try {
163 return decodeURIComponent(str);
164 }
165 catch (err) {
166 return String.fromCharCode(0xfffd); // UTF 8 invalid char
167 }
168 }
169 let res = '', tmp = '';
170 for (let i = 0; i < buf.length; i++) {
171 if (buf[i] <= 0x7f) {
172 res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i]);
173 tmp = '';
174 }
175 else {
176 tmp += '%' + buf[i].toString(16);
177 }
178 }
179 return Buffer.from(res + decodeUtf8Char(tmp));
180 }
181 if (entropy[0] === '#') {
182 entropy = entropy.slice(1);
183 }
184 const type = entropy[0];
185 entropy = entropy.slice(1);
186 let privateKey;
187 if (type === 'd') {
188 privateKey = (0, ethereumjs_util_1.sha256)((0, ethereumjs_util_1.toBuffer)(entropy));
189 }
190 else if (type === 'q') {
191 if (typeof password !== 'string') {
192 throw new Error('Password required');
193 }
194 const encryptedSeed = (0, ethereumjs_util_1.sha256)(Buffer.from(entropy.slice(0, 30)));
195 const checksum = entropy.slice(30, 46);
196 const salt = kryptoKitBrokenScryptSeed(encryptedSeed);
197 const aesKey = await (0, scrypt_js_1.scrypt)(Buffer.from(password, 'utf8'), salt, 16384, 8, 1, 32);
198 /* FIXME: try to use `crypto` instead of `aesjs`
199
200 // NOTE: ECB doesn't use the IV, so it can be anything
201 var decipher = crypto.createDecipheriv("aes-256-ecb", aesKey, Buffer.from(0))
202
203 // FIXME: this is a clear abuse, but seems to match how ECB in aesjs works
204 privKey = Buffer.concat([
205 decipher.update(encryptedSeed).slice(0, 16),
206 decipher.update(encryptedSeed).slice(0, 16),
207 ])
208 */
209 const decipher = new aesjs.ModeOfOperation.ecb(aesKey);
210 /* decrypt returns an Uint8Array, perhaps there is a better way to concatenate */
211 privateKey = Buffer.concat([
212 Buffer.from(decipher.decrypt(encryptedSeed.slice(0, 16))),
213 Buffer.from(decipher.decrypt(encryptedSeed.slice(16, 32))),
214 ]);
215 if (checksum.length > 0) {
216 if (checksum !== (0, ethereumjs_util_1.sha256)((0, ethereumjs_util_1.sha256)(privateKey)).slice(0, 8).toString('hex')) {
217 throw new Error('Failed to decrypt input - possibly invalid passphrase');
218 }
219 }
220 }
221 else {
222 throw new Error('Unsupported or invalid entropy type');
223 }
224 return new index_1.default(privateKey);
225}
226exports.fromKryptoKit = fromKryptoKit;
227/**
228 * Third Party API: Import a brain wallet used by Quorum Wallet
229 */
230function fromQuorumWallet(passphrase, userid) {
231 if (passphrase.length < 10) {
232 throw new Error('Passphrase must be at least 10 characters');
233 }
234 if (userid.length < 10) {
235 throw new Error('User id must be at least 10 characters');
236 }
237 const merged = passphrase + userid;
238 const seed = crypto.pbkdf2Sync(merged, merged, 2000, 32, 'sha256');
239 return new index_1.default(seed);
240}
241exports.fromQuorumWallet = fromQuorumWallet;
242const Thirdparty = {
243 fromEtherWallet,
244 fromEtherCamp,
245 fromKryptoKit,
246 fromQuorumWallet,
247};
248exports.default = Thirdparty;
249//# sourceMappingURL=thirdparty.js.map
\No newline at end of file