diff --git a/src/app/services.module.ts b/src/app/services.module.ts index 783eea73e7..89ebe65486 100644 --- a/src/app/services.module.ts +++ b/src/app/services.module.ts @@ -129,7 +129,7 @@ const environmentService = new EnvironmentService(apiService, storageService, no const eventService = new EventService(storageService, apiService, userService, cipherService); const systemService = new SystemService(storageService, vaultTimeoutService, messagingService, platformUtilsService, null); -const nativeMessagingService = new NativeMessagingService(cryptoFunctionService, cryptoService, platformUtilsService, logService); +const nativeMessagingService = new NativeMessagingService(cryptoFunctionService, cryptoService, platformUtilsService, logService, i18nService, userService); const analytics = new Analytics(window, () => isDev(), platformUtilsService, storageService, appIdService); containerService.attachToGlobal(window); diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index eaf6cd656c..df3db26ee3 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -1407,5 +1407,14 @@ }, "enableBrowserIntegrationDesc": { "message": "Browser integration is used for biometrics in browser." + }, + "approve": { + "message": "Approve" + }, + "verifyBrowserTitle": { + "message": "Verify browser connection" + }, + "verifyBrowserDescription": { + "message": "Please ensure the shown fingerprint is identical to the fingerprint showed in the browser extension." } } diff --git a/src/services/nativeMessaging.service.ts b/src/services/nativeMessaging.service.ts index 434956b378..750818c1a9 100644 --- a/src/services/nativeMessaging.service.ts +++ b/src/services/nativeMessaging.service.ts @@ -1,4 +1,5 @@ import { ipcRenderer } from 'electron'; +import Swal from 'sweetalert2'; import { CryptoService } from 'jslib/abstractions/crypto.service'; import { CryptoFunctionService } from 'jslib/abstractions/cryptoFunction.service'; @@ -6,16 +7,17 @@ import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; import { LogService } from 'jslib/abstractions/log.service'; import { Utils } from 'jslib/misc/utils'; import { SymmetricCryptoKey } from 'jslib/models/domain/symmetricCryptoKey'; +import { I18nService } from 'jslib/abstractions/i18n.service'; +import { UserService } from 'jslib/abstractions/user.service'; const MessageValidTimeout = 10 * 1000; const EncryptionAlgorithm = 'sha1'; export class NativeMessagingService { - private remotePublicKey: ArrayBuffer; private sharedSecret: any; constructor(private cryptoFunctionService: CryptoFunctionService, private cryptoService: CryptoService, - private platformUtilService: PlatformUtilsService, private logService: LogService) { + private platformUtilService: PlatformUtilsService, private logService: LogService, private i18nService: I18nService, private userService: UserService) { ipcRenderer.on('nativeMessaging', async (event: any, message: any) => { this.messageHandler(message); }); @@ -23,8 +25,24 @@ export class NativeMessagingService { private async messageHandler(rawMessage: any) { if (rawMessage.command == 'setupEncryption') { - this.remotePublicKey = Utils.fromB64ToArray(rawMessage.publicKey).buffer; - this.secureCommunication(); + const remotePublicKey = Utils.fromB64ToArray(rawMessage.publicKey).buffer; + 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('verifyBrowserDescription')}

${fingerprint}`, + showCancelButton: true, + cancelButtonText: this.i18nService.t('cancel'), + showConfirmButton: true, + confirmButtonText: this.i18nService.t('approve'), + allowOutsideClick: false, + }); + + if (submitted.value !== true) { + return; + } + + this.secureCommunication(remotePublicKey); return; } @@ -64,11 +82,11 @@ export class NativeMessagingService { ipcRenderer.send('nativeMessagingReply', encrypted); } - private async secureCommunication() { + private async secureCommunication(remotePublicKey: ArrayBuffer) { const secret = await this.cryptoFunctionService.randomBytes(64); this.sharedSecret = new SymmetricCryptoKey(secret); - const encryptedSecret = await this.cryptoFunctionService.rsaEncrypt(secret, this.remotePublicKey, EncryptionAlgorithm); + const encryptedSecret = await this.cryptoFunctionService.rsaEncrypt(secret, remotePublicKey, EncryptionAlgorithm); ipcRenderer.send('nativeMessagingReply', {command: 'setupEncryption', sharedSecret: Utils.fromBufferToB64(encryptedSecret)}); } }