/** * Copyright (c) 2020 TypeFox GmbH. All rights reserved. * Licensed under the GNU Affero General Public License (AGPL). * See License-AGPL.txt in the project root for license information. */ import * as crypto from 'crypto'; import { injectable } from 'inversify'; export interface KeyParams { iv: string } export interface EncryptedData { /** utf8 encoded string */ data: string, keyParams: KeyParams } export const EncryptionEngine = Symbol('EncryptionEngine'); export interface EncryptionEngine { /** * @param data utf8 encoded string */ encrypt(data: string, key: Buffer): EncryptedData; decrypt(encryptedData: EncryptedData, key: Buffer): string; } /** * For starters, let's use aes-cbc-256 with: * - 16 bytes/128 bits IV (the size of an aes-256-cbc block) * - no salt, as we pass in a real key (no salting needed to turn a password into a key) * The implementation closely follows the exampes in https://nodejs.org/api/crypto.html. */ @injectable() export class EncryptionEngineImpl { readonly algorithm = 'aes-256-cbc'; readonly enc = 'base64'; encrypt(data: string, key: Buffer): EncryptedData { const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv(this.algorithm, key, iv); const encrypted = cipher.update(new Buffer(data, 'utf8')); const finalEncrypted = Buffer.concat([encrypted, cipher.final()]); return { data: finalEncrypted.toString(this.enc), keyParams: { iv: iv.toString(this.enc) } }; } decrypt(encryptedData: EncryptedData, key: Buffer): string { const decipher = crypto.createDecipheriv(this.algorithm, key, new Buffer(encryptedData.keyParams.iv, this.enc)); let decrypted = decipher.update(new Buffer(encryptedData.data, this.enc)); const finalDecrypted = Buffer.concat([decrypted, decipher.final()]); return finalDecrypted.toString('utf8'); } }