diff --git a/packages/@uppy/companion/src/server/helpers/utils.js b/packages/@uppy/companion/src/server/helpers/utils.js index 70e075ce5..478d68c9c 100644 --- a/packages/@uppy/companion/src/server/helpers/utils.js +++ b/packages/@uppy/companion/src/server/helpers/utils.js @@ -93,20 +93,58 @@ module.exports.getURLBuilder = (options) => { } /** + * Ensure that a user-provided `secret` is 32 bytes long (the length required + * for an AES256 key) by hashing it with SHA256. * - * @param {*} input - * @param {string} secret + * @param {string|Buffer} secret + */ +function createSecret (secret) { + const hash = crypto.createHash('sha256') + hash.update(secret) + return hash.digest() +} + +/** + * Create an initialization vector for AES256. + * + * @return {Buffer} + */ +function createIv () { + return crypto.randomBytes(16) +} + +/** + * Encrypt a buffer or string with AES256 and a random iv. + * + * @param {string} input + * @param {string|Buffer} secret + * @return {string} Ciphertext as a hex string, prefixed with 32 hex characters containing the iv. */ module.exports.encrypt = (input, secret) => { - const cipher = crypto.createCipher('aes256', secret) - let encrypted = cipher.update(input, 'utf8', 'hex') + const iv = createIv() + const cipher = crypto.createCipheriv('aes256', createSecret(secret), iv) + let encrypted = iv.toString('hex') + encrypted += cipher.update(input, 'utf8', 'hex') encrypted += cipher.final('hex') return encrypted } +/** + * Decrypt an iv-prefixed or string with AES256. The iv should be in the first 32 hex characters. + * + * @param {string} encrypted + * @param {string|Buffer} secret + * @return {string} Decrypted value. + */ module.exports.decrypt = (encrypted, secret) => { - var decipher = crypto.createDecipher('aes256', secret) - var decrypted = decipher.update(encrypted, 'hex', 'utf8') + // Need at least 32 chars for the iv + if (encrypted.length < 32) { + throw new Error('Invalid encrypted value. Maybe it was generated with an old Companion version?') + } + + const iv = Buffer.from(encrypted.slice(0, 32), 'hex') + const decipher = crypto.createDecipheriv('aes256', createSecret(secret), iv) + let decrypted = decipher.update(encrypted.slice(32), 'hex', 'utf8') decrypted += decipher.final('utf8') return decrypted }