Table of Contents

NIST SP 800-185 - SHA-3 Derived Functions

Overview

NIST Special Publication 800-185 specifies SHA-3-derived functions:

  • cSHAKE - Customizable SHAKE (extendable-output function)
  • KMAC - Keccak Message Authentication Code
  • TupleHash - Hashing tuples of strings
  • ParallelHash - Parallel tree hashing

Official Reference

Implementation Status

Algorithm Status Class
cSHAKE128 ✅ Implemented CShake128
cSHAKE256 ✅ Implemented CShake256
KMAC128 ✅ Implemented KMac128
KMAC256 ✅ Implemented KMac256
TupleHash128 ⬜ Not implemented -
TupleHash256 ⬜ Not implemented -
ParallelHash128 ⬜ Not implemented -
ParallelHash256 ⬜ Not implemented -

cSHAKE (Customizable SHAKE)

Definition

cSHAKE is a customizable version of SHAKE that allows domain separation via:

  • N - Function name string (for NIST-defined functions)
  • S - Customization string (user-defined)

When both N and S are empty, cSHAKE is equivalent to SHAKE.

Parameters

Variant Capacity Rate Security
cSHAKE128 256 bits 1344 bits (168 bytes) 128 bits
cSHAKE256 512 bits 1088 bits (136 bytes) 256 bits

Padding

cSHAKE uses domain separator 0x04 (vs SHAKE's 0x1F) when N or S is non-empty.

Algorithm

cSHAKE(X, L, N, S):
  if N = "" and S = "":
    return SHAKE(X, L)
  else:
    return KECCAK[c](bytepad(encode_string(N) || encode_string(S), rate) || X || 00, L)

KMAC (Keccak Message Authentication Code)

Definition

KMAC is a MAC function based on Keccak (cSHAKE). It provides:

  • Variable-length output (or fixed-length via KMACXOF)
  • Optional customization string

Parameters

Variant Underlying Security
KMAC128 cSHAKE128 128 bits
KMAC256 cSHAKE256 256 bits

Algorithm

KMAC(K, X, L, S):
  newX = bytepad(encode_string(K), rate) || X || right_encode(L)
  return cSHAKE(newX, L, "KMAC", S)

Encoding Functions

left_encode(x)

Encodes integer x as a byte string with length prefix on the left.

n = smallest positive integer such that 2^(8n) > x
O = n as single byte || x as big-endian bytes

right_encode(x)

Encodes integer x as a byte string with length suffix on the right.

n = smallest positive integer such that 2^(8n) > x
O = x as big-endian bytes || n as single byte

encode_string(S)

Encodes a bit string with its length.

O = left_encode(len(S)) || S

bytepad(X, w)

Pads a byte string to a multiple of w bytes.

z = left_encode(w) || X
return z || 0^((-len(z)) mod w)

Test Vectors

cSHAKE128 (N = "", S = "Email Signature")

Sample #1 – 4-byte input

Message: 00010203
Function Name (N): "" (empty)
Customization (S): "Email Signature"
Output Length: 32 bytes
Digest: C1C36925B6409A04F1B504FCBCA9D82B4017277CB5ED2B2065FC1D3814D5AAF5

Sample #2 – 200-byte ramp input

Message: 200 bytes where byte[i] = i (00, 01, ..., C7)
Function Name (N): "" (empty)
Customization (S): "Email Signature"
Output Length: 32 bytes
Digest: C5221D50E4F822D96A2E8881A961420F294B7B24FE3D2094BAED2C6524CC166B

Compatibility Note

When both N and S are empty, cSHAKE128 reduces to SHAKE128 with domain separator 0x1F. The vectors in SHAKE-vectors.md therefore apply when calling CShake128.Create(outputBytes) without customization.

cSHAKE256 (N = "", S = "Email Signature")

Sample #1 – 4-byte input

Message: 00010203
Function Name (N): "" (empty)
Customization (S): "Email Signature"
Output Length: 64 bytes
Digest: D008828E2B80AC9D2218FFEE1D070C48B8E4C87BFF32C9699D5B6896EEE0EDD1
        64020E2BE0560858D9C00C037E34A96937C561A74C412BB4C746469527281C8C

Sample #2 – 200-byte ramp input

Message: 200 bytes where byte[i] = i (00, 01, ..., C7)
Function Name (N): "" (empty)
Customization (S): "Email Signature"
Output Length: 64 bytes
Digest: 07DC1EC55B0F7A0CE9A5A629E702A6B82E6F86BF5F40019A27A66D426FD79A5D
        9BA1DE0A99FBAF8FCB753C0A8CEC6556F2F90E64616B78B4F3B719779AB0F9E3

The NUnit suite in tests/Security/Cryptography/Hash/Keccak/CShakeTests.cs consumes both cSHAKE128 samples and both cSHAKE256 samples to guarantee parity with the NIST vectors across scalar and SIMD modes. Benchmark fixtures (tests/Security/Cryptography/Benchmarks/AllHashersAllSizesBenchmark.cs) further exercise each SIMD variant (AVX-512/AVX2/SSSE3/scalar) to detect regressions in optimized paths.

KMAC128

Sample #1

  • Key: 32 bytes (40 41 42 ... 5F)
  • Input: 00 01 02 03 (4 bytes)
  • S: "" (empty)
  • Output length: 32 bytes
  • Output: E5 78 0B 0D 3E A6 F7 D3 A4 29 C5 70 6A A4 3A 00 FA DB D7 D4 96 28 83 9E 31 87 24 3F 45 6E E1 4E

Sample #2

  • Key: 32 bytes (40 41 42 ... 5F)
  • Input: 00 01 02 03 (4 bytes)
  • S: "My Tagged Application"
  • Output length: 32 bytes
  • Output: 3B 1F BA 96 3C D8 B0 B5 9E 8C 1A 6D 71 88 8B 71 43 65 1A F8 BA 0A 70 70 C0 97 9E 28 11 32 4A A5

KMAC256

Sample #4 (NIST SP 800-185)

  • Key: 32 bytes (40 41 42 ... 5F)
  • Input: 00 01 02 03 (4 bytes)
  • S: "My Tagged Application"
  • Output length: 64 bytes
  • Output: 20 C5 70 C3 13 46 F7 03 C9 AC 36 C6 1C 03 CB 64 C3 97 0D 0C FC 78 7E 9B 79 59 9D 27 3A 68 D2 F7 F6 9D 4C C3 DE 9D 10 4A 35 16 89 F2 7C F6 F5 95 1F 01 03 F3 3F 4F 24 87 10 24 D9 C2 77 73 A8 DD

Sample #5 (NIST SP 800-185)

  • Key: 32 bytes (40 41 42 ... 5F)
  • Input: 200 bytes (00 01 02 ... C7)
  • S: "My Tagged Application"
  • Output length: 64 bytes
  • Output: Verified against BouncyCastle and .NET 9+ reference implementations

References

  1. NIST SP 800-185: https://doi.org/10.6028/NIST.SP.800-185
  2. Keccak Team: https://keccak.team/
  3. SHA-3 Standard (FIPS 202): https://doi.org/10.6028/NIST.FIPS.202