ACVP: test SHAKE. This is considered a component of ML-* and ACVP doesn't trust its testing of ML-* in isolation. Change-Id: I0d5c2a4c9e10b2c6e9b6cf404369f38427faa269 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/97828 Commit-Queue: Adam Langley <agl@google.com> Reviewed-by: David Benjamin <davidben@google.com> SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com>
diff --git a/util/fipstools/acvp/acvptool/subprocess/shake.go b/util/fipstools/acvp/acvptool/subprocess/shake.go index e826429..6c9e91f 100644 --- a/util/fipstools/acvp/acvptool/subprocess/shake.go +++ b/util/fipstools/acvp/acvptool/subprocess/shake.go
@@ -100,12 +100,12 @@ switch group.Type { case "AFT": - // "AFTs all produce a single digest size, matching the security strength of the extendable output function." - if test.BitOutLength != uint32(h.size*8) { - return nil, fmt.Errorf("AFT test case %d/%d has bit length %d but expected %d", group.ID, test.ID, test.BitOutLength, h.size*8) + outLenBytes := test.BitOutLength / 8 + if test.BitOutLength == 0 { + outLenBytes = uint32(h.size) } - m.TransactAsync(h.algo, 1, [][]byte{msg, uint32le(test.BitOutLength / 8)}, func(result [][]byte) error { + m.TransactAsync(h.algo, 1, [][]byte{msg, uint32le(outLenBytes)}, func(result [][]byte) error { response.Tests = append(response.Tests, shakeTestResponse{ ID: test.ID, DigestHex: hex.EncodeToString(result[0]),
diff --git a/util/fipstools/acvp/acvptool/test/expected/SHAKE-128.bz2 b/util/fipstools/acvp/acvptool/test/expected/SHAKE-128.bz2 index 2eb06d2..072d161 100644 --- a/util/fipstools/acvp/acvptool/test/expected/SHAKE-128.bz2 +++ b/util/fipstools/acvp/acvptool/test/expected/SHAKE-128.bz2 Binary files differ
diff --git a/util/fipstools/acvp/acvptool/test/expected/SHAKE-256.bz2 b/util/fipstools/acvp/acvptool/test/expected/SHAKE-256.bz2 index 1d27d71..bff0106 100644 --- a/util/fipstools/acvp/acvptool/test/expected/SHAKE-256.bz2 +++ b/util/fipstools/acvp/acvptool/test/expected/SHAKE-256.bz2 Binary files differ
diff --git a/util/fipstools/acvp/acvptool/test/tests.json b/util/fipstools/acvp/acvptool/test/tests.json index bc7cecb..af41452 100644 --- a/util/fipstools/acvp/acvptool/test/tests.json +++ b/util/fipstools/acvp/acvptool/test/tests.json
@@ -38,8 +38,8 @@ {"Wrapper": "modulewrapper", "In": "vectors/TLS13.bz2", "Out": "expected/TLS13.bz2"}, {"Wrapper": "testmodulewrapper", "In": "vectors/PBKDF.bz2", "Out": "expected/PBKDF.bz2"}, {"Wrapper": "testmodulewrapper", "In": "vectors/EDDSA.bz2", "Out": "expected/EDDSA.bz2"}, -{"Wrapper": "testmodulewrapper", "In": "vectors/SHAKE-128.bz2", "Out": "expected/SHAKE-128.bz2"}, -{"Wrapper": "testmodulewrapper", "In": "vectors/SHAKE-256.bz2", "Out": "expected/SHAKE-256.bz2"}, +{"Wrapper": "modulewrapper", "In": "vectors/SHAKE-128.bz2", "Out": "expected/SHAKE-128.bz2"}, +{"Wrapper": "modulewrapper", "In": "vectors/SHAKE-256.bz2", "Out": "expected/SHAKE-256.bz2"}, {"Wrapper": "testmodulewrapper", "In": "vectors/cSHAKE-128.bz2", "Out": "expected/cSHAKE-128.bz2"}, {"Wrapper": "testmodulewrapper", "In": "vectors/cSHAKE-256.bz2", "Out": "expected/cSHAKE-256.bz2"} ]
diff --git a/util/fipstools/acvp/acvptool/test/vectors/SHAKE-128.bz2 b/util/fipstools/acvp/acvptool/test/vectors/SHAKE-128.bz2 index 9b21c31..737ac89 100644 --- a/util/fipstools/acvp/acvptool/test/vectors/SHAKE-128.bz2 +++ b/util/fipstools/acvp/acvptool/test/vectors/SHAKE-128.bz2 Binary files differ
diff --git a/util/fipstools/acvp/acvptool/test/vectors/SHAKE-256.bz2 b/util/fipstools/acvp/acvptool/test/vectors/SHAKE-256.bz2 index 3617de6..f64a12b 100644 --- a/util/fipstools/acvp/acvptool/test/vectors/SHAKE-256.bz2 +++ b/util/fipstools/acvp/acvptool/test/vectors/SHAKE-256.bz2 Binary files differ
diff --git a/util/fipstools/acvp/modulewrapper/modulewrapper.cc b/util/fipstools/acvp/modulewrapper/modulewrapper.cc index ec0d279..8fffbac 100644 --- a/util/fipstools/acvp/modulewrapper/modulewrapper.cc +++ b/util/fipstools/acvp/modulewrapper/modulewrapper.cc
@@ -50,6 +50,7 @@ #include "../../../../crypto/bytestring/internal.h" #include "../../../../crypto/fipsmodule/bcm_interface.h" #include "../../../../crypto/fipsmodule/ec/internal.h" +#include "../../../../crypto/fipsmodule/keccak/internal.h" #include "../../../../crypto/fipsmodule/rand/internal.h" #include "../../../../crypto/fipsmodule/tls/internal.h" #include "modulewrapper.h" @@ -58,6 +59,9 @@ BSSL_NAMESPACE_BEGIN namespace acvp { +// A general sanity limit for values that are expected to be small. +constexpr size_t kMaxSize = 128 * 1024 * 1024; + #if defined(OPENSSL_TRUSTY) #include <trusty_log.h> #define LOG_ERROR(...) TLOGE(__VA_ARGS__) @@ -117,6 +121,26 @@ }] }, { + "algorithm": "SHAKE-128", + "revision": "1.0", + "inBit": false, + "inEmpty": true, + "outBit": false, + "outputLen": [{ + "min": 16, "max": 65536, "increment": 8 + }] + }, + { + "algorithm": "SHAKE-256", + "revision": "1.0", + "inBit": false, + "inEmpty": true, + "outBit": false, + "outputLen": [{ + "min": 16, "max": 65536, "increment": 8 + }] + }, + { "algorithm": "ACVP-AES-ECB", "revision": "1.0", "direction": ["encrypt", "decrypt"], @@ -919,6 +943,75 @@ return write_reply({Span(buf).subspan(2 * DigestLength, DigestLength)}); } +template <enum boringssl_keccak_config_t Config> +static bool SHAKE(const Span<const uint8_t> args[], ReplyCallback write_reply) { + const Span<const uint8_t> msg = args[0]; + if (args[1].size() != sizeof(uint32_t)) { + LOG_ERROR("Wrong length for SHAKE output length.\n"); + return false; + } + const uint32_t out_len = CRYPTO_load_u32_le(args[1].data()); + if (out_len > kMaxSize) { + LOG_ERROR("SHAKE output length too large.\n"); + return false; + } + + std::vector<uint8_t> out(out_len); + BORINGSSL_keccak(out.data(), out.size(), msg.data(), msg.size(), Config); + return write_reply({Span<const uint8_t>(out)}); +} + +// Monte Carlo Test for SHAKE. See +// https://pages.nist.gov/ACVP/draft-celi-acvp-sha3.html#name-shake-monte-carlo-test +template <enum boringssl_keccak_config_t Config> +static bool SHAKEMCT(const Span<const uint8_t> args[], + ReplyCallback write_reply) { + if (args[1].size() != sizeof(uint32_t) || + args[2].size() != sizeof(uint32_t) || + args[3].size() != sizeof(uint32_t)) { + LOG_ERROR("Bad parameter length for SHAKE MCT.\n"); + return false; + } + const uint32_t min_out_len = CRYPTO_load_u32_le(args[1].data()); + const uint32_t max_out_len = CRYPTO_load_u32_le(args[2].data()); + uint32_t out_len = CRYPTO_load_u32_le(args[3].data()); + + if (max_out_len < min_out_len || out_len > kMaxSize || + max_out_len > kMaxSize) { + LOG_ERROR("Bad SHAKE MCT lengths: %u %u %u.\n", min_out_len, max_out_len, + out_len); + return false; + } + const uint32_t range = max_out_len - min_out_len + 1; + + std::vector<uint8_t> md(args[0].begin(), args[0].end()); + for (size_t i = 0; i < 1000; i++) { + // The message is the leftmost 128 bits of the previous output, zero-padded + // if the previous output was shorter. + uint8_t msg[16] = {0}; + if (!md.empty()) { + memcpy(msg, md.data(), std::min(md.size(), sizeof(msg))); + } + + md.resize(out_len); + BORINGSSL_keccak(md.data(), md.size(), msg, sizeof(msg), Config); + + // The output length for the next iteration is derived from the rightmost + // 16 bits of the output. + uint16_t rightmost = 0; + if (out_len >= 2) { + rightmost = CRYPTO_load_u16_be(&md[out_len - 2]); + } else if (out_len == 1) { + rightmost = md[0]; + } + out_len = min_out_len + (rightmost % range); + } + + uint8_t out_len_bytes[sizeof(out_len)]; + CRYPTO_store_u32_le(out_len_bytes, out_len); + return write_reply({Span<const uint8_t>(md), MakeConstSpan(out_len_bytes)}); +} + static uint32_t GetIterations(const Span<const uint8_t> iterations_bytes) { uint32_t iterations; if (iterations_bytes.size() != sizeof(iterations)) { @@ -2397,6 +2490,12 @@ {"SHA2-384/MCT", 1, HashMCT<SHA384, SHA384_DIGEST_LENGTH>}, {"SHA2-512/MCT", 1, HashMCT<SHA512, SHA512_DIGEST_LENGTH>}, {"SHA2-512/256/MCT", 1, HashMCT<SHA512_256, SHA512_256_DIGEST_LENGTH>}, + {"SHAKE-128", 2, SHAKE<boringssl_shake128>}, + {"SHAKE-128/VOT", 2, SHAKE<boringssl_shake128>}, + {"SHAKE-128/MCT", 4, SHAKEMCT<boringssl_shake128>}, + {"SHAKE-256", 2, SHAKE<boringssl_shake256>}, + {"SHAKE-256/VOT", 2, SHAKE<boringssl_shake256>}, + {"SHAKE-256/MCT", 4, SHAKEMCT<boringssl_shake256>}, {"AES/encrypt", 3, AES<AES_set_encrypt_key, AES_encrypt>}, {"AES/decrypt", 3, AES<AES_set_decrypt_key, AES_decrypt>}, {"AES-CBC/encrypt", 4, AES_CBC<AES_set_encrypt_key, AES_ENCRYPT>},