mirror of
https://github.com/bitwarden/browser.git
synced 2025-02-16 01:21:48 +01:00
[PM-16612] Prevent emergency access fingerprint confirmation dialog being spoofable (#12651)
* Prevent emergency access dialog spoofing * Fix tests
This commit is contained in:
parent
1b64bc2462
commit
11a7eb2f73
@ -117,14 +117,7 @@ describe("EmergencyAccessService", () => {
|
||||
const granteeId = "grantee-id";
|
||||
const mockUserKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey;
|
||||
|
||||
const mockPublicKeyB64 = "some-public-key-in-base64";
|
||||
|
||||
// const publicKey = Utils.fromB64ToArray(publicKeyB64);
|
||||
|
||||
const mockUserPublicKeyResponse = new UserKeyResponse({
|
||||
UserId: granteeId,
|
||||
PublicKey: mockPublicKeyB64,
|
||||
});
|
||||
const publicKey = new Uint8Array(64);
|
||||
|
||||
const mockUserPublicKeyEncryptedUserKey = new EncString(
|
||||
EncryptionType.AesCbc256_HmacSha256_B64,
|
||||
@ -132,14 +125,13 @@ describe("EmergencyAccessService", () => {
|
||||
);
|
||||
|
||||
keyService.getUserKey.mockResolvedValueOnce(mockUserKey);
|
||||
apiService.getUserPublicKey.mockResolvedValueOnce(mockUserPublicKeyResponse);
|
||||
|
||||
encryptService.rsaEncrypt.mockResolvedValueOnce(mockUserPublicKeyEncryptedUserKey);
|
||||
|
||||
emergencyAccessApiService.postEmergencyAccessConfirm.mockResolvedValueOnce();
|
||||
|
||||
// Act
|
||||
await emergencyAccessService.confirm(id, granteeId);
|
||||
await emergencyAccessService.confirm(id, granteeId, publicKey);
|
||||
|
||||
// Assert
|
||||
expect(emergencyAccessApiService.postEmergencyAccessConfirm).toHaveBeenCalledWith(id, {
|
||||
|
@ -153,14 +153,13 @@ export class EmergencyAccessService
|
||||
* Intended for grantor.
|
||||
* @param id emergency access id
|
||||
* @param token secret token provided in email
|
||||
* @param publicKey public key of grantee
|
||||
*/
|
||||
async confirm(id: string, granteeId: string) {
|
||||
async confirm(id: string, granteeId: string, publicKey: Uint8Array): Promise<void> {
|
||||
const userKey = await this.keyService.getUserKey();
|
||||
if (!userKey) {
|
||||
throw new Error("No user key found");
|
||||
}
|
||||
const publicKeyResponse = await this.apiService.getUserPublicKey(granteeId);
|
||||
const publicKey = Utils.fromB64ToArray(publicKeyResponse.publicKey);
|
||||
|
||||
try {
|
||||
this.logService.debug(
|
||||
|
@ -4,10 +4,8 @@ import { DialogConfig, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
|
||||
import { Component, OnInit, Inject } from "@angular/core";
|
||||
import { FormBuilder } from "@angular/forms";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationManagementPreferencesService } from "@bitwarden/common/admin-console/abstractions/organization-management-preferences/organization-management-preferences.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
|
||||
@ -21,6 +19,8 @@ type EmergencyAccessConfirmDialogData = {
|
||||
userId: string;
|
||||
/** traces a unique emergency request */
|
||||
emergencyAccessId: string;
|
||||
/** user public key */
|
||||
publicKey: Uint8Array;
|
||||
};
|
||||
@Component({
|
||||
selector: "emergency-access-confirm",
|
||||
@ -36,7 +36,6 @@ export class EmergencyAccessConfirmComponent implements OnInit {
|
||||
constructor(
|
||||
@Inject(DIALOG_DATA) protected params: EmergencyAccessConfirmDialogData,
|
||||
private formBuilder: FormBuilder,
|
||||
private apiService: ApiService,
|
||||
private keyService: KeyService,
|
||||
protected organizationManagementPreferencesService: OrganizationManagementPreferencesService,
|
||||
private logService: LogService,
|
||||
@ -45,13 +44,12 @@ export class EmergencyAccessConfirmComponent implements OnInit {
|
||||
|
||||
async ngOnInit() {
|
||||
try {
|
||||
const publicKeyResponse = await this.apiService.getUserPublicKey(this.params.userId);
|
||||
if (publicKeyResponse != null) {
|
||||
const publicKey = Utils.fromB64ToArray(publicKeyResponse.publicKey);
|
||||
const fingerprint = await this.keyService.getFingerprint(this.params.userId, publicKey);
|
||||
if (fingerprint != null) {
|
||||
this.fingerprint = fingerprint.join("-");
|
||||
}
|
||||
const fingerprint = await this.keyService.getFingerprint(
|
||||
this.params.userId,
|
||||
this.params.publicKey,
|
||||
);
|
||||
if (fingerprint != null) {
|
||||
this.fingerprint = fingerprint.join("-");
|
||||
}
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
|
@ -4,6 +4,7 @@ import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { lastValueFrom, Observable, firstValueFrom, switchMap } from "rxjs";
|
||||
|
||||
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { OrganizationManagementPreferencesService } from "@bitwarden/common/admin-console/abstractions/organization-management-preferences/organization-management-preferences.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
@ -13,6 +14,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { DialogService, ToastService } from "@bitwarden/components";
|
||||
|
||||
import { EmergencyAccessService } from "../../emergency-access";
|
||||
@ -70,6 +72,7 @@ export class EmergencyAccessComponent implements OnInit {
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
protected organizationManagementPreferencesService: OrganizationManagementPreferencesService,
|
||||
private toastService: ToastService,
|
||||
private apiService: ApiService,
|
||||
private accountService: AccountService,
|
||||
) {
|
||||
this.canAccessPremium$ = this.accountService.activeAccount$.pipe(
|
||||
@ -147,6 +150,9 @@ export class EmergencyAccessComponent implements OnInit {
|
||||
return;
|
||||
}
|
||||
|
||||
const publicKeyResponse = await this.apiService.getUserPublicKey(contact.granteeId);
|
||||
const publicKey = Utils.fromB64ToArray(publicKeyResponse.publicKey);
|
||||
|
||||
const autoConfirm = await firstValueFrom(
|
||||
this.organizationManagementPreferencesService.autoConfirmFingerPrints.state$,
|
||||
);
|
||||
@ -156,11 +162,12 @@ export class EmergencyAccessComponent implements OnInit {
|
||||
name: this.userNamePipe.transform(contact),
|
||||
emergencyAccessId: contact.id,
|
||||
userId: contact?.granteeId,
|
||||
publicKey,
|
||||
},
|
||||
});
|
||||
const result = await lastValueFrom(dialogRef.closed);
|
||||
if (result === EmergencyAccessConfirmDialogResult.Confirmed) {
|
||||
await this.emergencyAccessService.confirm(contact.id, contact.granteeId);
|
||||
await this.emergencyAccessService.confirm(contact.id, contact.granteeId, publicKey);
|
||||
updateUser();
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
@ -171,7 +178,11 @@ export class EmergencyAccessComponent implements OnInit {
|
||||
return;
|
||||
}
|
||||
|
||||
this.actionPromise = this.emergencyAccessService.confirm(contact.id, contact.granteeId);
|
||||
this.actionPromise = this.emergencyAccessService.confirm(
|
||||
contact.id,
|
||||
contact.granteeId,
|
||||
publicKey,
|
||||
);
|
||||
await this.actionPromise;
|
||||
updateUser();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user