Cleanup, localize error.

This commit is contained in:
Hinton 2020-10-21 17:18:04 +02:00
parent 9064298309
commit 0a4d59092b
6 changed files with 52 additions and 45 deletions

View File

@ -1383,5 +1383,11 @@
}, },
"startDesktopDesc": { "startDesktopDesc": {
"message": "The bitwarden desktop application needs to be started before this function can be used." "message": "The bitwarden desktop application needs to be started before this function can be used."
} },
"errorEnableBiometricTitle": {
"message": "Unable to enable biometrics"
},
"errorEnableBiometricDesc": {
"message": "Action was canceld by the desktop applicaiton."
}
} }

View File

@ -151,7 +151,7 @@ export default class MainBackground {
}, },
() => { () => {
if (this.nativeMessagingBackground != null) { if (this.nativeMessagingBackground != null) {
const promise = this.nativeMessagingBackground.await(); const promise = this.nativeMessagingBackground.getResponse();
this.nativeMessagingBackground.send({command: 'biometricUnlock'}) this.nativeMessagingBackground.send({command: 'biometricUnlock'})
return promise.then((result) => result.response === 'unlocked'); return promise.then((result) => result.response === 'unlocked');
} }

View File

@ -20,7 +20,6 @@ export class NativeMessagingBackground {
private port: browser.runtime.Port | chrome.runtime.Port; private port: browser.runtime.Port | chrome.runtime.Port;
private resolver: any = null; private resolver: any = null;
private publicKey: ArrayBuffer;
private privateKey: ArrayBuffer = null; private privateKey: ArrayBuffer = null;
private secureSetupResolve: any = null; private secureSetupResolve: any = null;
private sharedSecret: SymmetricCryptoKey; private sharedSecret: SymmetricCryptoKey;
@ -36,27 +35,36 @@ export class NativeMessagingBackground {
this.connecting = true; this.connecting = true;
this.port.onMessage.addListener((message: any) => { this.port.onMessage.addListener(async (message: any) => {
if (message.command === 'connected') { switch (message.command) {
this.connected = true; case 'connected':
this.connecting = false; this.connected = true;
resolve(); this.connecting = false;
} else if (message.command === 'disconnected') { resolve();
if (this.connecting) { break;
this.messagingService.send('showDialog', { case 'disconnected':
text: this.i18nService.t('startDesktopDesc'), if (this.connecting) {
title: this.i18nService.t('startDesktopTitle'), this.messagingService.send('showDialog', {
confirmText: this.i18nService.t('ok'), text: this.i18nService.t('startDesktopDesc'),
type: 'error', title: this.i18nService.t('startDesktopTitle'),
}); confirmText: this.i18nService.t('ok'),
reject(); type: 'error',
} });
this.connected = false; reject();
this.port.disconnect(); }
return; this.connected = false;
} this.port.disconnect();
break;
case 'setupEncryption':
const encrypted = Utils.fromB64ToArray(message.sharedSecret);
const decrypted = await this.cryptoFunctionService.rsaDecrypt(encrypted.buffer, this.privateKey, EncryptionAlgorithm);
this.onMessage(message); this.sharedSecret = new SymmetricCryptoKey(decrypted);
this.secureSetupResolve();
break;
default:
this.onMessage(message);
}
}); });
this.port.onDisconnect.addListener(() => { this.port.onDisconnect.addListener(() => {
@ -75,7 +83,6 @@ export class NativeMessagingBackground {
} }
async send(message: any) { async send(message: any) {
// If not connected, try to connect
if (!this.connected) { if (!this.connected) {
await this.connect(); await this.connect();
} }
@ -90,21 +97,13 @@ export class NativeMessagingBackground {
this.port.postMessage(encrypted); this.port.postMessage(encrypted);
} }
await(): Promise<any> { getResponse(): Promise<any> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.resolver = resolve; this.resolver = resolve;
}); });
} }
private async onMessage(rawMessage: any) { private async onMessage(rawMessage: any) {
if (rawMessage.command === 'setupEncryption') {
const encrypted = Utils.fromB64ToArray(rawMessage.sharedSecret);
const decrypted = await this.cryptoFunctionService.rsaDecrypt(encrypted.buffer, this.privateKey, EncryptionAlgorithm);
this.sharedSecret = new SymmetricCryptoKey(decrypted);
this.secureSetupResolve();
return;
}
const message = JSON.parse(await this.cryptoService.decryptToUtf8(rawMessage, this.sharedSecret)); const message = JSON.parse(await this.cryptoService.decryptToUtf8(rawMessage, this.sharedSecret));
if (Math.abs(message.timestamp - Date.now()) > MessageValidTimeout) { if (Math.abs(message.timestamp - Date.now()) > MessageValidTimeout) {
@ -122,11 +121,15 @@ export class NativeMessagingBackground {
if (message.response === 'unlocked') { if (message.response === 'unlocked') {
await this.storageService.save(ConstantsService.biometricUnlockKey, true); await this.storageService.save(ConstantsService.biometricUnlockKey, true);
} }
break;
await this.cryptoService.toggleKey();
} }
if (this.vaultTimeoutService.biometricLocked) { // Ignore unlock if already unlockeded
if (!this.vaultTimeoutService.biometricLocked) {
break;
}
if (message.response === 'unlocked') {
this.cryptoService.setKey(new SymmetricCryptoKey(Utils.fromB64ToArray(message.keyB64).buffer)); this.cryptoService.setKey(new SymmetricCryptoKey(Utils.fromB64ToArray(message.keyB64).buffer));
this.vaultTimeoutService.biometricLocked = false; this.vaultTimeoutService.biometricLocked = false;
this.runtimeBackground.processMessage({command: 'unlocked'}, null, null); this.runtimeBackground.processMessage({command: 'unlocked'}, null, null);
@ -143,12 +146,11 @@ export class NativeMessagingBackground {
} }
private async secureCommunication() { private async secureCommunication() {
// Using crypto function service directly since we cannot encrypt the private key as const [publicKey, privateKey] = await this.cryptoFunctionService.rsaGenerateKeyPair(2048);
// master key might not be available this.privateKey = privateKey;
[this.publicKey, this.privateKey] = await this.cryptoFunctionService.rsaGenerateKeyPair(2048);
this.sendUnencrypted({command: 'setupEncryption', publicKey: Utils.fromBufferToB64(this.publicKey)}); this.sendUnencrypted({command: 'setupEncryption', publicKey: Utils.fromBufferToB64(publicKey)});
const fingerprint = (await this.cryptoService.getFingerprint(await this.userService.getUserId(), this.publicKey)).join(' '); const fingerprint = (await this.cryptoService.getFingerprint(await this.userService.getUserId(), publicKey)).join(' ');
this.messagingService.send('showDialog', { this.messagingService.send('showDialog', {
html: `${this.i18nService.t('desktopIntegrationVerificationText')}<br><br><strong>${fingerprint}</strong>`, html: `${this.i18nService.t('desktopIntegrationVerificationText')}<br><br><strong>${fingerprint}</strong>`,

View File

@ -46,7 +46,7 @@ export class LockComponent extends BaseLockComponent {
const div = document.createElement('div'); const div = document.createElement('div');
div.innerHTML = `<div class="swal2-text">${this.i18nService.t('awaitDesktop')}</div>`; div.innerHTML = `<div class="swal2-text">${this.i18nService.t('awaitDesktop')}</div>`;
const submitted = Swal.fire({ Swal.fire({
heightAuto: false, heightAuto: false,
buttonsStyling: false, buttonsStyling: false,
html: div, html: div,

View File

@ -44,7 +44,7 @@
</div> </div>
<div class="box-content-row box-content-row-checkbox" appBoxRow> <div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="biometric">{{'unlockWithBiometric' | i18n}}</label> <label for="biometric">{{'unlockWithBiometric' | i18n}}</label>
<input id="biometric" type="checkbox" (change)="updateBiometric()" [ngModel]="biometric"> <input id="biometric" type="checkbox" (change)="updateBiometric()" [checked]="biometric">
</div> </div>
<a class="box-content-row box-content-row-flex text-default" href="#" appStopClick appBlurClick <a class="box-content-row box-content-row-flex text-default" href="#" appStopClick appBlurClick
(click)="lock()"> (click)="lock()">

View File

@ -209,7 +209,6 @@ export class SettingsComponent implements OnInit {
async updateBiometric() { async updateBiometric() {
if (this.biometric) { if (this.biometric) {
this.biometric = false; this.biometric = false;
// TODO: Remove biometric stuff
await this.storageService.remove(ConstantsService.biometricUnlockKey); await this.storageService.remove(ConstantsService.biometricUnlockKey);
this.vaultTimeoutService.biometricLocked = false; this.vaultTimeoutService.biometricLocked = false;
} else { } else {
@ -241,7 +240,7 @@ export class SettingsComponent implements OnInit {
Swal.close(); Swal.close();
if (this.biometric === false) { if (this.biometric === false) {
this.platformUtilsService.showToast('error', 'Unable to enable biometrics', 'Ensure the desktop application is running, and browser integration is enabled.'); this.platformUtilsService.showToast('error', this.i18nService.t('errorEnableBiometricTitle'), this.i18nService.t('errorEnableBiometricDesc'));
} }
}) })
]); ]);