Skip to content

HMAC Generator — Python 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 Python implementation — the same logic the interactive tool runs, in a shareable, citable form.

"""hmac-generator — Python polyglot showcase port

Language: Python
Ported from: src/lib/hmac.ts
Display source — part of CosmoDev's polyglot tool pages.

Keyed-hash HMAC via Python's standard-library ``hmac`` and ``hashlib`` modules
(both backed by OpenSSL). ``hmac`` implements RFC 2104; ``hashlib`` supplies
the vetted, constant-time hash constructors. No third-party packages required.

Behavior mirrors the TypeScript reference: UTF-8 inputs, lowercase hex output,
SHA-256 by default, and rejection of empty secrets and unknown algorithms.
"""

from __future__ import annotations

import hashlib
import hmac
from typing import Final

# Canonical algorithm names. The spellings match the TypeScript union so the
# same string works across every port. Each maps to a hashlib constructor.
SHA1: Final[str] = "SHA-1"
SHA256: Final[str] = "SHA-256"  # default algorithm
SHA384: Final[str] = "SHA-384"
SHA512: Final[str] = "SHA-512"

DEFAULT_ALGORITHM: Final[str] = SHA256

# Dispatch table: canonical name -> hashlib constructor. Module-level so the
# supported set is visible in one place and easy to extend.
_HASH_CONSTRUCTORS = {
    SHA1: hashlib.sha1,
    SHA256: hashlib.sha256,
    SHA384: hashlib.sha384,
    SHA512: hashlib.sha512,
}


def _digest_for(algorithm: str):
    """Return the hashlib constructor for the canonical algorithm name.

    Raises ValueError (not KeyError) so callers get a clear, domain-specific
    message rather than a leaky lookup error.
    """
    try:
        return _HASH_CONSTRUCTORS[algorithm]
    except KeyError:
        raise ValueError(f"Unsupported HMAC algorithm: {algorithm}") from None


def hmac_hex(message: str, secret: str, algorithm: str = SHA256) -> str:
    """Compute HMAC(``message``, ``secret``) and return it as lowercase hex.

    Both inputs are UTF-8 encoded before hashing. An empty ``algorithm``
    selects SHA-256; an empty ``secret`` is rejected: ``hmac.new`` would accept
    a zero-length key, but the TypeScript reference (SubtleCrypto) refuses one,
    and matching that contract keeps the ports in parity.

    Passing the constructor (rather than the algorithm name string) as
    ``digestmod`` makes an unknown algorithm fail fast here instead of deep
    inside ``hmac.new``.
    """
    if algorithm == "":
        algorithm = DEFAULT_ALGORITHM

    digestmod = _digest_for(algorithm)

    if secret == "":
        raise ValueError("HMAC secret must not be empty")

    # key/message -> UTF-8 bytes; hexdigest() returns the lowercase hex string
    # the TypeScript reference produces.
    return hmac.new(
        secret.encode("utf-8"),
        message.encode("utf-8"),
        digestmod,
    ).hexdigest()

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 →