# hmac _(no submodules)_ ## Purpose Provides HMAC (Hash-based Message Authentication Code) over SHA-2 variants. HMAC proves that a message was produced by a party in possession of a shared key, giving both data integrity and authenticity guarantees without asymmetric cryptography. This module wraps the audited `@noble/hashes` HMAC primitive and restricts the algorithm surface to the same `HashAlgorithm` union used throughout `@suaveplan/crypto-hash`. ## Features - `hmac(algo, key, data)` — one-shot HMAC-SHA-{256,384,512} - Throws `HashError("HASH.ALGORITHM.UNSUPPORTED")` for unrecognised algorithm strings - Passes through RFC 4231 HMAC-SHA-256 test vectors - Deterministic: same `(algo, key, data)` triple always produces the same tag - Zero runtime state; safe for concurrent use ## Usage: Basic Example ```ts import { hmac } from "@suaveplan/crypto-hash/mac"; import { toHex } from "@suaveplan/crypto-hash/encoding"; const encoder = new TextEncoder(); const key = encoder.encode("super-secret-key"); const data = encoder.encode("payment:amount=100"); const tag = hmac("SHA-256", key, data); console.log(toHex(tag)); // 32-byte hex string ``` ## Usage: Advanced Example ```ts import { hmac } from "@suaveplan/crypto-hash/mac"; import { toHex, fromHex } from "@suaveplan/crypto-hash/encoding"; // Verify a webhook signature in constant time function verifyWebhookTag( secret: Uint8Array, payload: Uint8Array, receivedTagHex: string ): boolean { const expected = hmac("SHA-256", secret, payload); const received = fromHex(receivedTagHex); if (expected.length !== received.length) return false; let diff = 0; for (let i = 0; i < expected.length; i++) { diff |= expected[i] ^ received[i]; } return diff === 0; } const encoder = new TextEncoder(); const secret = encoder.encode("webhook-secret"); const body = encoder.encode('{"event":"charge.succeeded"}'); const tag = toHex(hmac("SHA-256", secret, body)); console.log(verifyWebhookTag(secret, body, tag)); // true ``` ## API Reference ### `hmac(algo: HashAlgorithm, key: Uint8Array, data: Uint8Array): Uint8Array` Computes the HMAC of `data` under `key` using the specified SHA-2 algorithm. | Parameter | Type | Description | |-----------|------|-------------| | `algo` | `HashAlgorithm` | `"SHA-256"`, `"SHA-384"`, or `"SHA-512"` | | `key` | `Uint8Array` | Shared secret key (any length ≥ 1 byte) | | `data` | `Uint8Array` | Message to authenticate | **Returns:** Tag bytes — 32 / 48 / 64 bytes for SHA-256 / SHA-384 / SHA-512 respectively. **Throws:** `HashError("HASH.ALGORITHM.UNSUPPORTED")` if `algo` is not one of the three supported variants. ## Implementation Notes The internal `hasherFor` helper maps `HashAlgorithm` strings to the corresponding `@noble/hashes` hash constructor. It is a private module function — not exported from this file or from any barrel — so it cannot be called by consumers. This design keeps the public API surface minimal while allowing `hkdf.ts` and `pbkdf2.ts` to import `hasherFor` from this module directly for their own internal use. `argon2id.ts` does not use `hasherFor` because Argon2id is a password-hashing algorithm that accepts its own internal parameterisation rather than a bare hash constructor. HMAC output length equals the underlying hash output length. The `@noble/hashes` implementation is constant-time within the hash primitive itself; callers are responsible for constant-time comparison of the resulting tags.