UNPKG

@fbl-plugins/crypto

Version:
156 lines 6.13 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.CryptoService = void 0; const crypto_1 = require("crypto"); const fs_1 = require("fs"); const util_1 = require("util"); const fbl_1 = require("fbl"); const path_1 = require("path"); class CryptoService { constructor() { } static get instance() { if (!this.pInstance) { this.pInstance = new CryptoService(); } return this.pInstance; } /* istanbul ignore next */ static reset() { this.pInstance = null; } /** * Create pbkdf2 hash with 100k iterations with provided password and optionally salt * If salt is not provided - it will be generated and returned back * @param password * @param salt */ getPasswordHash(password, salt) { return __awaiter(this, void 0, void 0, function* () { salt = salt || crypto_1.randomBytes(CryptoService.saltSize); const hash = yield util_1.promisify(crypto_1.pbkdf2)(password, salt, CryptoService.hashIterations, CryptoService.keySize, CryptoService.hashAlgorithm); return { salt: salt, hash: hash, }; }); } /** * Encrypt file with provided password * @param source * @param destination * @param password */ encrypt(source, destination, password) { return __awaiter(this, void 0, void 0, function* () { const passwordHash = yield this.getPasswordHash(password); const iv = crypto_1.randomBytes(CryptoService.ivSize); const cipher = crypto_1.createCipheriv(CryptoService.encryptionAlgorithm, passwordHash.hash, iv); let tmpFile; let ws; if (source === destination) { tmpFile = yield fbl_1.TempPathsRegistry.instance.createTempFile(true); ws = fs_1.createWriteStream(tmpFile); } else { yield fbl_1.FSUtil.mkdirp(path_1.dirname(destination)); ws = fs_1.createWriteStream(destination); } const rs = fs_1.createReadStream(source); const writeAsync = (chunk) => { return new Promise((resolve) => { ws.write(chunk, () => resolve()); }); }; yield writeAsync(CryptoService.version); yield writeAsync(passwordHash.salt); yield writeAsync(iv); yield new Promise((resolve) => { rs.pipe(cipher).pipe(ws); rs.on('close', () => { ws.on('finish', () => { resolve(); }); ws.end(); }); }); if (source === destination) { yield util_1.promisify(fs_1.rename)(tmpFile, destination); } }); } /** * Decrypt file * @param source * @param destination * @param password */ decrypt(source, destination, password) { return __awaiter(this, void 0, void 0, function* () { const headerSize = 2 + CryptoService.saltSize + CryptoService.ivSize; let rs = fs_1.createReadStream(source, { end: headerSize, }); const header = yield this.streamToBuffer(rs); const salt = header.slice(2, 2 + CryptoService.saltSize); const iv = header.slice(2 + CryptoService.saltSize, headerSize); rs = fs_1.createReadStream(source, { start: headerSize, }); const passwordHash = yield this.getPasswordHash(password, salt); const decipher = crypto_1.createDecipheriv(CryptoService.encryptionAlgorithm, passwordHash.hash, iv); let ws; let tmpFile; if (source === destination) { tmpFile = yield fbl_1.TempPathsRegistry.instance.createTempFile(true); ws = fs_1.createWriteStream(tmpFile); } else { yield fbl_1.FSUtil.mkdirp(path_1.dirname(destination)); ws = fs_1.createWriteStream(destination); } yield new Promise((resolve) => { rs.pipe(decipher).pipe(ws); rs.on('close', () => { ws.on('finish', () => { resolve(); }); ws.end(); }); }); if (source === destination) { yield util_1.promisify(fs_1.rename)(tmpFile, destination); } }); } /** * Read stream into buffer * @param stream */ streamToBuffer(stream) { return new Promise((resolve, reject) => { const buffers = []; stream.on('error', reject); stream.on('data', (data) => buffers.push(data)); stream.on('end', () => resolve(Buffer.concat(buffers))); }); } } exports.CryptoService = CryptoService; CryptoService.encryptionAlgorithm = 'aes-256-cbc'; CryptoService.hashAlgorithm = 'sha512'; CryptoService.hashIterations = 100000; CryptoService.keySize = 32; CryptoService.ivSize = 16; CryptoService.saltSize = 24; // encryption logic version, reserved for future use CryptoService.version = Buffer.alloc(2, '0001', 'hex'); //# sourceMappingURL=CryptoService.js.map