mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-24 12:06:15 +01:00
Merge pull request #1498 from Hinton/hotfix/multiple-extensions-nativemsg
Native Messaging - Support multiple extensions concurrently
This commit is contained in:
commit
1bb75d47aa
@ -1415,7 +1415,19 @@
|
|||||||
"message": "Desktop application invalidated the secure communication channel. Please retry this operation"
|
"message": "Desktop application invalidated the secure communication channel. Please retry this operation"
|
||||||
},
|
},
|
||||||
"nativeMessagingInvalidEncryptionTitle": {
|
"nativeMessagingInvalidEncryptionTitle": {
|
||||||
"message": "Desktop communication interupted"
|
"message": "Desktop communication interrupted"
|
||||||
|
},
|
||||||
|
"biometricsNotEnabledTitle": {
|
||||||
|
"message": "Biometrics not enabled"
|
||||||
|
},
|
||||||
|
"biometricsNotEnabledDesc": {
|
||||||
|
"message": "Browser biometrics requires desktop biometric to be enabled in the settings first."
|
||||||
|
},
|
||||||
|
"biometricsNotSupportedTitle": {
|
||||||
|
"message": "Biometrics not supported"
|
||||||
|
},
|
||||||
|
"biometricsNotSupportedDesc": {
|
||||||
|
"message": "Browser biometrics is not supported on this device."
|
||||||
},
|
},
|
||||||
"personalOwnershipSubmitError": {
|
"personalOwnershipSubmitError": {
|
||||||
"message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections."
|
"message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available Collections."
|
||||||
|
@ -248,7 +248,7 @@ export default class MainBackground {
|
|||||||
this.analytics, this.notificationsService, this.systemService, this.vaultTimeoutService,
|
this.analytics, this.notificationsService, this.systemService, this.vaultTimeoutService,
|
||||||
this.environmentService, this.policyService, this.userService);
|
this.environmentService, this.policyService, this.userService);
|
||||||
this.nativeMessagingBackground = new NativeMessagingBackground(this.storageService, this.cryptoService, this.cryptoFunctionService,
|
this.nativeMessagingBackground = new NativeMessagingBackground(this.storageService, this.cryptoService, this.cryptoFunctionService,
|
||||||
this.vaultTimeoutService, this.runtimeBackground, this.i18nService, this.userService, this.messagingService);
|
this.vaultTimeoutService, this.runtimeBackground, this.i18nService, this.userService, this.messagingService, this.appIdService);
|
||||||
this.commandsBackground = new CommandsBackground(this, this.passwordGenerationService,
|
this.commandsBackground = new CommandsBackground(this, this.passwordGenerationService,
|
||||||
this.platformUtilsService, this.analytics, this.vaultTimeoutService);
|
this.platformUtilsService, this.analytics, this.vaultTimeoutService);
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ConstantsService } from 'jslib/services/constants.service';
|
import { ConstantsService } from 'jslib/services/constants.service';
|
||||||
|
import { AppIdService } from 'jslib/abstractions/appId.service';
|
||||||
import { CryptoFunctionService } from 'jslib/abstractions/cryptoFunction.service';
|
import { CryptoFunctionService } from 'jslib/abstractions/cryptoFunction.service';
|
||||||
import { CryptoService } from 'jslib/abstractions/crypto.service';
|
import { CryptoService } from 'jslib/abstractions/crypto.service';
|
||||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||||
@ -23,15 +24,19 @@ export class NativeMessagingBackground {
|
|||||||
|
|
||||||
private resolver: any = null;
|
private resolver: any = null;
|
||||||
private privateKey: ArrayBuffer = null;
|
private privateKey: ArrayBuffer = null;
|
||||||
|
private publicKey: ArrayBuffer = null;
|
||||||
private secureSetupResolve: any = null;
|
private secureSetupResolve: any = null;
|
||||||
private sharedSecret: SymmetricCryptoKey;
|
private sharedSecret: SymmetricCryptoKey;
|
||||||
|
private appId: string;
|
||||||
|
|
||||||
constructor(private storageService: StorageService, private cryptoService: CryptoService,
|
constructor(private storageService: StorageService, private cryptoService: CryptoService,
|
||||||
private cryptoFunctionService: CryptoFunctionService, private vaultTimeoutService: VaultTimeoutService,
|
private cryptoFunctionService: CryptoFunctionService, private vaultTimeoutService: VaultTimeoutService,
|
||||||
private runtimeBackground: RuntimeBackground, private i18nService: I18nService, private userService: UserService,
|
private runtimeBackground: RuntimeBackground, private i18nService: I18nService, private userService: UserService,
|
||||||
private messagingService: MessagingService) {}
|
private messagingService: MessagingService, private appIdService: AppIdService) {}
|
||||||
|
|
||||||
async connect() {
|
async connect() {
|
||||||
|
this.appId = await this.appIdService.getAppId();
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.port = BrowserApi.connectNative('com.8bit.bitwarden');
|
this.port = BrowserApi.connectNative('com.8bit.bitwarden');
|
||||||
|
|
||||||
@ -58,6 +63,11 @@ export class NativeMessagingBackground {
|
|||||||
this.port.disconnect();
|
this.port.disconnect();
|
||||||
break;
|
break;
|
||||||
case 'setupEncryption':
|
case 'setupEncryption':
|
||||||
|
// Ignore since it belongs to another device
|
||||||
|
if (message.appId !== this.appId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const encrypted = Utils.fromB64ToArray(message.sharedSecret);
|
const encrypted = Utils.fromB64ToArray(message.sharedSecret);
|
||||||
const decrypted = await this.cryptoFunctionService.rsaDecrypt(encrypted.buffer, this.privateKey, EncryptionAlgorithm);
|
const decrypted = await this.cryptoFunctionService.rsaDecrypt(encrypted.buffer, this.privateKey, EncryptionAlgorithm);
|
||||||
|
|
||||||
@ -65,6 +75,11 @@ export class NativeMessagingBackground {
|
|||||||
this.secureSetupResolve();
|
this.secureSetupResolve();
|
||||||
break;
|
break;
|
||||||
case 'invalidateEncryption':
|
case 'invalidateEncryption':
|
||||||
|
// Ignore since it belongs to another device
|
||||||
|
if (message.appId !== this.appId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.sharedSecret = null;
|
this.sharedSecret = null;
|
||||||
this.privateKey = null;
|
this.privateKey = null;
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
@ -75,8 +90,19 @@ export class NativeMessagingBackground {
|
|||||||
confirmText: this.i18nService.t('ok'),
|
confirmText: this.i18nService.t('ok'),
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
|
case 'verifyFingerprint': {
|
||||||
|
if (this.sharedSecret == null) {
|
||||||
|
this.showFingerprintDialog();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
this.onMessage(message);
|
// Ignore since it belongs to another device
|
||||||
|
if (message.appId !== this.appId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onMessage(message.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -118,7 +144,7 @@ export class NativeMessagingBackground {
|
|||||||
message.timestamp = Date.now();
|
message.timestamp = Date.now();
|
||||||
|
|
||||||
const encrypted = await this.cryptoService.encrypt(JSON.stringify(message), this.sharedSecret);
|
const encrypted = await this.cryptoService.encrypt(JSON.stringify(message), this.sharedSecret);
|
||||||
this.port.postMessage(encrypted);
|
this.port.postMessage({appId: this.appId, message: encrypted});
|
||||||
}
|
}
|
||||||
|
|
||||||
getResponse(): Promise<any> {
|
getResponse(): Promise<any> {
|
||||||
@ -140,6 +166,24 @@ export class NativeMessagingBackground {
|
|||||||
case 'biometricUnlock':
|
case 'biometricUnlock':
|
||||||
await this.storageService.remove(ConstantsService.biometricAwaitingAcceptance);
|
await this.storageService.remove(ConstantsService.biometricAwaitingAcceptance);
|
||||||
|
|
||||||
|
if (message.response === 'not enabled') {
|
||||||
|
this.messagingService.send('showDialog', {
|
||||||
|
text: this.i18nService.t('biometricsNotEnabledDesc'),
|
||||||
|
title: this.i18nService.t('biometricsNotEnabledTitle'),
|
||||||
|
confirmText: this.i18nService.t('ok'),
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
} else if (message.response === 'not supported') {
|
||||||
|
this.messagingService.send('showDialog', {
|
||||||
|
text: this.i18nService.t('biometricsNotSupportedDesc'),
|
||||||
|
title: this.i18nService.t('biometricsNotSupportedTitle'),
|
||||||
|
confirmText: this.i18nService.t('ok'),
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
const enabled = await this.storageService.get(ConstantsService.biometricUnlockKey);
|
const enabled = await this.storageService.get(ConstantsService.biometricUnlockKey);
|
||||||
if (enabled === null || enabled === false) {
|
if (enabled === null || enabled === false) {
|
||||||
if (message.response === 'unlocked') {
|
if (message.response === 'unlocked') {
|
||||||
@ -171,17 +215,10 @@ export class NativeMessagingBackground {
|
|||||||
|
|
||||||
private async secureCommunication() {
|
private async secureCommunication() {
|
||||||
const [publicKey, privateKey] = await this.cryptoFunctionService.rsaGenerateKeyPair(2048);
|
const [publicKey, privateKey] = await this.cryptoFunctionService.rsaGenerateKeyPair(2048);
|
||||||
|
this.publicKey = publicKey;
|
||||||
this.privateKey = privateKey;
|
this.privateKey = privateKey;
|
||||||
|
|
||||||
this.sendUnencrypted({command: 'setupEncryption', publicKey: Utils.fromBufferToB64(publicKey)});
|
this.sendUnencrypted({command: 'setupEncryption', publicKey: Utils.fromBufferToB64(publicKey)});
|
||||||
const fingerprint = (await this.cryptoService.getFingerprint(await this.userService.getUserId(), publicKey)).join(' ');
|
|
||||||
|
|
||||||
this.messagingService.send('showDialog', {
|
|
||||||
html: `${this.i18nService.t('desktopIntegrationVerificationText')}<br><br><strong>${fingerprint}</strong>`,
|
|
||||||
title: this.i18nService.t('desktopSyncVerificationTitle'),
|
|
||||||
confirmText: this.i18nService.t('ok'),
|
|
||||||
type: 'warning',
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => this.secureSetupResolve = resolve);
|
return new Promise((resolve, reject) => this.secureSetupResolve = resolve);
|
||||||
}
|
}
|
||||||
@ -193,6 +230,17 @@ export class NativeMessagingBackground {
|
|||||||
|
|
||||||
message.timestamp = Date.now();
|
message.timestamp = Date.now();
|
||||||
|
|
||||||
this.port.postMessage(message);
|
this.port.postMessage({appId: this.appId, message: message});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async showFingerprintDialog() {
|
||||||
|
const fingerprint = (await this.cryptoService.getFingerprint(await this.userService.getUserId(), this.publicKey)).join(' ');
|
||||||
|
|
||||||
|
this.messagingService.send('showDialog', {
|
||||||
|
html: `${this.i18nService.t('desktopIntegrationVerificationText')}<br><br><strong>${fingerprint}</strong>`,
|
||||||
|
title: this.i18nService.t('desktopSyncVerificationTitle'),
|
||||||
|
confirmText: this.i18nService.t('ok'),
|
||||||
|
type: 'warning',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user