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>},