UNPKG

@dotenc/cli

Version:

🔐 Secure, encrypted environment variables that live in your codebase

71 lines (70 loc) 2.91 kB
import crypto from "node:crypto"; import fs from "node:fs/promises"; // AES-256-GCM constants const ALGORITHM = "aes-256-gcm"; const IV_LENGTH = 12; // 96 bits, recommended for GCM const AUTH_TAG_LENGTH = 16; // 128 bits, standard for GCM /** * Encrypts a file using AES-256-GCM. * @param {string} key - The encryption key (must be 32 bytes for AES-256). * @param {string} input - The input string to encrypt. * @param {string} outputFile - Path to the output encrypted file. */ export async function encrypt(key, input, outputFile) { const keyBuffer = Buffer.from(key, "base64"); if (keyBuffer.length !== 32) { throw new Error("Key must be 32 bytes (256 bits) for AES-256-GCM."); } // Generate a random IV const iv = crypto.randomBytes(IV_LENGTH); // Create the cipher const cipher = crypto.createCipheriv(ALGORITHM, keyBuffer, iv); // Encrypt the data const encrypted = Buffer.concat([cipher.update(input), cipher.final()]); // Get the auth tag const authTag = cipher.getAuthTag(); // Combine IV + encrypted content + auth tag const result = Buffer.concat([iv, encrypted, authTag]); // Write the encrypted file await fs.writeFile(outputFile, result); } /** * Decrypts a file using AES-256-GCM. * @param {string} key - The decryption key (must be 32 bytes for AES-256). * @param {string} inputFile - The input file to decrypt. */ export async function decrypt(key, inputFile) { const keyBuffer = Buffer.from(key, "base64"); if (keyBuffer.length !== 32) { throw new Error("Key must be 32 bytes (256 bits) for AES-256-GCM."); } // Read the encrypted file const encryptedData = await fs.readFile(inputFile); // Extract the IV from the start of the file const iv = encryptedData.subarray(0, IV_LENGTH); // Extract the auth tag from the end of the file const authTag = encryptedData.subarray(encryptedData.length - AUTH_TAG_LENGTH); // Extract the ciphertext (everything between IV and auth tag) const ciphertext = encryptedData.subarray(IV_LENGTH, encryptedData.length - AUTH_TAG_LENGTH); try { // Create the decipher const decipher = crypto.createDecipheriv(ALGORITHM, keyBuffer, iv); decipher.setAuthTag(authTag); // Decrypt the ciphertext const decrypted = Buffer.concat([ decipher.update(ciphertext), decipher.final(), ]); return decrypted.toString(); } catch (error) { if (error instanceof Error && error.message.includes("unable to authenticate")) { throw new Error("Failed to decrypt file. This could be because:\n" + "1. The encryption key may be incorrect\n" + "2. The encrypted file may be corrupted\n" + "3. The encrypted file may have been tampered with"); } throw error; } }