UNPKG

czip

Version:

Nodejs console app for file encryption

200 lines (189 loc) 6.44 kB
const path = require('path'); const fs = require('fs'); const AES = require('../../third-party/aes'); const kill = require('../process/kill'); const { encryptCBC, decryptCBC, getKey, checkRV } = require('../core/crypto'); const { uint8ArrayToint32, int32ToUint8Array } = require('../functions/encoding'); const { getChildren, parsePath } = require('../functions/folder'); const { getRV, getIV } = require('../functions/rand'); const META = require('../environments/meta'); const INT32BYTES = 4; const BLOCKSIZE = 16; const DEFAULT_POW = 14; class ZipService { constructor(app) { this.app = app; } readFolder(nodeName) { const isFolder = fs.lstatSync(nodeName).isDirectory(); if (!isFolder) { kill('Error: Not a folder. Only folders are available for encryption'); } const id = parsePath(nodeName).name; const children = getChildren(nodeName); children.sort((a, b) => { // Show folder before files const aFolderStatus = a.isFolder ? 1 : 0; const bFolderStatus = b.isFolder ? 1 : 0; const folderStatus = bFolderStatus - aFolderStatus; // Show parents before children const aNodeLength = parsePath(a.path).length; const bNodeLength = parsePath(b.path).length; const lengthStatus = aNodeLength - bNodeLength; if (folderStatus !== 0) { return folderStatus; } else if (lengthStatus !== 0) { return lengthStatus; } else { return a.path.localeCompare(b.path); } }); return this.app.protoService.getProto(id, children); } encrypt(protoTree, password) { // Encrypt tree console.time('Encrypted'); const key = getKey(password, DEFAULT_POW); const tree = protoTree.serializeBinary(); const treeRV = new Uint8Array([...getRV(), ...tree]); const paddingTree = AES.padding.pkcs7.pad(treeRV); const treeLength = paddingTree.length; const RVT = encryptCBC(paddingTree, key, getIV()); // Write let i = 0; const rootFile = path.join(process.cwd(), protoTree.getMeta().getId() + '.czip'); const writeId = fs.openSync(rootFile, 'w'); let buffer; // Title const czipTitle = AES.utils.utf8.toBytes(META.name + META.version); buffer = Buffer.alloc(1); buffer[0] = czipTitle.length; fs.writeSync(writeId, buffer, 0, buffer.length, 0); i += 1; buffer = Buffer.from(czipTitle); fs.writeSync(writeId, buffer, 0, buffer.length, i); i += buffer.length; // PoW buffer = Buffer.alloc(1); buffer[0] = DEFAULT_POW; fs.writeSync(writeId, buffer, 0, buffer.length, i); i += 1; // Tree size const treeSize = int32ToUint8Array(treeLength); buffer = Buffer.from(treeSize); fs.writeSync(writeId, buffer, 0, buffer.length, i); i += buffer.length; // Tree buffer = Buffer.from(RVT); fs.writeSync(writeId, buffer, 0, buffer.length, i); i += buffer.length; // Encrypt blocks this.app.threadingService.encryptBlocks(writeId, protoTree, i); } decrypt(nodeName, password) { console.time('Decrypted'); const readId = fs.openSync(nodeName + '.czip', 'r'); this.app.dataService.readId = readId; let buffer; let i = 0; // Get title length buffer = Buffer.alloc(1); fs.readSync(readId, buffer, 0, 1, 0); const length = buffer[0]; i += 1; // Get title buffer = Buffer.alloc(length); fs.readSync(readId, buffer, 0, length, i); this.app.dataService.title = buffer.toString(); i += length; // Get CostFactor (N=2^pow) buffer = Buffer.alloc(1); fs.readSync(readId, buffer, 0, 1, i); this.app.dataService.pow = buffer[0]; i += 1; // Get size of tree buffer = Buffer.alloc(INT32BYTES); fs.readSync(readId, buffer, 0, INT32BYTES, i); const treeSize = Uint8Array.from(buffer); const treeLength = uint8ArrayToint32(treeSize); i += INT32BYTES; // Get rv const key = getKey(password, this.app.dataService.pow); const iv = getIV(); buffer = Buffer.alloc(BLOCKSIZE); fs.readSync(readId, buffer, 0, BLOCKSIZE, i); const encryptedRV = Uint8Array.from(buffer); this.app.dataService.rv = decryptCBC(encryptedRV, key, iv); if (!checkRV(this.app.dataService.rv)) { kill('Error: Invalid password'); } // Get encrypted tree buffer = Buffer.alloc(treeLength); fs.readSync(readId, buffer, 0, treeLength, i); const RVT = Uint8Array.from(buffer); i += treeLength; this.app.dataService.blocksPosition = i; // Decrypt tree const paddingTree = decryptCBC(RVT, key, iv); const treeRV = AES.padding.pkcs7.strip(paddingTree); const tree = treeRV.slice(BLOCKSIZE); this.app.protoService.setProto(tree); } unpack() { const id = this.app.dataService.tree.getMeta().getId(); const root = path.join(process.cwd(), id); if (fs.existsSync(root)) { kill('Error: Folder already exist'); } fs.mkdirSync(root); this.app.dataService.tree.getFolderList().forEach(folder => { if (folder.getPath() === '/') { return; } const folderPath = path.join(root, folder.getPath()); fs.mkdirSync(folderPath); }); this.app.threadingService.decryptBlocks(root); } password(nodeName, password) { const readId = fs.openSync(nodeName + '.czip', 'r'); let buffer; let i = 0; // "CZIP2.46" buffer = Buffer.alloc(1); fs.readSync(readId, buffer, 0, 1, 0); const length = buffer[0]; i += length + 1; // Get CostFactor (N=2^pow) buffer = Buffer.alloc(1); fs.readSync(readId, buffer, 0, 1, i); this.app.dataService.pow = buffer[0]; i += 1; // Size of tree i += INT32BYTES; // Get rv const key = getKey(password, this.app.dataService.pow); const iv = getIV(); buffer = Buffer.alloc(BLOCKSIZE); fs.readSync(readId, buffer, 0, BLOCKSIZE, i); const encryptedRV = Uint8Array.from(buffer); const rv = decryptCBC(encryptedRV, key, iv); return checkRV(rv); } getTitle(nodeName) { const readId = fs.openSync(nodeName + '.czip', 'r'); let buffer; let i = 0; // Get title length buffer = Buffer.alloc(1); fs.readSync(readId, buffer, 0, 1, 0); const length = buffer[0]; i += 1; // Get title buffer = Buffer.alloc(length); fs.readSync(readId, buffer, 0, length, i); fs.close(readId); return buffer.toString(); } } module.exports = ZipService;