From e01e334fa9153b3aaf5bf2636f90e7de40118b8e Mon Sep 17 00:00:00 2001 From: Keerthi Niranjan Date: Mon, 21 Aug 2017 13:39:07 +0530 Subject: [PATCH] SEARCH-116-GCM 1. Removed the key creation in crypto 2. Hash SHA256 3. Changed the encrypted zip name --- js/cryptoLib/crypto.js | 89 +++++++++++------------------------------- js/cryptoLib/index.js | 9 +++-- package.json | 7 ++-- 3 files changed, 32 insertions(+), 73 deletions(-) diff --git a/js/cryptoLib/crypto.js b/js/cryptoLib/crypto.js index 8cde0dbc..f7d9feb3 100644 --- a/js/cryptoLib/crypto.js +++ b/js/cryptoLib/crypto.js @@ -2,9 +2,6 @@ * AES GCM Stream * This module exports encrypt and decrypt stream constructors which can be * used to protect data with authenticated encryption. - * - * Helper methods are also provided to do things like generate secure keys - * and salt. */ 'use strict'; @@ -12,11 +9,8 @@ let stream = require('stream'); let Transform = stream.Transform; let util = require('util'); let crypto = require('crypto'); +let forge = require('node-forge'); -let PBKDF2_PASS_LENGTH = 256; -let PBKDF2_SALT_LENGTH = 32; -let PBKDF2_ITERATIONS = 5000; -let PBKDF2_DIGEST = 'sha256'; let KEY_LENGTH = 32; // bytes let GCM_NONCE_LENGTH = 12; //bytes let GCM_MAC_LENGTH = 16; //bytes @@ -34,44 +28,33 @@ let validateAndConvertKey = function(key) { if (key && key instanceof Buffer && key.length === KEY_LENGTH) { return key; } else if (key && typeof key === 'string') { - // Because we don't have a reliable way to test string encoding, we assume that a string type - // key was created by the createEncodedKey method and that it is using the keyEncoding which - // has been set on the module, whether that's the default or something explicitly set by the - // user via the setKeyEncoding method. - let bufKey = new Buffer(key, keyEncoding); + let md = forge.md.sha256.create(); + md.update(key); + let bufKey = new Buffer(bits2b64(md.digest().getBytes()), keyEncoding); if (bufKey.length !== KEY_LENGTH) { let encodingErrorMessage = 'Provided key string is either of an unknown encoding (expected: ' + keyEncoding + ') or the wrong length.'; throw new Error(encodingErrorMessage); } return bufKey; - } else { - let message = 'The key options property is required! Expected ' + - keyEncoding + ' encoded string or a buffer.'; - throw new Error(message); } + let message = 'The key options property is required! Expected ' + + keyEncoding + ' encoded string or a buffer.'; + throw new Error(message); +}; + +/** + * encode bits to b64 + * @param bits + * @returns {*} + */ +let bits2b64 = function (bits) { + return forge.util.encode64(bits); }; exports.encrypt = EncryptionStream; exports.decrypt = DecryptionStream; -/** - * getEncoding - * Helper which returns the current encoding being used for keys - * @returns {string} - */ -exports.getKeyEncoding = function() { - return keyEncoding; -}; - -/** - * setEncoding - * Helper to set the encoding being used for keys - * @param enc - */ -exports.setKeyEncoding = function(enc) { - keyEncoding = Buffer.isEncoding(enc) ? enc : keyEncoding; -}; /** * createSalt @@ -88,32 +71,6 @@ exports.createSalt = function(length) { } }; -/** - * createKeyBuffer - * Method which returns a buffer representing a secure key generated with PBKDF2 - * @returns Buffer - */ -exports.createKeyBuffer = function() { - try { - let passphrase = crypto.randomBytes(PBKDF2_PASS_LENGTH); - let salt = this.createSalt(PBKDF2_SALT_LENGTH); - return crypto.pbkdf2Sync(passphrase, salt, PBKDF2_ITERATIONS, KEY_LENGTH, PBKDF2_DIGEST); - } catch (ex) { - console.error('Problem reading random data and generating a key!'); - throw ex; - } -}; - -/** - * createEncodedKey - * Helper method that returns an encoded key - * @returns string - * @throws error - */ -exports.createEncodedKey = function() { - return exports.createKeyBuffer().toString(keyEncoding); -}; - /** * EncryptionStream * A constructor which returns an encryption stream @@ -151,7 +108,6 @@ EncryptionStream.prototype._flush = function(cb) { cb(); }; - /** * DecryptionStream * A constructor which returns a decryption stream @@ -179,13 +135,14 @@ util.inherits(DecryptionStream, Transform); DecryptionStream.prototype._transform = function(chunk, enc, cb) { let chunkLength = chunk.length; let chunkOffset = 0; + let _chunk = chunk; if (!this._started) { if (this._nonceBytesRead < GCM_NONCE_LENGTH) { let nonceRemaining = GCM_NONCE_LENGTH - this._nonceBytesRead; chunkOffset = chunkLength <= nonceRemaining ? chunkLength : nonceRemaining; - chunk.copy(this._nonce, this._nonceBytesRead, 0, chunkOffset); - chunk = chunk.slice(chunkOffset); - chunkLength = chunk.length; + _chunk.copy(this._nonce, this._nonceBytesRead, 0, chunkOffset); + _chunk = _chunk.slice(chunkOffset); + chunkLength = _chunk.length; this._nonceBytesRead += chunkOffset; } @@ -199,7 +156,7 @@ DecryptionStream.prototype._transform = function(chunk, enc, cb) { // We can't use an else because we have no idea how long our chunks will be // all we know is that once we've got a nonce we can start storing cipher text if (this._started) { - this._cipherTextChunks.push(chunk); + this._cipherTextChunks.push(_chunk); } cb(); @@ -222,7 +179,7 @@ DecryptionStream.prototype._flush = function(cb) { decrypted.forEach(function(item) { this.push(item); }, this); - cb(); + return cb(); }; function pullOutMac(array) { @@ -242,7 +199,7 @@ function pullOutMac(array) { } } if (macByteCount !== GCM_MAC_LENGTH) { - return; + return null; } macBits.reverse(); return Buffer.concat(macBits, GCM_MAC_LENGTH); diff --git a/js/cryptoLib/index.js b/js/cryptoLib/index.js index 08c880eb..1006274f 100644 --- a/js/cryptoLib/index.js +++ b/js/cryptoLib/index.js @@ -24,6 +24,7 @@ class Crypto { // will be handling after implementing in client app this.indexDataFolder = INDEX_DATA_FOLDER + '_' + userId + '_' + INDEX_VERSION; + this.permanentIndexFolderName = 'search_index_' + userId + '_' + INDEX_VERSION; this.dump = TEMPORARY_PATH; this.key = "XrwVgWR4czB1a9scwvgRUNbXiN3W0oWq7oUBenyq7bo="; // temporary only this.encryptedIndex = 'encryptedIndex.enc'; @@ -44,18 +45,18 @@ class Crypto { return; } - let output = fs.createWriteStream(`${this.dump}/content.zip`); + let output = fs.createWriteStream(`${this.dump}/${this.permanentIndexFolderName}.zip`); zipArchive.on('end', () => { - if (!fs.existsSync(`${this.dump}/content.zip`)){ + if (!fs.existsSync(`${this.dump}/${this.permanentIndexFolderName}.zip`)){ // will be handling after implementing in client app reject(); return; } - const input = fs.createReadStream(`${this.dump}/content.zip`); + const input = fs.createReadStream(`${this.dump}/${this.permanentIndexFolderName}.zip`); const outputEncryption = fs.createWriteStream(this.encryptedIndex); let config = { key: this.key @@ -67,7 +68,7 @@ class Crypto { reject(new Error(err)); } if (!this.zipErrored) { - fs.unlinkSync(`${this.dump}/content.zip`); + fs.unlinkSync(`${this.dump}/${this.permanentIndexFolderName}.zip`); Crypto.deleteFolderRecursive(this.indexDataFolder) .then(function () { resolve(res); diff --git a/package.json b/package.json index 8ec04720..d389597f 100644 --- a/package.json +++ b/package.json @@ -89,17 +89,18 @@ "dependencies": { "@paulcbetts/system-idle-time": "^1.0.4", "appdirectory": "^0.1.0", + "archiver": "^2.0.0", "async.map": "^0.5.2", "async.mapseries": "^0.5.2", "auto-launch": "^5.0.1", "electron-dl": "^1.9.0", "electron-spellchecker": "^1.2.0", "electron-squirrel-startup": "^1.0.0", + "extract-zip": "^1.6.5", "filesize": "^3.5.10", "keymirror": "0.1.1", - "winreg": "^1.2.3", - "archiver": "latest", - "extract-zip": "latest" + "node-forge": "^0.7.1", + "winreg": "^1.2.3" }, "optionalDependencies": { "screen-snippet": "git+https://github.com/symphonyoss/ScreenSnippet.git#v1.0.1"