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:
@@ -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}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -264,6 +264,9 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
kdfConfig.validateKdfConfigForPrelogin();
|
||||
|
||||
return await this.cryptoService.makeMasterKey(masterPassword, email, kdfConfig);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user