blob: d1ffe6cfd44f547fa6f37ef93e080279d843a8aa [file]
/*
* Copyright 2021, The Android Open Source Project
*
* 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
*
* http://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.
*/
#define TLOG_TAG "acvp"
#include "modulewrapper.h"
#include <string>
#include <vector>
#include <assert.h>
#include <interface/acvp/acvp.h>
#include <lib/tipc/tipc.h>
#include <lib/tipc/tipc_srv.h>
#include <lk/err_ptr.h>
#include <openssl/span.h>
#include <sys/auxv.h>
#include <sys/mman.h>
#include <trusty_ipc.h>
#include <trusty_log.h>
#define PAGE_SIZE getauxval(AT_PAGESZ)
// Keep modulewrapper.h and acvp.h in sync
static_assert(bssl::acvp::kMaxArgs == ACVP_MAX_NUM_ARGUMENTS);
static_assert(bssl::acvp::kMaxNameLength == ACVP_MAX_NAME_LENGTH);
static struct tipc_port_acl kAcvpPortAcl = {
.flags = IPC_PORT_ALLOW_TA_CONNECT | IPC_PORT_ALLOW_NS_CONNECT,
.uuid_num = 0,
.uuids = NULL,
.extra_data = NULL,
};
static struct tipc_port kAcvpPort = {
.name = ACVP_PORT,
.msg_max_size = ACVP_MAX_MESSAGE_LENGTH,
.msg_queue_len = 1,
.acl = &kAcvpPortAcl,
.priv = NULL,
};
static inline size_t AlignUpToPage(size_t size) {
return (size + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
}
class TrustyAcvpTool {
public:
TrustyAcvpTool(handle_t chan) : chan_(chan) {}
// Send a reply back to the acvptool.
//
// This function is used by the handler functions to write out results and
// should be customized by the tool implementation.
bool WriteReply(std::vector<bssl::Span<const uint8_t>> spans);
bool MapShm(handle_t handle, size_t shm_size);
const uint8_t* arg_buffer() const {
assert(arg_buffer_);
return arg_buffer_;
}
~TrustyAcvpTool();
private:
// Communication handle with the Android modulewrapper tool
handle_t chan_;
// Handle to the shared memory region for arguments
handle_t shm_handle_;
// Size of arg_buffer_ (must be page-aligned)
size_t arg_buffer_size_;
// Mapped buffer from shm_handle_
uint8_t* arg_buffer_;
};
bool TrustyAcvpTool::WriteReply(std::vector<bssl::Span<const uint8_t>> spans) {
if (spans.empty() || spans.size() > bssl::acvp::kMaxArgs) {
abort();
}
struct acvp_resp resp;
resp.num_spans = spans.size();
uint8_t* cur_buffer = arg_buffer_;
for (size_t i = 0; i < spans.size(); i++) {
const auto& span = spans[i];
resp.lengths[i] = span.size();
if (span.empty()) {
continue;
}
assert(span.size() < arg_buffer_size_ &&
cur_buffer - arg_buffer_ + span.size() <= arg_buffer_size_);
memcpy(cur_buffer, span.data(), span.size());
cur_buffer += span.size();
}
int rc = tipc_send1(chan_, &resp, sizeof(struct acvp_resp));
if (rc != sizeof(struct acvp_resp)) {
TLOGE("Failed to send ACVP response\n");
return false;
}
return true;
}
bool TrustyAcvpTool::MapShm(handle_t shm, size_t size) {
arg_buffer_size_ = AlignUpToPage(size);
shm_handle_ = shm;
arg_buffer_ = (uint8_t*)mmap(NULL, arg_buffer_size_, PROT_READ | PROT_WRITE,
0, shm_handle_, 0);
if (!arg_buffer_) {
return false;
}
return true;
}
TrustyAcvpTool::~TrustyAcvpTool() {
if (arg_buffer_) {
munmap((void*)arg_buffer_, arg_buffer_size_);
}
if (shm_handle_ != INVALID_IPC_HANDLE) {
close(shm_handle_);
}
if (chan_ != INVALID_IPC_HANDLE) {
close(chan_);
}
}
static int ParseAcvpMessage(handle_t chan,
uint8_t buffer[ACVP_MAX_MESSAGE_LENGTH],
struct acvp_req** request,
handle_t* shared_mem) {
int rc;
struct ipc_msg_info msg_info;
rc = get_msg(chan, &msg_info);
if (rc != NO_ERROR) {
TLOGE("failed (%d) to get_msg()\n", rc);
return rc;
}
struct iovec iov = {
.iov_base = buffer,
.iov_len = ACVP_MAX_MESSAGE_LENGTH,
};
struct ipc_msg msg = {
.num_iov = 1,
.iov = &iov,
.num_handles = msg_info.num_handles,
.handles = shared_mem,
};
if (msg_info.len < sizeof(struct acvp_req)) {
TLOGE("Message is too short: %zd\n", msg_info.len);
rc = ERR_BAD_LEN;
goto err;
}
if (msg_info.num_handles > 1) {
TLOGE("Expected 0 or 1 handles, found %d\n", msg_info.num_handles);
rc = ERR_BAD_LEN;
goto err;
}
rc = read_msg(chan, msg_info.id, 0, &msg);
if (rc != sizeof(struct acvp_req)) {
TLOGE("failed (%d) to read_msg()\n", rc);
if (rc >= 0) {
rc = ERR_BAD_LEN;
}
goto err;
}
rc = NO_ERROR;
*request = (struct acvp_req*)buffer;
err:
put_msg(chan, msg_info.id);
return rc;
}
static int AcvpOnConnect(const struct tipc_port* port,
handle_t chan,
const struct uuid* peer,
void** ctx_p) {
TrustyAcvpTool* tool = new TrustyAcvpTool(chan);
*ctx_p = reinterpret_cast<void*>(tool);
return NO_ERROR;
}
static int AcvpOnMessage(const struct tipc_port* port,
handle_t chan,
void* ctx) {
assert(port == &kAcvpPort);
assert(ctx != nullptr);
TrustyAcvpTool* tool = reinterpret_cast<TrustyAcvpTool*>(ctx);
uint8_t message_buffer[ACVP_MAX_MESSAGE_LENGTH];
struct acvp_req* request = nullptr;
handle_t shared_mem;
int rc = ParseAcvpMessage(chan, message_buffer, &request, &shared_mem);
if (rc != NO_ERROR) {
TLOGE("Could not parse ACVP message: %d\n", rc);
return rc;
}
if (request->num_args > bssl::acvp::kMaxArgs) {
TLOGE("Too many args in ACVP message: %d\n", request->num_args);
return ERR_INVALID_ARGS;
}
bssl::Span<const uint8_t> args[bssl::acvp::kMaxArgs];
if (!tool->MapShm(shared_mem, request->buffer_size)) {
return ERR_NO_MEMORY;
}
uint32_t cur_offset = 0;
for (uint32_t i = 0; i < request->num_args; ++i) {
args[i] = bssl::Span<const uint8_t>(tool->arg_buffer() + cur_offset,
request->lengths[i]);
cur_offset += request->lengths[i];
}
auto handler = bssl::acvp::FindHandler(bssl::Span(args, request->num_args));
if (!handler) {
const std::string name(reinterpret_cast<const char*>(args[0].data()),
args[0].size());
TLOGE("Unknown operation: %s\n", name.c_str());
return ERR_NOT_FOUND;
}
bssl::acvp::ReplyCallback callback = [tool](auto spans) {
return tool->WriteReply(spans);
};
if (!handler(&args[1], callback)) {
const std::string name(reinterpret_cast<const char*>(args[0].data()),
args[0].size());
TLOGE("\'%s\' operation failed.\n", name.c_str());
return ERR_GENERIC;
}
return NO_ERROR;
}
static void AcvpOnChannelCleanup(void* ctx) {
TrustyAcvpTool* tool = reinterpret_cast<TrustyAcvpTool*>(ctx);
delete tool;
}
static struct tipc_srv_ops kAcvpOps = {
.on_connect = AcvpOnConnect,
.on_message = AcvpOnMessage,
.on_channel_cleanup = AcvpOnChannelCleanup,
};
int main(void) {
struct tipc_hset* hset = tipc_hset_create();
if (IS_ERR(hset)) {
return PTR_ERR(hset);
}
int rc = tipc_add_service(hset, &kAcvpPort, 1, 1, &kAcvpOps);
if (rc < 0) {
return rc;
}
rc = tipc_run_event_loop(hset);
return rc;
}