const crypto = require('crypto');

// Demo implementation of using `aes-256-gcm` with node.js's `crypto` lib.
const aes256gcm = (key) => {
    const ALGO = 'aes-256-gcm';

    // encrypt returns base64-encoded ciphertext
    const encrypt = (str) => {
        // The `iv` for a given key must be globally unique to prevent
        // against forgery attacks. `randomBytes` is convenient for
        // demonstration but a poor way to achieve this in practice.
        //
        // See: e.g. https://csrc.nist.gov/publications/detail/sp/800-38d/final
        const iv = crypto.randomBytes(12);
        const cipher = crypto.createCipheriv(ALGO, key, iv);

        // Hint: Larger inputs (it's GCM, after all!) should use the stream API
        let enc = cipher.update(str, 'utf8', 'base64');
        enc += cipher.final('base64');
        return [enc, iv.toString('base64'), cipher.getAuthTag().toString('base64')].join('.');
    };

    // decrypt decodes base64-encoded ciphertext into a utf8-encoded string
    const decrypt = (encrypted, key) => {
        // Split the encrypted string into components: encrypted data, IV, and authentication tag
        const [encString, ivString, authTagString] = encrypted.split('.');

        // Convert each component from base64
        const enc = Buffer.from(encString, 'base64');
        const iv = Buffer.from(ivString, 'base64');
        const authTag = Buffer.from(authTagString, 'base64');

        // Create a decipher using the provided algorithm, key, and IV
        const decipher = crypto.createDecipheriv(ALGO, key, iv);

        // Set the authentication tag for the decipher
        decipher.setAuthTag(authTag);

        // Update the decipher with the encrypted data, specifying input and output encodings
        let str = decipher.update(enc, 'base64', 'utf8');

        // Finalize the decryption process and append the final chunk
        str += decipher.final('utf8');

        // Return the decrypted string
        return str;
    };

    return {
        encrypt,
        decrypt,
    };
};

const KEY = Buffer.from('KaPdSgVkYp3s6v9y$B&E(H+MbQeThWmZ', 'utf8');

const aesCipher = aes256gcm(KEY);

const decrypted = aesCipher.decrypt('7A==.QD43v7oKiTYmtX4o.R465CR1SJT9PzXO8A36H9g==', KEY);

console.log(decrypted); // 'hello, world'
