mirror of
https://github.com/finos/SymphonyElectron.git
synced 2024-11-21 16:38:41 -06:00
Typescript - Completed native crypto implementation & fixed bugs
This commit is contained in:
parent
3f799f10cc
commit
c17d5e8536
@ -119,6 +119,7 @@
|
||||
"wdio-selenium-standalone-service": "0.0.12"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/ffi": "0.2.1",
|
||||
"archiver": "3.0.0",
|
||||
"async.map": "0.5.2",
|
||||
"async.mapseries": "0.5.2",
|
||||
|
136
src/browser/crypto-library-handler.ts
Normal file
136
src/browser/crypto-library-handler.ts
Normal file
@ -0,0 +1,136 @@
|
||||
import { app } from 'electron';
|
||||
import { Library } from 'ffi';
|
||||
import * as path from 'path';
|
||||
import { refType, types } from 'ref';
|
||||
|
||||
const execPath = path.dirname(app.getPath('exe'));
|
||||
import { isDevEnv, isMac } from '../common/env';
|
||||
import { logger } from '../common/logger';
|
||||
|
||||
const TAG_LENGTH = 16;
|
||||
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 library = new Library((cryptoLibPath), {
|
||||
AESEncryptGCM: [types.int32, [
|
||||
refType(types.uchar),
|
||||
types.int32,
|
||||
refType(types.uchar),
|
||||
types.int32,
|
||||
refType(types.uchar),
|
||||
refType(types.uchar),
|
||||
types.uint32,
|
||||
refType(types.uchar),
|
||||
refType(types.uchar),
|
||||
types.uint32,
|
||||
]],
|
||||
|
||||
AESDecryptGCM: [types.int32, [
|
||||
refType(types.uchar),
|
||||
types.int32,
|
||||
refType(types.uchar),
|
||||
types.int32,
|
||||
refType(types.uchar),
|
||||
types.uint32,
|
||||
refType(types.uchar),
|
||||
refType(types.uchar),
|
||||
types.uint32,
|
||||
refType(types.uchar),
|
||||
]],
|
||||
|
||||
getVersion: [types.CString, []],
|
||||
});
|
||||
|
||||
export interface ICryptoLib {
|
||||
AESGCMEncrypt: (name: string, base64IV: string, base64AAD: string, base64Key: string, base64In: string) => string | null;
|
||||
AESGCMDecrypt: (base64IV: string, base64AAD: string, base64Key: string, base64In: string) => string | null;
|
||||
}
|
||||
|
||||
class CryptoLibrary implements ICryptoLib {
|
||||
|
||||
/**
|
||||
* Encrypt / Decrypt
|
||||
*
|
||||
* @param name {string} Method name to execute
|
||||
* @param base64IV {string}
|
||||
* @param base64AAD {string}
|
||||
* @param base64Key {string}
|
||||
* @param base64In {string}
|
||||
* @constructor
|
||||
*/
|
||||
public static EncryptDecrypt(name: string, base64IV: string, base64AAD: string, base64Key: string, base64In: string): string | null {
|
||||
let base64Input = base64In;
|
||||
|
||||
if (!base64Input) {
|
||||
base64Input = '';
|
||||
}
|
||||
|
||||
const IV: Buffer = Buffer.from(base64IV, 'base64');
|
||||
const AAD: Buffer = Buffer.from(base64AAD, 'base64');
|
||||
const KEY: Buffer = Buffer.from(base64Key, 'base64');
|
||||
const IN: Buffer = Buffer.from(base64Input, 'base64');
|
||||
|
||||
if (name === 'AESGCMEncrypt') {
|
||||
const outPtr: Buffer = Buffer.alloc(IN.length);
|
||||
const TAG: Buffer = 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) {
|
||||
logger.error(`AESEncryptGCM, Failed to encrypt with exit code ${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) {
|
||||
logger.error(`AESDecryptGCM, Failed to decrypt with exit code ${resultCode}`);
|
||||
}
|
||||
return outPtr.toString('base64');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts the given data
|
||||
*
|
||||
* @param base64IV {string}
|
||||
* @param base64AAD {string}
|
||||
* @param base64Key {string}
|
||||
* @param base64In {string}
|
||||
* @constructor
|
||||
*/
|
||||
public AESGCMEncrypt(base64IV: string, base64AAD: string, base64Key: string, base64In: string): string | null {
|
||||
return CryptoLibrary.EncryptDecrypt('AESGCMEncrypt', base64IV, base64AAD, base64Key, base64In);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the give data
|
||||
*
|
||||
* @param base64IV {string}
|
||||
* @param base64AAD {string}
|
||||
* @param base64Key {string}
|
||||
* @param base64In {string}
|
||||
* @constructor
|
||||
*/
|
||||
public AESGCMDecrypt(base64IV: string, base64AAD: string, base64Key: string, base64In: string): string | null {
|
||||
return CryptoLibrary.EncryptDecrypt('AESGCMDecrypt', base64IV, base64AAD, base64Key, base64In);
|
||||
}
|
||||
}
|
||||
|
||||
const cryptoLibrary = new CryptoLibrary();
|
||||
|
||||
export { cryptoLibrary };
|
@ -69,6 +69,8 @@ export class WindowHandler {
|
||||
private readonly config: IConfig;
|
||||
// Window reference
|
||||
private readonly windows: object;
|
||||
private readonly isCustomTitleBarAndWindowOS: boolean;
|
||||
|
||||
private mainWindow: ICustomBrowserWindow | null;
|
||||
private loadingWindow: Electron.BrowserWindow | null;
|
||||
private aboutAppWindow: Electron.BrowserWindow | null;
|
||||
@ -82,6 +84,8 @@ export class WindowHandler {
|
||||
this.windows = {};
|
||||
this.windowOpts = { ...this.getMainWindowOpts(), ...opts };
|
||||
this.isAutoReload = false;
|
||||
this.isCustomTitleBarAndWindowOS = isWindowsOS && this.config.isCustomTitleBar;
|
||||
|
||||
this.appMenu = null;
|
||||
// Window references
|
||||
this.mainWindow = null;
|
||||
@ -127,7 +131,7 @@ export class WindowHandler {
|
||||
if (!this.mainWindow || this.mainWindow.isDestroyed()) return;
|
||||
|
||||
// Injects custom title bar css into the webContents
|
||||
if (isWindowsOS && this.mainWindow && this.config.isCustomTitleBar) {
|
||||
if (this.mainWindow && this.isCustomTitleBarAndWindowOS) {
|
||||
this.mainWindow.webContents.insertCSS(
|
||||
fs.readFileSync(path.join(__dirname, '..', '/renderer/styles/title-bar.css'), 'utf8').toString(),
|
||||
);
|
||||
@ -136,7 +140,7 @@ export class WindowHandler {
|
||||
this.mainWindow.webContents.insertCSS(
|
||||
fs.readFileSync(path.join(__dirname, '..', '/renderer/styles/snack-bar.css'), 'utf8').toString(),
|
||||
);
|
||||
this.mainWindow.webContents.send('page-load');
|
||||
this.mainWindow.webContents.send('page-load', { isWindowsOS });
|
||||
this.appMenu = new AppMenu();
|
||||
this.monitorWindowActions();
|
||||
// Ready to show the window
|
||||
@ -274,11 +278,9 @@ export class WindowHandler {
|
||||
* Main window opts
|
||||
*/
|
||||
private getMainWindowOpts(): ICustomBrowserWindowConstructorOpts {
|
||||
// config fields
|
||||
const { isCustomTitleBar } = this.config;
|
||||
return {
|
||||
alwaysOnTop: false,
|
||||
frame: !(isCustomTitleBar && isWindowsOS),
|
||||
frame: !this.isCustomTitleBarAndWindowOS,
|
||||
minHeight: 300,
|
||||
minWidth: 300,
|
||||
show: false,
|
||||
|
@ -37,10 +37,12 @@ const createAPI = () => {
|
||||
createAPI();
|
||||
|
||||
// When the window is completely loaded
|
||||
ipcRenderer.on('page-load', () => {
|
||||
// injects custom window title bar
|
||||
const titleBar = React.createElement(WindowsTitleBar);
|
||||
ReactDOM.render(titleBar, document.body.appendChild(document.createElement( 'div' )));
|
||||
ipcRenderer.on('page-load', (_event, { isWindowsOS }) => {
|
||||
if (isWindowsOS) {
|
||||
// injects custom window title bar
|
||||
const titleBar = React.createElement(WindowsTitleBar);
|
||||
ReactDOM.render(titleBar, document.body.appendChild(document.createElement( 'div' )));
|
||||
}
|
||||
|
||||
// injects snack bar
|
||||
const snackBar = React.createElement(SnackBar);
|
||||
|
@ -22,27 +22,12 @@ export default class SnackBar extends React.Component<{}, IState> {
|
||||
this.removeSnackBar = this.removeSnackBar.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the custom title bar
|
||||
*/
|
||||
public render(): JSX.Element | undefined {
|
||||
const { show } = this.state;
|
||||
|
||||
return show ? (
|
||||
<div className='SnackBar SnackBar-show'>
|
||||
<span >{i18n.t('Press ', SNACKBAR_NAMESPACE)()}</span>
|
||||
<span className='SnackBar-esc'>{i18n.t('esc', SNACKBAR_NAMESPACE)()}</span>
|
||||
<span >{i18n.t(' to exit full screen', SNACKBAR_NAMESPACE)()}</span>
|
||||
</div>
|
||||
) : <div> </div>;
|
||||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
ipcRenderer.on('window-enter-full-screen', this.showSnackBar);
|
||||
ipcRenderer.on('window-leave-full-screen', this.removeSnackBar);
|
||||
}
|
||||
|
||||
public componentWillMount(): void {
|
||||
public componentWillUnmount(): void {
|
||||
ipcRenderer.removeListener('window-enter-full-screen', this.showSnackBar);
|
||||
ipcRenderer.removeListener('window-leave-full-screen', this.removeSnackBar);
|
||||
}
|
||||
@ -66,4 +51,19 @@ export default class SnackBar extends React.Component<{}, IState> {
|
||||
clearTimeout(this.snackBarTimer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the custom title bar
|
||||
*/
|
||||
public render(): JSX.Element | null {
|
||||
const { show } = this.state;
|
||||
|
||||
return show ? (
|
||||
<div className='SnackBar SnackBar-show'>
|
||||
<span >{i18n.t('Press ', SNACKBAR_NAMESPACE)()}</span>
|
||||
<span className='SnackBar-esc'>{i18n.t('esc', SNACKBAR_NAMESPACE)()}</span>
|
||||
<span >{i18n.t(' to exit full screen', SNACKBAR_NAMESPACE)()}</span>
|
||||
</div>
|
||||
) : <div />;
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { ipcRenderer, remote } from 'electron';
|
||||
|
||||
import { ICryptoLib } from '../browser/crypto-library-handler';
|
||||
import {
|
||||
apiCmds,
|
||||
apiName,
|
||||
@ -35,10 +36,24 @@ const throttledSetLocale = throttle((locale) => {
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
let cryptoLib: ICryptoLib | null;
|
||||
try {
|
||||
cryptoLib = remote.require('../browser/crypto-library-handler.js').cryptoLibrary;
|
||||
} catch (e) {
|
||||
cryptoLib = null;
|
||||
// tslint:disable-next-line
|
||||
console.warn('Failed to initialize Crypto Lib. You\'ll need to include the Crypto library. Contact the developers for more details');
|
||||
}
|
||||
|
||||
export class SSFApi {
|
||||
|
||||
/**
|
||||
* allows JS to register a activity detector that can be used by electron main process.
|
||||
* Native encryption and decryption.
|
||||
*/
|
||||
public CryptoLib: ICryptoLib | null = cryptoLib; // tslint:disable-line
|
||||
|
||||
/**
|
||||
* Allows JS to register a activity detector that can be used by electron main process.
|
||||
*
|
||||
* @param {Object} period - minimum user idle time in millisecond
|
||||
* @param {Object} activityDetection - function that can be called accepting
|
||||
@ -72,7 +87,7 @@ export class SSFApi {
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the count on the tray icon to the given number.
|
||||
* Sets the count on the tray icon to the given number.
|
||||
*
|
||||
* @param {number} count count to be displayed
|
||||
* note: count of 0 will remove the displayed count.
|
||||
|
Loading…
Reference in New Issue
Block a user