1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-11 10:10:25 +01:00

Auth/PM-5099 Ensure consistent casing of email used for fingerprint generation in Auth Requests (#8571)

* Created method for handilng email-address-based fingerprint.

* Added test for new method.

* Added returns to annotation
This commit is contained in:
Todd Martin 2024-09-04 10:22:06 -04:00 committed by GitHub
parent b90563aa50
commit 86fab07a37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 36 additions and 9 deletions

View File

@ -79,9 +79,10 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
this.email = await await firstValueFrom( this.email = await await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.email)), this.accountService.activeAccount$.pipe(map((a) => a?.email)),
); );
this.fingerprintPhrase = ( this.fingerprintPhrase = await this.authRequestService.getFingerprintPhrase(
await this.cryptoService.getFingerprint(this.email, publicKey) this.email,
).join("-"); publicKey,
);
this.updateTimeText(); this.updateTimeText();
this.interval = setInterval(() => { this.interval = setInterval(() => {

View File

@ -210,9 +210,10 @@ export class LoginViaAuthRequestComponent
const derivedPublicKeyArrayBuffer = await this.cryptoFunctionService.rsaExtractPublicKey( const derivedPublicKeyArrayBuffer = await this.cryptoFunctionService.rsaExtractPublicKey(
adminAuthReqStorable.privateKey, adminAuthReqStorable.privateKey,
); );
this.fingerprintPhrase = ( this.fingerprintPhrase = await this.authRequestService.getFingerprintPhrase(
await this.cryptoService.getFingerprint(this.email, derivedPublicKeyArrayBuffer) this.email,
).join("-"); derivedPublicKeyArrayBuffer,
);
// Request denied // Request denied
if (adminAuthReqResponse.isAnswered && !adminAuthReqResponse.requestApproved) { if (adminAuthReqResponse.isAnswered && !adminAuthReqResponse.requestApproved) {
@ -259,9 +260,10 @@ export class LoginViaAuthRequestComponent
length: 25, length: 25,
}); });
this.fingerprintPhrase = ( this.fingerprintPhrase = await this.authRequestService.getFingerprintPhrase(
await this.cryptoService.getFingerprint(this.email, this.authRequestKeyPair.publicKey) this.email,
).join("-"); this.authRequestKeyPair.publicKey,
);
this.authRequest = new CreateAuthRequest( this.authRequest = new CreateAuthRequest(
this.email, this.email,

View File

@ -96,4 +96,12 @@ export abstract class AuthRequestServiceAbstraction {
* @remark We should only be receiving approved push notifications to prevent enumeration. * @remark We should only be receiving approved push notifications to prevent enumeration.
*/ */
abstract sendAuthRequestPushNotification: (notification: AuthRequestPushNotification) => void; abstract sendAuthRequestPushNotification: (notification: AuthRequestPushNotification) => void;
/**
* Creates a dash-delimited fingerprint for use in confirming the `AuthRequest` between the requesting and approving device.
* @param email The email address of the user.
* @param publicKey The public key for the user.
* @returns The dash-delimited fingerprint phrase.
*/
abstract getFingerprintPhrase(email: string, publicKey: Uint8Array): Promise<string>;
} }

View File

@ -27,6 +27,7 @@ describe("AuthRequestService", () => {
const apiService = mock<ApiService>(); const apiService = mock<ApiService>();
let mockPrivateKey: Uint8Array; let mockPrivateKey: Uint8Array;
let mockPublicKey: Uint8Array;
const mockUserId = Utils.newGuid() as UserId; const mockUserId = Utils.newGuid() as UserId;
beforeEach(() => { beforeEach(() => {
@ -44,6 +45,7 @@ describe("AuthRequestService", () => {
); );
mockPrivateKey = new Uint8Array(64); mockPrivateKey = new Uint8Array(64);
mockPublicKey = new Uint8Array(64);
}); });
describe("authRequestPushNotification$", () => { describe("authRequestPushNotification$", () => {
@ -262,4 +264,14 @@ describe("AuthRequestService", () => {
expect(result.masterKeyHash).toEqual(mockDecryptedMasterKeyHash); expect(result.masterKeyHash).toEqual(mockDecryptedMasterKeyHash);
}); });
}); });
describe("getFingerprintPhrase", () => {
it("returns the same fingerprint regardless of email casing", () => {
const email = "test@email.com";
const emailUpperCase = email.toUpperCase();
const phrase = sut.getFingerprintPhrase(email, mockPublicKey);
const phraseUpperCase = sut.getFingerprintPhrase(emailUpperCase, mockPublicKey);
expect(phrase).toEqual(phraseUpperCase);
});
});
}); });

View File

@ -198,4 +198,8 @@ export class AuthRequestService implements AuthRequestServiceAbstraction {
this.authRequestPushNotificationSubject.next(notification.id); this.authRequestPushNotificationSubject.next(notification.id);
} }
} }
async getFingerprintPhrase(email: string, publicKey: Uint8Array): Promise<string> {
return (await this.cryptoService.getFingerprint(email.toLowerCase(), publicKey)).join("-");
}
} }