1
0
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:
Bernd Schoolmann 2024-08-06 21:35:04 +02:00 committed by GitHub
parent b84becd9e4
commit 7cd6fcf265
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 48 additions and 18 deletions

View File

@ -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"
},

View File

@ -1049,6 +1049,7 @@ export default class MainBackground {
this.logService,
this.authService,
this.biometricStateService,
this.accountService,
);
this.commandsBackground = new CommandsBackground(
this,

View File

@ -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" });

View File

@ -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>;
}

View File

@ -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;
}