Skip to content

Commit

Permalink
[PM-12806] Fix minimum KDF validation (#11786)
Browse files Browse the repository at this point in the history
* Fix minimum KDF validation

* Add better error messages

* Fix tests

* Fix tests
  • Loading branch information
quexten authored Oct 30, 2024
1 parent dd6def2 commit 912ff88
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ describe("LoginStrategyService", () => {
new IdentityTokenResponse({
ForcePasswordReset: false,
Kdf: KdfType.PBKDF2_SHA256,
KdfIterations: PBKDF2KdfConfig.PRELOGIN_ITERATIONS.min - 1,
KdfIterations: PBKDF2KdfConfig.PRELOGIN_ITERATIONS_MIN - 1,
Key: "KEY",
PrivateKey: "PRIVATE_KEY",
ResetMasterPassword: false,
Expand All @@ -309,7 +309,7 @@ describe("LoginStrategyService", () => {
apiService.postPrelogin.mockResolvedValue(
new PreloginResponse({
Kdf: KdfType.PBKDF2_SHA256,
KdfIterations: PBKDF2KdfConfig.PRELOGIN_ITERATIONS.min - 1,
KdfIterations: PBKDF2KdfConfig.PRELOGIN_ITERATIONS_MIN - 1,
}),
);

Expand All @@ -321,7 +321,7 @@ describe("LoginStrategyService", () => {
});

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.`,
);
});
});
26 changes: 13 additions & 13 deletions libs/common/src/auth/models/domain/kdf-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export type KdfConfig = PBKDF2KdfConfig | Argon2KdfConfig;
*/
export class PBKDF2KdfConfig {
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;
iterations: number;

Expand All @@ -38,9 +38,9 @@ export class PBKDF2KdfConfig {
* A Valid PBKDF2 KDF configuration has KDF iterations between the 5000 and 2_000_000.
*/
validateKdfConfigForPrelogin(): void {
if (!PBKDF2KdfConfig.PRELOGIN_ITERATIONS.inRange(this.iterations)) {
if (PBKDF2KdfConfig.PRELOGIN_ITERATIONS_MIN > this.iterations) {
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.`,
);
}
}
Expand All @@ -58,9 +58,9 @@ export class Argon2KdfConfig {
static PARALLELISM = new RangeWithDefault(1, 16, 4);
static ITERATIONS = new RangeWithDefault(2, 10, 3);

static PRELOGIN_MEMORY = Argon2KdfConfig.MEMORY;
static PRELOGIN_PARALLELISM = Argon2KdfConfig.PARALLELISM;
static PRELOGIN_ITERATIONS = Argon2KdfConfig.ITERATIONS;
static PRELOGIN_MEMORY_MIN = 16;
static PRELOGIN_PARALLELISM_MIN = 1;
static PRELOGIN_ITERATIONS_MIN = 2;

kdfType: KdfType.Argon2id = KdfType.Argon2id;
iterations: number;
Expand All @@ -86,7 +86,7 @@ export class Argon2KdfConfig {

if (!Argon2KdfConfig.MEMORY.inRange(this.memory)) {
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`,
);
}

Expand All @@ -101,21 +101,21 @@ export class Argon2KdfConfig {
* Validates the Argon2 KDF configuration for pre-login.
*/
validateKdfConfigForPrelogin(): void {
if (!Argon2KdfConfig.PRELOGIN_ITERATIONS.inRange(this.iterations)) {
if (Argon2KdfConfig.PRELOGIN_ITERATIONS_MIN > this.iterations) {
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(
`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(
`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.`,
);
}
}
Expand Down
57 changes: 6 additions & 51 deletions libs/common/src/auth/services/kdf-config.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(3, 64, 17);
expect(() => kdfConfig.validateKdfConfigForSetting()).toThrow(
Expand All @@ -108,70 +101,32 @@ describe("KdfConfigService", () => {

it("validateKdfConfigForPrelogin(): should throw an error for too low PBKDF2 iterations", () => {
const kdfConfig: PBKDF2KdfConfig = new PBKDF2KdfConfig(
PBKDF2KdfConfig.PRELOGIN_ITERATIONS.min - 1,
PBKDF2KdfConfig.PRELOGIN_ITERATIONS_MIN - 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 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}`,
`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 low Argon2 iterations", () => {
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(
Argon2KdfConfig.ITERATIONS.min - 1,
Argon2KdfConfig.PRELOGIN_ITERATIONS_MIN - 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 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}`,
`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 low Argon2 memory", () => {
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(
3,
Argon2KdfConfig.PRELOGIN_MEMORY.min - 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 memory", () => {
const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(
3,
Argon2KdfConfig.PRELOGIN_MEMORY.max + 1,
Argon2KdfConfig.PRELOGIN_MEMORY_MIN - 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}`,
`Argon2 memory must be at least ${Argon2KdfConfig.PRELOGIN_MEMORY_MIN} MiB, but was ${kdfConfig.memory} MiB; possible pre-login downgrade attack detected.`,
);
});
});

0 comments on commit 912ff88

Please sign in to comment.