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
- Document: NIST SP 800-185
- Title: SHA-3 Derived Functions: cSHAKE, KMAC, TupleHash, and ParallelHash
- URL: https://csrc.nist.gov/publications/detail/sp/800-185/final
- PDF: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf
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.
- NIST SP 800-185: https://doi.org/10.6028/NIST.SP.800-185
- Keccak Team: https://keccak.team/
- SHA-3 Standard (FIPS 202): https://doi.org/10.6028/NIST.FIPS.202