Make fingerprint validation optional, update readme with debug info for native messaging
This commit is contained in:
parent
02a3fbde99
commit
e639fa6674
12
README.md
12
README.md
|
@ -26,6 +26,18 @@ npm install
|
||||||
npm run electron
|
npm run electron
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Debug Native Messaging**
|
||||||
|
|
||||||
|
Native Messaging (communication with the browser extension) works by having the browser start a lightweight proxy application baked into our desktop binary. To setup an environment which allows
|
||||||
|
for easy debugging you will need to build the application for distribution, i.e. `npm run dist:<platform>`, start the dist version and enable desktop integration. This will write some manifests
|
||||||
|
to disk, Consult the [native manifests](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#Manifest_location) documentation for more details of the manigest
|
||||||
|
format, and the exact locations for the different platforms. *Note* that disabling the desktop integration will delete the manifests, and the files will need to be updated again.
|
||||||
|
|
||||||
|
The generated manifests are pre-configured with the production ID for the browser extensions. In order to use them with the development builds, the browser extension ID of the development build
|
||||||
|
needs to be added to the `allowed_extensions` section of the manifest. These IDs are generated by the browser, and can be found in the extension settings within the browser.
|
||||||
|
|
||||||
|
It will then be possible to run the desktop application as usual using `npm run electron` and communicate with the browser.
|
||||||
|
|
||||||
# Contribute
|
# Contribute
|
||||||
|
|
||||||
Code contributions are welcome! Please commit any pull requests against the `master` branch. Learn more about how to contribute by reading the [`CONTRIBUTING.md`](CONTRIBUTING.md) file.
|
Code contributions are welcome! Please commit any pull requests against the `master` branch. Learn more about how to contribute by reading the [`CONTRIBUTING.md`](CONTRIBUTING.md) file.
|
||||||
|
|
|
@ -98,6 +98,16 @@
|
||||||
</div>
|
</div>
|
||||||
<small class="help-block">{{'enableBrowserIntegrationDesc' | i18n}}</small>
|
<small class="help-block">{{'enableBrowserIntegrationDesc' | i18n}}</small>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<label for="enableBrowserIntegrationFingerprint">
|
||||||
|
<input id="enableBrowserIntegrationFingerprint" type="checkbox" name="EnableBrowserIntegrationFingerprint"
|
||||||
|
[(ngModel)]="enableBrowserIntegrationFingerprint" (change)="saveBrowserIntegrationFingerprint()" [disabled]="!enableBrowserIntegration">
|
||||||
|
{{'enableBrowserIntegrationFingerprint' | i18n}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<small class="help-block">{{'enableBrowserIntegrationFingerprintDesc' | i18n}}</small>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label for="enableTray">
|
<label for="enableTray">
|
||||||
|
|
|
@ -34,6 +34,7 @@ export class SettingsComponent implements OnInit {
|
||||||
pin: boolean = null;
|
pin: boolean = null;
|
||||||
disableFavicons: boolean = false;
|
disableFavicons: boolean = false;
|
||||||
enableBrowserIntegration: boolean = false;
|
enableBrowserIntegration: boolean = false;
|
||||||
|
enableBrowserIntegrationFingerprint: boolean = false;
|
||||||
enableMinToTray: boolean = false;
|
enableMinToTray: boolean = false;
|
||||||
enableCloseToTray: boolean = false;
|
enableCloseToTray: boolean = false;
|
||||||
enableTray: boolean = false;
|
enableTray: boolean = false;
|
||||||
|
@ -146,6 +147,7 @@ export class SettingsComponent implements OnInit {
|
||||||
this.disableFavicons = await this.storageService.get<boolean>(ConstantsService.disableFaviconKey);
|
this.disableFavicons = await this.storageService.get<boolean>(ConstantsService.disableFaviconKey);
|
||||||
this.enableBrowserIntegration = await this.storageService.get<boolean>(
|
this.enableBrowserIntegration = await this.storageService.get<boolean>(
|
||||||
ElectronConstants.enableBrowserIntegration);
|
ElectronConstants.enableBrowserIntegration);
|
||||||
|
this.enableBrowserIntegrationFingerprint = await this.storageService.get<boolean>(ElectronConstants.enableBrowserIntegrationFingerprint);
|
||||||
this.enableMinToTray = await this.storageService.get<boolean>(ElectronConstants.enableMinimizeToTrayKey);
|
this.enableMinToTray = await this.storageService.get<boolean>(ElectronConstants.enableMinimizeToTrayKey);
|
||||||
this.enableCloseToTray = await this.storageService.get<boolean>(ElectronConstants.enableCloseToTrayKey);
|
this.enableCloseToTray = await this.storageService.get<boolean>(ElectronConstants.enableCloseToTrayKey);
|
||||||
this.enableTray = await this.storageService.get<boolean>(ElectronConstants.enableTrayKey);
|
this.enableTray = await this.storageService.get<boolean>(ElectronConstants.enableTrayKey);
|
||||||
|
@ -329,6 +331,15 @@ export class SettingsComponent implements OnInit {
|
||||||
|
|
||||||
await this.storageService.save(ElectronConstants.enableBrowserIntegration, this.enableBrowserIntegration);
|
await this.storageService.save(ElectronConstants.enableBrowserIntegration, this.enableBrowserIntegration);
|
||||||
this.messagingService.send(this.enableBrowserIntegration ? 'enableBrowserIntegration' : 'disableBrowserIntegration');
|
this.messagingService.send(this.enableBrowserIntegration ? 'enableBrowserIntegration' : 'disableBrowserIntegration');
|
||||||
|
|
||||||
|
if (!this.enableBrowserIntegration) {
|
||||||
|
this.enableBrowserIntegrationFingerprint = false;
|
||||||
|
this.saveBrowserIntegrationFingerprint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveBrowserIntegrationFingerprint() {
|
||||||
|
await this.storageService.save(ElectronConstants.enableBrowserIntegrationFingerprint, this.enableBrowserIntegrationFingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private callAnalytics(name: string, enabled: boolean) {
|
private callAnalytics(name: string, enabled: boolean) {
|
||||||
|
|
|
@ -135,7 +135,7 @@ const eventService = new EventService(storageService, apiService, userService, c
|
||||||
const systemService = new SystemService(storageService, vaultTimeoutService, messagingService, platformUtilsService,
|
const systemService = new SystemService(storageService, vaultTimeoutService, messagingService, platformUtilsService,
|
||||||
null);
|
null);
|
||||||
const nativeMessagingService = new NativeMessagingService(cryptoFunctionService, cryptoService, platformUtilsService,
|
const nativeMessagingService = new NativeMessagingService(cryptoFunctionService, cryptoService, platformUtilsService,
|
||||||
logService, i18nService, userService, messagingService, vaultTimeoutService);
|
logService, i18nService, userService, messagingService, vaultTimeoutService, storageService);
|
||||||
|
|
||||||
const analytics = new Analytics(window, () => isDev(), platformUtilsService, storageService, appIdService);
|
const analytics = new Analytics(window, () => isDev(), platformUtilsService, storageService, appIdService);
|
||||||
containerService.attachToGlobal(window);
|
containerService.attachToGlobal(window);
|
||||||
|
|
|
@ -1453,6 +1453,12 @@
|
||||||
"browserIntegrationMasOnlyDesc": {
|
"browserIntegrationMasOnlyDesc": {
|
||||||
"message": "Unfortunately browser integration is only supported in the Mac App Store version for now."
|
"message": "Unfortunately browser integration is only supported in the Mac App Store version for now."
|
||||||
},
|
},
|
||||||
|
"enableBrowserIntegrationFingerprint": {
|
||||||
|
"message": "Require verification for browser integration"
|
||||||
|
},
|
||||||
|
"enableBrowserIntegrationFingerprintDesc": {
|
||||||
|
"message": "Enable an additional layer of security by requiring fingerprint phrase validation when establishing a link between your desktop and browser. When enabled, this requires user intervention and verification each time a connection is established."
|
||||||
|
},
|
||||||
"approve": {
|
"approve": {
|
||||||
"message": "Approve"
|
"message": "Approve"
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { WindowMain } from 'jslib/electron/window.main';
|
||||||
|
|
||||||
export class NativeMessagingMain {
|
export class NativeMessagingMain {
|
||||||
private connected = false;
|
private connected = false;
|
||||||
|
private socket: any;
|
||||||
|
|
||||||
constructor(private logService: LogService, private windowMain: WindowMain, private userPath: string, private appPath: string) {}
|
constructor(private logService: LogService, private windowMain: WindowMain, private userPath: string, private appPath: string) {}
|
||||||
|
|
||||||
|
@ -19,15 +20,16 @@ export class NativeMessagingMain {
|
||||||
|
|
||||||
ipc.serve(() => {
|
ipc.serve(() => {
|
||||||
ipc.server.on('message', (data: any, socket: any) => {
|
ipc.server.on('message', (data: any, socket: any) => {
|
||||||
// This is a ugly hack until electron is updated 7.0.0 which supports ipcMain.invoke
|
this.socket = socket;
|
||||||
this.windowMain.win.webContents.send('nativeMessaging', data);
|
this.windowMain.win.webContents.send('nativeMessaging', data);
|
||||||
ipcMain.once('nativeMessagingReply', (event, msg) => {
|
|
||||||
if (msg != null) {
|
|
||||||
this.send(msg, socket);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.on('nativeMessagingReply', (event, msg) => {
|
||||||
|
if (this.socket != null && msg != null) {
|
||||||
|
this.send(msg, this.socket);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
ipc.server.on('connect', () => {
|
ipc.server.on('connect', () => {
|
||||||
this.connected = true;
|
this.connected = true;
|
||||||
})
|
})
|
||||||
|
@ -36,6 +38,7 @@ export class NativeMessagingMain {
|
||||||
'socket.disconnected',
|
'socket.disconnected',
|
||||||
(socket: any, destroyedSocketID: any) => {
|
(socket: any, destroyedSocketID: any) => {
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
|
this.socket = null;
|
||||||
ipc.log(
|
ipc.log(
|
||||||
'client ' + destroyedSocketID + ' has disconnected!'
|
'client ' + destroyedSocketID + ' has disconnected!'
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,6 +12,8 @@ import { VaultTimeoutService } from 'jslib/abstractions/vaultTimeout.service';
|
||||||
|
|
||||||
import { Utils } from 'jslib/misc/utils';
|
import { Utils } from 'jslib/misc/utils';
|
||||||
import { SymmetricCryptoKey } from 'jslib/models/domain/symmetricCryptoKey';
|
import { SymmetricCryptoKey } from 'jslib/models/domain/symmetricCryptoKey';
|
||||||
|
import { StorageService } from 'jslib/abstractions';
|
||||||
|
import { ElectronConstants } from 'jslib/electron/electronConstants';
|
||||||
|
|
||||||
const MessageValidTimeout = 10 * 1000;
|
const MessageValidTimeout = 10 * 1000;
|
||||||
const EncryptionAlgorithm = 'sha1';
|
const EncryptionAlgorithm = 'sha1';
|
||||||
|
@ -21,7 +23,7 @@ export class NativeMessagingService {
|
||||||
|
|
||||||
constructor(private cryptoFunctionService: CryptoFunctionService, private cryptoService: CryptoService,
|
constructor(private cryptoFunctionService: CryptoFunctionService, private cryptoService: CryptoService,
|
||||||
private platformUtilService: PlatformUtilsService, private logService: LogService, private i18nService: I18nService,
|
private platformUtilService: PlatformUtilsService, private logService: LogService, private i18nService: I18nService,
|
||||||
private userService: UserService, private messagingService: MessagingService, private vaultTimeoutService: VaultTimeoutService) {
|
private userService: UserService, private messagingService: MessagingService, private vaultTimeoutService: VaultTimeoutService, private storageService: StorageService) {
|
||||||
ipcRenderer.on('nativeMessaging', async (event: any, message: any) => {
|
ipcRenderer.on('nativeMessaging', async (event: any, message: any) => {
|
||||||
this.messageHandler(message);
|
this.messageHandler(message);
|
||||||
});
|
});
|
||||||
|
@ -34,25 +36,30 @@ export class NativeMessagingService {
|
||||||
// Request to setup secure encryption
|
// Request to setup secure encryption
|
||||||
if (rawMessage.command === 'setupEncryption') {
|
if (rawMessage.command === 'setupEncryption') {
|
||||||
const remotePublicKey = Utils.fromB64ToArray(rawMessage.publicKey).buffer;
|
const remotePublicKey = Utils.fromB64ToArray(rawMessage.publicKey).buffer;
|
||||||
const fingerprint = (await this.cryptoService.getFingerprint(await this.userService.getUserId(), remotePublicKey)).join(' ');
|
|
||||||
|
|
||||||
this.messagingService.send('setFocus');
|
if (await this.storageService.get<boolean>(ElectronConstants.enableBrowserIntegrationFingerprint)) {
|
||||||
|
ipcRenderer.send('nativeMessagingReply', {command: 'verifyFingerprint', appId: appId});
|
||||||
|
|
||||||
// Await confirmation that fingerprint is correct
|
const fingerprint = (await this.cryptoService.getFingerprint(await this.userService.getUserId(), remotePublicKey)).join(' ');
|
||||||
const submitted = await Swal.fire({
|
|
||||||
title: this.i18nService.t('verifyBrowserTitle'),
|
|
||||||
html: `${this.i18nService.t('verifyBrowserDesc')}<br><br><strong>${fingerprint}</strong>`,
|
|
||||||
showCancelButton: true,
|
|
||||||
cancelButtonText: this.i18nService.t('cancel'),
|
|
||||||
showConfirmButton: true,
|
|
||||||
confirmButtonText: this.i18nService.t('approve'),
|
|
||||||
allowOutsideClick: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (submitted.value !== true) {
|
this.messagingService.send('setFocus');
|
||||||
return;
|
|
||||||
|
// Await confirmation that fingerprint is correct
|
||||||
|
const submitted = await Swal.fire({
|
||||||
|
title: this.i18nService.t('verifyBrowserTitle'),
|
||||||
|
html: `${this.i18nService.t('verifyBrowserDesc')}<br><br><strong>${fingerprint}</strong>`,
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonText: this.i18nService.t('cancel'),
|
||||||
|
showConfirmButton: true,
|
||||||
|
confirmButtonText: this.i18nService.t('approve'),
|
||||||
|
allowOutsideClick: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (submitted.value !== true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.secureCommunication(remotePublicKey, appId);
|
this.secureCommunication(remotePublicKey, appId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue