mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-26 12:25:20 +01:00
[PM-2192] Improve userkey verification on biometric unlock (#10326)
* Improve biometric unlock userkey verification * Add early return * Pass activeuserid to cryptoservice functions
This commit is contained in:
parent
b84becd9e4
commit
7cd6fcf265
@ -2000,6 +2000,12 @@
|
|||||||
"nativeMessagingWrongUserTitle": {
|
"nativeMessagingWrongUserTitle": {
|
||||||
"message": "Account missmatch"
|
"message": "Account missmatch"
|
||||||
},
|
},
|
||||||
|
"nativeMessagingWrongUserKeyDesc": {
|
||||||
|
"message": "Biometric unlock failed. The biometric secret key failed to unlock the vault. Please try to set up biometrics again."
|
||||||
|
},
|
||||||
|
"nativeMessagingWrongUserKeyTitle": {
|
||||||
|
"message": "Biometric key missmatch"
|
||||||
|
},
|
||||||
"biometricsNotEnabledTitle": {
|
"biometricsNotEnabledTitle": {
|
||||||
"message": "Biometrics not set up"
|
"message": "Biometrics not set up"
|
||||||
},
|
},
|
||||||
|
@ -1049,6 +1049,7 @@ export default class MainBackground {
|
|||||||
this.logService,
|
this.logService,
|
||||||
this.authService,
|
this.authService,
|
||||||
this.biometricStateService,
|
this.biometricStateService,
|
||||||
|
this.accountService,
|
||||||
);
|
);
|
||||||
this.commandsBackground = new CommandsBackground(
|
this.commandsBackground = new CommandsBackground(
|
||||||
this,
|
this,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom, map } from "rxjs";
|
||||||
|
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||||
@ -81,6 +82,7 @@ export class NativeMessagingBackground {
|
|||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private biometricStateService: BiometricStateService,
|
private biometricStateService: BiometricStateService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
if (chrome?.permissions?.onAdded) {
|
if (chrome?.permissions?.onAdded) {
|
||||||
// Reload extension to activate nativeMessaging
|
// Reload extension to activate nativeMessaging
|
||||||
@ -223,6 +225,16 @@ export class NativeMessagingBackground {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showIncorrectUserKeyDialog() {
|
||||||
|
this.messagingService.send("showDialog", {
|
||||||
|
title: { key: "nativeMessagingWrongUserKeyTitle" },
|
||||||
|
content: { key: "nativeMessagingWrongUserKeyDesc" },
|
||||||
|
acceptButtonText: { key: "ok" },
|
||||||
|
cancelButtonText: null,
|
||||||
|
type: "danger",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async send(message: Message) {
|
async send(message: Message) {
|
||||||
if (!this.connected) {
|
if (!this.connected) {
|
||||||
await this.connect();
|
await this.connect();
|
||||||
@ -350,7 +362,26 @@ export class NativeMessagingBackground {
|
|||||||
const userKey = new SymmetricCryptoKey(
|
const userKey = new SymmetricCryptoKey(
|
||||||
Utils.fromB64ToArray(message.userKeyB64),
|
Utils.fromB64ToArray(message.userKeyB64),
|
||||||
) as UserKey;
|
) as UserKey;
|
||||||
await this.cryptoService.setUserKey(userKey);
|
const activeUserId = await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||||
|
);
|
||||||
|
const isUserKeyValid = await this.cryptoService.validateUserKey(
|
||||||
|
userKey,
|
||||||
|
activeUserId,
|
||||||
|
);
|
||||||
|
if (isUserKeyValid) {
|
||||||
|
await this.cryptoService.setUserKey(userKey, activeUserId);
|
||||||
|
} else {
|
||||||
|
this.logService.error("Unable to verify biometric unlocked userkey");
|
||||||
|
await this.cryptoService.clearKeys(activeUserId);
|
||||||
|
this.showIncorrectUserKeyDialog();
|
||||||
|
|
||||||
|
// Exit early
|
||||||
|
if (this.resolver) {
|
||||||
|
this.resolver(message);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error("No key received");
|
throw new Error("No key received");
|
||||||
}
|
}
|
||||||
@ -371,21 +402,6 @@ export class NativeMessagingBackground {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify key is correct by attempting to decrypt a secret
|
|
||||||
try {
|
|
||||||
await this.cryptoService.getFingerprint(await this.stateService.getUserId());
|
|
||||||
} catch (e) {
|
|
||||||
this.logService.error("Unable to verify key: " + e);
|
|
||||||
await this.cryptoService.clearKeys();
|
|
||||||
this.showWrongUserDialog();
|
|
||||||
|
|
||||||
// Exit early
|
|
||||||
if (this.resolver) {
|
|
||||||
this.resolver(message);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.runtimeBackground.processMessage({ command: "unlocked" });
|
this.runtimeBackground.processMessage({ command: "unlocked" });
|
||||||
|
@ -418,4 +418,11 @@ export abstract class CryptoService {
|
|||||||
* @throws If an invalid user id is passed in.
|
* @throws If an invalid user id is passed in.
|
||||||
*/
|
*/
|
||||||
abstract userPublicKey$(userId: UserId): Observable<UserPublicKey>;
|
abstract userPublicKey$(userId: UserId): Observable<UserPublicKey>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates that a userkey is correct for a given user
|
||||||
|
* @param key The key to validate
|
||||||
|
* @param userId The user id for the key
|
||||||
|
*/
|
||||||
|
abstract validateUserKey(key: UserKey, userId: UserId): Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
@ -620,7 +620,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---HELPERS---
|
// ---HELPERS---
|
||||||
protected async validateUserKey(key: UserKey, userId: UserId): Promise<boolean> {
|
async validateUserKey(key: UserKey, userId: UserId): Promise<boolean> {
|
||||||
if (!key) {
|
if (!key) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user