crypto/internal/fips140test: add cSHAKE ACVP tests

Adds ACVP test coverage for the SP 800-185 cSHAKE-128 and cSHAKE-256
algorithms based on the NIST spec:

  https://pages.nist.gov/ACVP/draft-celi-acvp-xof.html

Updates #69642

Change-Id: I4a6ef9a99dfe520f3177e0e7c258326475690f5f
Reviewed-on: https://go-review.googlesource.com/c/go/+/648455
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
This commit is contained in:
Daniel McCarney 2025-01-23 14:34:46 -05:00 committed by Gopher Robot
parent 102406edbf
commit b941d2b6d8
3 changed files with 96 additions and 1 deletions

View file

@ -13,6 +13,8 @@
{"algorithm":"SHAKE-128","inBit":false,"outBit":false,"inEmpty":true,"outputLen":[{"min":16,"max":65536,"increment":8}],"revision":"1.0"},
{"algorithm":"SHAKE-256","inBit":false,"outBit":false,"inEmpty":true,"outputLen":[{"min":16,"max":65536,"increment":8}],"revision":"1.0"},
{"algorithm":"cSHAKE-128","hexCustomization":false,"outputLen":[{"min":16,"max":65536,"increment":8}],"msgLen":[{"min":0,"max":65536,"increment":8}],"revision":"1.0"},
{"algorithm":"cSHAKE-256","hexCustomization":false,"outputLen":[{"min":16,"max":65536,"increment":8}],"msgLen":[{"min":0,"max":65536,"increment":8}],"revision":"1.0"},
{"algorithm":"HMAC-SHA2-224","keyLen":[{"increment":8,"max":524288,"min":8}],"macLen":[{"increment":8,"max":224,"min":32}],"revision":"1.0"},
{"algorithm":"HMAC-SHA2-256","keyLen":[{"increment":8,"max":524288,"min":8}],"macLen":[{"increment":8,"max":256,"min":32}],"revision":"1.0"},

View file

@ -13,6 +13,8 @@
{"Wrapper": "go", "In": "vectors/SHAKE-128.bz2", "Out": "expected/SHAKE-128.bz2"},
{"Wrapper": "go", "In": "vectors/SHAKE-256.bz2", "Out": "expected/SHAKE-256.bz2"},
{"Wrapper": "go", "In": "vectors/cSHAKE-128.bz2", "Out": "expected/cSHAKE-128.bz2"},
{"Wrapper": "go", "In": "vectors/cSHAKE-256.bz2", "Out": "expected/cSHAKE-256.bz2"},
{"Wrapper": "go", "In": "vectors/HMAC-SHA2-224.bz2", "Out": "expected/HMAC-SHA2-224.bz2"},
{"Wrapper": "go", "In": "vectors/HMAC-SHA2-256.bz2", "Out": "expected/HMAC-SHA2-256.bz2"},

View file

