Add ACVP modulewrapper for the jitter entropy SHA-384 implementation.

Change-Id: Ie07a1ad744a2eb6df3fc4d2b69d40222a74c5416
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/82167
Auto-Submit: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1e086c5..23bd9bb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -706,6 +706,8 @@
 if(FIPS)
   add_executable(modulewrapper ${MODULEWRAPPER_SOURCES})
   target_link_libraries(modulewrapper crypto)
+  add_executable(entropy_modulewrapper ${ENTROPY_MODULEWRAPPER_SOURCES})
+  target_link_libraries(entropy_modulewrapper crypto)
 endif()
 
 add_executable(bssl ${BSSL_SOURCES})
diff --git a/build.json b/build.json
index 757b6cb..ac6a935 100644
--- a/build.json
+++ b/build.json
@@ -1082,7 +1082,18 @@
     "modulewrapper": {
         "srcs": [
             "util/fipstools/acvp/modulewrapper/main.cc",
-            "util/fipstools/acvp/modulewrapper/modulewrapper.cc"
+            "util/fipstools/acvp/modulewrapper/modulewrapper.cc",
+            "util/fipstools/acvp/modulewrapper/proto.cc"
+        ],
+        "internal_hdrs": [
+            "util/fipstools/acvp/modulewrapper/modulewrapper.h"
+        ]
+    },
+    "entropy_modulewrapper": {
+        "srcs": [
+            "util/fipstools/acvp/entropy_modulewrapper/main.cc",
+            "util/fipstools/acvp/entropy_modulewrapper/modulewrapper.cc",
+            "util/fipstools/acvp/modulewrapper/proto.cc"
         ],
         "internal_hdrs": [
             "util/fipstools/acvp/modulewrapper/modulewrapper.h"
diff --git a/gen/sources.bzl b/gen/sources.bzl
index cb6ded9..7f77cf0 100644
--- a/gen/sources.bzl
+++ b/gen/sources.bzl
@@ -1140,6 +1140,16 @@
     "decrepit/xts/xts_test.cc",
 ]
 
+entropy_modulewrapper_sources = [
+    "util/fipstools/acvp/entropy_modulewrapper/main.cc",
+    "util/fipstools/acvp/entropy_modulewrapper/modulewrapper.cc",
+    "util/fipstools/acvp/modulewrapper/proto.cc",
+]
+
+entropy_modulewrapper_internal_headers = [
+    "util/fipstools/acvp/modulewrapper/modulewrapper.h",
+]
+
 fuzz_sources = [
     "fuzz/arm_cpuinfo.cc",
     "fuzz/bn_div.cc",
@@ -1180,6 +1190,7 @@
 modulewrapper_sources = [
     "util/fipstools/acvp/modulewrapper/main.cc",
     "util/fipstools/acvp/modulewrapper/modulewrapper.cc",
+    "util/fipstools/acvp/modulewrapper/proto.cc",
 ]
 
 modulewrapper_internal_headers = [
diff --git a/gen/sources.cmake b/gen/sources.cmake
index b0311e8..32895a8 100644
--- a/gen/sources.cmake
+++ b/gen/sources.cmake
@@ -1173,6 +1173,20 @@
 )
 
 set(
+  ENTROPY_MODULEWRAPPER_SOURCES
+
+  util/fipstools/acvp/entropy_modulewrapper/main.cc
+  util/fipstools/acvp/entropy_modulewrapper/modulewrapper.cc
+  util/fipstools/acvp/modulewrapper/proto.cc
+)
+
+set(
+  ENTROPY_MODULEWRAPPER_INTERNAL_HEADERS
+
+  util/fipstools/acvp/modulewrapper/modulewrapper.h
+)
+
+set(
   FUZZ_SOURCES
 
   fuzz/arm_cpuinfo.cc
@@ -1216,6 +1230,7 @@
 
   util/fipstools/acvp/modulewrapper/main.cc
   util/fipstools/acvp/modulewrapper/modulewrapper.cc
+  util/fipstools/acvp/modulewrapper/proto.cc
 )
 
 set(
diff --git a/gen/sources.gni b/gen/sources.gni
index ece0757..93f0e69 100644
--- a/gen/sources.gni
+++ b/gen/sources.gni
@@ -1140,6 +1140,16 @@
   "decrepit/xts/xts_test.cc",
 ]
 
+entropy_modulewrapper_sources = [
+  "util/fipstools/acvp/entropy_modulewrapper/main.cc",
+  "util/fipstools/acvp/entropy_modulewrapper/modulewrapper.cc",
+  "util/fipstools/acvp/modulewrapper/proto.cc",
+]
+
+entropy_modulewrapper_internal_headers = [
+  "util/fipstools/acvp/modulewrapper/modulewrapper.h",
+]
+
 fuzz_sources = [
   "fuzz/arm_cpuinfo.cc",
   "fuzz/bn_div.cc",
@@ -1180,6 +1190,7 @@
 modulewrapper_sources = [
   "util/fipstools/acvp/modulewrapper/main.cc",
   "util/fipstools/acvp/modulewrapper/modulewrapper.cc",
+  "util/fipstools/acvp/modulewrapper/proto.cc",
 ]
 
 modulewrapper_internal_headers = [
diff --git a/gen/sources.json b/gen/sources.json
index 3d59d7b..7336522 100644
--- a/gen/sources.json
+++ b/gen/sources.json
@@ -1121,6 +1121,16 @@
       "decrepit/xts/xts_test.cc"
     ]
   },
+  "entropy_modulewrapper": {
+    "srcs": [
+      "util/fipstools/acvp/entropy_modulewrapper/main.cc",
+      "util/fipstools/acvp/entropy_modulewrapper/modulewrapper.cc",
+      "util/fipstools/acvp/modulewrapper/proto.cc"
+    ],
+    "internal_hdrs": [
+      "util/fipstools/acvp/modulewrapper/modulewrapper.h"
+    ]
+  },
   "fuzz": {
     "srcs": [
       "fuzz/arm_cpuinfo.cc",
@@ -1162,7 +1172,8 @@
   "modulewrapper": {
     "srcs": [
       "util/fipstools/acvp/modulewrapper/main.cc",
-      "util/fipstools/acvp/modulewrapper/modulewrapper.cc"
+      "util/fipstools/acvp/modulewrapper/modulewrapper.cc",
+      "util/fipstools/acvp/modulewrapper/proto.cc"
     ],
     "internal_hdrs": [
       "util/fipstools/acvp/modulewrapper/modulewrapper.h"
diff --git a/gen/sources.mk b/gen/sources.mk
index e58826e..5feb9ce 100644
--- a/gen/sources.mk
+++ b/gen/sources.mk
@@ -1124,6 +1124,14 @@
   decrepit/ripemd/ripemd_test.cc \
   decrepit/xts/xts_test.cc
 
+boringssl_entropy_modulewrapper_sources := \
+  util/fipstools/acvp/entropy_modulewrapper/main.cc \
+  util/fipstools/acvp/entropy_modulewrapper/modulewrapper.cc \
+  util/fipstools/acvp/modulewrapper/proto.cc
+
+boringssl_entropy_modulewrapper_internal_headers := \
+  util/fipstools/acvp/modulewrapper/modulewrapper.h
+
 boringssl_fuzz_sources := \
   fuzz/arm_cpuinfo.cc \
   fuzz/bn_div.cc \
@@ -1162,7 +1170,8 @@
 
 boringssl_modulewrapper_sources := \
   util/fipstools/acvp/modulewrapper/main.cc \
-  util/fipstools/acvp/modulewrapper/modulewrapper.cc
+  util/fipstools/acvp/modulewrapper/modulewrapper.cc \
+  util/fipstools/acvp/modulewrapper/proto.cc
 
 boringssl_modulewrapper_internal_headers := \
   util/fipstools/acvp/modulewrapper/modulewrapper.h
diff --git a/util/fipstools/acvp/entropy_modulewrapper/main.cc b/util/fipstools/acvp/entropy_modulewrapper/main.cc
new file mode 100644
index 0000000..6f179f3
--- /dev/null
+++ b/util/fipstools/acvp/entropy_modulewrapper/main.cc
@@ -0,0 +1,41 @@
+// Copyright 2025 The BoringSSL Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../modulewrapper/modulewrapper.h"
+
+
+int main(int argc, char **argv) {
+  if (argc != 1 && strcmp(argv[1], "--version") == 0) {
+    printf("Built for architecture: ");
+
+#if defined(OPENSSL_X86_64)
+    puts("x86-64 (64-bit)");
+#elif defined(OPENSSL_ARM)
+    puts("ARM (32-bit)");
+#elif defined(OPENSSL_AARCH64)
+    puts("aarch64 (64-bit)");
+#else
+#error "FIPS build not supported on this architecture"
+#endif
+    return 0;
+  } else if (argc != 1) {
+    fprintf(stderr, "Usage: %s [--version]\n", argv[0]);
+    return 4;
+  }
+
+  return bssl::acvp::RunModuleWrapper();
+}
diff --git a/util/fipstools/acvp/entropy_modulewrapper/modulewrapper.cc b/util/fipstools/acvp/entropy_modulewrapper/modulewrapper.cc
new file mode 100644
index 0000000..16c8892
--- /dev/null
+++ b/util/fipstools/acvp/entropy_modulewrapper/modulewrapper.cc
@@ -0,0 +1,111 @@
+// Copyright 2025 The BoringSSL Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <memory>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include <string.h>
+
+#include "../../../../crypto/fipsmodule/entropy/sha512.cc.inc"
+#include "../modulewrapper/modulewrapper.h"
+
+
+namespace bssl {
+namespace acvp {
+
+static bool GetConfig(const Span<const uint8_t> args[],
+                      ReplyCallback write_reply) {
+  static constexpr char kConfig[] =
+      R"([
+      {
+        "algorithm": "SHA2-384",
+        "revision": "1.0",
+        "messageLength": [{
+          "min": 0, "max": 65528, "increment": 8
+        }]
+      }
+    ])";
+  return write_reply({bssl::StringAsBytes(kConfig)});
+}
+
+constexpr size_t DigestLength = 48;
+
+static void HashSHA384(uint8_t *out_digest, Span<const uint8_t> input) {
+  bssl::entropy::SHA512_CTX ctx;
+  bssl::entropy::SHA384_Init(&ctx);
+  bssl::entropy::SHA384_Update(&ctx, input.data(), input.size());
+  bssl::entropy::SHA384_Final(out_digest, &ctx);
+}
+
+static bool SHA384(const Span<const uint8_t> args[],
+                   ReplyCallback write_reply) {
+  uint8_t digest[DigestLength];
+  HashSHA384(digest, args[0]);
+  return write_reply({Span<const uint8_t>(digest)});
+}
+
+static bool SHA384MCT(const Span<const uint8_t> args[],
+                      ReplyCallback write_reply) {
+  if (args[0].size() != DigestLength) {
+    return false;
+  }
+
+  uint8_t buf[DigestLength * 3];
+  memcpy(buf, args[0].data(), DigestLength);
+  memcpy(buf + DigestLength, args[0].data(), DigestLength);
+  memcpy(buf + 2 * DigestLength, args[0].data(), DigestLength);
+
+  for (size_t i = 0; i < 1000; i++) {
+    uint8_t digest[DigestLength];
+    HashSHA384(digest, buf);
+    memmove(buf, buf + DigestLength, DigestLength * 2);
+    memcpy(buf + DigestLength * 2, digest, DigestLength);
+  }
+
+  return write_reply({Span(buf).subspan(2 * DigestLength, DigestLength)});
+}
+
+static constexpr struct {
+  char name[kMaxNameLength + 1];
+  uint8_t num_expected_args;
+  bool (*handler)(const Span<const uint8_t> args[], ReplyCallback write_reply);
+} kFunctions[] = {
+    {"getConfig", 0, GetConfig},
+    {"SHA2-384", 1, SHA384},
+    {"SHA2-384/MCT", 1, SHA384MCT},
+};
+
+Handler FindHandler(Span<const Span<const uint8_t>> args) {
+  auto algorithm = bssl::BytesAsStringView(args[0]);
+  for (const auto &func : kFunctions) {
+    if (algorithm == func.name) {
+      if (args.size() - 1 != func.num_expected_args) {
+        fprintf(stderr,
+                "\'%s\' operation received %zu arguments but expected %u.\n",
+                func.name, args.size() - 1, func.num_expected_args);
+        return nullptr;
+      }
+
+      return func.handler;
+    }
+  }
+
+  fprintf(stderr, "Unknown operation: %s\n", std::string(algorithm).c_str());
+  return nullptr;
+}
+
+}  // namespace acvp
+}  // namespace bssl
diff --git a/util/fipstools/acvp/modulewrapper/main.cc b/util/fipstools/acvp/modulewrapper/main.cc
index 9a1118b..87c6634 100644
--- a/util/fipstools/acvp/modulewrapper/main.cc
+++ b/util/fipstools/acvp/modulewrapper/main.cc
@@ -15,12 +15,8 @@
 #include <inttypes.h>
 #include <stdio.h>
 #include <string.h>
-#include <unistd.h>
-#include <string>
-#include <string_view>
 
 #include <openssl/crypto.h>
-#include <openssl/span.h>
 
 #include "modulewrapper.h"
 
@@ -76,39 +72,5 @@
     return 4;
   }
 
-  // modulewrapper buffers responses to the greatest degree allowed in order to
-  // fully exercise the async handling in acvptool.
-  std::unique_ptr<bssl::acvp::RequestBuffer> buffer =
-      bssl::acvp::RequestBuffer::New();
-  const bssl::acvp::ReplyCallback write_reply = std::bind(
-      bssl::acvp::WriteReplyToFd, STDOUT_FILENO, std::placeholders::_1);
-  const bssl::acvp::ReplyCallback buffer_reply =
-      std::bind(bssl::acvp::WriteReplyToBuffer, std::placeholders::_1);
-
-  for (;;) {
-    const bssl::Span<const bssl::Span<const uint8_t>> args =
-        ParseArgsFromFd(STDIN_FILENO, buffer.get());
-    if (args.empty()) {
-      return 1;
-    }
-
-    auto name = bssl::BytesAsStringView(args[0]);
-    if (name == "flush") {
-      if (!bssl::acvp::FlushBuffer(STDOUT_FILENO)) {
-        abort();
-      }
-      continue;
-    }
-
-    const bssl::acvp::Handler handler = bssl::acvp::FindHandler(args);
-    if (!handler) {
-      return 2;
-    }
-
-    auto &reply_callback = name == "getConfig" ? write_reply : buffer_reply;
-    if (!handler(args.subspan(1).data(), reply_callback)) {
-      fprintf(stderr, "\'%s\' operation failed.\n", std::string(name).c_str());
-      return 3;
-    }
-  }
+  return bssl::acvp::RunModuleWrapper();
 }
diff --git a/util/fipstools/acvp/modulewrapper/modulewrapper.cc b/util/fipstools/acvp/modulewrapper/modulewrapper.cc
index 235d024..044b96e 100644
--- a/util/fipstools/acvp/modulewrapper/modulewrapper.cc
+++ b/util/fipstools/acvp/modulewrapper/modulewrapper.cc
@@ -19,10 +19,8 @@
 #include <vector>
 
 #include <assert.h>
-#include <errno.h>
 #include <limits.h>
 #include <string.h>
-#include <sys/uio.h>
 #include <unistd.h>
 #include <cstdarg>
 
@@ -65,212 +63,6 @@
 #define LOG_ERROR(...) fprintf(stderr, __VA_ARGS__)
 #endif  // OPENSSL_TRUSTY
 
-constexpr size_t kMaxArgLength = (1 << 20);
-
-RequestBuffer::~RequestBuffer() = default;
-
-class RequestBufferImpl : public RequestBuffer {
- public:
-  ~RequestBufferImpl() = default;
-
-  std::vector<uint8_t> buf;
-  Span<const uint8_t> args[kMaxArgs];
-};
-
-// static
-std::unique_ptr<RequestBuffer> RequestBuffer::New() {
-  return std::make_unique<RequestBufferImpl>();
-}
-
-static bool ReadAll(int fd, void *in_data, size_t data_len) {
-  uint8_t *data = reinterpret_cast<uint8_t *>(in_data);
-  size_t done = 0;
-
-  while (done < data_len) {
-    ssize_t r;
-    do {
-      r = read(fd, &data[done], data_len - done);
-    } while (r == -1 && errno == EINTR);
-
-    if (r <= 0) {
-      return false;
-    }
-
-    done += r;
-  }
-
-  return true;
-}
-
-Span<const Span<const uint8_t>> ParseArgsFromFd(int fd,
-                                                RequestBuffer *in_buffer) {
-  RequestBufferImpl *buffer = reinterpret_cast<RequestBufferImpl *>(in_buffer);
-  uint32_t nums[1 + kMaxArgs];
-  const Span<const Span<const uint8_t>> empty_span;
-
-  if (!ReadAll(fd, nums, sizeof(uint32_t) * 2)) {
-    return empty_span;
-  }
-
-  const size_t num_args = nums[0];
-  if (num_args == 0) {
-    LOG_ERROR("Invalid, zero-argument operation requested.\n");
-    return empty_span;
-  } else if (num_args > kMaxArgs) {
-    LOG_ERROR("Operation requested with %zu args, but %zu is the limit.\n",
-              num_args, kMaxArgs);
-    return empty_span;
-  }
-
-  if (num_args > 1 &&
-      !ReadAll(fd, &nums[2], sizeof(uint32_t) * (num_args - 1))) {
-    return empty_span;
-  }
-
-  size_t need = 0;
-  for (size_t i = 0; i < num_args; i++) {
-    const size_t arg_length = nums[i + 1];
-    if (i == 0 && arg_length > kMaxNameLength) {
-      LOG_ERROR("Operation with name of length %zu exceeded limit of %zu.\n",
-                arg_length, kMaxNameLength);
-      return empty_span;
-    } else if (arg_length > kMaxArgLength) {
-      LOG_ERROR(
-          "Operation with argument of length %zu exceeded limit of %zu.\n",
-          arg_length, kMaxArgLength);
-      return empty_span;
-    }
-
-    // This static_assert confirms that the following addition doesn't
-    // overflow.
-    static_assert((kMaxArgs - 1 * kMaxArgLength) + kMaxNameLength > (1 << 30),
-                  "Argument limits permit excessive messages");
-    need += arg_length;
-  }
-
-  if (need > buffer->buf.size()) {
-    size_t alloced = need + (need >> 1);
-    if (alloced < need) {
-      abort();
-    }
-    buffer->buf.resize(alloced);
-  }
-
-  if (!ReadAll(fd, buffer->buf.data(), need)) {
-    return empty_span;
-  }
-
-  size_t offset = 0;
-  for (size_t i = 0; i < num_args; i++) {
-    buffer->args[i] = Span<const uint8_t>(&buffer->buf[offset], nums[i + 1]);
-    offset += nums[i + 1];
-  }
-
-  return Span<const Span<const uint8_t>>(buffer->args, num_args);
-}
-
-// g_reply_buffer contains buffered replies which will be flushed when acvp
-// requests.
-static std::vector<uint8_t> g_reply_buffer;
-
-bool WriteReplyToBuffer(const std::vector<Span<const uint8_t>> &spans) {
-  if (spans.size() > kMaxArgs) {
-    abort();
-  }
-
-  uint8_t buf[4];
-  CRYPTO_store_u32_le(buf, spans.size());
-  g_reply_buffer.insert(g_reply_buffer.end(), buf, buf + sizeof(buf));
-  for (const auto &span : spans) {
-    CRYPTO_store_u32_le(buf, span.size());
-    g_reply_buffer.insert(g_reply_buffer.end(), buf, buf + sizeof(buf));
-  }
-  for (const auto &span : spans) {
-    g_reply_buffer.insert(g_reply_buffer.end(), span.begin(), span.end());
-  }
-
-  return true;
-}
-
-bool FlushBuffer(int fd) {
-  size_t done = 0;
-
-  while (done < g_reply_buffer.size()) {
-    ssize_t n;
-    do {
-      n = write(fd, g_reply_buffer.data() + done, g_reply_buffer.size() - done);
-    } while (n < 0 && errno == EINTR);
-
-    if (n < 0) {
-      return false;
-    }
-    done += static_cast<size_t>(n);
-  }
-
-  g_reply_buffer.clear();
-
-  return true;
-}
-
-bool WriteReplyToFd(int fd, const std::vector<Span<const uint8_t>> &spans) {
-  if (spans.size() > kMaxArgs) {
-    abort();
-  }
-
-  uint32_t nums[1 + kMaxArgs];
-  iovec iovs[kMaxArgs + 1];
-  nums[0] = spans.size();
-  iovs[0].iov_base = nums;
-  iovs[0].iov_len = sizeof(uint32_t) * (1 + spans.size());
-
-  size_t num_iov = 1;
-  for (size_t i = 0; i < spans.size(); i++) {
-    const auto &span = spans[i];
-    nums[i + 1] = span.size();
-    if (span.empty()) {
-      continue;
-    }
-
-    iovs[num_iov].iov_base = const_cast<uint8_t *>(span.data());
-    iovs[num_iov].iov_len = span.size();
-    num_iov++;
-  }
-
-  size_t iov_done = 0;
-  while (iov_done < num_iov) {
-    ssize_t r;
-    do {
-      r = writev(fd, &iovs[iov_done], num_iov - iov_done);
-    } while (r == -1 && errno == EINTR);
-
-    if (r <= 0) {
-      return false;
-    }
-
-    size_t written = r;
-    for (size_t i = iov_done; i < num_iov && written > 0; i++) {
-      iovec &iov = iovs[i];
-
-      size_t done = written;
-      if (done > iov.iov_len) {
-        done = iov.iov_len;
-      }
-
-      iov.iov_base = reinterpret_cast<uint8_t *>(iov.iov_base) + done;
-      iov.iov_len -= done;
-      written -= done;
-
-      if (iov.iov_len == 0) {
-        iov_done++;
-      }
-    }
-
-    assert(written == 0);
-  }
-
-  return true;
-}
-
 static bool GetConfig(const Span<const uint8_t> args[],
                       ReplyCallback write_reply) {
   static constexpr char kConfig[] =
diff --git a/util/fipstools/acvp/modulewrapper/modulewrapper.h b/util/fipstools/acvp/modulewrapper/modulewrapper.h
index 3f9a591..3997bed 100644
--- a/util/fipstools/acvp/modulewrapper/modulewrapper.h
+++ b/util/fipstools/acvp/modulewrapper/modulewrapper.h
@@ -21,8 +21,7 @@
 #include <openssl/span.h>
 
 
-namespace bssl {
-namespace acvp {
+namespace bssl::acvp {
 
 // kMaxArgs is the maximum number of arguments (including the function name)
 // that an ACVP request can contain.
@@ -72,5 +71,7 @@
 // a reason and returns |nullptr| if none is found.
 Handler FindHandler(Span<const Span<const uint8_t>> args);
 
-}  // namespace acvp
-}  // namespace bssl
+// Run the I/O loop until error or EOF. Returns the exit code for the binary.
+int RunModuleWrapper();
+
+}  // namespace bssl::acvp
diff --git a/util/fipstools/acvp/modulewrapper/proto.cc b/util/fipstools/acvp/modulewrapper/proto.cc
new file mode 100644
index 0000000..2a3436d
--- /dev/null
+++ b/util/fipstools/acvp/modulewrapper/proto.cc
@@ -0,0 +1,282 @@
+// Copyright 2025 The BoringSSL Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include "../../../../crypto/internal.h"
+#include "modulewrapper.h"
+
+
+namespace bssl::acvp {
+
+#if defined(OPENSSL_TRUSTY)
+#include <trusty_log.h>
+#define LOG_ERROR(...) TLOGE(__VA_ARGS__)
+#define TLOG_TAG "modulewrapper"
+#else
+#define LOG_ERROR(...) fprintf(stderr, __VA_ARGS__)
+#endif  // OPENSSL_TRUSTY
+
+constexpr size_t kMaxArgLength = (1 << 20);
+
+RequestBuffer::~RequestBuffer() = default;
+
+class RequestBufferImpl : public RequestBuffer {
+ public:
+  ~RequestBufferImpl() = default;
+
+  std::vector<uint8_t> buf;
+  Span<const uint8_t> args[kMaxArgs];
+};
+
+// static
+std::unique_ptr<RequestBuffer> RequestBuffer::New() {
+  return std::make_unique<RequestBufferImpl>();
+}
+
+static bool ReadAll(int fd, void *in_data, size_t data_len) {
+  uint8_t *data = reinterpret_cast<uint8_t *>(in_data);
+  size_t done = 0;
+
+  while (done < data_len) {
+    ssize_t r;
+    do {
+      r = read(fd, &data[done], data_len - done);
+    } while (r == -1 && errno == EINTR);
+
+    if (r <= 0) {
+      return false;
+    }
+
+    done += r;
+  }
+
+  return true;
+}
+
+Span<const Span<const uint8_t>> ParseArgsFromFd(int fd,
+                                                RequestBuffer *in_buffer) {
+  RequestBufferImpl *buffer = reinterpret_cast<RequestBufferImpl *>(in_buffer);
+  uint32_t nums[1 + kMaxArgs];
+  const Span<const Span<const uint8_t>> empty_span;
+
+  if (!ReadAll(fd, nums, sizeof(uint32_t) * 2)) {
+    return empty_span;
+  }
+
+  const size_t num_args = nums[0];
+  if (num_args == 0) {
+    LOG_ERROR("Invalid, zero-argument operation requested.\n");
+    return empty_span;
+  } else if (num_args > kMaxArgs) {
+    LOG_ERROR("Operation requested with %zu args, but %zu is the limit.\n",
+              num_args, kMaxArgs);
+    return empty_span;
+  }
+
+  if (num_args > 1 &&
+      !ReadAll(fd, &nums[2], sizeof(uint32_t) * (num_args - 1))) {
+    return empty_span;
+  }
+
+  size_t need = 0;
+  for (size_t i = 0; i < num_args; i++) {
+    const size_t arg_length = nums[i + 1];
+    if (i == 0 && arg_length > kMaxNameLength) {
+      LOG_ERROR("Operation with name of length %zu exceeded limit of %zu.\n",
+                arg_length, kMaxNameLength);
+      return empty_span;
+    } else if (arg_length > kMaxArgLength) {
+      LOG_ERROR(
+          "Operation with argument of length %zu exceeded limit of %zu.\n",
+          arg_length, kMaxArgLength);
+      return empty_span;
+    }
+
+    // This static_assert confirms that the following addition doesn't
+    // overflow.
+    static_assert((kMaxArgs - 1 * kMaxArgLength) + kMaxNameLength > (1 << 30),
+                  "Argument limits permit excessive messages");
+    need += arg_length;
+  }
+
+  if (need > buffer->buf.size()) {
+    size_t alloced = need + (need >> 1);
+    if (alloced < need) {
+      abort();
+    }
+    buffer->buf.resize(alloced);
+  }
+
+  if (!ReadAll(fd, buffer->buf.data(), need)) {
+    return empty_span;
+  }
+
+  size_t offset = 0;
+  for (size_t i = 0; i < num_args; i++) {
+    buffer->args[i] = Span<const uint8_t>(&buffer->buf[offset], nums[i + 1]);
+    offset += nums[i + 1];
+  }
+
+  return Span<const Span<const uint8_t>>(buffer->args, num_args);
+}
+
+// g_reply_buffer contains buffered replies which will be flushed when acvp
+// requests.
+static std::vector<uint8_t> g_reply_buffer;
+
+bool WriteReplyToBuffer(const std::vector<Span<const uint8_t>> &spans) {
+  if (spans.size() > kMaxArgs) {
+    abort();
+  }
+
+  uint8_t buf[4];
+  CRYPTO_store_u32_le(buf, spans.size());
+  g_reply_buffer.insert(g_reply_buffer.end(), buf, buf + sizeof(buf));
+  for (const auto &span : spans) {
+    CRYPTO_store_u32_le(buf, span.size());
+    g_reply_buffer.insert(g_reply_buffer.end(), buf, buf + sizeof(buf));
+  }
+  for (const auto &span : spans) {
+    g_reply_buffer.insert(g_reply_buffer.end(), span.begin(), span.end());
+  }
+
+  return true;
+}
+
+bool FlushBuffer(int fd) {
+  size_t done = 0;
+
+  while (done < g_reply_buffer.size()) {
+    ssize_t n;
+    do {
+      n = write(fd, g_reply_buffer.data() + done, g_reply_buffer.size() - done);
+    } while (n < 0 && errno == EINTR);
+
+    if (n < 0) {
+      return false;
+    }
+    done += static_cast<size_t>(n);
+  }
+
+  g_reply_buffer.clear();
+
+  return true;
+}
+
+bool WriteReplyToFd(int fd, const std::vector<Span<const uint8_t>> &spans) {
+  if (spans.size() > kMaxArgs) {
+    abort();
+  }
+
+  uint32_t nums[1 + kMaxArgs];
+  iovec iovs[kMaxArgs + 1];
+  nums[0] = spans.size();
+  iovs[0].iov_base = nums;
+  iovs[0].iov_len = sizeof(uint32_t) * (1 + spans.size());
+
+  size_t num_iov = 1;
+  for (size_t i = 0; i < spans.size(); i++) {
+    const auto &span = spans[i];
+    nums[i + 1] = span.size();
+    if (span.empty()) {
+      continue;
+    }
+
+    iovs[num_iov].iov_base = const_cast<uint8_t *>(span.data());
+    iovs[num_iov].iov_len = span.size();
+    num_iov++;
+  }
+
+  size_t iov_done = 0;
+  while (iov_done < num_iov) {
+    ssize_t r;
+    do {
+      r = writev(fd, &iovs[iov_done], num_iov - iov_done);
+    } while (r == -1 && errno == EINTR);
+
+    if (r <= 0) {
+      return false;
+    }
+
+    size_t written = r;
+    for (size_t i = iov_done; i < num_iov && written > 0; i++) {
+      iovec &iov = iovs[i];
+
+      size_t done = written;
+      if (done > iov.iov_len) {
+        done = iov.iov_len;
+      }
+
+      iov.iov_base = reinterpret_cast<uint8_t *>(iov.iov_base) + done;
+      iov.iov_len -= done;
+      written -= done;
+
+      if (iov.iov_len == 0) {
+        iov_done++;
+      }
+    }
+
+    assert(written == 0);
+  }
+
+  return true;
+}
+
+int RunModuleWrapper() {
+  // modulewrapper buffers responses to the greatest degree allowed in order to
+  // fully exercise the async handling in acvptool.
+  std::unique_ptr<bssl::acvp::RequestBuffer> buffer =
+      bssl::acvp::RequestBuffer::New();
+  const bssl::acvp::ReplyCallback write_reply = std::bind(
+      bssl::acvp::WriteReplyToFd, STDOUT_FILENO, std::placeholders::_1);
+  const bssl::acvp::ReplyCallback buffer_reply =
+      std::bind(bssl::acvp::WriteReplyToBuffer, std::placeholders::_1);
+
+  for (;;) {
+    const bssl::Span<const bssl::Span<const uint8_t>> args =
+        ParseArgsFromFd(STDIN_FILENO, buffer.get());
+    if (args.empty()) {
+      return 1;
+    }
+
+    auto name = bssl::BytesAsStringView(args[0]);
+    if (name == "flush") {
+      if (!bssl::acvp::FlushBuffer(STDOUT_FILENO)) {
+        abort();
+      }
+      continue;
+    }
+
+    const bssl::acvp::Handler handler = bssl::acvp::FindHandler(args);
+    if (!handler) {
+      return 2;
+    }
+
+    auto &reply_callback = name == "getConfig" ? write_reply : buffer_reply;
+    if (!handler(args.subspan(1).data(), reply_callback)) {
+      fprintf(stderr, "\'%s\' operation failed.\n", std::string(name).c_str());
+      return 3;
+    }
+  }
+}
+
+}  // namespace bssl::acvp