UNPKG

@izzius94/crypter

Version:

The crypter library makes easy to share encrypted data with a Laravel project.

146 lines (145 loc) 5.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.encrypt = exports.decrypt = void 0; const crypto_1 = require("crypto"); /** * Decrypt an encrypted string * * @param encrypted The encrypted string to decrypt * @param key The key to use to decrypt the string, must be the same used during encryption * @param algorithm The algorithm to use use to decrypt the string, must be the same used during encryption * @returns The string decrypted */ const decrypt = (encrypted, key, algorithm = 'aes-256-cbc') => { const payload = getJsonPayload(encrypted, key, algorithm); ensureTagIsValid(payload.tag === undefined || payload.tag === '' ? null : payload.tag, algorithm); const decipher = createDecipher(algorithm, key, payload); const decrypted = decipher.update(payload.value, 'base64'); return Buffer.concat([decrypted, decipher.final()]).toString(); }; exports.decrypt = decrypt; /** * Enctypt a string * * @param value The value to encrypt * @param key The key to use to encrypt the string * @param algorithm The algorithm to use to encrypt the string * @returns The string encrypted */ const encrypt = (value, key, algorithm = 'aes-256-cbc') => { if (algorithm === 'aes-256-gcm' || algorithm === 'aes-128-gcm') { const iv = (0, crypto_1.randomBytes)(12); const cipher = (0, crypto_1.createCipheriv)(algorithm, key, iv, { authTagLength: 16 }); const encrypted = cipher.update(value); const payload = { iv: iv.toString('base64'), value: Buffer.concat([encrypted, cipher.final()]).toString('base64'), mac: '', tag: cipher.getAuthTag().toString('base64') }; return Buffer.from(JSON.stringify(payload)).toString('base64'); } const iv = (0, crypto_1.randomBytes)(16); const ivBased = iv.toString('base64'); const cipher = (0, crypto_1.createCipheriv)(algorithm, key, iv); const encrypted = cipher.update(value); const value64 = Buffer.concat([encrypted, cipher.final()]).toString('base64'); const payload = { iv: iv.toString('base64'), value: value64, mac: hash(ivBased, value64, key).digest('hex'), tag: '' }; return Buffer.from(JSON.stringify(payload)).toString('base64'); }; exports.encrypt = encrypt; /** * Get the JSON object from the given payload. * It can rise errors if the json is invalid or the MAC is invalid * * @param encrypted The encrypted string to decrypt * @param key The key to use to decrypt the string, must be the same used during encryption * @param algorithm The algorithm to use use to decrypt the string, must be the same used during encryption * @returns The payload converted in object */ const getJsonPayload = (encrypted, key, algorithm) => { const payload = JSON.parse(Buffer.from(encrypted, 'base64').toString('utf8')); if (!validPayload(payload, algorithm)) { throw new Error('Invalid payload.'); } if (algorithm !== 'aes-256-gcm' && algorithm !== 'aes-128-gcm' && !validMac(payload, key)) { throw new Error('Invalid MAC.'); } return payload; }; /** * Check if the given paylod is valid * * @param payload The payload contained in the encrypted string * @param algorithm The algorithm to use use to decrypt the string, must be the same used during encryption * @returns */ const validPayload = (payload, algorithm) => { var _a; if (typeof payload !== 'object') { return false; } ['iv', 'value', 'mac'].forEach(el => { if (payload[el] === null || payload[el] === undefined || typeof payload[el] !== 'string') { return false; } }); return Buffer.from(payload.iv, 'base64').toString('ascii').length === ((_a = (0, crypto_1.getCipherInfo)(algorithm)) === null || _a === void 0 ? void 0 : _a.ivLength); }; /** * Determine if the MAC for the given payload is valid. * * @param payload The payload to check * @param key The key to use to decrypt the string, must be the same used during encryption * @returns The validity of the MAC */ const validMac = (payload, key) => { return (0, crypto_1.timingSafeEqual)(hash(payload.iv, payload.value, key).digest(), Buffer.from(payload.mac, 'hex')); }; /** * Create a MAC for the given value * * @param iv The iv used to create the hash * @param value The value used to create the hash * @param key The key used to create the hash * @returns The hash */ const hash = (iv, value, key) => { const hmac = (0, crypto_1.createHmac)('sha256', key); hmac.update(iv + value); return hmac; }; /** * Create the decipher for decryption * * @param algorithm The algorithm to use to decrypt the value in the payload * @param key The key to use to decrypt the value in the payload * @param payload The payload object * @returns */ const createDecipher = (algorithm, key, payload) => { if (algorithm === 'aes-256-gcm' || algorithm === 'aes-128-gcm') { return (0, crypto_1.createDecipheriv)(algorithm, key, Buffer.from(payload.iv, 'base64'), { authTagLength: 16 }) .setAuthTag(Buffer.from(payload.tag, 'base64')); } return (0, crypto_1.createDecipheriv)(algorithm, key, Buffer.from(payload.iv, 'base64')); }; /** * Ensure the given tag is a valid tag given the selected cipher * * @param tag The tag contained in the payload * @param algorithm The algorithm to use to decrypt the value in the payload */ const ensureTagIsValid = (tag, algorithm) => { if ((algorithm === 'aes-128-gcm' || algorithm === 'aes-256-gcm') && (tag === null || Buffer.from(tag, 'base64').length !== 16)) { throw new Error('Could not decrypt the data.'); } if ((algorithm === 'aes-128-cbc' || algorithm === 'aes-256-cbc') && typeof tag === 'string') { throw new Error('Unable to use tag because the cipher algorithm does not support AEAD.'); } };