blob: 63d0e1385737d423787382798b4a503477a6de20 [file] [log] [blame]
/*
* Copyright (C) 2012 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.
*/
// TODO: add guard in header
extern "C" {
#include <stdlib.h>
}
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <err.h>
#include <trusty_std.h>
#include <interface/keymaster/keymaster.h>
#include <UniquePtr.h>
#include "trusty_keymaster.h"
#include "trusty_logger.h"
#include "keymaster_ipc.h"
using namespace keymaster;
uuid_t gatekeeper_uuid = {
0x38ba0cdc, 0xdf0e, 0x11e4, {0x98, 0x69, 0x23, 0x3f, 0xb6, 0xae, 0x47, 0x95}};
typedef void (*event_handler_proc_t)(const uevent_t* ev, void* ctx);
struct tipc_event_handler {
event_handler_proc_t proc;
void* priv;
};
struct keymaster_chan_ctx {
struct tipc_event_handler handler;
uuid_t uuid;
handle_t chan;
long (*dispatch)(keymaster_chan_ctx*, keymaster_message*, uint32_t, UniquePtr<uint8_t[]>*,
uint32_t*);
};
struct keymaster_srv_ctx {
handle_t port_secure;
handle_t port_non_secure;
};
static void keymaster_port_handler_secure(const uevent_t* ev, void* priv);
static void keymaster_port_handler_non_secure(const uevent_t* ev, void* priv);
static tipc_event_handler keymaster_port_evt_handler_secure = {
.proc = keymaster_port_handler_secure, .priv = NULL,
};
static tipc_event_handler keymaster_port_evt_handler_non_secure = {
.proc = keymaster_port_handler_non_secure, .priv = NULL,
};
static void keymaster_chan_handler(const uevent_t* ev, void* priv);
TrustyKeymaster* device;
int32_t message_version = -1;
class MessageDeleter {
public:
explicit MessageDeleter(handle_t chan, int id) {
chan_ = chan;
id_ = id;
}
~MessageDeleter() { put_msg(chan_, id_); }
private:
handle_t chan_;
int id_;
};
static long handle_port_errors(const uevent_t* ev) {
if ((ev->event & IPC_HANDLE_POLL_ERROR) || (ev->event & IPC_HANDLE_POLL_HUP) ||
(ev->event & IPC_HANDLE_POLL_MSG) || (ev->event & IPC_HANDLE_POLL_SEND_UNBLOCKED)) {
/* should never happen with port handles */
LOG_E("error event (0x%x) for port (%d)", ev->event, ev->handle);
return ERR_BAD_STATE;
}
return NO_ERROR;
}
static long send_response(handle_t chan, uint32_t cmd, uint8_t* out_buf, uint32_t out_buf_size) {
struct keymaster_message km_msg;
km_msg.cmd = cmd | KM_RESP_BIT;
iovec_t iov[2] = {{&km_msg, sizeof(km_msg)}, {out_buf, out_buf_size}};
ipc_msg_t msg = {2, iov, 0, NULL};
long rc = send_msg(chan, &msg);
// fatal error
if (rc < 0) {
LOG_E("failed (%d) to send_msg for chan (%d)", rc, chan);
return rc;
}
return NO_ERROR;
}
static long send_error_response(handle_t chan, uint32_t cmd, keymaster_error_t err) {
return send_response(chan, cmd, reinterpret_cast<uint8_t*>(&err), sizeof(err));
}
template <typename Request, typename Response>
static long do_dispatch(void (AndroidKeymaster::*operation)(const Request&, Response*),
struct keymaster_message* msg, uint32_t payload_size,
UniquePtr<uint8_t[]>* out, uint32_t* out_size) {
const uint8_t* payload = msg->payload;
Request req;
req.message_version = message_version;
if (!req.Deserialize(&payload, msg->payload + payload_size))
return ERR_NOT_VALID;
Response rsp;
rsp.message_version = message_version;
(device->*operation)(req, &rsp);
*out_size = rsp.SerializedSize();
if (*out_size > KEYMASTER_MAX_BUFFER_LENGTH) {
*out_size = 0;
return ERR_NOT_ENOUGH_BUFFER;
}
out->reset(new uint8_t[*out_size]);
if (out->get() == NULL) {
*out_size = 0;
return ERR_NO_MEMORY;
}
rsp.Serialize(out->get(), out->get() + *out_size);
return NO_ERROR;
}
static long get_auth_token_key(UniquePtr<uint8_t[]>* key_buf, uint32_t* key_size) {
keymaster_key_blob_t key;
long rc = device->GetAuthTokenKey(&key);
if (rc != NO_ERROR) {
return rc;
}
if (key.key_material_size > KEYMASTER_MAX_BUFFER_LENGTH) {
return ERR_NOT_ENOUGH_BUFFER;
}
key_buf->reset(new uint8_t[key.key_material_size]);
if (key_buf->get() == NULL) {
return ERR_NO_MEMORY;
}
*key_size = key.key_material_size;
memcpy(key_buf->get(), key.key_material, key.key_material_size);
return NO_ERROR;
}
static long keymaster_dispatch_secure(keymaster_chan_ctx* ctx, keymaster_message* msg,
uint32_t payload_size, UniquePtr<uint8_t[]>* out,
uint32_t* out_size) {
switch (msg->cmd) {
case KM_GET_AUTH_TOKEN_KEY:
return get_auth_token_key(out, out_size);
default:
return ERR_NOT_IMPLEMENTED;
}
}
static long keymaster_dispatch_non_secure(keymaster_chan_ctx* ctx, keymaster_message* msg,
uint32_t payload_size, UniquePtr<uint8_t[]>* out,
uint32_t* out_size) {
LOG_D("Dispatching command %d", msg->cmd);
switch (msg->cmd) {
case KM_GENERATE_KEY:
LOG_D("Dispatching GENERATE_KEY, size: %d", payload_size);
return do_dispatch(&TrustyKeymaster::GenerateKey, msg, payload_size, out, out_size);
case KM_BEGIN_OPERATION:
LOG_D("Dispatching BEGIN_OPERATION, size: %d", payload_size);
return do_dispatch(&TrustyKeymaster::BeginOperation, msg, payload_size, out, out_size);
case KM_UPDATE_OPERATION:
LOG_D("Dispatching UPDATE_OPERATION, size: %d", payload_size);
return do_dispatch(&TrustyKeymaster::UpdateOperation, msg, payload_size, out, out_size);
case KM_FINISH_OPERATION:
LOG_D("Dispatching FINISH_OPERATION, size: %d", payload_size);
return do_dispatch(&TrustyKeymaster::FinishOperation, msg, payload_size, out, out_size);
case KM_IMPORT_KEY:
LOG_D("Dispatching IMPORT_KEY, size: %d", payload_size);
return do_dispatch(&TrustyKeymaster::ImportKey, msg, payload_size, out, out_size);
case KM_EXPORT_KEY:
LOG_D("Dispatching EXPORT_KEY, size: %d", payload_size);
return do_dispatch(&TrustyKeymaster::ExportKey, msg, payload_size, out, out_size);
case KM_GET_VERSION:
LOG_D("Dispatching GET_VERSION, size: %d", payload_size);
return do_dispatch(&TrustyKeymaster::GetVersion, msg, payload_size, out, out_size);
case KM_ADD_RNG_ENTROPY:
LOG_D("Dispatching ADD_RNG_ENTROPY, size: %d", payload_size);
return do_dispatch(&TrustyKeymaster::AddRngEntropy, msg, payload_size, out, out_size);
case KM_GET_SUPPORTED_ALGORITHMS:
LOG_D("Dispatching GET_SUPPORTED_ALGORITHMS, size: %d", payload_size);
return do_dispatch(&TrustyKeymaster::SupportedAlgorithms, msg, payload_size, out, out_size);
case KM_GET_SUPPORTED_BLOCK_MODES:
LOG_D("Dispatching GET_SUPPORTED_BLOCK_MODES, size: %d", payload_size);
return do_dispatch(&TrustyKeymaster::SupportedBlockModes, msg, payload_size, out, out_size);
case KM_GET_SUPPORTED_PADDING_MODES:
LOG_D("Dispatching GET_SUPPORTED_PADDING_MODES, size: %d", payload_size);
return do_dispatch(&TrustyKeymaster::SupportedPaddingModes, msg, payload_size, out,
out_size);
case KM_GET_SUPPORTED_DIGESTS:
LOG_D("Dispatching GET_SUPPORTED_DIGESTS, size: %d", payload_size);
return do_dispatch(&TrustyKeymaster::SupportedDigests, msg, payload_size, out, out_size);
case KM_GET_SUPPORTED_IMPORT_FORMATS:
LOG_D("Dispatching GET_SUPPORTED_IMPORT_FORMATS, size: %d", payload_size);
return do_dispatch(&TrustyKeymaster::SupportedImportFormats, msg, payload_size, out,
out_size);
case KM_GET_SUPPORTED_EXPORT_FORMATS:
LOG_D("Dispatching GET_SUPPORTED_EXPORT_FORMATS, size: %d", payload_size);
return do_dispatch(&TrustyKeymaster::SupportedExportFormats, msg, payload_size, out,
out_size);
case KM_GET_KEY_CHARACTERISTICS:
LOG_D("Dispatching GET_KEY_CHARACTERISTICS, size: %d", payload_size);
return do_dispatch(&TrustyKeymaster::GetKeyCharacteristics, msg, payload_size, out,
out_size);
case KM_ABORT_OPERATION:
LOG_D("Dispatching ABORT_OPERATION, size %d", payload_size);
return do_dispatch(&TrustyKeymaster::AbortOperation, msg, payload_size, out, out_size);
default:
return ERR_NOT_IMPLEMENTED;
}
}
static bool keymaster_port_accessible(uuid_t* uuid, bool secure) {
return !secure || memcmp(uuid, &gatekeeper_uuid, sizeof(gatekeeper_uuid)) == 0;
}
static keymaster_chan_ctx* keymaster_ctx_open(handle_t chan, uuid_t* uuid, bool secure) {
if (!keymaster_port_accessible(uuid, secure)) {
LOG_E("access denied for client uuid", 0);
return NULL;
}
keymaster_chan_ctx* ctx = new keymaster_chan_ctx;
if (ctx == NULL) {
return ctx;
}
ctx->handler.proc = &keymaster_chan_handler;
ctx->handler.priv = ctx;
ctx->uuid = *uuid;
ctx->chan = chan;
ctx->dispatch = secure ? &keymaster_dispatch_secure : &keymaster_dispatch_non_secure;
return ctx;
}
static void keymaster_ctx_close(keymaster_chan_ctx* ctx) {
close(ctx->chan);
delete ctx;
}
static long handle_msg(keymaster_chan_ctx* ctx) {
handle_t chan = ctx->chan;
/* get message info */
ipc_msg_info_t msg_inf;
int rc = get_msg(chan, &msg_inf);
if (rc == ERR_NO_MSG)
return NO_ERROR; /* no new messages */
// fatal error
if (rc != NO_ERROR) {
LOG_E("failed (%d) to get_msg for chan (%d), closing connection", rc, chan);
return rc;
}
MessageDeleter md(chan, msg_inf.id);
// allocate msg_buf, with one extra byte for null-terminator
UniquePtr<uint8_t[]> msg_buf(new uint8_t[msg_inf.len + 1]);
msg_buf[msg_inf.len] = 0;
/* read msg content */
iovec_t iov = {msg_buf.get(), msg_inf.len};
ipc_msg_t msg = {1, &iov, 0, NULL};
rc = read_msg(chan, msg_inf.id, 0, &msg);
// fatal error
if (rc < 0) {
LOG_E("failed to read msg (%d)", rc, chan);
return rc;
}
LOG_D("Read %d-byte message", rc);
if (((unsigned long)rc) < sizeof(keymaster_message)) {
LOG_E("invalid message of size (%d)", rc, chan);
return ERR_NOT_VALID;
}
UniquePtr<uint8_t[]> out_buf;
uint32_t out_buf_size = 0;
keymaster_message* in_msg = reinterpret_cast<keymaster_message*>(msg_buf.get());
rc = ctx->dispatch(ctx, in_msg, msg_inf.len - sizeof(*in_msg), &out_buf, &out_buf_size);
if (rc < 0) {
LOG_E("error handling message (%d)", rc);
return send_error_response(chan, in_msg->cmd, KM_ERROR_UNKNOWN_ERROR);
}
LOG_D("Sending %d-byte response", out_buf_size);
return send_response(chan, in_msg->cmd, out_buf.get(), out_buf_size);
}
static void keymaster_chan_handler(const uevent_t* ev, void* priv) {
keymaster_chan_ctx* ctx = reinterpret_cast<keymaster_chan_ctx*>(priv);
if (ctx == NULL) {
LOG_E("error: no context on channel %d", ev->handle);
close(ev->handle);
return;
}
if ((ev->event & IPC_HANDLE_POLL_ERROR) || (ev->event & IPC_HANDLE_POLL_READY)) {
/* close it as it is in an error state */
LOG_E("error event (0x%x) for chan (%d)", ev->event, ev->handle);
close(ev->handle);
return;
}
if (ev->event & IPC_HANDLE_POLL_MSG) {
long rc = handle_msg(ctx);
if (rc != NO_ERROR) {
/* report an error and close channel */
LOG_E("failed (%d) to handle event on channel %d", rc, ev->handle);
keymaster_ctx_close(ctx);
return;
}
}
if (ev->event & IPC_HANDLE_POLL_HUP) {
/* closed by peer. */
keymaster_ctx_close(ctx);
return;
}
}
static void keymaster_port_handler(const uevent_t* ev, void* priv, bool secure) {
long rc = handle_port_errors(ev);
if (rc != NO_ERROR) {
abort();
}
uuid_t peer_uuid;
if (ev->event & IPC_HANDLE_POLL_READY) {
/* incoming connection: accept it */
int rc = accept(ev->handle, &peer_uuid);
if (rc < 0) {
LOG_E("failed (%d) to accept on port %d", rc, ev->handle);
return;
}
handle_t chan = (handle_t)rc;
keymaster_chan_ctx* ctx = keymaster_ctx_open(chan, &peer_uuid, secure);
if (ctx == NULL) {
LOG_E("failed to allocate context on chan %d", chan);
close(chan);
return;
}
rc = set_cookie(chan, ctx);
if (rc < 0) {
LOG_E("failed (%d) to set_cookie on chan %d", rc, chan);
keymaster_ctx_close(ctx);
return;
}
}
}
static void keymaster_port_handler_secure(const uevent_t* ev, void* priv) {
keymaster_port_handler(ev, priv, true);
}
static void keymaster_port_handler_non_secure(const uevent_t* ev, void* priv) {
keymaster_port_handler(ev, priv, false);
}
static void dispatch_event(const uevent_t* ev) {
if (ev == NULL)
return;
if (ev->event == IPC_HANDLE_POLL_NONE) {
/* not really an event, do nothing */
LOG_E("got an empty event", 0);
return;
}
/* check if we have handler */
tipc_event_handler* handler = reinterpret_cast<tipc_event_handler*>(ev->cookie);
if (handler && handler->proc) {
/* invoke it */
handler->proc(ev, handler->priv);
return;
}
/* no handler? close it */
LOG_E("no handler for event (0x%x) with handle %d", ev->event, ev->handle);
close(ev->handle);
return;
}
static long keymaster_ipc_init(keymaster_srv_ctx* ctx) {
int rc;
/* Initialize secure-side service */
rc = port_create(KEYMASTER_SECURE_PORT, 1, KEYMASTER_MAX_BUFFER_LENGTH,
IPC_PORT_ALLOW_TA_CONNECT);
if (rc < 0) {
LOG_E("Failed (%d) to create port %s", rc, KEYMASTER_SECURE_PORT);
return rc;
}
ctx->port_secure = (handle_t)rc;
rc = set_cookie(ctx->port_secure, &keymaster_port_evt_handler_secure);
if (rc) {
LOG_E("failed (%d) to set_cookie on port %d", rc, ctx->port_secure);
close(ctx->port_secure);
return rc;
}
/* initialize non-secure side service */
rc = port_create(KEYMASTER_PORT, 1, KEYMASTER_MAX_BUFFER_LENGTH, IPC_PORT_ALLOW_NS_CONNECT);
if (rc < 0) {
LOG_E("Failed (%d) to create port %s", rc, KEYMASTER_PORT);
return rc;
}
ctx->port_non_secure = (handle_t)rc;
rc = set_cookie(ctx->port_non_secure, &keymaster_port_evt_handler_non_secure);
if (rc) {
LOG_E("failed (%d) to set_cookie on port %d", rc, ctx->port_non_secure);
close(ctx->port_non_secure);
return rc;
}
return NO_ERROR;
}
int main(void) {
long rc;
uevent_t event;
device = new TrustyKeymaster(new TrustyKeymasterContext, 16);
TrustyLogger::initialize();
LOG_I("Initializing", 0);
GetVersionRequest request;
GetVersionResponse response;
device->GetVersion(request, &response);
if (response.error == KM_ERROR_OK) {
message_version = MessageVersion(
response.major_ver, response.minor_ver, response.subminor_ver);
} else {
LOG_E("Error %d determining AndroidKeymaster version.", response.error);
return ERR_GENERIC;
}
keymaster_srv_ctx ctx;
rc = keymaster_ipc_init(&ctx);
if (rc < 0) {
LOG_E("failed (%d) to initialize keymaster", rc);
return rc;
}
/* enter main event loop */
while (true) {
event.handle = INVALID_IPC_HANDLE;
event.event = 0;
event.cookie = NULL;
rc = wait_any(&event, -1);
if (rc < 0) {
LOG_E("wait_any failed (%d)", rc);
break;
}
if (rc == NO_ERROR) { /* got an event */
dispatch_event(&event);
}
}
return 0;
}