if ("decrypt" === subcommand) {
let addr = args.shift() || "";
if (!addr) {
- decryptAll(null);
+ await decryptAll(null);
+ await Fs.writeFile(shadowPath, "", "utf8").catch(emptyStringOnErrEnoent);
return;
}
let keypath = await findWif(addr);
* @param {Array<String>} args
*/
async function setPassphrase({ _askPreviousPassphrase }, args) {
+ let result = {
+ passphrase: "",
+ changed: false,
+ };
let date = getFsDateString();
// get the old passphrase
if (false !== _askPreviousPassphrase) {
- await cmds.getPassphrase(null, []);
+ await cmds.getPassphrase({ _rotatePassphrase: true }, []);
}
// get the new passphrase
- let newPassphrase;
- for (;;) {
- newPassphrase = await Prompt.prompt("Enter (new) passphrase: ", {
- mask: true,
- });
- newPassphrase = newPassphrase.trim();
-
- let _newPassphrase = await Prompt.prompt("Enter passphrase again: ", {
- mask: true,
- });
- _newPassphrase = _newPassphrase.trim();
-
- let match = Cipher.secureCompare(newPassphrase, _newPassphrase);
- if (match) {
- break;
- }
-
- console.error("passphrases do not match");
- }
+ let newPassphrase = await promptPassphrase();
let curShadow = await Fs.readFile(shadowPath, "utf8").catch(
emptyStringOnErrEnoent,
);
await encryptAll(rawKeys, { rotateKey: true });
+ result.passphrase = newPassphrase;
+ result.changed = true;
+ return result;
+}
+
+async function promptPassphrase() {
+ let newPassphrase;
+ for (;;) {
+ newPassphrase = await Prompt.prompt("Enter (new) passphrase: ", {
+ mask: true,
+ });
+ newPassphrase = newPassphrase.trim();
+
+ let _newPassphrase = await Prompt.prompt("Enter passphrase again: ", {
+ mask: true,
+ });
+ _newPassphrase = _newPassphrase.trim();
+
+ let match = Cipher.secureCompare(newPassphrase, _newPassphrase);
+ if (match) {
+ break;
+ }
+
+ console.error("passphrases do not match");
+ }
return newPassphrase;
}
}
let date = getFsDateString();
+ let passphrase = cmds._getPassphrase();
+ if (!passphrase) {
+ let result = await cmds.getPassphrase({ _force: true }, []);
+ if (result.changed) {
+ // encryptAll was already called on rotation
+ return;
+ }
+ passphrase = result.passphrase;
+ }
+
console.info(`Encrypting...`);
console.info(``);
await rawKeys.reduce(async function (promise, key) {
console.info(`🙈 ${key.addr} [already encrypted]`);
return;
}
- let encWif = await maybeEncrypt(key.wif);
+ let encWif = await maybeEncrypt(key.wif, { force: true });
await safeSave(
Path.join(keysDir, `${key.addr}.wif`),
encWif,
}
/**
- * @param {Null} psuedoState
+ * @param {Object} opts
+ * @param {Boolean} [opts._rotatePassphrase]
+ * @param {Boolean} [opts._force]
* @param {Array<String>} args
*/
-cmds.getPassphrase = async function (psuedoState, args) {
+cmds.getPassphrase = async function ({ _rotatePassphrase, _force }, args) {
+ let result = {
+ passphrase: "",
+ changed: false,
+ };
+ /*
+ if (!_rotatePassphrase) {
+ let cachedphrase = cmds._getPassphrase();
+ if (cachedphrase) {
+ return cachedphrase;
+ }
+ }
+ */
+
// Three possible states:
// 1. no shadow file yet (ask to set one)
// 2. empty shadow file (initialized, but not set - don't ask to set one)
}
throw err;
});
+ if (!shadow && _force) {
+ needsInit = true;
+ }
// State 1: not initialized, what does the user want?
if (needsInit) {
for (;;) {
- let no = await Prompt.prompt(
- "Would you like to set an encryption passphrase? [Y/n]: ",
- );
+ let no;
+ if (!_force) {
+ no = await Prompt.prompt(
+ "Would you like to set an encryption passphrase? [Y/n]: ",
+ );
+ }
// Set a passphrase and create shadow file
if (!no || ["yes", "y"].includes(no.toLowerCase())) {
- let passphrase = await setPassphrase(
- { _askPreviousPassphrase: false },
- args,
- );
- cmds._setPassphrase(passphrase);
- return passphrase;
+ result = await setPassphrase({ _askPreviousPassphrase: false }, args);
+ cmds._setPassphrase(result.passphrase);
+ return result;
}
// ask user again
// No passphrase, create empty shadow file
await Fs.writeFile(shadowPath, "", "utf8");
- return "";
+ return result;
}
}
// (user doesn't want a passphrase)
if (!shadow) {
cmds._setPassphrase("");
- return "";
+ return result;
}
// State 3: passphrase & shadow already in use
for (;;) {
- let passphrase = await Prompt.prompt("Enter (current) passphrase: ", {
+ let prompt = `Enter passphrase: `;
+ if (_rotatePassphrase) {
+ prompt = `Enter (current) passphrase: `;
+ }
+ result.passphrase = await Prompt.prompt(prompt, {
mask: true,
});
- passphrase = passphrase.trim();
- if (!passphrase || "q" === passphrase) {
+ result.passphrase = result.passphrase.trim();
+ if (!result.passphrase || "q" === result.passphrase) {
console.error("cancel: no passphrase");
process.exit(1);
- return;
+ return result;
}
- let match = await Cipher.checkPassphrase(passphrase, shadow);
+ let match = await Cipher.checkPassphrase(result.passphrase, shadow);
if (match) {
- cmds._setPassphrase(passphrase);
+ cmds._setPassphrase(result.passphrase);
console.info(``);
- return passphrase;
+ return result;
}
console.error("incorrect passphrase");
async function decrypt(encWif) {
let passphrase = cmds._getPassphrase();
if (!passphrase) {
- passphrase = await cmds.getPassphrase(null, []);
+ let result = await cmds.getPassphrase({}, []);
+ passphrase = result.passphrase;
+ // we don't return just in case they're setting a passphrase to
+ // decrypt a previously encrypted file (i.e. for recovery from elsewhere)
}
let key128 = await Cipher.deriveKey(passphrase);
let cipher = Cipher.create(key128);
return cipher.decrypt(encWif);
}
+// tuple example {Promise<[String, Boolean]>}
/**
+ * @param {Object} [opts]
+ * @param {Boolean} [opts.force]
* @param {String} plainWif
*/
-async function maybeEncrypt(plainWif) {
+async function maybeEncrypt(plainWif, opts) {
let passphrase = cmds._getPassphrase();
if (!passphrase) {
- passphrase = await cmds.getPassphrase(null, []);
+ let result = await cmds.getPassphrase({}, []);
+ passphrase = result.passphrase;
}
if (!passphrase) {
+ if (opts?.force) {
+ throw new Error(`no passphrase with which to encrypt file`);
+ }
return plainWif;
}