| // 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 <functional> | |
| #include <memory> | |
| #include <vector> | |
| #include <openssl/span.h> | |
| #include "../../../../crypto/internal.h" | |
| #include "modulewrapper.h" | |
| BSSL_NAMESPACE_BEGIN | |
| namespace 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<acvp::RequestBuffer> buffer = acvp::RequestBuffer::New(); | |
| const acvp::ReplyCallback write_reply = | |
| std::bind(acvp::WriteReplyToFd, STDOUT_FILENO, std::placeholders::_1); | |
| const acvp::ReplyCallback buffer_reply = | |
| std::bind(acvp::WriteReplyToBuffer, std::placeholders::_1); | |
| for (;;) { | |
| const Span<const Span<const uint8_t>> args = | |
| ParseArgsFromFd(STDIN_FILENO, buffer.get()); | |
| if (args.empty()) { | |
| return 1; | |
| } | |
| auto name = BytesAsStringView(args[0]); | |
| if (name == "flush") { | |
| if (!acvp::FlushBuffer(STDOUT_FILENO)) { | |
| abort(); | |
| } | |
| continue; | |
| } | |
| const acvp::Handler handler = 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 acvp | |
| BSSL_NAMESPACE_END |