diff --git a/CHANGELOG.md b/CHANGELOG.md index 3eeb679..f645cc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,8 @@ ## [2.1.1](https://github.com/filipo11021/nodejs-password-hashing/compare/v2.1.0...v2.1.1) (2026-01-26) - ### Bug Fixes -* **#26:** downgrade @types/node to v24 and lock in dependabot ([#37](https://github.com/filipo11021/nodejs-password-hashing/issues/37)) ([dcfd6d7](https://github.com/filipo11021/nodejs-password-hashing/commit/dcfd6d77ab1c0e72cacbee17599154108101a3cc)), closes [#26](https://github.com/filipo11021/nodejs-password-hashing/issues/26) [#26](https://github.com/filipo11021/nodejs-password-hashing/issues/26) +- **#26:** downgrade @types/node to v24 and lock in dependabot ([#37](https://github.com/filipo11021/nodejs-password-hashing/issues/37)) ([dcfd6d7](https://github.com/filipo11021/nodejs-password-hashing/commit/dcfd6d77ab1c0e72cacbee17599154108101a3cc)), closes [#26](https://github.com/filipo11021/nodejs-password-hashing/issues/26) [#26](https://github.com/filipo11021/nodejs-password-hashing/issues/26) # [2.1.0](https://github.com/filipo11021/nodejs-password-hashing/compare/v2.0.0...v2.1.0) (2026-01-23) diff --git a/src/argon2/argon2-validation.test.ts b/src/argon2/argon2-validation.test.ts new file mode 100644 index 0000000..f7ac380 --- /dev/null +++ b/src/argon2/argon2-validation.test.ts @@ -0,0 +1,149 @@ +import { createArgon2Hashing } from "./argon2.ts"; +import assert from "node:assert/strict"; +import { describe, it } from "node:test"; + +void describe("Argon2 Parameter Validation - RFC 9106 Compliance", () => { + void describe("parallelism parameter", () => { + void it("should accept parallelism = 1 (minimum per RFC 9106)", () => { + assert.doesNotThrow(() => { + createArgon2Hashing({ + parallelism: 1, + memory: 8, + }); + }, "parallelism = 1 should be valid per RFC 9106"); + }); + + void it("should reject parallelism = 0 (below minimum)", () => { + assert.throws( + () => { + createArgon2Hashing({ + parallelism: 0, + memory: 8, + }); + }, + { + name: "ZodError", + }, + "parallelism must be at least 1", + ); + }); + }); + + void describe("tagLength parameter", () => { + void it("should accept tagLength = 4 (minimum per RFC 9106)", () => { + assert.doesNotThrow(() => { + createArgon2Hashing({ + tagLength: 4, + }); + }, "tagLength = 4 should be valid per RFC 9106"); + }); + + void it("should reject tagLength = 3 (below minimum)", () => { + assert.throws( + () => { + createArgon2Hashing({ + tagLength: 3, + }); + }, + { + name: "ZodError", + }, + "tagLength must be at least 4", + ); + }); + }); + + void describe("memory parameter relative to parallelism", () => { + void it("should accept memory = 8 * parallelism (minimum per RFC 9106)", () => { + assert.doesNotThrow(() => { + createArgon2Hashing({ + parallelism: 2, + memory: 16, // Exactly 8 * 2 + }); + }, "memory = 8 * parallelism should be valid per RFC 9106"); + }); + + void it("should reject memory < 8 * parallelism (below minimum)", () => { + assert.throws( + () => { + createArgon2Hashing({ + parallelism: 2, + memory: 15, // Less than 8 * 2 + }); + }, + { + name: "ZodError", + }, + "memory must be at least 8 * parallelism", + ); + }); + }); + + void describe("passes parameter", () => { + void it("should accept passes = 1 (minimum per RFC 9106)", () => { + assert.doesNotThrow(() => { + createArgon2Hashing({ + passes: 1, + }); + }, "passes = 1 should be valid per RFC 9106"); + }); + + void it("should reject passes = 0 (below minimum)", () => { + assert.throws( + () => { + createArgon2Hashing({ + passes: 0, + }); + }, + { + name: "ZodError", + }, + "passes must be at least 1", + ); + }); + }); + + void describe("edge cases - absolute minimums", () => { + void it("should accept absolute minimum configuration (parallelism=1, memory=8)", () => { + assert.doesNotThrow(() => { + createArgon2Hashing({ + parallelism: 1, + memory: 8, // Exactly 8 * 1 + passes: 1, + tagLength: 4, + }); + }, "absolute minimum configuration should be valid per RFC 9106"); + }); + + void it("should reject memory = 7 when parallelism = 1", () => { + assert.throws( + () => { + createArgon2Hashing({ + parallelism: 1, + memory: 7, + }); + }, + { + name: "ZodError", + }, + "memory = 7 should be rejected when parallelism = 1", + ); + }); + }); + + void describe("edge cases - invalid values", () => { + void it("should reject negative values", () => { + assert.throws( + () => { + createArgon2Hashing({ + passes: -1, + }); + }, + { + name: "ZodError", + }, + "passes must be positive", + ); + }); + }); +}); diff --git a/src/argon2/argon2.ts b/src/argon2/argon2.ts index ace2336..6dd495e 100644 --- a/src/argon2/argon2.ts +++ b/src/argon2/argon2.ts @@ -12,7 +12,7 @@ import { MAX_UINT24, MAX_UINT32 } from "../utils/numbers.ts"; const optionsSchema = z .object({ memory: z.number().max(MAX_UINT32), - passes: z.number().min(2).max(MAX_UINT32), + passes: z.number().min(1).max(MAX_UINT32), parallelism: z.number().min(1).max(MAX_UINT24), tagLength: z.number().min(4).max(MAX_UINT32), saltLength: z.number().min(16).max(1024), @@ -23,7 +23,8 @@ const optionsSchema = z return params.memory >= 8 * params.parallelism; }, { - message: "memory parameter must be at least 8 * parallelism", + message: + "memory parameter must be greater than or equal to 8 * parallelism", }, ) .readonly();