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