ahfc
Version:
Aarav Hyper Fortress Cipher (AHFC) - Powerful file encryption CLI with unbreakable logic.
324 lines (261 loc) • 12 kB
JavaScript
#!/usr/bin/env node
const fs = require('fs');
const crypto = require('crypto');
const zlib = require('zlib');
const argon2 = require('argon2');
const chalk = require('chalk').default;
const cliProgress = require('cli-progress');
const readline = require('readline');
const VERSION = 'AHFCv1';
const BLOCK_SIZE = 1024 * 1024;
const modes = {
lite: { rounds: 10, minLength: 4, name: 'Lite' },
normal: { rounds: 64, minLength: 16, name: 'Normal' },
beast: { rounds: 128, minLength: 24, name: 'Beast' }
};
const ASCII_SIGNATURE = Buffer.from(`
===============================
= AHFC Encrypted File =
= ${VERSION} =
===============================\n`, 'utf-8');
function printBanner() {
console.log(chalk.cyan(`
╔════════════════════════════════════════════════════╗
║ AHFC File Encryptor v1 ║
╚════════════════════════════════════════════════════╝
`));
}
// Custom password prompt with obscuring and backspace
function promptPassword(promptText) {
return new Promise((resolve) => {
const stdin = process.stdin;
const stdout = process.stdout;
let password = '';
stdout.write(chalk.green(promptText));
stdin.resume();
stdin.setRawMode(true);
stdin.setEncoding('utf8');
stdin.on('data', (ch) => {
if (ch === '\n' || ch === '\r' || ch === '\u0004') {
stdin.setRawMode(false);
stdout.write('\n');
stdin.pause();
resolve(password);
} else if (ch === '\u0003') {
process.exit();
} else if (ch === '\u0008' || ch === '\u007F') {
if (password.length > 0) {
password = password.slice(0, -1);
stdout.write('\b \b');
}
} else {
password += ch;
stdout.write('*');
}
});
});
}
function getModeFromArgs(args) {
const modeArg = args.find(a => a.startsWith('--'));
if (!modeArg) return 'normal';
const key = modeArg.replace('--', '').toLowerCase();
if (['l', 'lite'].includes(key)) return 'lite';
if (['n', 'normal'].includes(key)) return 'normal';
if (['b', 'beast'].includes(key)) return 'beast';
return 'normal';
}
async function deriveKey(password, salt) {
return await argon2.hash(password, { salt, type: argon2.argon2id, raw: true, hashLength: 32 });
}
function transformBlock(data, key, rounds) {
let output = Buffer.from(data);
for (let i = 0; i < rounds; i++) {
for (let j = 0; j < output.length; j++) {
output[j] ^= key[j % key.length] ^ (i & 0xff);
}
}
return output;
}
async function encryptFile(inputPath, outputPath, mode) {
const modeInfo = modes[mode];
console.log(chalk.magentaBright(`✨ Mode selected: ${chalk.bold(modeInfo.name)} (${modeInfo.rounds} rounds)`));
console.log(chalk.gray(`🔑 Password must be at least ${modeInfo.minLength} characters.`));
const password = await promptPassword('Enter password: ');
if (password.length < modeInfo.minLength) return console.error(chalk.red(`Password must be at least ${modeInfo.minLength} characters.`));
const salt = crypto.randomBytes(16);
const key = await deriveKey(password, salt);
const input = fs.readFileSync(inputPath);
// enhanced beast
if (mode === 'beast') {
const compressed = zlib.deflateSync(input);
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
const totalSize = compressed.length;
const chunkSize = BLOCK_SIZE;
const bar = new cliProgress.SingleBar({
format: `${chalk.blue('Encrypting')} |{bar}| {percentage}% | ETA: {eta}s | ${modeInfo.name}`,
}, cliProgress.Presets.shades_classic);
bar.start(Math.ceil(totalSize / chunkSize), 0);
let encryptedChunks = [];
for (let i = 0; i < totalSize; i += chunkSize) {
const chunk = compressed.slice(i, i + chunkSize);
encryptedChunks.push(cipher.update(chunk));
bar.increment();
}
encryptedChunks.push(cipher.final());
bar.stop();
const encrypted = Buffer.concat(encryptedChunks);
const authTag = cipher.getAuthTag();
const metadata = Buffer.from(JSON.stringify({ version: VERSION, mode }));
const metaLen = Buffer.alloc(4);
metaLen.writeUInt32BE(metadata.length);
const data = Buffer.concat([
ASCII_SIGNATURE,
metaLen,
metadata,
salt,
iv,
authTag,
encrypted
]);
const hmac = crypto.createHmac(mode === 'beast' ? 'sha512' : 'sha256', key).update(data).digest();
fs.writeFileSync(outputPath, Buffer.concat([data, hmac]));
console.log(chalk.greenBright('✅ File encrypted successfully!'));
console.log(chalk.green(`🎉 Output written to: ${chalk.underline(outputPath)}`));
return;
}
const compressed = zlib.deflateSync(input);
const blocks = [];
const bar = new cliProgress.SingleBar({ format: `${chalk.blue('Encrypting')} |{bar}| {percentage}% | ETA: {eta}s | ${modeInfo.name}` }, cliProgress.Presets.shades_classic);
bar.start(Math.ceil(compressed.length / BLOCK_SIZE), 0);
for (let i = 0; i < compressed.length; i += BLOCK_SIZE) {
const block = compressed.slice(i, i + BLOCK_SIZE);
const encrypted = transformBlock(block, key, modeInfo.rounds);
blocks.push(encrypted);
bar.update(blocks.length);
}
bar.stop();
const metadata = Buffer.from(JSON.stringify({ version: VERSION, mode }));
const metaLen = Buffer.alloc(4);
metaLen.writeUInt32BE(metadata.length);
const data = Buffer.concat([ASCII_SIGNATURE, metaLen, metadata, salt, ...blocks]);
const hmac = crypto.createHmac('sha256', key).update(data).digest();
fs.writeFileSync(outputPath, Buffer.concat([data, hmac]));
console.log(chalk.greenBright('✅ File encrypted successfully!'));
console.log(chalk.green(`🎉 Output written to: ${chalk.underline(outputPath)}`));
}
async function decryptFile(inputPath, outputPath) {
const file = fs.readFileSync(inputPath);
let offset = ASCII_SIGNATURE.length;
const metaLen = file.readUInt32BE(offset);
const metadata = JSON.parse(file.slice(offset + 4, offset + 4 + metaLen));
const modeInfo = modes[metadata.mode];
console.log(chalk.magentaBright(`✨ Mode selected: ${chalk.bold(modeInfo.name)} (${modeInfo.rounds} rounds)`));
if (metadata.version !== VERSION) {
return console.error(chalk.redBright(`❌ Version mismatch. Expected ${VERSION}, got ${metadata.version}`));
}
const salt = file.slice(offset + 4 + metaLen, offset + 20 + metaLen);
if (metadata.mode === 'beast') {
const ivStart = offset + 20 + metaLen;
const authTagStart = ivStart + 12;
const encryptedStart = authTagStart + 16;
const hmacStart = file.length - 64;
const iv = file.slice(ivStart, authTagStart);
const authTag = file.slice(authTagStart, encryptedStart);
const encrypted = file.slice(encryptedStart, hmacStart);
const hmacStored = file.slice(hmacStart);
console.log(chalk.gray(`🔑 Password must be at least ${modeInfo.minLength} characters.`));
const password = await promptPassword('Enter password: ');
if (password.length < modeInfo.minLength) {
return console.error(chalk.red(`Password must be at least ${modeInfo.minLength} characters.`));
}
const key = await deriveKey(password, salt);
const hmacActual = crypto.createHmac('sha512', key).update(file.slice(0, hmacStart)).digest();
if (!crypto.timingSafeEqual(hmacStored, hmacActual)) {
await new Promise(r => setTimeout(r, 1000)); // Anti-brute-force delay
return console.error(chalk.redBright('❌ Signature verification failed: File tampered or incorrect password!'));
}
try {
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(authTag);
const totalSize = encrypted.length;
const chunkSize = BLOCK_SIZE;
const bar = new cliProgress.SingleBar({
format: `${chalk.yellow('Decrypting')} |{bar}| {percentage}% | ETA: {eta}s | ${modeInfo.name}`,
}, cliProgress.Presets.shades_classic);
bar.start(Math.ceil(totalSize / chunkSize), 0);
let decryptedChunks = [];
for (let i = 0; i < totalSize; i += chunkSize) {
const chunk = encrypted.slice(i, i + chunkSize);
decryptedChunks.push(decipher.update(chunk));
bar.increment();
}
decryptedChunks.push(decipher.final());
bar.stop();
const decrypted = Buffer.concat(decryptedChunks);
const output = zlib.inflateSync(decrypted);
fs.writeFileSync(outputPath, output);
console.log(chalk.greenBright('✅ File decrypted successfully!'));
console.log(chalk.green(`🎉 Output written to: ${chalk.underline(outputPath)}`));
} catch (err) {
return console.error(chalk.redBright('❌ Decryption failed: Incorrect password or corrupted file.'));
}
return;
}
const hmacStart = metadata.mode === 'beast' ? file.length - 64 : file.length - 32;
const hmacStored = file.slice(hmacStart);
const encrypted = file.slice(0, hmacStart);
console.log(chalk.gray(`🔑 Password must be at least ${modeInfo.minLength} characters.`));
const password = await promptPassword('Enter password: ');
if (password.length < modeInfo.minLength) return console.error(chalk.red(`Password must be at least ${modeInfo.minLength} characters.`));
const key = await deriveKey(password, salt);
const hmacActual = crypto.createHmac('sha256', key).update(encrypted).digest();
if (!crypto.timingSafeEqual(hmacStored, hmacActual)) {
return console.error(chalk.redBright('❌ Signature verification failed: File tampered or incorrect password!'));
}
const bar = new cliProgress.SingleBar({ format: `${chalk.yellow('Decrypting')} |{bar}| {percentage}% | ETA: {eta}s | ${modeInfo.name}` }, cliProgress.Presets.shades_classic);
const blocks = [];
const start = offset + 20 + metaLen;
const totalBlocks = Math.ceil((hmacStart - start) / BLOCK_SIZE);
bar.start(totalBlocks, 0);
for (let i = start; i < hmacStart; i += BLOCK_SIZE) {
const block = file.slice(i, Math.min(i + BLOCK_SIZE, hmacStart));
const decrypted = transformBlock(block, key, modeInfo.rounds);
blocks.push(decrypted);
bar.increment();
}
bar.stop();
const compressed = Buffer.concat(blocks);
const output = zlib.inflateSync(compressed);
fs.writeFileSync(outputPath, output);
console.log(chalk.greenBright('✅ File decrypted successfully!'));
console.log(chalk.green(`🎉 Output written to: ${chalk.underline(outputPath)}`));
}
if (require.main === module) {
// CLI Mode
(async () => {
console.log('\n');
printBanner();
const args = process.argv.slice(2);
const action = args[0];
const input = args[1];
const output = args[2];
const mode = getModeFromArgs(args);
if (action === 'encrypt') {
if (args.length < 3) return console.log(chalk.yellow('Usage: node ahfc.js encrypt <input> <output> [--lite|--normal|--beast]'));
await encryptFile(input, output, mode);
} else if (action === 'decrypt') {
if (args.length < 3) return console.log(chalk.yellow('Usage: node ahfc.js decrypt <input> <output>'));
await decryptFile(input, output);
} else {
console.log(chalk.red('Invalid command. Use encrypt or decrypt.'));
}
})();
}
// Export functions for project-based usage
module.exports = {
encryptFile,
decryptFile,
getModeFromArgs,
printBanner
};