From 5ea1746380cd099f0f90292b969b8f8f6ffb9965 Mon Sep 17 00:00:00 2001 From: Suraj B M Date: Mon, 30 Sep 2024 22:46:47 +0530 Subject: [PATCH] feat: new reset --- confidant.ts | 32 ++++++++++---- src/main.ts | 115 ++++++++++++++++++++++++++++++++------------------- 2 files changed, 96 insertions(+), 51 deletions(-) diff --git a/confidant.ts b/confidant.ts index 09557e4..633a6c1 100755 --- a/confidant.ts +++ b/confidant.ts @@ -4,7 +4,7 @@ import { input, password } from "@inquirer/prompts"; import { $ } from "bun"; import chalk from "chalk-template"; import { Command } from "commander"; -import { decrypt_vault, encrypt_vault, initialize, recovery } from "./src/main"; +import { decrypt_vault, encrypt_vault, initialize, reset } from "./src/main"; import { existsSync, readdirSync } from "fs"; import checkForFiles, { Files, @@ -26,7 +26,7 @@ program.name("confidant").description("Creates a very secure file vault."); program .command("init") .description("initialize a confidant vault") - .argument("[directory]", "Directory to use to create a vault") + .argument("[directory]", "directory to use to create a vault") .action(async (dirname) => { if (!dirname) { dirname = (await getDirectoryNames()) as string; @@ -61,7 +61,7 @@ program program .command("decrypt") .description("decrypt the vault") - .argument("[vault]", "Name of the vault to decrypt") + .argument("[vault]", "name of the vault to decrypt") .option("-l, --live", "decrypt in live mode") .action(async (args, opts) => { if (!args) { @@ -98,7 +98,7 @@ program program .command("encrypt") .description("encrypt the vault") - .argument("[vault]", "Name of the vault to decrypt") + .argument("[vault]", "name of the vault to encrypt") .action(async (vault) => { if (!vault) { vault = await getDecryptedName(); @@ -119,15 +119,29 @@ program }); program - .command("recover") - .description("recover vault when password is forgotten") - .action(async () => { - checkForFiles(); + .command("reset") + .description("reset a vault's password") + .argument("[vault]", "name of the vault to decrypt") + .action(async (vault) => { + if (!vault) { + vault = await getVaultName(); + } else { + const vaults = new Files(/(.*).vault/g).intersection( + new Files(/(.*).key/g), + ).data as string[]; + if (!vaults.includes(vault)) { + console.log( + chalk`{red Vault "${vault}" not found in current directory.}`, + ); + exit(1); + } + } + log`{blue Resetting password of "{green ${vault}}"...}`; const recphrase = await input({ message: chalk`{reset {yellow Enter the recovery phrase:}}`, }); - recovery(recphrase); + reset(vault, recphrase); }); await program.parseAsync(); diff --git a/src/main.ts b/src/main.ts index 79c5147..8d4e321 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,6 +11,7 @@ import { encrypt, encrypt_file, generate_recovery_phrase, + getRandomPassword, hmac, log, panic, @@ -22,8 +23,7 @@ import { import env from "../env"; import { AES, enc, HmacSHA256 } from "crypto-js"; import chalk from "chalk-template"; -import type { ConData, Config, FidData } from "../types"; -import { password } from "@inquirer/prompts"; +import { input, password } from "@inquirer/prompts"; console.info = function () {}; const { exit } = process; @@ -165,45 +165,76 @@ export async function encrypt_vault(dirname: string) { } } -export async function recovery(recoverystring: string) { - // Check if recovery phrase is correct - let config = Object(parse(readFileSync("config.toml").toString("utf8"))); - let recovery_auth_key = HmacSHA256(recoverystring, env.AUTH_KEY).toString( - enc.Base64, - ); - const dec = AES.decrypt( - config.config.recphrasestore, - recovery_auth_key, - ).toString(enc.Utf8); - if (dec !== env.PHRASE) { - panic`Wrong recovery string. Please try again.`; +export async function reset(dirname: string, recoverystring: string) { + try { + // Read and split the vault file + const combined = readFileSync(`${dirname}.vault`); + const index = combined.indexOf(separator); + const D_C = combined.subarray(0, index).toString("utf8"); + const E_Z = combined.subarray(index + separator.length); + + // Check if recovery key is correct + const recovery_auth_key = HmacSHA256(recoverystring, env.AUTH_KEY).toString( + enc.Base64, + ); + const config = JSON.parse(Buffer.from(D_C, "base64").toString()); + const phrase = AES.decrypt( + config.recphrasestore, + recovery_auth_key, + ).toString(enc.Utf8); + if (phrase !== env.PHRASE) { + panic`Incorrect recovery phrase. Try again.`; + } + + // create new credentials + const auth_secret = decrypt( + config.recoverystore, + buffer(recovery_auth_key), + ); + + const genPass = getRandomPassword(14); + const pass = await input({ + message: chalk`{reset {yellow Enter a new password:}}`, + default: genPass, + }); + const confpass = await input({ + message: chalk`{reset {yellow Enter the password again:}}`, + default: genPass, + }); + if (pass !== confpass) { + panic`Passwords don't match. Exiting...`; + } + const newrecphrase = generate_recovery_phrase(); + + const password_auth_key = HmacSHA256(pass, env.AUTH_KEY).toString( + enc.Base64, + ); + const _recovery_auth_key = HmacSHA256(newrecphrase, env.AUTH_KEY).toString( + enc.Base64, + ); + + const _D_C = { + ...config, + keystore: stringy(encrypt(auth_secret, buffer(password_auth_key))), + recoverystore: stringy(encrypt(auth_secret, buffer(_recovery_auth_key))), + phrasestore: AES.encrypt(env.PHRASE, password_auth_key).toString(), + recphrasestore: AES.encrypt(env.PHRASE, _recovery_auth_key).toString(), + }; + + // create dirname.vault + writeFileSync( + `${dirname}.vault`, + Buffer.concat([ + Buffer.from(Buffer.from(JSON.stringify(_D_C)).toString("base64")), + separator, + E_Z, + ]), + ); + + writeFileSync(`${dirname}_recovery.txt`, newrecphrase); + console.log(`New recovery phrase:`); + console.log(chalk`{magenta ${newrecphrase}}`); + } catch (e) { + panic`Error recovering vault. The files could be corrupted.`; } - - // Generate a fresh config file - const auth_secret = decrypt( - buffer(config.config.recoverystore), - buffer(recovery_auth_key), - ); - const newpass = await password({ - message: chalk`{reset {yellow Enter a new password:}}`, - mask: "•", - }); - const recstring = generate_recovery_phrase(); - recovery_auth_key = HmacSHA256(recstring, env.AUTH_KEY).toString(enc.Base64); - const password_auth_key = HmacSHA256(newpass, env.AUTH_KEY).toString( - enc.Base64, - ); - - config.config = { - ...config.config, - keystore: stringy(encrypt(auth_secret, buffer(password_auth_key))), - recoverystore: stringy(encrypt(auth_secret, buffer(recovery_auth_key))), - phrasestore: AES.encrypt(env.PHRASE, password_auth_key).toString(), - recphrasestore: AES.encrypt(env.PHRASE, recovery_auth_key).toString(), - }; - writeFileSync("config.toml", buffer(stringify(config), "utf8")); - - writeFileSync("recovery.txt", recstring + "\n"); - console.log(`New recovery phrase:`); - log`{magenta ${recstring}}`; }