const aes = require("aes-js"); const argon2 = require("argon2"); const crypto = require("crypto"); const cryptoJS = require("crypto-js"); // Encrypt using AES-256-CTR-Argon2-HMAC-SHA-256 async function aes256ctrEncrypt(plaintext, password) { let argon2salt = crypto.randomBytes(16); // 128-bit salt for argon2 let argon2Settings = { type: argon2.argon2di, raw: true, timeCost: 8, memoryCost: 2 ** 15, parallelism: 2, hashLength: 32, salt: argon2salt }; let secretKey = await argon2.hash(password, argon2Settings); console.log("Derived Argon2 encryption key:", secretKey.toString('hex')); let plainTextBytes = aes.utils.utf8.toBytes(plaintext); let aesIV = crypto.randomBytes(16); // 128-bit initial vector (salt) let aesCTR = new aes.ModeOfOperation.ctr(secretKey, new aes.Counter(aesIV)); let ciphertextBytes = aesCTR.encrypt(plainTextBytes); let ciphertextHex = aes.utils.hex.fromBytes(ciphertextBytes); let hmac = cryptoJS.HmacSHA256(plaintext, secretKey.toString('hex')); return { kdf: 'argon2', kdfSettings: { salt: argon2salt.toString('hex') }, cipher: 'aes-256-ctr', cipherSettings: {iv: aesIV.toString('hex') }, ciphertext: ciphertextHex, mac: hmac.toString() } } // Decrypt using AES-256-CTR-Argon2-HMAC-SHA-256 async function aes256ctrDecrypt(encryptedMsg, password) { let saltBytes = Buffer.from(encryptedMsg.kdfSettings.salt, 'hex'); let argon2Settings = { type: argon2.argon2di, raw: true, timeCost: 8, memoryCost: 2 ** 15, parallelism: 2, hashLength: 32, salt: saltBytes }; let secretKey = await argon2.hash(password, argon2Settings); console.log("Derived Argon2 decryption key:", secretKey.toString('hex')); let aesCTR = new aes.ModeOfOperation.ctr(secretKey, new aes.Counter(Buffer.from(encryptedMsg.cipherSettings.iv, 'hex'))); let decryptedBytes = aesCTR.decrypt( Buffer.from(encryptedMsg.ciphertext, 'hex')); let decryptedPlaintext = aes.utils.utf8.fromBytes(decryptedBytes); let hmac = cryptoJS.HmacSHA256(decryptedPlaintext, secretKey.toString('hex')); if (hmac != encryptedMsg.mac) throw new Error('MAC does not match: maybe wrong password'); return decryptedPlaintext; } (async () => { let encryptedMsg = await aes256ctrEncrypt("some text", "pass@123"); console.log("Encrypted msg:", encryptedMsg); let decryptedPlainText = await aes256ctrDecrypt(encryptedMsg, "pass@123"); console.log("Successfully decrypted:", decryptedPlainText); try { await aes256ctrDecrypt(encryptedMsg, "wrong!Pass"); } catch (error) { console.log(error.message); } })(); // Output: // Derived Argon2 encryption key: 2c695b63f7ae8cfc1701910694fb0d087bd30810dcfe1692d15f6d61d27716a8 // Encrypted msg: { kdf: 'argon2', // kdfSettings: { salt: 'e484d60ea0a365c257e0a78a928d130f' }, // cipher: 'aes-256-ctr', // cipherSettings: { iv: '1c2b12fc5e50014cba8751d48299e92f' }, // ciphertext: 'f4128e2de7ab6d5d26', // mac: 'ea9393dec2aab69a6724904eb725846f532d4fb469fdb0ecc75b605128fbe34d' } // Derived Argon2 decryption key: 2c695b63f7ae8cfc1701910694fb0d087bd30810dcfe1692d15f6d61d27716a8 // Successfully decrypted: some text // Derived Argon2 decryption key: fe5daf262e59124021c6ecb3ed915d9c143aca442f5bfec9ea6108a15a5d06be // MAC does not match: maybe wrong password