mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-11 10:10:25 +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": {
|
||||
"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": {
|
||||
"message": "Biometrics not set up"
|
||||
},
|
||||
|
@ -1049,6 +1049,7 @@ export default class MainBackground {
|
||||
this.logService,
|
||||
this.authService,
|
||||
this.biometricStateService,
|
||||
this.accountService,
|
||||
);
|
||||
this.commandsBackground = new CommandsBackground(
|
||||
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 { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||
@ -81,6 +82,7 @@ export class NativeMessagingBackground {
|
||||
private logService: LogService,
|
||||
private authService: AuthService,
|
||||
private biometricStateService: BiometricStateService,
|
||||
private accountService: AccountService,
|
||||
) {
|
||||
if (chrome?.permissions?.onAdded) {
|
||||
// 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) {
|
||||
if (!this.connected) {
|
||||
await this.connect();
|
||||
@ -350,7 +362,26 @@ export class NativeMessagingBackground {
|
||||
const userKey = new SymmetricCryptoKey(
|
||||
Utils.fromB64ToArray(message.userKeyB64),
|
||||
) 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 {
|
||||
throw new Error("No key received");
|
||||
}
|
||||
@ -371,21 +402,6 @@ export class NativeMessagingBackground {
|
||||
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.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.runtimeBackground.processMessage({ command: "unlocked" });
|
||||
|
@ -418,4 +418,11 @@ export abstract class CryptoService {
|
||||
* @throws If an invalid user id is passed in.
|
||||
*/
|
||||
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---
|
||||
protected async validateUserKey(key: UserKey, userId: UserId): Promise<boolean> {
|
||||
async validateUserKey(key: UserKey, userId: UserId): Promise<boolean> {
|
||||
if (!key) {
|
||||
return false;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user