UNPKG

easy-cipher-mate

Version:

A CLI and programmatic tool for encryption/decryption supporting AES-GCM and ChaCha20-Poly1305 algorithms, with text encoding options and line-by-line text file processing.

120 lines (119 loc) 6.21 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ChaCha20Poly1305EncryptionConfig = exports.ChaCha20Poly1305Encryption = void 0; const crypto_1 = require("crypto"); const encodingUtils_1 = require("../utils/encodingUtils"); const DEFAULT_TAG_LENGTH = 16; const DEFAULT_KEY_LENGTH = 32; const DEFAULT_ITERATIONS = 100000; const DEFAULT_NONCE_LENGTH = 12; class ChaCha20Poly1305Encryption { async encryptText(plaintext, config, encoding) { const { password, textEncoding = 'utf-8' } = config; // Generate random salt and nonce for each encryption operation const salt = Buffer.from(crypto.getRandomValues(new Uint8Array(16))); const nonce = Buffer.from(crypto.getRandomValues(new Uint8Array(ChaCha20Poly1305Encryption.NONCE_LENGTH))); const key = this.deriveKey(password, salt); const textBuffer = (0, encodingUtils_1.encodeText)(plaintext, encoding !== null && encoding !== void 0 ? encoding : textEncoding); const cipher = (0, crypto_1.createCipheriv)('chacha20-poly1305', key, nonce, { authTagLength: ChaCha20Poly1305Encryption.TAG_LENGTH, }); const encrypted = Buffer.concat([ cipher.update(Buffer.from(textBuffer)), cipher.final() ]); const tag = cipher.getAuthTag(); // Package salt + nonce + encrypted + tag together const finalBuffer = Buffer.concat([ salt, nonce, encrypted, tag, ]); return { data: finalBuffer.buffer.slice(finalBuffer.byteOffset, finalBuffer.byteOffset + finalBuffer.byteLength) }; } async decryptText(encryptedData, config, encoding) { const { password, textEncoding = 'utf-8' } = config; // Parse salt + nonce + ciphertext + tag from the packaged data const data = Buffer.from(encryptedData); const salt = data.subarray(0, 16); const nonce = data.subarray(16, 16 + ChaCha20Poly1305Encryption.NONCE_LENGTH); const encryptedWithTag = data.subarray(16 + ChaCha20Poly1305Encryption.NONCE_LENGTH); const tag = encryptedWithTag.subarray(encryptedWithTag.length - ChaCha20Poly1305Encryption.TAG_LENGTH); const ciphertext = encryptedWithTag.subarray(0, encryptedWithTag.length - ChaCha20Poly1305Encryption.TAG_LENGTH); const key = this.deriveKey(password, salt); const decipher = (0, crypto_1.createDecipheriv)('chacha20-poly1305', key, nonce, { authTagLength: ChaCha20Poly1305Encryption.TAG_LENGTH, }); decipher.setAuthTag(tag); const decrypted = Buffer.concat([ decipher.update(ciphertext), decipher.final() ]); return (0, encodingUtils_1.decodeText)(decrypted.buffer.slice(decrypted.byteOffset, decrypted.byteOffset + decrypted.byteLength), encoding !== null && encoding !== void 0 ? encoding : textEncoding); } async encryptFile(fileBuffer, config) { const { password } = config; // Generate random salt and nonce for each encryption operation const salt = Buffer.from(crypto.getRandomValues(new Uint8Array(16))); const nonce = Buffer.from(crypto.getRandomValues(new Uint8Array(ChaCha20Poly1305Encryption.NONCE_LENGTH))); const key = this.deriveKey(password, salt); const dataBuffer = Buffer.from(fileBuffer); const cipher = (0, crypto_1.createCipheriv)('chacha20-poly1305', key, nonce, { authTagLength: ChaCha20Poly1305Encryption.TAG_LENGTH, }); const encrypted = Buffer.concat([ cipher.update(dataBuffer), cipher.final() ]); const tag = cipher.getAuthTag(); // Package salt + nonce + encrypted + tag together const finalBuffer = Buffer.concat([ salt, nonce, encrypted, tag, ]); return { data: finalBuffer.buffer.slice(finalBuffer.byteOffset, finalBuffer.byteOffset + finalBuffer.byteLength) }; } async decryptFile(encryptedBuffer, config) { const { password } = config; // Parse salt + nonce + ciphertext + tag from the packaged data const data = Buffer.from(encryptedBuffer); const salt = data.subarray(0, 16); const nonce = data.subarray(16, 16 + ChaCha20Poly1305Encryption.NONCE_LENGTH); const encryptedWithTag = data.subarray(16 + ChaCha20Poly1305Encryption.NONCE_LENGTH); const tag = encryptedWithTag.subarray(encryptedWithTag.length - ChaCha20Poly1305Encryption.TAG_LENGTH); const ciphertext = encryptedWithTag.subarray(0, encryptedWithTag.length - ChaCha20Poly1305Encryption.TAG_LENGTH); const key = this.deriveKey(password, salt); const decipher = (0, crypto_1.createDecipheriv)('chacha20-poly1305', key, nonce, { authTagLength: ChaCha20Poly1305Encryption.TAG_LENGTH, }); decipher.setAuthTag(tag); const decrypted = Buffer.concat([ decipher.update(ciphertext), decipher.final() ]); return decrypted.buffer.slice(decrypted.byteOffset, decrypted.byteOffset + decrypted.byteLength); } deriveKey(password, salt) { return (0, crypto_1.pbkdf2Sync)(password, salt, ChaCha20Poly1305Encryption.ITERATIONS, ChaCha20Poly1305Encryption.KEY_LENGTH, 'sha256'); } validateNonce(nonce) { if (nonce.length !== ChaCha20Poly1305Encryption.NONCE_LENGTH) { throw new Error(`Nonce must be ${ChaCha20Poly1305Encryption.NONCE_LENGTH} bytes`); } } } exports.ChaCha20Poly1305Encryption = ChaCha20Poly1305Encryption; ChaCha20Poly1305Encryption.TAG_LENGTH = DEFAULT_TAG_LENGTH; ChaCha20Poly1305Encryption.KEY_LENGTH = DEFAULT_KEY_LENGTH; ChaCha20Poly1305Encryption.ITERATIONS = DEFAULT_ITERATIONS; ChaCha20Poly1305Encryption.NONCE_LENGTH = DEFAULT_NONCE_LENGTH; class ChaCha20Poly1305EncryptionConfig { constructor(password, textEncoding) { this.password = password; this.textEncoding = textEncoding !== null && textEncoding !== void 0 ? textEncoding : 'utf-8'; } } exports.ChaCha20Poly1305EncryptionConfig = ChaCha20Poly1305EncryptionConfig;