ethers.js/admin/config.js

115 lines
3.2 KiB
JavaScript
Raw Normal View History

2019-05-15 01:25:46 +03:00
"use strict";
const fs = require("fs");
const os = require("os");
const resolve = require("path").resolve;
const AES = require("aes-js");
const scrypt = require("scrypt-js");
const { prompt } = require("../packages/cli");
2019-05-15 01:25:46 +03:00
const randomBytes = require("../packages/random").randomBytes;
const computeHmac = require("../packages/sha2").computeHmac;
const colorify = require("./log").colorify;
function getScrypt(message, password, salt) {
let progressBar = prompt.getProgressBar(message);
return new Promise((resolve, reject) => {
scrypt(Buffer.from(password), Buffer.from(salt), (1 << 17), 8, 1, 64, (error, progress, key) => {
if (error) { return reject(error); }
progressBar(progress);
if (key) { resolve(key); }
});
});
}
2019-09-21 05:38:03 +03:00
function Config(filename) {
this.salt = null;
this.dkey = null;
this.values = { };
this.filename = filename;
}
Config.prototype.load = async function() {
if (this.dkey) { return; }
let data = null;
if (fs.existsSync(this.filename)) {
data = JSON.parse(fs.readFileSync(this.filename));
} else {
data = {
salt: Buffer.from(randomBytes(32)).toString("hex")
};
2019-05-15 01:25:46 +03:00
}
2019-09-21 05:38:03 +03:00
this.salt = data.salt;
2019-05-15 01:25:46 +03:00
2019-09-21 05:38:03 +03:00
const password = await prompt.getPassword(colorify("Password (config-store): ", "bold"));
2019-05-15 01:25:46 +03:00
2019-09-21 05:38:03 +03:00
this.dkey = await getScrypt(colorify("Unlocking config", "bold"), password, this.salt);
2019-05-15 01:25:46 +03:00
2019-09-21 05:38:03 +03:00
if (data.ciphertext) {
const ciphertext = Buffer.from(data.ciphertext, "base64");
const iv = Buffer.from(data.iv, "base64");
const aes = new AES.ModeOfOperation.ctr(this.dkey.slice(0, 32), new AES.Counter(iv));
const plaintext = aes.decrypt(ciphertext);
const hmac = computeHmac("sha512", this.dkey.slice(32, 64), plaintext);
if (hmac !== data.hmac) {
throw new Error("wrong password");
}
2019-05-15 01:25:46 +03:00
2019-09-21 05:38:03 +03:00
this.values = JSON.parse(Buffer.from(plaintext).toString());
}
};
2019-05-15 01:25:46 +03:00
2019-09-21 05:38:03 +03:00
Config.prototype.save = function() {
this.values._junk = Buffer.from(randomBytes(16 + parseInt(Math.random() * 48))).toString("base64")
2019-05-15 01:25:46 +03:00
2019-09-21 05:38:03 +03:00
const plaintext = Buffer.from(JSON.stringify(this.values));
2019-05-15 01:25:46 +03:00
2019-09-21 05:38:03 +03:00
const iv = Buffer.from(randomBytes(16));
const hmac = computeHmac("sha512", this.dkey.slice(32, 64), plaintext);
2019-05-15 01:25:46 +03:00
2019-09-21 05:38:03 +03:00
const aes = new AES.ModeOfOperation.ctr(this.dkey.slice(0, 32), new AES.Counter(iv));
const ciphertext = Buffer.from(aes.encrypt(plaintext));
const data = {
2019-05-15 01:25:46 +03:00
ciphertext: ciphertext.toString("base64"),
iv: iv.toString("base64"),
2019-09-21 05:38:03 +03:00
salt: this.salt,
2019-05-15 01:25:46 +03:00
hmac: hmac
};
2019-09-21 05:38:03 +03:00
fs.writeFileSync(this.filename, JSON.stringify(data, null, 2));
}
Config.prototype.get = async function(key) {
await this.load();
return this.values[key];
};
Config.prototype.set = async function(key, value) {
await this.load();
this.values[key] = value;
this.save();
};
Config.prototype.lock = function() {
this.salt = this.dkey = null;
2019-05-15 01:25:46 +03:00
}
2019-09-21 05:38:03 +03:00
const config = new Config(resolve(os.homedir(), ".ethers-dist"));
2019-05-15 01:25:46 +03:00
module.exports = {
2019-09-21 05:38:03 +03:00
get: function(key) {
return config.get(key);
},
set: function(key, value) {
config.set(key, value);
},
lock: function() {
config.lock();
}
2019-05-15 01:25:46 +03:00
}