← Back to Blog

KMAC and cSHAKE: Domain-Separated SHA-3 Functions

KMAC and cSHAKE: Domain-Separated SHA-3 Functions - QNSQY post-quantum encryption guide

When NIST standardized SHA-3 in 2015, the family included not just fixed-output hash functions like SHA3-256 and SHA3-512 but also two extendable-output functions, SHAKE128 and SHAKE256. Extendable-output (XOF) means you can ask for any number of output bytes, not just a fixed 256 or 512. This flexibility opened up new design possibilities. NIST followed up with Special Publication 800-185, published in December 2016, which defines four SHA-3 derived functions: cSHAKE, KMAC, TupleHash, and ParallelHash.

These four are not just curiosities. They solve real problems that older constructions handled clumsily. KMAC replaces HMAC with a cleaner keyed MAC. cSHAKE provides an extendable-output function with built-in domain separation. TupleHash hashes a list of strings without ambiguity, fixing a class of bugs. ParallelHash takes advantage of multi-core CPUs.

This article focuses on KMAC and cSHAKE, the two most widely useful of the four. We cover TupleHash in TupleHash: Domain-Separated Hashing of Multiple Inputs and ParallelHash in ParallelHash: Multi-Core SHA-3 Hashing.

What Domain Separation Means

Before getting into KMAC and cSHAKE, it helps to understand the problem they solve: domain separation.

Suppose your protocol uses a hash function for two purposes: hashing a message, and hashing a key followed by a message (a MAC). If you use the same hash function for both, an attacker might find inputs M1 and (K, M2) such that Hash(M1) = Hash(K || M2). Now your hash output cannot be safely interpreted; it is ambiguous what it represents.

Domain separation is the principle that distinct uses of the same primitive should produce non-colliding outputs by construction. The standard fix is to prepend a context string that identifies the use. Hash("message:" || M) and Hash("mac:" || K || M) cannot collide unless the underlying hash itself does.

Older protocols handled domain separation ad-hoc, with mixed results. SP 800-185 builds it directly into the function definition, eliminating the bug class.

cSHAKE: Customizable SHAKE

cSHAKE stands for customizable SHAKE. It is SHAKE128 or SHAKE256 with two extra parameters: a function name N and a customization string S.

cSHAKE128(X, L, N, S) and cSHAKE256(X, L, N, S) take an input X, an output length L (in bits), a function name N, and a customization string S, and produce L bits of output.

Internally, cSHAKE prepends a domain-separated header that includes N and S, then hashes X and absorbs into the SHA-3 sponge, then squeezes L bits out. The header encoding is structured: N is supposed to be a registered function name (so KMAC, TupleHash, and ParallelHash all use it), and S is a free-form customization string set by the application.

If both N and S are empty, cSHAKE is identical to SHAKE. Otherwise, the prepended header makes the output unique to that (N, S) pair. Two applications using cSHAKE with different S values get non-colliding outputs even if they have the same input X and output length L.

This is the cleanest available domain separation primitive. You do not have to worry about how to encode the context bytes; cSHAKE does it for you with explicit length prefixing.

KMAC: SHA-3's Native MAC

KMAC stands for KECCAK Message Authentication Code. It is built on top of cSHAKE and provides a keyed MAC.

KMAC128(K, X, L, S) and KMAC256(K, X, L, S) take a key K, a message X, an output length L (in bits), and a customization string S, and produce L bits of MAC tag.

Internally, KMAC encodes the key with a length prefix, prepends it to the message, encodes the requested output length, and runs the result through cSHAKE with N = "KMAC" and the user's S. The output is L bits.

Two key properties make KMAC pleasant to use:

The output length L is bound into the input. Calling KMAC(K, X, 256, "") and KMAC(K, X, 512, "") produces unrelated outputs. There is no concern about truncation collisions or related-output attacks.

The customization string S is free. Different protocols can pick different S values and get cleanly separated MACs without coordination.

Compared to HMAC, KMAC is cleaner. HMAC has the awkward ipad/opad construction that exists to compensate for length-extension in Merkle-Damgard hashes. KMAC just prepends the key to the input because SHA-3 is a sponge and does not have length-extension issues. The simpler construction is easier to analyze and easier to implement correctly.

When to Use KMAC vs HMAC

For most existing systems, HMAC-SHA-256 is the default and stays the default. It is faster on hardware with SHA-256 instructions (Intel SHA-NI, ARM Cryptography Extensions), it is approved by FIPS 198-1, and it is what TLS 1.3, IPsec, and most other protocols use.

For new designs that want SHA-3, KMAC256 is the right MAC. It is approved by SP 800-185 and integrated into FIPS 140-3 module testing.

For protocols that need fine-grained domain separation across many keys and contexts, KMAC's customization string is significantly more convenient than HMAC's reliance on careful key prefixing.

