# argon2id _(no submodules)_ ## Purpose Provides Argon2id password hashing as specified in RFC 9106. Argon2id is the current winner of the Password Hashing Competition and is recommended by OWASP for new applications. It is resistant to both GPU-based brute force (via high memory cost) and time-memory trade-off attacks (via the hybrid id variant). This module enforces non-pathological lower bounds on all four tuning parameters and produces a structured `HashError` with per-field context when they are violated, making it safe for frameworks to call without a separate validation layer. ## Features - `argon2id(password, options)` — Argon2id key derivation - Exported constants: `ARGON2ID_MIN_MEMORY_COST_KIB`, `ARGON2ID_MIN_TIME_COST`, `ARGON2ID_MIN_PARALLELISM` - Validates each parameter individually; error context includes `{ field, value, minimum }` - Throws `HashError("HASH.KDF.INVALID_PARAMS")` for any out-of-range parameter - Deterministic: same inputs always produce the same hash output ## Usage: Basic Example ```ts import { argon2id, ARGON2ID_MIN_MEMORY_COST_KIB } from "@suaveplan/crypto-hash/kdf"; const encoder = new TextEncoder(); const password = encoder.encode("hunter2"); const salt = new Uint8Array(16); // use crypto.getRandomValues in production const hash = argon2id(password, { salt, timeCost: 3, memoryCost: ARGON2ID_MIN_MEMORY_COST_KIB * 1024, // 8 MiB in KiB parallelism: 1, length: 32, }); console.log(hash.length); // 32 ``` ## Usage: Advanced Example ```ts import { argon2id, ARGON2ID_MIN_MEMORY_COST_KIB, ARGON2ID_MIN_TIME_COST, ARGON2ID_MIN_PARALLELISM, } from "@suaveplan/crypto-hash/kdf"; import { toHex } from "@suaveplan/crypto-hash/encoding"; import { HashError } from "@suaveplan/crypto-hash/errors"; interface PasswordHashConfig { timeCost: number; memoryCostKib: number; parallelism: number; outputBytes: number; } function hashPassword(password: string, salt: Uint8Array, cfg: PasswordHashConfig): string { try { const raw = argon2id(new TextEncoder().encode(password), { salt, timeCost: cfg.timeCost, memoryCost: cfg.memoryCostKib, parallelism: cfg.parallelism, length: cfg.outputBytes, }); return toHex(raw); } catch (err) { if (err instanceof HashError && err.context?.field) { throw new RangeError( `Argon2id parameter out of range: ${err.context.field} = ${err.context.value} ` + `(minimum: ${err.context.minimum})` ); } throw err; } } const salt = new Uint8Array(16); const hex = hashPassword("secure-password", salt, { timeCost: ARGON2ID_MIN_TIME_COST, memoryCostKib: ARGON2ID_MIN_MEMORY_COST_KIB, parallelism: ARGON2ID_MIN_PARALLELISM, outputBytes: 32, }); console.log(hex.length); // 64 hex chars = 32 bytes ``` ## API Reference ### `argon2id(password: Uint8Array, options: Argon2idOptions): Uint8Array` Derives `options.length` bytes from `password` using Argon2id. | Parameter | Type | Description | |-----------|------|-------------| | `password` | `Uint8Array` | User-supplied password bytes | | `options.salt` | `Uint8Array` | Per-credential random salt (≥ 16 bytes recommended) | | `options.timeCost` | `number` | Integer ≥ `ARGON2ID_MIN_TIME_COST` (number of iterations) | | `options.memoryCost` | `number` | Integer ≥ `ARGON2ID_MIN_MEMORY_COST_KIB` (in KiB) | | `options.parallelism` | `number` | Integer ≥ `ARGON2ID_MIN_PARALLELISM` (lane count) | | `options.length` | `number` | Positive integer byte count for derived output | **Returns:** `Uint8Array` of `options.length` bytes. **Throws:** `HashError("HASH.KDF.INVALID_PARAMS")` for any validation failure. Error `context` contains `{ field: string, value: number, minimum: number }`. --- ### `ARGON2ID_MIN_MEMORY_COST_KIB = 8` Minimum memory cost in KiB. Based on RFC 9106 §4 minimum recommendation. ### `ARGON2ID_MIN_TIME_COST = 1` Minimum number of Argon2id iterations. Lower values are pathological. ### `ARGON2ID_MIN_PARALLELISM = 1` Minimum lane count. Lower values are rejected as invalid. --- ### `Argon2idOptions` ```ts interface Argon2idOptions { readonly salt: Uint8Array; readonly timeCost: number; readonly memoryCost: number; readonly parallelism: number; readonly length: number; } ``` ## Implementation Notes Parameters are validated in order: `timeCost`, `memoryCost`, `parallelism`, `length`. The first failing field short-circuits and throws with that field's context — no partial execution occurs. The implementation delegates to `@noble/hashes/argon2` which is a pure-JavaScript Argon2id implementation reviewed by the cryptographic community. Argon2id does not use the `HashAlgorithm` dispatch pattern used by `hmac`, `hkdf`, and `pbkdf2` — Argon2id's internal structure is fixed and parameterised by `t`, `m`, and `p` rather than a hash constructor. For this reason, `argon2id.ts` does not import from `mac/hmac.ts`.