1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-14 23:33:31 +00:00

[PM-12806] Enforce 5000 iteration minimum for prelogin (#11332)

* Enforce 5000 iteration minimum for prelogin

* Fix tests

* Add more extensive tests

* Add loginstrategy prelogin downgrade test
This commit is contained in:
Bernd Schoolmann
2024-10-17 06:58:07 -07:00
committed by GitHub
parent 288b0cff2f
commit e8f0135d50
5 changed files with 212 additions and 18 deletions

View File

@@ -11,9 +11,11 @@ import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
import { PBKDF2KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request";
import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response";
import { IdentityTwoFactorResponse } from "@bitwarden/common/auth/models/response/identity-two-factor.response";
import { PreloginResponse } from "@bitwarden/common/auth/models/response/prelogin.response";
import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
@@ -159,6 +161,9 @@ describe("LoginStrategyService", () => {
new IdentityTokenResponse({
ForcePasswordReset: false,
Kdf: KdfType.Argon2id,
KdfIterations: 2,
KdfMemory: 16,
KdfParallelism: 1,
Key: "KEY",
PrivateKey: "PRIVATE_KEY",
ResetMasterPassword: false,
@@ -169,6 +174,15 @@ describe("LoginStrategyService", () => {
token_type: "Bearer",
}),
);
apiService.postPrelogin.mockResolvedValue(
new PreloginResponse({
Kdf: KdfType.Argon2id,
KdfIterations: 2,
KdfMemory: 16,
KdfParallelism: 1,
}),
);
tokenService.decodeAccessToken.calledWith("ACCESS_TOKEN").mockResolvedValue({
sub: "USER_ID",
name: "NAME",
@@ -194,6 +208,15 @@ describe("LoginStrategyService", () => {
}),
);
apiService.postPrelogin.mockResolvedValue(
new PreloginResponse({
Kdf: KdfType.Argon2id,
KdfIterations: 2,
KdfMemory: 16,
KdfParallelism: 1,
}),
);
await sut.logIn(credentials);
const twoFactorToken = new TokenTwoFactorRequest(
@@ -205,6 +228,9 @@ describe("LoginStrategyService", () => {
new IdentityTokenResponse({
ForcePasswordReset: false,
Kdf: KdfType.Argon2id,
KdfIterations: 2,
KdfMemory: 16,
KdfParallelism: 1,
Key: "KEY",
PrivateKey: "PRIVATE_KEY",
ResetMasterPassword: false,
@@ -241,6 +267,15 @@ describe("LoginStrategyService", () => {
}),
);
apiService.postPrelogin.mockResolvedValue(
new PreloginResponse({
Kdf: KdfType.Argon2id,
KdfIterations: 2,
KdfMemory: 16,
KdfParallelism: 1,
}),
);
await sut.logIn(credentials);
loginStrategyCacheExpirationState.stateSubject.next(new Date(Date.now() - 1000 * 60 * 5));
@@ -253,4 +288,40 @@ describe("LoginStrategyService", () => {
await expect(sut.logInTwoFactor(twoFactorToken, "CAPTCHA")).rejects.toThrow();
});
it("throw error on too low kdf config", async () => {
const credentials = new PasswordLoginCredentials("EMAIL", "MASTER_PASSWORD");
apiService.postIdentityToken.mockResolvedValue(
new IdentityTokenResponse({
ForcePasswordReset: false,
Kdf: KdfType.PBKDF2_SHA256,
KdfIterations: PBKDF2KdfConfig.PRELOGIN_ITERATIONS.min - 1,
Key: "KEY",
PrivateKey: "PRIVATE_KEY",
ResetMasterPassword: false,
access_token: "ACCESS_TOKEN",
expires_in: 3600,
refresh_token: "REFRESH_TOKEN",
scope: "api offline_access",
token_type: "Bearer",
}),
);
apiService.postPrelogin.mockResolvedValue(
new PreloginResponse({
Kdf: KdfType.PBKDF2_SHA256,
KdfIterations: PBKDF2KdfConfig.PRELOGIN_ITERATIONS.min - 1,
}),
);
tokenService.decodeAccessToken.calledWith("ACCESS_TOKEN").mockResolvedValue({
sub: "USER_ID",
name: "NAME",
email: "EMAIL",
premium: false,
});
await expect(sut.logIn(credentials)).rejects.toThrow(
`PBKDF2 iterations must be between ${PBKDF2KdfConfig.PRELOGIN_ITERATIONS.min} and ${PBKDF2KdfConfig.PRELOGIN_ITERATIONS.max}`,
);
});
});

View File

@@ -264,6 +264,9 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
throw e;
}
}
kdfConfig.validateKdfConfigForPrelogin();
return await this.cryptoService.makeMasterKey(masterPassword, email, kdfConfig);
}