HMAC Generator — JavaScript source
Generate a keyed-hash HMAC (SHA-1/256/384/512) for a message and secret. Runs entirely in your browser via Web Crypto, with a shareable link to your exact input.
This is the JavaScript implementation — the same logic the interactive tool runs, in a shareable, citable form.
// hmac-generator - JavaScript polyglot showcase port
//
// Language: JavaScript (Web Crypto API / SubtleCrypto)
// Ported from: src/lib/hmac.ts
// Display source - part of CosmoDev's polyglot tool pages.
//
// Keyed-hash HMAC via the Web Crypto API. Pure logic - no DOM, no React.
// Deterministic for a given (message, secret, algorithm). Async only because
// SubtleCrypto is; requires a secure context (https or localhost).
//
// This is the same WebCrypto surface as the canonical TypeScript implementation,
// shown here without type annotations. Behavior matches exactly: UTF-8 inputs,
// lowercase hex output, SHA-256 by default, and rejection of empty secrets and
// unsupported algorithms.
// Supported HMAC hash algorithms. These spellings are SubtleCrypto's own
// algorithm names and are shared verbatim across every language port.
const HMAC_ALGORITHMS = ['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512'];
/**
* Lowercase hexadecimal encoding of an ArrayBuffer.
*
* Spreading the byte view into a plain array lets map/join build one string
* with no trailing separator; each byte is zero-padded to two hex digits.
*/
function toHex(buf) {
return Array.from(new Uint8Array(buf))
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
}
/**
* Compute the HMAC of `message` under `secret` and return it as lowercase hex.
*
* Both inputs are UTF-8 encoded via TextEncoder. SubtleCrypto performs the
* actual HMAC (RFC 2104) over the named hash. An empty secret is rejected -
* SubtleCrypto refuses a zero-length key, and making the check explicit gives
* a clearer error than the raw DOMException.
*
* @param {string} message - The payload to authenticate.
* @param {string} secret - The shared key.
* @param {string} [algorithm='SHA-256'] - One of HMAC_ALGORITHMS.
* @returns {Promise<string>} Lowercase hex HMAC.
* @throws {Error} On empty secret or unsupported algorithm.
*/
export async function hmacHex(message, secret, algorithm = 'SHA-256') {
if (!HMAC_ALGORITHMS.includes(algorithm)) {
throw new Error(`Unsupported HMAC algorithm: ${algorithm}`);
}
if (secret.length === 0) {
throw new Error('HMAC secret must not be empty');
}
// importKey binds the raw key bytes to the HMAC algorithm + hash choice.
// extractable=false and usages=['sign'] keep the key non-exportable and
// scoped to signing only - the tightest capability the API allows.
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
'raw',
encoder.encode(secret),
{ name: 'HMAC', hash: algorithm },
false,
['sign'],
);
// sign() produces the tag as an ArrayBuffer; hex-encode for output.
const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(message));
return toHex(signature);
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →