mirror of
https://github.com/finos/SymphonyElectron.git
synced 2025-02-25 18:55:29 -06:00
ELECTRON-600 (Implement Native Encryption for AESGCMEncrypt, AESGCMDecrypt & RSADecrypt) (#452)
- Implement Native Encryption for AESGCMEncrypt & AESGCMDecrypt - Refactor and remove spectron test case for security reasons - Update library file for MacOS - Include RSADecrypt method - Throw errors instead of logs - Remove exceptions throw - Fix tag buffer in AESGCMDecrypt - Optimize EncryptDecrypt method
This commit is contained in:
committed by
Vishwas Shashidhar
parent
65b107a1ab
commit
668b49a826
247
js/cryptoLib.js
Normal file
247
js/cryptoLib.js
Normal file
@@ -0,0 +1,247 @@
|
||||
const electron = require('electron');
|
||||
const app = electron.app;
|
||||
const ffi = require('ffi');
|
||||
const ref = require('ref');
|
||||
const path = require('path');
|
||||
const execPath = path.dirname(app.getPath('exe'));
|
||||
|
||||
const log = require('./log.js');
|
||||
const logLevels = require('./enums/logLevels.js');
|
||||
const { isMac, isDevEnv } = require('../js/utils/misc');
|
||||
|
||||
const TAG_LENGTH = 16;
|
||||
const KEY_LENGTH = 32;
|
||||
const arch = process.arch === 'ia32';
|
||||
const winLibraryPath = isDevEnv ? path.join(__dirname, '..', 'library') : path.join(execPath, 'library');
|
||||
const macLibraryPath = isDevEnv ? path.join(__dirname, '..', 'library') : path.join(execPath, '..', 'library');
|
||||
|
||||
const cryptoLibPath = isMac ?
|
||||
path.join(macLibraryPath, 'cryptoLib.dylib') :
|
||||
(arch ? path.join(winLibraryPath, 'libsymphonysearch-x86.dll') : path.join(winLibraryPath, 'libsymphonysearch-x64.dll'));
|
||||
|
||||
const voidPtr = ref.refType(ref.types.void);
|
||||
const RSAKeyPair = exports.RSAKeyPair = voidPtr;
|
||||
const RSAKeyPairPtr = exports.RSAKeyPairPtr = ref.refType(RSAKeyPair);
|
||||
const RSAPubKey = exports.RSAPubKey = voidPtr;
|
||||
const RSAPubKeyPtr = exports.RSAPubKeyPtr = ref.refType(RSAPubKey);
|
||||
|
||||
const library = new ffi.Library((cryptoLibPath), {
|
||||
|
||||
AESEncryptGCM: [ref.types.int32, [
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.int32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.int32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
]],
|
||||
|
||||
AESDecryptGCM: [ref.types.int32, [
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.int32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.int32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
ref.refType(ref.types.uchar),
|
||||
]],
|
||||
|
||||
encryptRSA: [ref.types.uint32, [
|
||||
RSAPubKeyPtr,
|
||||
ref.types.int32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
]],
|
||||
|
||||
decryptRSA: [ref.types.uint32, [
|
||||
RSAPubKeyPtr,
|
||||
ref.types.int32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
]],
|
||||
|
||||
deserializeRSAPubKey: [RSAPubKey, [
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
]],
|
||||
deserializeRSAKeyPair: [RSAKeyPairPtr, [
|
||||
ref.refType(ref.types.uchar),
|
||||
ref.types.uint32,
|
||||
]],
|
||||
|
||||
getRSAKeySize: [ref.types.uint32, [
|
||||
RSAKeyPairPtr
|
||||
]],
|
||||
|
||||
getVersion: [ref.types.CString, []],
|
||||
});
|
||||
|
||||
/**
|
||||
* Method to decrypt content
|
||||
* @param Base64IV
|
||||
* @param Base64AAD
|
||||
* @param Base64Key
|
||||
* @param Base64In
|
||||
* @return {*}
|
||||
* @constructor
|
||||
*/
|
||||
const AESGCMEncrypt = function(Base64IV, Base64AAD, Base64Key, Base64In) {
|
||||
return EncryptDecrypt('AESGCMEncrypt', Base64IV, Base64AAD, Base64Key, Base64In);
|
||||
};
|
||||
|
||||
/**
|
||||
* Method to decrypt content
|
||||
* @param Base64IV
|
||||
* @param Base64AAD
|
||||
* @param Base64Key
|
||||
* @param Base64In
|
||||
* @return {*}
|
||||
* @constructor
|
||||
*/
|
||||
const AESGCMDecrypt = function(Base64IV, Base64AAD, Base64Key, Base64In) {
|
||||
return EncryptDecrypt('AESGCMDecrypt', Base64IV, Base64AAD, Base64Key, Base64In);
|
||||
};
|
||||
|
||||
/**
|
||||
* Encrypt / Decrypt
|
||||
* @param name {String} - Method name
|
||||
* @param Base64IV {String} base64
|
||||
* @param Base64AAD {String} base64
|
||||
* @param Base64Key {String} base64
|
||||
* @param Base64In {String} base64
|
||||
* @return {*}
|
||||
* @constructor
|
||||
*/
|
||||
const EncryptDecrypt = function(name, Base64IV, Base64AAD, Base64Key, Base64In) {
|
||||
let base64In = Base64In;
|
||||
|
||||
if (!base64In) {
|
||||
base64In = "";
|
||||
}
|
||||
|
||||
const IV = Buffer.from(Base64IV, 'base64');
|
||||
const AAD = Buffer.from(Base64AAD, 'base64');
|
||||
const Key = Buffer.from(Base64Key, 'base64');
|
||||
const In = Buffer.from(base64In, 'base64');
|
||||
|
||||
if (name === 'AESGCMEncrypt') {
|
||||
const OutPtr = Buffer.alloc(In.length);
|
||||
const Tag = Buffer.alloc(TAG_LENGTH);
|
||||
|
||||
const resultCode = library.AESEncryptGCM(In, In.length, AAD, AAD.length, Key, IV, IV.length, OutPtr, Tag, TAG_LENGTH);
|
||||
|
||||
if (resultCode < 0) {
|
||||
log.send(logLevels.ERROR, `AESEncryptGCM, Failed to encrypt with exit code ${resultCode}`);
|
||||
}
|
||||
log.send(logLevels.INFO, `Output from AESEncryptGCM ${resultCode}`);
|
||||
const bufferArray = [OutPtr, Tag];
|
||||
return Buffer.concat(bufferArray).toString('base64');
|
||||
}
|
||||
|
||||
if (name === 'AESGCMDecrypt') {
|
||||
const CipherTextLen = In.length - TAG_LENGTH;
|
||||
const Tag = Buffer.from(In.slice(In.length - 16, In.length));
|
||||
const OutPtr = Buffer.alloc(In.length - TAG_LENGTH);
|
||||
|
||||
const resultCode = library.AESDecryptGCM(In, CipherTextLen, AAD, AAD.length, Tag, TAG_LENGTH, Key, IV, IV.length, OutPtr);
|
||||
|
||||
if (resultCode < 0) {
|
||||
log.send(logLevels.ERROR, `AESDecryptGCM, Failed to decrypt with exit code ${resultCode}`);
|
||||
}
|
||||
log.send(logLevels.INFO, `Output from AESDecryptGCM ${resultCode}`);
|
||||
return OutPtr.toString('base64');
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decrypt RSA
|
||||
* @param pemKey
|
||||
* @param input
|
||||
* @return {*}
|
||||
* @constructor
|
||||
*/
|
||||
const RSADecrypt = function (pemKey, input) {
|
||||
return RSAEncryptDecrypt("RSADecrypt", pemKey, input);
|
||||
};
|
||||
|
||||
/**
|
||||
* Encrypt / Decrypt RSA
|
||||
* @param action
|
||||
* @param pemKey
|
||||
* @param inputStr
|
||||
* @return {String}
|
||||
* @constructor
|
||||
*/
|
||||
const RSAEncryptDecrypt = function (action, pemKey, inputStr) {
|
||||
|
||||
let rsaKey = getRSAKeyFromPEM(pemKey);
|
||||
|
||||
if (!rsaKey) {
|
||||
log.send(logLevels.ERROR, `Failed to parse formatted RSA PEM key`);
|
||||
}
|
||||
|
||||
let input = Buffer.from(inputStr, 'base64');
|
||||
let outLen = library.getRSAKeySize(rsaKey);
|
||||
|
||||
let outPtr = Buffer.alloc(KEY_LENGTH);
|
||||
|
||||
let ret = 0;
|
||||
|
||||
if (action === 'RSAEncrypt') {
|
||||
ret = library.encryptRSA(rsaKey, 0, input, input.length, outPtr, outLen);
|
||||
} else {
|
||||
outLen = library.decryptRSA(rsaKey, 0, input, input.length, outPtr, outLen);
|
||||
|
||||
if (outLen < 0) {
|
||||
ret = outLen;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret !== 0) {
|
||||
log.send(logLevels.ERROR, `${action} failed due to -> ${ret}`);
|
||||
}
|
||||
return Buffer.from(outPtr.toString('hex'), 'hex').toString('base64');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get RSA key from PEM key
|
||||
* @param pemKey
|
||||
* @return {*}
|
||||
*/
|
||||
const getRSAKeyFromPEM = function (pemKey) {
|
||||
|
||||
let pemKeyBytes = Buffer.from(pemKey, 'utf-8');
|
||||
|
||||
let rsaKey;
|
||||
|
||||
if (pemKey.startsWith("-----BEGIN PUBLIC KEY-----")) {
|
||||
rsaKey = library.deserializeRSAPubKey(pemKeyBytes, pemKey.length);
|
||||
} else {
|
||||
rsaKey = library.deserializeRSAKeyPair(pemKeyBytes, pemKey.length);
|
||||
}
|
||||
|
||||
if (rsaKey === 0) {
|
||||
log.send(logLevels.ERROR, 'RSAKey is 0!!');
|
||||
}
|
||||
return rsaKey;
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
AESGCMEncrypt: AESGCMEncrypt,
|
||||
AESGCMDecrypt: AESGCMDecrypt,
|
||||
RSADecrypt: RSADecrypt,
|
||||
};
|
||||
@@ -31,6 +31,7 @@ const KeyCodes = {
|
||||
|
||||
let Search;
|
||||
let SearchUtils;
|
||||
let CryptoLib;
|
||||
let isAltKey = false;
|
||||
let isMenuOpen = false;
|
||||
|
||||
@@ -48,6 +49,14 @@ try {
|
||||
console.warn("Failed to initialize swift search (Utils). You'll need to include the search dependency. Contact the developers for more details");
|
||||
}
|
||||
|
||||
try {
|
||||
CryptoLib = remote.require('./cryptoLib.js');
|
||||
} catch (e) {
|
||||
CryptoLib = null;
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn("Failed to initialize Crypto Lib. You'll need to include the Crypto library. Contact the developers for more details");
|
||||
}
|
||||
|
||||
require('../downloadManager');
|
||||
let snackBar;
|
||||
|
||||
@@ -202,6 +211,11 @@ function createAPI() {
|
||||
*/
|
||||
SearchUtils: SearchUtils || null,
|
||||
|
||||
/**
|
||||
* Native encryption and decryption.
|
||||
*/
|
||||
CryptoLib: CryptoLib,
|
||||
|
||||
/**
|
||||
* Brings window forward and gives focus.
|
||||
* @param {String} windowName Name of window. Note: main window name is 'main'
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"extraFiles": [
|
||||
"config/Symphony.config",
|
||||
"library/libsymphonysearch.dylib",
|
||||
"library/cryptoLib.dylib",
|
||||
"library/lz4.exec"
|
||||
],
|
||||
"appId": "com.symphony.electron-desktop",
|
||||
@@ -127,7 +128,9 @@
|
||||
"lodash.omit": "4.5.0",
|
||||
"lodash.pick": "4.4.0",
|
||||
"shell-path": "2.1.0",
|
||||
"winreg": "1.2.4"
|
||||
"winreg": "1.2.4",
|
||||
"ffi": "2.2.0",
|
||||
"ref": "1.3.5"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"screen-snippet": "git+https://github.com/symphonyoss/ScreenSnippet.git#v1.0.2",
|
||||
|
||||
Reference in New Issue
Block a user