3.5 KiB
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
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
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.