In post-quantum protocols specifically, KMAC256 with a 256-bit key gives 256-bit symmetric security, which means 128 bits of effective quantum security. This matches the strength target of ML-KEM-1024 and ML-DSA-87 in the high-security NIST levels. We discuss security levels in NIST FIPS Guide and 2035 NSA CNSA Deadline Plan.

A Worked Example

Suppose you are designing a new protocol with two channels: a control channel that needs MAC tags and a data channel that needs key derivation. You want clean domain separation.

With KMAC and cSHAKE, you write:

control_tag = KMAC256(K, message, 256, S = "control v1") derived_data_key = cSHAKE256(K, 256, N = "DataKDF", S = "data v1")

The two outputs are independent. An attacker cannot interleave attacks across the two channels because the customization strings differ.

If you wanted to do this with HMAC and HKDF, you would write:

control_tag = HMAC-SHA-256(K_control, message) data_key = HKDF-Expand(K_data, info = "data v1", L = 32)

But you would need separate keys K_control and K_data, derived ahead of time, to get the same domain separation. With KMAC and cSHAKE, the customization string handles it inline.

Where KMAC and cSHAKE Are Used

NIST FIPS 203 (ML-KEM) uses cSHAKE-style derivations internally. The K-PKE primitive prepends domain-separation bytes before its SHAKE calls.

NIST FIPS 204 (ML-DSA) uses SHAKE128 and SHAKE256 with explicit prepended bytes for domain separation, mirroring the cSHAKE pattern even where it does not call cSHAKE by name.

NIST FIPS 205 (SLH-DSA) uses MGF1-SHA-256 and SHAKE-based derivations in its address-based key tree.

In the QNSQY hybrid encryption design, the choice for derivation is HMAC-SHA-256 in HKDF for compatibility with widely-deployed code, but KMAC256 is supported as an alternative. See Hybrid Encryption.

In the IETF post-quantum drafts (covered in IETF PQC Internet Drafts), KMAC and cSHAKE appear as approved primitives for specific roles in certain hybrid combiners.

In hardware security modules, vendors increasingly support KMAC and cSHAKE as named primitives for new key-derivation use cases. PKCS#11 has standardized KMAC mechanisms.

SP 800-108 with KMAC

NIST SP 800-108 Rev. 1, the counter mode KDF (covered in NIST SP 800-108 Counter Mode KDFs), explicitly supports KMAC as one of its PRFs.

This means you can build a counter mode KDF on top of KMAC instead of HMAC:

T_i = KMAC256(K, [i] || Label || Context || [L], 256, "SP800-108 v1")

This combines SP 800-108's counter mode structure with KMAC's clean domain separation, giving the strongest available subkey derivation pattern in NIST's toolkit.

SP 800-56C with KMAC

Similarly, SP 800-56C Rev. 2 (covered in Concat KDF (SP 800-56C)) supports KMAC as the Extract MAC. So:

PRK = KMAC256(salt, Z, 256, "Extract v1")

This gives a KMAC-based version of HKDF Extract. For the Expand step, you can use SP 800-108 counter mode with KMAC, completing a fully SHA-3-based derivation chain.

For protocols that want to be entirely SHA-3-based and avoid SHA-2 (perhaps for a long-term hedge against SHA-2 weaknesses, however unlikely), this is the right combination.

Performance and Hardware

SHA-3 in software is slower than SHA-2 on most CPUs because Intel and AMD provide hardware acceleration for SHA-256 (and recently SHA-512) but not for SHA-3. ARM v8.4-A and later include SHA-3 hardware (called sha3 extensions), but most servers and desktops do not have it.

In benchmarks on modern x86_64 CPUs:

HMAC-SHA-256: 1-2 GB/s with hardware acceleration. HMAC-SHA-512: 800 MB/s. KMAC256 (SHA3-256-based): 200-400 MB/s, software only.

For most use cases the speed difference does not matter; you compute one MAC per message and the message dominates. For high-throughput tunneling (VPNs, fast disk encryption), HMAC-SHA-256 wins on commodity hardware.

Common Implementation Mistakes

Empty customization string. If you pass S = "" to cSHAKE, it falls back to plain SHAKE behavior. This is allowed but defeats the domain separation purpose. Always pass a meaningful S.

Confusing function name N with customization S. N is supposed to be a fixed registered name like "KMAC", "TupleHash", "ParallelHash". S is your application's free-form context. Some implementations let you set both; only set N if you are defining a new derived function.

Wrong output length encoding. KMAC encodes the requested output length as bits, not bytes. Asking for "256 bits" gives 32 bytes; asking for "256 bytes" gives 2048 bits = 256 bytes after a different encoding step. Read your library's API carefully.

Truncating tag bytes. KMAC tags can be any length, but truncating below 128 bits weakens security. NIST recommends at least 128 bits for general use, 256 bits for post-quantum-safe high-security use.

Missing length prefixes for variable-length keys or messages. The internal cSHAKE encoding handles this correctly when you call the spec-compliant API. Hand-rolled implementations sometimes miss it.

