@ethereumjs/wallet
Version:
Utilities for handling Ethereum keys
149 lines • 5.51 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.Thirdparty = exports.fromQuorumWallet = exports.fromEtherCamp = exports.fromEtherWallet = void 0;
const util_1 = require("@ethereumjs/util");
const base_1 = require("@scure/base");
const aes_js_1 = require("ethereum-cryptography/aes.js");
const keccak_js_1 = require("ethereum-cryptography/keccak.js");
const pbkdf2_js_1 = require("ethereum-cryptography/pbkdf2.js");
const js_md5_1 = require("js-md5");
const wallet_js_1 = require("./wallet.js");
const evpKdfDefaults = {
count: 1,
keysize: 16,
ivsize: 16,
digest: 'md5',
};
function mergeEvpKdfOptsWithDefaults(opts) {
if (!opts) {
return evpKdfDefaults;
}
return {
count: opts.count ?? evpKdfDefaults.count,
keysize: opts.keysize ?? evpKdfDefaults.keysize,
ivsize: opts.ivsize ?? evpKdfDefaults.ivsize,
digest: opts.digest ?? evpKdfDefaults.digest,
};
}
/*
* opts:
* - digest - digest algorithm, defaults to md5
* - count - hash iterations
* - keysize - desired key size
* - ivsize - desired IV size
*
* Algorithm form https://www.openssl.org/docs/manmaster/crypto/EVP_BytesToKey.html
*
* FIXME: not optimised at all
*/
function evp_kdf(data, salt, opts) {
const params = mergeEvpKdfOptsWithDefaults(opts);
// A single EVP iteration, returns `D_i`, where block equlas to `D_(i-1)`
function iter(block) {
if (params.digest !== 'md5')
throw new Error('Only md5 is supported in evp_kdf');
let hash = js_md5_1.md5.create();
hash.update(block);
hash.update(data);
hash.update(salt);
block = Uint8Array.from(hash.array());
for (let i = 1, len = params.count; i < len; i++) {
hash = js_md5_1.md5.create();
hash.update(block);
block = new Uint8Array(hash.arrayBuffer());
}
return block;
}
const ret = [];
let i = 0;
while ((0, util_1.concatBytes)(...ret).length < params.keysize + params.ivsize) {
ret[i] = iter(i === 0 ? new Uint8Array() : ret[i - 1]);
i++;
}
const tmp = (0, util_1.concatBytes)(...ret);
return {
key: tmp.subarray(0, params.keysize),
iv: tmp.subarray(params.keysize, params.keysize + params.ivsize),
};
}
// http://stackoverflow.com/questions/25288311/cryptojs-aes-pattern-always-ends-with
function decodeCryptojsSalt(input) {
const ciphertext = base_1.base64.decode(input);
if ((0, util_1.bytesToUtf8)(ciphertext.subarray(0, 8)) === 'Salted__') {
return {
salt: ciphertext.subarray(8, 16),
ciphertext: ciphertext.subarray(16),
};
}
return { ciphertext };
}
/*
* Third Party API: Import a wallet generated by EtherWallet
* This wallet format is created by https://github.com/SilentCicero/ethereumjs-accounts
* and used on https://www.myetherwallet.com/
*/
async function fromEtherWallet(input, password) {
const json = typeof input === 'object' ? input : JSON.parse(input);
let privateKey;
if (!json.locked) {
if (json.private.length !== 64) {
throw new Error('Invalid private key length');
}
privateKey = (0, util_1.unprefixedHexToBytes)(json.private);
}
else {
if (typeof password !== 'string') {
throw new Error('Password required');
}
if (password.length < 7) {
throw new Error('Password must be at least 7 characters');
}
// the "encrypted" version has the low 4 bytes
// of the hash of the address appended
const hash = json.encrypted ? json.private.slice(0, 128) : json.private;
// decode openssl ciphertext + salt encoding
const cipher = decodeCryptojsSalt(hash);
if (!cipher.salt) {
throw new Error('Unsupported EtherWallet key format');
}
// derive key/iv using OpenSSL EVP as implemented in CryptoJS
const evp = evp_kdf((0, util_1.utf8ToBytes)(password), cipher.salt, { keysize: 32, ivsize: 16 });
const pr = await (0, aes_js_1.decrypt)(cipher.ciphertext, evp.key, evp.iv, 'aes-256-cbc');
// NOTE: yes, they've run it through UTF8
privateKey = (0, util_1.unprefixedHexToBytes)((0, util_1.bytesToUtf8)(pr));
}
const wallet = new wallet_js_1.Wallet(privateKey);
if (wallet.getAddressString() !== json.address) {
throw new Error('Invalid private key or address');
}
return wallet;
}
exports.fromEtherWallet = fromEtherWallet;
/**
* Third Party API: Import a brain wallet used by Ether.Camp
*/
function fromEtherCamp(passphrase) {
return new wallet_js_1.Wallet((0, keccak_js_1.keccak256)((0, util_1.utf8ToBytes)(passphrase)));
}
exports.fromEtherCamp = fromEtherCamp;
/**
* Third Party API: Import a brain wallet used by Quorum Wallet
*/
function fromQuorumWallet(passphrase, userid) {
if (passphrase.length < 10) {
throw new Error('Passphrase must be at least 10 characters');
}
if (userid.length < 10) {
throw new Error('User id must be at least 10 characters');
}
const merged = (0, util_1.utf8ToBytes)(passphrase + userid);
const seed = (0, pbkdf2_js_1.pbkdf2Sync)(merged, merged, 2000, 32, 'sha256');
return new wallet_js_1.Wallet(seed);
}
exports.fromQuorumWallet = fromQuorumWallet;
exports.Thirdparty = {
fromEtherWallet,
fromEtherCamp,
fromQuorumWallet,
};
//# sourceMappingURL=thirdparty.js.map
;