MAC (message authentication code) reference
A MAC binds a message to a secret key, producing a short tag that proves the message is both intact and from someone holding the key. MACs protect API tokens, cookies, JWTs and the integrity half of authenticated encryption. This reference compares HMAC, GMAC, Poly1305, CMAC and KMAC by construction type, key size, tag length and the AEAD scheme each is paired with.
How it works
MACs come in three families:
- Hash-based (HMAC) — wraps a hash like SHA-256 in a keyed nested construction. Secure even on length-extendable hashes.
- Cipher-based (CMAC, GMAC) — built from a block cipher such as AES. GMAC is the authentication part of GCM.
- Polynomial (Poly1305) — evaluates a polynomial over a prime field; very fast and used in ChaCha20-Poly1305.
To stay secure you must verify the tag with a constant-time comparison, and for one-time MACs (Poly1305, GMAC) you must never reuse the nonce/one-time key, or an attacker can forge tags.
Tips and notes
- For standalone authentication, HMAC-SHA-256 is the safe default.
- For authenticated encryption, use AES-GCM (GMAC) or ChaCha20-Poly1305.
- Truncating a 256-bit HMAC tag to 128 bits is acceptable; shorter tags weaken forgery resistance.
- Always compare tags in constant time and never reuse nonces with one-time MACs.
Choosing the right MAC for your use case
Picking a MAC is not just about security strength — it is about the context in which authentication is needed:
API request signing: HMAC-SHA-256 is the industry standard here, used by AWS Signature Version 4, GitHub webhooks, Stripe webhooks, and virtually every other API that signs requests. The sender includes HMAC-SHA-256(secret, message) in a header; the receiver recomputes it and compares. The key advantage is that it needs only a shared secret, no PKI.
Authenticated encryption: if you are encrypting data and also need integrity guarantees, use an AEAD cipher rather than a separate MAC. AES-256-GCM and ChaCha20-Poly1305 provide confidentiality and authentication in one pass. Constructing encrypt-then-MAC manually is error-prone; AEAD eliminates the opportunity to get the composition wrong.
Cookie and session token integrity: HMAC-SHA-256 or HMAC-SHA-512 are the typical choices. JWT’s HS256 algorithm is HMAC-SHA-256 under the hood. Never use a bare hash (SHA-256 without a key) to sign a cookie — anyone who knows the payload format can recompute a valid hash.
FIPS-compliant contexts: CMAC (using AES) is often required when you need a FIPS 140-2 approved MAC that does not rely on a hash function. HMAC is also FIPS-approved when paired with an approved hash.
The nonce reuse catastrophe for one-time MACs
Poly1305 and GMAC (the MAC inside AES-GCM) are one-time MAC constructions: each message must use a fresh, never-repeated nonce. If an attacker ever observes two messages authenticated under the same nonce with a one-time MAC, they can solve for the authentication key and forge arbitrary messages. This is not a theoretical concern — nonce reuse in AES-GCM has been exploited in practice. The AEAD construction handles nonce management as part of the API (for example, the 96-bit random nonce in AES-GCM), but if you use these MACs directly, never reuse a nonce under the same key.
Length extension attacks: why HMAC exists
MD5, SHA-1, and the SHA-2 family (SHA-256, SHA-512) use a Merkle-Damgård construction that is vulnerable to length extension: given hash(secret || message) and the length of the message, an attacker can compute hash(secret || message || padding || extra_data) without knowing the secret. The naive key-prepend construction MD5(key || message) is therefore insecure. HMAC defeats this by applying the key twice in a nested structure: HMAC = hash(key_outer || hash(key_inner || message)). The outer hash prevents the extension attack. Always use HMAC rather than raw keyed hashes.