mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 22:03:36 +00:00
[PM-12806] Fix minimum KDF validation (#11786)
* Fix minimum KDF validation * Add better error messages * Fix tests * Fix tests
This commit is contained in:
@@ -295,7 +295,7 @@ describe("LoginStrategyService", () => {
|
|||||||
new IdentityTokenResponse({
|
new IdentityTokenResponse({
|
||||||
ForcePasswordReset: false,
|
ForcePasswordReset: false,
|
||||||
Kdf: KdfType.PBKDF2_SHA256,
|
Kdf: KdfType.PBKDF2_SHA256,
|
||||||
KdfIterations: PBKDF2KdfConfig.PRELOGIN_ITERATIONS.min - 1,
|
KdfIterations: PBKDF2KdfConfig.PRELOGIN_ITERATIONS_MIN - 1,
|
||||||
Key: "KEY",
|
Key: "KEY",
|
||||||
PrivateKey: "PRIVATE_KEY",
|
PrivateKey: "PRIVATE_KEY",
|
||||||
ResetMasterPassword: false,
|
ResetMasterPassword: false,
|
||||||
@@ -309,7 +309,7 @@ describe("LoginStrategyService", () => {
|
|||||||
apiService.postPrelogin.mockResolvedValue(
|
apiService.postPrelogin.mockResolvedValue(
|
||||||
new PreloginResponse({
|
new PreloginResponse({
|
||||||
Kdf: KdfType.PBKDF2_SHA256,
|
Kdf: KdfType.PBKDF2_SHA256,
|
||||||
KdfIterations: PBKDF2KdfConfig.PRELOGIN_ITERATIONS.min - 1,
|
KdfIterations: PBKDF2KdfConfig.PRELOGIN_ITERATIONS_MIN - 1,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -321,7 +321,7 @@ describe("LoginStrategyService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await expect(sut.logIn(credentials)).rejects.toThrow(
|
await expect(sut.logIn(credentials)).rejects.toThrow(
|
||||||
`PBKDF2 iterations must be between ${PBKDF2KdfConfig.PRELOGIN_ITERATIONS.min} and ${PBKDF2KdfConfig.PRELOGIN_ITERATIONS.max}`,
|
`PBKDF2 iterations must be at least ${PBKDF2KdfConfig.PRELOGIN_ITERATIONS_MIN}, but was ${PBKDF2KdfConfig.PRELOGIN_ITERATIONS_MIN - 1}; possible pre-login downgrade attack detected.`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export type KdfConfig = PBKDF2KdfConfig | Argon2KdfConfig;
|
|||||||
*/
|
*/
|
||||||
export class PBKDF2KdfConfig {
|
export class PBKDF2KdfConfig {
|
||||||
static ITERATIONS = new RangeWithDefault(600_000, 2_000_000, 600_000);
|
static ITERATIONS = new RangeWithDefault(600_000, 2_000_000, 600_000);
|
||||||
static PRELOGIN_ITERATIONS = new RangeWithDefault(5000, 2_000_000, 600_000);
|
static PRELOGIN_ITERATIONS_MIN = 5000;
|
||||||
kdfType: KdfType.PBKDF2_SHA256 = KdfType.PBKDF2_SHA256;
|
kdfType: KdfType.PBKDF2_SHA256 = KdfType.PBKDF2_SHA256;
|
||||||
iterations: number;
|
iterations: number;
|
||||||
|
|
||||||
@@ -38,9 +38,9 @@ export class PBKDF2KdfConfig {
|
|||||||
* A Valid PBKDF2 KDF configuration has KDF iterations between the 5000 and 2_000_000.
|
* A Valid PBKDF2 KDF configuration has KDF iterations between the 5000 and 2_000_000.
|
||||||
*/
|
*/
|
||||||
validateKdfConfigForPrelogin(): void {
|
validateKdfConfigForPrelogin(): void {
|
||||||
if (!PBKDF2KdfConfig.PRELOGIN_ITERATIONS.inRange(this.iterations)) {
|
if (PBKDF2KdfConfig.PRELOGIN_ITERATIONS_MIN > this.iterations) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`PBKDF2 iterations must be between ${PBKDF2KdfConfig.PRELOGIN_ITERATIONS.min} and ${PBKDF2KdfConfig.PRELOGIN_ITERATIONS.max}`,
|
`PBKDF2 iterations must be at least ${PBKDF2KdfConfig.PRELOGIN_ITERATIONS_MIN}, but was ${this.iterations}; possible pre-login downgrade attack detected.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,9 +58,9 @@ export class Argon2KdfConfig {
|
|||||||
static PARALLELISM = new RangeWithDefault(1, 16, 4);
|
static PARALLELISM = new RangeWithDefault(1, 16, 4);
|
||||||
static ITERATIONS = new RangeWithDefault(2, 10, 3);
|
static ITERATIONS = new RangeWithDefault(2, 10, 3);
|
||||||
|
|
||||||
static PRELOGIN_MEMORY = Argon2KdfConfig.MEMORY;
|
static PRELOGIN_MEMORY_MIN = 16;
|
||||||
static PRELOGIN_PARALLELISM = Argon2KdfConfig.PARALLELISM;
|
static PRELOGIN_PARALLELISM_MIN = 1;
|
||||||
static PRELOGIN_ITERATIONS = Argon2KdfConfig.ITERATIONS;
|
static PRELOGIN_ITERATIONS_MIN = 2;
|
||||||
|
|
||||||
kdfType: KdfType.Argon2id = KdfType.Argon2id;
|
kdfType: KdfType.Argon2id = KdfType.Argon2id;
|
||||||
iterations: number;
|
iterations: number;
|
||||||
@@ -86,7 +86,7 @@ export class Argon2KdfConfig {
|
|||||||
|
|
||||||
if (!Argon2KdfConfig.MEMORY.inRange(this.memory)) {
|
if (!Argon2KdfConfig.MEMORY.inRange(this.memory)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Argon2 memory must be between ${Argon2KdfConfig.MEMORY.min}mb and ${Argon2KdfConfig.MEMORY.max}mb`,
|
`Argon2 memory must be between ${Argon2KdfConfig.MEMORY.min} MiB and ${Argon2KdfConfig.MEMORY.max} MiB`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,21 +101,21 @@ export class Argon2KdfConfig {
|
|||||||
* Validates the Argon2 KDF configuration for pre-login.
|
* Validates the Argon2 KDF configuration for pre-login.
|
||||||
*/
|
*/
|
||||||
validateKdfConfigForPrelogin(): void {
|
validateKdfConfigForPrelogin(): void {
|
||||||
if (!Argon2KdfConfig.PRELOGIN_ITERATIONS.inRange(this.iterations)) {
|
if (Argon2KdfConfig.PRELOGIN_ITERATIONS_MIN > this.iterations) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Argon2 iterations must be between ${Argon2KdfConfig.PRELOGIN_ITERATIONS.min} and ${Argon2KdfConfig.PRELOGIN_ITERATIONS.max}`,
|
`Argon2 iterations must be at least ${Argon2KdfConfig.PRELOGIN_ITERATIONS_MIN}, but was ${this.iterations}; possible pre-login downgrade attack detected.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Argon2KdfConfig.PRELOGIN_MEMORY.inRange(this.memory)) {
|
if (Argon2KdfConfig.PRELOGIN_MEMORY_MIN > this.memory) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Argon2 memory must be between ${Argon2KdfConfig.PRELOGIN_MEMORY.min}mb and ${Argon2KdfConfig.PRELOGIN_MEMORY.max}mb`,
|
`Argon2 memory must be at least ${Argon2KdfConfig.PRELOGIN_MEMORY_MIN} MiB, but was ${this.memory} MiB; possible pre-login downgrade attack detected.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Argon2KdfConfig.PRELOGIN_PARALLELISM.inRange(this.parallelism)) {
|
if (Argon2KdfConfig.PRELOGIN_PARALLELISM_MIN > this.parallelism) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Argon2 parallelism must be between ${Argon2KdfConfig.PRELOGIN_PARALLELISM.min} and ${Argon2KdfConfig.PRELOGIN_PARALLELISM.max}.`,
|
`Argon2 parallelism must be at least ${Argon2KdfConfig.PRELOGIN_PARALLELISM_MIN}, but was ${this.parallelism}; possible pre-login downgrade attack detected.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,13 +82,6 @@ describe("KdfConfigService", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("validateKdfConfigForSetting(): should throw an error for invalid Argon2 memory", () => {
|
|
||||||
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(3, 1025, 4);
|
|
||||||
expect(() => kdfConfig.validateKdfConfigForSetting()).toThrow(
|
|
||||||
`Argon2 memory must be between ${Argon2KdfConfig.MEMORY.min}mb and ${Argon2KdfConfig.MEMORY.max}mb`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("validateKdfConfigForSetting(): should throw an error for invalid Argon2 parallelism", () => {
|
it("validateKdfConfigForSetting(): should throw an error for invalid Argon2 parallelism", () => {
|
||||||
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(3, 64, 17);
|
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(3, 64, 17);
|
||||||
expect(() => kdfConfig.validateKdfConfigForSetting()).toThrow(
|
expect(() => kdfConfig.validateKdfConfigForSetting()).toThrow(
|
||||||
@@ -108,70 +101,32 @@ describe("KdfConfigService", () => {
|
|||||||
|
|
||||||
it("validateKdfConfigForPrelogin(): should throw an error for too low PBKDF2 iterations", () => {
|
it("validateKdfConfigForPrelogin(): should throw an error for too low PBKDF2 iterations", () => {
|
||||||
const kdfConfig: PBKDF2KdfConfig = new PBKDF2KdfConfig(
|
const kdfConfig: PBKDF2KdfConfig = new PBKDF2KdfConfig(
|
||||||
PBKDF2KdfConfig.PRELOGIN_ITERATIONS.min - 1,
|
PBKDF2KdfConfig.PRELOGIN_ITERATIONS_MIN - 1,
|
||||||
);
|
);
|
||||||
expect(() => kdfConfig.validateKdfConfigForPrelogin()).toThrow(
|
expect(() => kdfConfig.validateKdfConfigForPrelogin()).toThrow(
|
||||||
`PBKDF2 iterations must be between ${PBKDF2KdfConfig.PRELOGIN_ITERATIONS.min} and ${PBKDF2KdfConfig.PRELOGIN_ITERATIONS.max}`,
|
`PBKDF2 iterations must be at least ${PBKDF2KdfConfig.PRELOGIN_ITERATIONS_MIN}, but was ${kdfConfig.iterations}; possible pre-login downgrade attack detected.`,
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("validateKdfConfigForPrelogin(): should throw an error for too high PBKDF2 iterations", () => {
|
|
||||||
const kdfConfig: PBKDF2KdfConfig = new PBKDF2KdfConfig(
|
|
||||||
PBKDF2KdfConfig.PRELOGIN_ITERATIONS.max + 1,
|
|
||||||
);
|
|
||||||
expect(() => kdfConfig.validateKdfConfigForPrelogin()).toThrow(
|
|
||||||
`PBKDF2 iterations must be between ${PBKDF2KdfConfig.PRELOGIN_ITERATIONS.min} and ${PBKDF2KdfConfig.PRELOGIN_ITERATIONS.max}`,
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("validateKdfConfigForPrelogin(): should throw an error for too low Argon2 iterations", () => {
|
it("validateKdfConfigForPrelogin(): should throw an error for too low Argon2 iterations", () => {
|
||||||
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(
|
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(
|
||||||
Argon2KdfConfig.ITERATIONS.min - 1,
|
Argon2KdfConfig.PRELOGIN_ITERATIONS_MIN - 1,
|
||||||
64,
|
64,
|
||||||
4,
|
4,
|
||||||
);
|
);
|
||||||
expect(() => kdfConfig.validateKdfConfigForPrelogin()).toThrow(
|
expect(() => kdfConfig.validateKdfConfigForPrelogin()).toThrow(
|
||||||
`Argon2 iterations must be between ${Argon2KdfConfig.ITERATIONS.min} and ${Argon2KdfConfig.ITERATIONS.max}`,
|
`Argon2 iterations must be at least ${Argon2KdfConfig.PRELOGIN_ITERATIONS_MIN}, but was ${kdfConfig.iterations}; possible pre-login downgrade attack detected.`,
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("validateKdfConfigForPrelogin(): should throw an error for too high Argon2 iterations", () => {
|
|
||||||
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(
|
|
||||||
Argon2KdfConfig.PRELOGIN_ITERATIONS.max + 1,
|
|
||||||
64,
|
|
||||||
4,
|
|
||||||
);
|
|
||||||
expect(() => kdfConfig.validateKdfConfigForPrelogin()).toThrow(
|
|
||||||
`Argon2 iterations must be between ${Argon2KdfConfig.ITERATIONS.min} and ${Argon2KdfConfig.ITERATIONS.max}`,
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("validateKdfConfigForPrelogin(): should throw an error for too low Argon2 memory", () => {
|
it("validateKdfConfigForPrelogin(): should throw an error for too low Argon2 memory", () => {
|
||||||
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(
|
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(
|
||||||
3,
|
3,
|
||||||
Argon2KdfConfig.PRELOGIN_MEMORY.min - 1,
|
Argon2KdfConfig.PRELOGIN_MEMORY_MIN - 1,
|
||||||
4,
|
4,
|
||||||
);
|
);
|
||||||
expect(() => kdfConfig.validateKdfConfigForPrelogin()).toThrow(
|
expect(() => kdfConfig.validateKdfConfigForPrelogin()).toThrow(
|
||||||
`Argon2 memory must be between ${Argon2KdfConfig.PRELOGIN_MEMORY.min}mb and ${Argon2KdfConfig.PRELOGIN_MEMORY.max}mb`,
|
`Argon2 memory must be at least ${Argon2KdfConfig.PRELOGIN_MEMORY_MIN} MiB, but was ${kdfConfig.memory} MiB; possible pre-login downgrade attack detected.`,
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("validateKdfConfigForPrelogin(): should throw an error for too high Argon2 memory", () => {
|
|
||||||
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(
|
|
||||||
3,
|
|
||||||
Argon2KdfConfig.PRELOGIN_MEMORY.max + 1,
|
|
||||||
4,
|
|
||||||
);
|
|
||||||
expect(() => kdfConfig.validateKdfConfigForPrelogin()).toThrow(
|
|
||||||
`Argon2 memory must be between ${Argon2KdfConfig.PRELOGIN_MEMORY.min}mb and ${Argon2KdfConfig.PRELOGIN_MEMORY.max}mb`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("validateKdfConfigForPrelogin(): should throw an error for too high Argon2 parallelism", () => {
|
|
||||||
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(3, 64, 17);
|
|
||||||
expect(() => kdfConfig.validateKdfConfigForPrelogin()).toThrow(
|
|
||||||
`Argon2 parallelism must be between ${Argon2KdfConfig.PRELOGIN_PARALLELISM.min} and ${Argon2KdfConfig.PRELOGIN_PARALLELISM.max}`,
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user