laksa-extend-keystore
Version:
Extend keystore for laksa-core-crypto
200 lines (178 loc) • 7.45 kB
JavaScript
/**
* This source code is being disclosed to you solely for the purpose of your participation in
* testing Zilliqa and Laksa. You may view, compile and run the code for that purpose and pursuant to
* the protocols and algorithms that are programmed into, and intended by, the code. You may
* not do anything else with the code without express permission from Zilliqa Research Pte. Ltd.,
* including modifying or publishing the code (or any part of it), and developing or forming
* another public or private blockchain network. This source code is provided ‘as is’ and no
* warranties are given as to title or non-infringement, merchantability or fitness for purpose
* and, to the extent permitted by law, all liability for your use of the code is disclaimed.
* Some programs in this code are governed by the GNU General Public License v3.0 (available at
* https://www.gnu.org/licenses/gpl-3.0.en.html) (‘GPLv3’). The programs that are governed by
* GPLv3.0 are those programs that are located in the folders src/depends and tests/depends
* and which include a reference to GPLv3 in their program files.
*/
;
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var _regeneratorRuntime = _interopDefault(require('@babel/runtime/regenerator'));
require('core-js/modules/es6.regexp.to-string');
require('regenerator-runtime/runtime');
var _asyncToGenerator = _interopDefault(require('@babel/runtime/helpers/asyncToGenerator'));
require('core-js/modules/es6.promise');
var aes = _interopDefault(require('aes-js'));
var pbkdf2 = require('pbkdf2');
var scrypt = _interopDefault(require('scrypt.js'));
var uuid = _interopDefault(require('uuid'));
var laksaCoreCrypto = require('laksa-core-crypto');
var ALGO_IDENTIFIER = 'aes-128-ctr';
/**
* getDerivedKey
*
* NOTE: only scrypt and pbkdf2 are supported.
*
* @param {Buffer} key - the passphrase
* @param {KDF} kdf - the key derivation function to be used
* @param {KDFParams} params - params for the kdf
*
* @returns {Promise<Buffer>}
*/
var getDerivedKey = function getDerivedKey(key, kdf, params) {
return new Promise(function (resolve, reject) {
var n = params.n,
r = params.r,
p = params.p,
dklen = params.dklen;
var salt = Buffer.from(params.salt);
if (kdf !== 'pbkdf2' && kdf !== 'scrypt') {
reject(new Error('Only pbkdf2 and scrypt are supported'));
}
var derivedKey = kdf === 'scrypt' ? scrypt(key, salt, n, r, p, dklen) : pbkdf2.pbkdf2Sync(key, salt, n, dklen, 'sha256');
resolve(derivedKey);
});
};
/**
* encryptPrivateKey
*
* Encodes and encrypts an account in the format specified by
* https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition.
* However, note that, in keeping with the hash function used by Zilliqa's
* core protocol, the MAC is generated using sha256 instead of keccak.
*
* NOTE: only scrypt and pbkdf2 are supported.
*
* @param {KDF} kdf - the key derivation function to be used
* @param {string} privateKey - hex-encoded private key
* @param {string} passphrase - a passphrase used for encryption
*
* @returns {Promise<string>}
*/
var encrypt =
/*#__PURE__*/
function () {
var _ref = _asyncToGenerator(
/*#__PURE__*/
_regeneratorRuntime.mark(function _callee(privateKey, passphrase, options) {
var salt, iv, kdf, level, n, kdfparams, derivedKey, CTR, cipher, ciphertext;
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
salt = laksaCoreCrypto.randomBytes(32);
iv = Buffer.from(laksaCoreCrypto.randomBytes(16), 'hex');
kdf = options !== undefined ? options.kdf ? options.kdf : 'scrypt' : 'scrypt';
level = options !== undefined ? options.level ? options.level : 8192 : 8192;
n = kdf === 'pbkdf2' ? 262144 : level;
kdfparams = {
salt: salt,
n: n,
r: 8,
p: 1,
dklen: 32
};
_context.next = 8;
return getDerivedKey(Buffer.from(passphrase), kdf, kdfparams);
case 8:
derivedKey = _context.sent;
CTR = aes.ModeOfOperation.ctr;
cipher = new CTR(derivedKey.slice(0, 16), new aes.Counter(iv));
ciphertext = Buffer.from(cipher.encrypt(Buffer.from(privateKey, 'hex')));
return _context.abrupt("return", {
crypto: {
cipher: ALGO_IDENTIFIER,
cipherparams: {
iv: iv.toString('hex')
},
ciphertext: ciphertext.toString('hex'),
kdf: kdf,
kdfparams: kdfparams,
mac: laksaCoreCrypto.hashjs.hmac(laksaCoreCrypto.hashjs.sha256, derivedKey, 'hex').update(Buffer.concat([derivedKey.slice(16, 32), ciphertext, iv, Buffer.from(ALGO_IDENTIFIER)]), 'hex').digest('hex')
},
id: uuid.v4({
random: laksaCoreCrypto.randomBytes(16)
}),
version: 3
});
case 13:
case "end":
return _context.stop();
}
}
}, _callee);
}));
return function encrypt(_x, _x2, _x3) {
return _ref.apply(this, arguments);
};
}();
/**
* decryptPrivateKey
*
* Recovers the private key from a keystore file using the given passphrase.
*
* @param {string} passphrase
* @param {KeystoreV3} keystore
* @returns {Promise<string>}
*/
var decrypt =
/*#__PURE__*/
function () {
var _ref2 = _asyncToGenerator(
/*#__PURE__*/
_regeneratorRuntime.mark(function _callee2(keystore, passphrase) {
var ciphertext, iv, kdfparams, derivedKey, mac, CTR, cipher, decrypted;
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
ciphertext = Buffer.from(keystore.crypto.ciphertext, 'hex');
iv = Buffer.from(keystore.crypto.cipherparams.iv, 'hex');
kdfparams = keystore.crypto.kdfparams;
_context2.next = 5;
return getDerivedKey(Buffer.from(passphrase), keystore.crypto.kdf, kdfparams);
case 5:
derivedKey = _context2.sent;
mac = laksaCoreCrypto.hashjs.hmac(laksaCoreCrypto.hashjs.sha256, derivedKey, 'hex').update(Buffer.concat([derivedKey.slice(16, 32), ciphertext, iv, Buffer.from(ALGO_IDENTIFIER)]), 'hex').digest('hex');
if (!(mac.toUpperCase() !== keystore.crypto.mac.toUpperCase())) {
_context2.next = 9;
break;
}
return _context2.abrupt("return", Promise.reject(new Error('Failed to decrypt.')));
case 9:
CTR = aes.ModeOfOperation.ctr;
cipher = new CTR(derivedKey.slice(0, 16), new aes.Counter(iv));
decrypted = Buffer.from(cipher.decrypt(ciphertext)).toString('hex');
return _context2.abrupt("return", decrypted);
case 13:
case "end":
return _context2.stop();
}
}
}, _callee2);
}));
return function decrypt(_x4, _x5) {
return _ref2.apply(this, arguments);
};
}();
exports.uuid = uuid;
exports.encrypt = encrypt;
exports.decrypt = decrypt;