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