@ -105,6 +105,8 @@ var (
// https://pages.nist.gov/ACVP/draft-celi-acvp-sha.html#section-7.2
// SHA3 and SHAKE algorithm capabilities:
// https://pages.nist.gov/ACVP/draft-celi-acvp-sha3.html#name-sha3-and-shake-algorithm-ca
// cSHAKE algorithm capabilities:
// https://pages.nist.gov/ACVP/draft-celi-acvp-xof.html#section-7.2
// HMAC algorithm capabilities:
// https://pages.nist.gov/ACVP/draft-fussell-acvp-mac.html#section-7
// PBKDF2 algorithm capabilities:
@ -179,6 +181,11 @@ var (
"SHAKE-256/VOT": cmdShakeAftVot(sha3.NewShake256()),
"SHAKE-256/MCT": cmdShakeMct(sha3.NewShake256()),
"cSHAKE-128": cmdCShakeAft(func(N, S []byte) *sha3.SHAKE { return sha3.NewCShake128(N, S) }),
"cSHAKE-128/MCT": cmdCShakeMct(func(N, S []byte) *sha3.SHAKE { return sha3.NewCShake128(N, S) }),
"cSHAKE-256": cmdCShakeAft(func(N, S []byte) *sha3.SHAKE { return sha3.NewCShake256(N, S) }),
"cSHAKE-256/MCT": cmdCShakeMct(func(N, S []byte) *sha3.SHAKE { return sha3.NewCShake256(N, S) }),
"HMAC-SHA2-224": cmdHmacAft(func() fips140.Hash { return sha256.New224() }),
"HMAC-SHA2-256": cmdHmacAft(func() fips140.Hash { return sha256.New() }),
"HMAC-SHA2-384": cmdHmacAft(func() fips140.Hash { return sha512.New384() }),
@ -609,6 +616,90 @@ func cmdShakeMct(h *sha3.SHAKE) command {
}
}
func cmdCShakeAft(hFn func(N, S []byte) *sha3.SHAKE) command {
return command{
requiredArgs: 4, // Message, output length bytes, function name, customization
handler: func(args [][]byte) ([][]byte, error) {
msg := args[0]
outLenBytes := binary.LittleEndian.Uint32(args[1])
functionName := args[2]
customization := args[3]
h := hFn(functionName, customization)
h.Write(msg)
out := make([]byte, outLenBytes)
h.Read(out)
return [][]byte{out}, nil
},
}
}
func cmdCShakeMct(hFn func(N, S []byte) *sha3.SHAKE) command {
return command{
requiredArgs: 6, // Message, min output length (bits), max output length (bits), output length (bits), increment (bits), customization
handler: func(args [][]byte) ([][]byte, error) {
message := args[0]
minOutLenBytes := binary.LittleEndian.Uint32(args[1])
maxOutLenBytes := binary.LittleEndian.Uint32(args[2])
outputLenBytes := binary.LittleEndian.Uint32(args[3])
incrementBytes := binary.LittleEndian.Uint32(args[4])
customization := args[5]
if outputLenBytes < 2 {
return nil, fmt.Errorf("invalid output length: %d", outputLenBytes)
}
rangeBits := (maxOutLenBytes*8 - minOutLenBytes*8) + 1
if rangeBits == 0 {
return nil, fmt.Errorf("invalid maxOutLenBytes and minOutLenBytes: %d, %d", maxOutLenBytes, minOutLenBytes)
}
// cSHAKE Monte Carlo test inner loop:
// https://pages.nist.gov/ACVP/draft-celi-acvp-xof.html#section-6.2.1
for i := 0; i < 1000; i++ {
// InnerMsg = Left(Output[i-1] || ZeroBits(128), 128);
boundary := min(len(message), 16)
innerMsg := make([]byte, 16)
copy(innerMsg, message[:boundary])
// Output[i] = CSHAKE(InnerMsg, OutputLen, FunctionName, Customization);
h := hFn(nil, customization) // Note: function name fixed to "" for MCT.
h.Write(innerMsg)
digest := make([]byte, outputLenBytes)
h.Read(digest)
message = digest
// Rightmost_Output_bits = Right(Output[i], 16);
rightmostOutput := digest[outputLenBytes-2:]
// IMPORTANT: the specification says:
// NOTE: For the "Rightmost_Output_bits % Range" operation, the Rightmost_Output_bits bit string
// should be interpretted as a little endian-encoded number.
// This is **a lie**! It has to be interpreted as a big-endian number.
rightmostOutputBE := binary.BigEndian.Uint16(rightmostOutput)
// OutputLen = MinOutLen + (floor((Rightmost_Output_bits % Range) / OutLenIncrement) * OutLenIncrement);
incrementBits := incrementBytes * 8
outputLenBits := (minOutLenBytes * 8) + (((uint32)(rightmostOutputBE)%rangeBits)/incrementBits)*incrementBits
outputLenBytes = outputLenBits / 8
// Customization = BitsToString(InnerMsg || Rightmost_Output_bits);
msgWithBits := append(innerMsg, rightmostOutput...)
customization = make([]byte, len(msgWithBits))
for i, b := range msgWithBits {
customization[i] = (b % 26) + 65
}
}
encodedOutputLenBytes := make([]byte, 4)
binary.LittleEndian.PutUint32(encodedOutputLenBytes, outputLenBytes)
return [][]byte{message, encodedOutputLenBytes, customization}, nil
},
}
}
func cmdHmacAft(h func() fips140.Hash) command {
return command{
requiredArgs: 2, // Message and key
@ -1973,7 +2064,7 @@ func TestACVP(t *testing.T) {
bsslModule = "boringssl.googlesource.com/boringssl.git"
bsslVersion = "v0.0.0-20250207174145-0bb19f6126cb"
goAcvpModule = "github.com/cpu/go-acvp"
goAcvpVersion = "v0.0.0-20250117180340-0406d83a4b0d"
goAcvpVersion = "v0.0.0-20250126154732-de1ba727a0be"
)
// In crypto/tls/bogo_shim_test.go the test is skipped if run on a builder with runtime.GOOS == "windows"