# Capability: hash-primitives ## ADDED Requirements ### Package identity - REQ-HASH-001: Package name is `@suaveplan/hash`, located at `packages/universal/hash`. - REQ-HASH-002: Runtime deps: `@noble/hashes`, `@suaveplan/error`, `tslib`, `zod`. Zero browser/React/Node-only deps. - REQ-HASH-003: Ships `"sideEffects": false`. All modules are individually subpath-exported for tree-shaking. ### Hash digests - REQ-HASH-010: Exports `sha256(data: Uint8Array): Uint8Array` — 32-byte SHA-256 digest. - REQ-HASH-011: Exports `sha384(data: Uint8Array): Uint8Array` — 48-byte SHA-384 digest. - REQ-HASH-012: Exports `sha512(data: Uint8Array): Uint8Array` — 64-byte SHA-512 digest. - REQ-HASH-013: Exports `HashAlgorithm = "SHA-256" | "SHA-384" | "SHA-512"`. - REQ-HASH-014: Exports `hash(algo: HashAlgorithm, data: Uint8Array): Uint8Array` — algorithm dispatcher. ### HMAC - REQ-HASH-020: Exports `hmac(algo: HashAlgorithm, key: Uint8Array, data: Uint8Array): Uint8Array`. - REQ-HASH-021: Empty key is permitted (library-level; policy enforcement is the caller's responsibility). ### HKDF - REQ-HASH-030: Exports `hkdf(algo: HashAlgorithm, ikm: Uint8Array, options: HkdfOptions): Uint8Array`. - REQ-HASH-031: `HkdfOptions = { salt: Uint8Array; info: Uint8Array; length: number }`. All fields required. - REQ-HASH-032: Throws `HashError("HASH.KDF.INVALID_PARAMS")` if `length <= 0` or `length > 255 * hashLen`. ### PBKDF2 - REQ-HASH-040: Exports `pbkdf2(algo: HashAlgorithm, password: Uint8Array, options: Pbkdf2Options): Uint8Array`. - REQ-HASH-041: `Pbkdf2Options = { salt: Uint8Array; iterations: number; length: number }`. - REQ-HASH-042: Throws `HashError("HASH.KDF.INVALID_PARAMS")` if `iterations < 600_000` (OWASP 2023 minimum for SHA-256). Same minimum for SHA-384/512 (conservative — these are also interactive-login strength minimums). - REQ-HASH-043: Exports `PBKDF2_MIN_ITERATIONS = 600_000`. ### Argon2id - REQ-HASH-050: Exports `argon2id(password: Uint8Array, options: Argon2idOptions): Uint8Array`. - REQ-HASH-051: `Argon2idOptions = { salt: Uint8Array; timeCost: number; memoryCost: number; parallelism: number; length: number }`. - REQ-HASH-052: Throws `HashError("HASH.KDF.INVALID_PARAMS")` per-field: `timeCost < 1`, `memoryCost < 8`, `parallelism < 1`, `length <= 0`. Each error's `context` includes `{ field, value, minimum }`. - REQ-HASH-053: Exports `ARGON2ID_MIN_MEMORY_COST_KIB = 8`, `ARGON2ID_MIN_TIME_COST = 1`, `ARGON2ID_MIN_PARALLELISM = 1`. ### Encoding utilities - REQ-HASH-060: Exports `toHex(bytes: Uint8Array): string` and `fromHex(hex: string): Uint8Array`. - REQ-HASH-061: Exports `toBase64(bytes: Uint8Array): string`, `fromBase64(b64: string): Uint8Array`, `toBase64Url(bytes: Uint8Array): string`, `fromBase64Url(b64url: string): Uint8Array`. - REQ-HASH-062: `fromHex`/`fromBase64`/`fromBase64Url` throw `HashError("HASH.ENCODING.INVALID_INPUT")` on malformed input. ### Error surface - REQ-HASH-070: Exports `HashError extends SuaveplanError`. - REQ-HASH-071: Error codes namespace: `HASH.KDF.INVALID_PARAMS`, `HASH.ALGORITHM.UNSUPPORTED`, `HASH.ENCODING.INVALID_INPUT`. - REQ-HASH-072: No error message or context field contains raw key material or password bytes. ### Quality - REQ-HASH-080: 100% statement + branch coverage (istanbul). - REQ-HASH-081: fast-check property tests: determinism (same input → same output), HMAC known-vector (RFC 4231 vectors). - REQ-HASH-082: mitata benchmarks: sha256/sha384/sha512 at 1 KiB and 1 MiB; argon2id at minimum params. - REQ-HASH-083: Every `.ts` implementation file has a sibling `.md` (≥200 words). - REQ-HASH-084: `tests/no-disallowed.test.ts` asserts no `Math.random`, `Date.now`, or `crypto.getRandomValues` in `src/`.