Frequently Asked Questions

Should I use KMAC for new designs?

If you are starting fresh and have flexibility, KMAC256 is a strong choice. If you need maximum compatibility with existing libraries and protocols, HMAC-SHA-256 remains safer to deploy.

Is KMAC FIPS-approved?

Yes. KMAC is part of NIST SP 800-185 and is approved as a FIPS 140-3 PRF. KMAC128 provides 128-bit security strength; KMAC256 provides 256-bit. For post-quantum-era applications, prefer KMAC256.

How does cSHAKE compare to BLAKE3 for XOF use?

Both are extendable-output functions. cSHAKE is NIST-standardized and FIPS-approved. BLAKE3 is faster on commodity CPUs (no hardware required) and supports parallel hashing natively. For FIPS environments, use cSHAKE; for performance-critical non-FIPS code, BLAKE3 is excellent. See BLAKE3 Hashing.

Can I use KMAC inside HKDF?

The HKDF spec (RFC 5869) is HMAC-only. But the abstract construction (extract-then-expand) works with any PRF. NIST SP 800-56C Rev. 2 explicitly defines a two-step KDF with KMAC. So yes, but call it the SP 800-56C two-step KDF, not HKDF, for spec correctness.

What is the smallest reasonable KMAC tag size?

For non-quantum-safe use, 128 bits. For post-quantum-safe use, 256 bits. NIST recommends matching the tag size to the security level of the rest of the protocol.

Can I use cSHAKE as a stream cipher?

cSHAKE is an extendable-output function, so you can squeeze any number of bytes from a fixed input. If the input contains a key and a nonce, the output bytes are a pseudorandom keystream that you could XOR with plaintext for encryption. This is technically possible but unusual. Standard stream ciphers (ChaCha20, AES-CTR) are faster and more thoroughly analyzed for that role. cSHAKE-as-stream-cipher might be appropriate in environments that already have SHA-3 hardware and want to minimize primitive count.

Does KMAC have a streaming API?

Yes. Most implementations expose a three-step API: init (with key, customization, and requested length), update (call multiple times with input chunks), and finalize (returns the tag). The Keccak sponge naturally supports incremental absorption, so streaming is efficient.

Side-by-Side: HMAC vs KMAC Implementation

To make the difference concrete, here is how a 256-bit MAC over a 1 KB message looks in pseudocode.

HMAC-SHA-256:

`` K' = pad_or_hash_key_to_block_size(K, block_size = 64) inner = SHA256((K' XOR ipad) || message) tag = SHA256((K' XOR opad) || inner) truncate tag to 256 bits ``

KMAC256:

`` encoded_key = bytepad(encode_string(K), rate = 136) encoded_length = right_encode(256) // bits requested prefix = encoded_key message_padded = encoded_length || prefix output = cSHAKE256(message, 256, "KMAC", customization_string) ``

The KMAC version has more lines but no XOR-with-padding-byte trickery, no nested hash calls, and an explicit customization string. The cSHAKE call inside KMAC is itself just SHA-3 with a known prefix structure, so the security analysis chains cleanly.

The HMAC version is harder to get right because of the ipad/opad XOR. Many HMAC implementation bugs over the years have come from off-by-one errors in this padding. The KMAC structure removes that class of bug.

That said, HMAC has been deployed and audited for nearly thirty years. The bugs are mostly known and fixed. KMAC is newer and has had less time to accumulate hard-won implementation experience. For new designs that need maximum scrutiny history, HMAC remains the safer choice; for new designs that prefer cleaner construction, KMAC.

Sources

  1. NIST Special Publication 800-185. "SHA-3 Derived Functions: cSHAKE, KMAC, TupleHash, and ParallelHash." December 2016. https://csrc.nist.gov/pubs/sp/800/185/final
  2. NIST FIPS 202. "SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions." August 2015. https://csrc.nist.gov/pubs/fips/202/final
  3. NIST FIPS 203. "Module-Lattice-Based Key-Encapsulation Mechanism Standard." August 2024. https://csrc.nist.gov/pubs/fips/203/final
  4. NIST Special Publication 800-108 Rev. 1. "Recommendation for Key Derivation Using Pseudorandom Functions." August 2022. https://csrc.nist.gov/pubs/sp/800/108/r1/final
  5. NIST Special Publication 800-56C Rev. 2. "Recommendation for Key-Derivation Methods in Key-Establishment Schemes." August 2020. https://csrc.nist.gov/pubs/sp/800/56/c/r2/final
  6. Bertoni, G., Daemen, J., Peeters, M., and Van Assche, G. "KECCAK Sponge Function Family Main Document." Submission to the SHA-3 competition, 2010. https://keccak.team/files/Keccak-main-2.1.pdf

Related Articles

Protect Your Data Before Q-Day Arrives

QNSQY's NIST-standardized post-quantum encryption protects files against both current and quantum-era threats.

Try QNSQY