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 ✅ Implemented ParallelHash (static), IncrementalParallelHash
ParallelHash256 ✅ Implemented ParallelHash (static), IncrementalParallelHash

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

ParallelHash

Definition

ParallelHash is a tree-hashing construction that splits input into fixed-size blocks, hashes each block independently with SHAKE128 or SHAKE256, then finalizes with cSHAKE. This allows parallel computation across blocks and is suitable for large-input scenarios.

Parameters

Variant Inner hash Finalization Chaining value Security
ParallelHash128 SHAKE128 (256-bit output) cSHAKE128 32 bytes 128 bits
ParallelHash256 SHAKE256 (512-bit output) cSHAKE256 64 bytes 256 bits

Algorithm (Appendix A.4 KECCAK-direct form)

ParallelHash128(X, B, L, S):
  n = max(1, ⌈len(X) / B⌉)
  z = left_encode(B)
  for i = 0 to n−1:
    z = z || KECCAK[256](X[i*B … (i+1)*B−1] || 1111, 256)  -- SHAKE128 inner block
  z = z || right_encode(n) || right_encode(L)
  T = bytepad(encode_string("ParallelHash") || encode_string(S), 168)
  return KECCAK[256](T || z || 00, L)                        -- cSHAKE128 finalization

KECCAK[256](M || 1111, n) is SHAKE128 (domain separator 0x1F).
KECCAK[256](T || M || 00, L) is cSHAKE128 (domain separator 0x04 when T is non-empty).
ParallelHash256 uses KECCAK[512] (SHAKE256 / cSHAKE256, rate 136 bytes) with the same structure.

Test Vectors (NIST SP 800-185 Sample Data)

ParallelHash128 Sample #1

Input:    000102030405060710111213141516172021222324252627 (24 bytes)
B:        8 bytes
S:        "" (empty)
L:        256 bits (32 bytes)
Output:   BA8DC1D1D979331D3F813603C67F72609AB5E44B94A0B8F9AF46514454A2B4F5

ParallelHash128 Sample #2

Input:    000102030405060710111213141516172021222324252627 (24 bytes)
B:        8 bytes
S:        "Parallel Data"
L:        256 bits (32 bytes)
Output:   FC484DCB3F84DCEEDC353438151BEE58157D6EFED0445A81F165E495795B7206

ParallelHash128 Sample #3

Input:    000102030405060708090A0B101112131415161718191A1B
          202122232425262728292A2B303132333435363738393A3B
          404142434445464748494A4B505152535455565758595A5B
          606162636465666768696A6B707172737475767778797A7B (72 bytes)
B:        12 bytes
S:        "Parallel Data"
L:        256 bits (32 bytes)
Output:   F7FD5312896C6685C828AF7E2ADB97E393E7F8D54E3C2EA4B95E5ACA3796E8FC

ParallelHash256 Sample #1

Input:    000102030405060710111213141516172021222324252627 (24 bytes)
B:        8 bytes
S:        "" (empty)
L:        512 bits (64 bytes)
Output:   BC1EF124DA34495E948EAD207DD9842235DA432D2BBC54B4C110E64C45110553
          1B7F2A3E0CE055C02805E7C2DE1FB746AF97A1DD01F43B824E31B87612410429

ParallelHash256 Sample #2

Input:    000102030405060710111213141516172021222324252627 (24 bytes)
B:        8 bytes
S:        "Parallel Data"
L:        512 bits (64 bytes)
Output:   CDF15289B54F6212B4BC270528B49526006DD9B54E2B6ADD1EF6900DDA3963BB
          33A72491F236969CA8AFAEA29C682D47A393C065B38E29FAE651A2091C833110

ParallelHash256 Sample #3

Input:    000102030405060708090A0B101112131415161718191A1B
          202122232425262728292A2B303132333435363738393A3B
          404142434445464748494A4B505152535455565758595A5B
          606162636465666768696A6B707172737475767778797A7B (72 bytes)
B:        12 bytes
S:        "Parallel Data"
L:        512 bits (64 bytes)
Output:   69D0FCB764EA055DD09334BC6021CB7E4B61348DFF375DA262671CDEC3EFFA8D
          1B4568A6CCE16B1CAD946DDDE27F6CE2B8DEE4CD1B24851EBF00EB90D43813E9

All 6 NIST sample vectors are verified in tests/Security/Cryptography/Hash/ParallelHashTests.cs.


  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