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 | ⬜ 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
- 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