· 7 years ago · Nov 18, 2018, 12:50 AM
1const aes = require("aes-js");
2const argon2 = require("argon2");
3const crypto = require("crypto");
4const cryptoJS = require("crypto-js");
5
6// Encrypt using AES-256-CTR-Argon2-HMAC-SHA-256
7async function aes256ctrEncrypt(plaintext, password) {
8 let argon2salt = crypto.randomBytes(16); // 128-bit salt for argon2
9 let argon2Settings = { type: argon2.argon2di, raw: true,
10 timeCost: 8, memoryCost: 2 ** 15, parallelism: 2,
11 hashLength: 32, salt: argon2salt };
12 let secretKey = await argon2.hash(password, argon2Settings);
13 console.log("Derived Argon2 encryption key:", secretKey.toString('hex'));
14
15 let plainTextBytes = aes.utils.utf8.toBytes(plaintext);
16 let aesIV = crypto.randomBytes(16); // 128-bit initial vector (salt)
17 let aesCTR = new aes.ModeOfOperation.ctr(secretKey, new aes.Counter(aesIV));
18 let ciphertextBytes = aesCTR.encrypt(plainTextBytes);
19 let ciphertextHex = aes.utils.hex.fromBytes(ciphertextBytes);
20 let hmac = cryptoJS.HmacSHA256(plaintext, secretKey.toString('hex'));
21
22 return {
23 kdf: 'argon2', kdfSettings: { salt: argon2salt.toString('hex') },
24 cipher: 'aes-256-ctr', cipherSettings: {iv: aesIV.toString('hex') },
25 ciphertext: ciphertextHex, mac: hmac.toString()
26 }
27}
28
29// Decrypt using AES-256-CTR-Argon2-HMAC-SHA-256
30async function aes256ctrDecrypt(encryptedMsg, password) {
31 let saltBytes = Buffer.from(encryptedMsg.kdfSettings.salt, 'hex');
32 let argon2Settings = { type: argon2.argon2di, raw: true,
33 timeCost: 8, memoryCost: 2 ** 15, parallelism: 2,
34 hashLength: 32, salt: saltBytes };
35 let secretKey = await argon2.hash(password, argon2Settings);
36 console.log("Derived Argon2 decryption key:", secretKey.toString('hex'));
37
38 let aesCTR = new aes.ModeOfOperation.ctr(secretKey,
39 new aes.Counter(Buffer.from(encryptedMsg.cipherSettings.iv, 'hex')));
40 let decryptedBytes = aesCTR.decrypt(
41 Buffer.from(encryptedMsg.ciphertext, 'hex'));
42 let decryptedPlaintext = aes.utils.utf8.fromBytes(decryptedBytes);
43
44 let hmac = cryptoJS.HmacSHA256(decryptedPlaintext, secretKey.toString('hex'));
45 if (hmac != encryptedMsg.mac)
46 throw new Error('MAC does not match: maybe wrong password');
47
48 return decryptedPlaintext;
49}
50
51(async () => {
52 let encryptedMsg = await aes256ctrEncrypt("some text", "pass@123");
53 console.log("Encrypted msg:", encryptedMsg);
54
55 let decryptedPlainText = await aes256ctrDecrypt(encryptedMsg, "pass@123");
56 console.log("Successfully decrypted:", decryptedPlainText);
57
58 try {
59 await aes256ctrDecrypt(encryptedMsg, "wrong!Pass");
60 } catch (error) {
61 console.log(error.message);
62 }
63})();
64
65
66// Output:
67
68// Derived Argon2 encryption key: 2c695b63f7ae8cfc1701910694fb0d087bd30810dcfe1692d15f6d61d27716a8
69// Encrypted msg: { kdf: 'argon2',
70// kdfSettings: { salt: 'e484d60ea0a365c257e0a78a928d130f' },
71// cipher: 'aes-256-ctr',
72// cipherSettings: { iv: '1c2b12fc5e50014cba8751d48299e92f' },
73// ciphertext: 'f4128e2de7ab6d5d26',
74// mac: 'ea9393dec2aab69a6724904eb725846f532d4fb469fdb0ecc75b605128fbe34d' }
75// Derived Argon2 decryption key: 2c695b63f7ae8cfc1701910694fb0d087bd30810dcfe1692d15f6d61d27716a8
76// Successfully decrypted: some text
77// Derived Argon2 decryption key: fe5daf262e59124021c6ecb3ed915d9c143aca442f5bfec9ea6108a15a5d06be
78// MAC does not match: maybe wrong password