[hwcrypto] Add generic support for opaque key handles
An opaque key handle uniquely identifies and gives adccess to a keyslot
for a particular client without exposing the key material to that
client. Given a handle, a delegated process (e.g. hwkey) will be able
to, if allowed, retrieve the key material.
Add structures and handler for opaque key handles. Key retrieval for
hwaes will follow in a separate change.
Bug: 185483587
Test: build.py --test com.android.trusty.hwcrypto.test
Change-Id: I8e0fcfc24bb8f03b9075d06418cf9132ebfe4884
diff --git a/hwcrypto-unittest/main.c b/hwcrypto-unittest/main.c
index e4139b0..d3bc768 100644
--- a/hwcrypto-unittest/main.c
+++ b/hwcrypto-unittest/main.c
@@ -43,17 +43,26 @@
#define HWCRYPTO_UNITTEST_KEYBOX_ID "com.android.trusty.hwcrypto.unittest.key32"
#define HWCRYPTO_UNITTEST_DERIVED_KEYBOX_ID \
"com.android.trusty.hwcrypto.unittest.derived_key32"
+#define HWCRYPTO_UNITTEST_OPAQUE_HANDLE_ID \
+ "com.android.trusty.hwcrypto.unittest.opaque_handle"
+#define HWCRYPTO_UNITTEST_OPAQUE_HANDLE2_ID \
+ "com.android.trusty.hwcrypto.unittest.opaque_handle2"
+#define HWCRYPTO_UNITTEST_OPAQUE_HANDLE_NOACCESS_ID \
+ "com.android.trusty.hwcrypto.unittest.opaque_handle_noaccess"
#define STORAGE_AUTH_KEY_SIZE 32
-#if WITH_HWCRYPTO_UNITTEST
static const uint8_t UNITTEST_KEYSLOT[] = "unittestkeyslotunittestkeyslotun";
static const uint8_t UNITTEST_DERIVED_KEYSLOT[] =
"unittestderivedkeyslotunittestde";
+
+#if WITH_HWCRYPTO_UNITTEST
+#define DISABLED_WITHOUT_HWCRYPTO_UNITTEST(name) name
#else
#pragma message \
"hwcrypto-unittest is built with the WITH_HWCRYPTO_UNITTEST define not enabled." \
"Hwkey tests will not test anything."
+#define DISABLED_WITHOUT_HWCRYPTO_UNITTEST(name) DISABLED_##name
#endif
/*
@@ -234,6 +243,228 @@
#endif
}
+TEST_F(hwkey, get_opaque_handle) {
+ uint8_t dest[HWKEY_OPAQUE_HANDLE_MAX_SIZE] = {0};
+ uint32_t actual_size = sizeof(dest);
+ long rc = hwkey_get_keyslot_data(_state->hwkey_session,
+ HWCRYPTO_UNITTEST_OPAQUE_HANDLE_ID, dest,
+ &actual_size);
+#if WITH_HWCRYPTO_UNITTEST
+ EXPECT_EQ(NO_ERROR, rc, "get hwcrypto-unittest opaque keybox");
+ EXPECT_LE(actual_size, HWKEY_OPAQUE_HANDLE_MAX_SIZE);
+
+ rc = strnlen((const char*)dest, HWKEY_OPAQUE_HANDLE_MAX_SIZE);
+ EXPECT_LT(rc, HWKEY_OPAQUE_HANDLE_MAX_SIZE,
+ "opaque handle is unexpected size");
+#else
+ EXPECT_EQ(ERR_NOT_FOUND, rc, "hwcrypto-unittest not enabled");
+#endif
+}
+
+/* The following tests require hwcrpyto-unittest to do anything useful. */
+
+TEST_F(hwkey, DISABLED_WITHOUT_HWCRYPTO_UNITTEST(get_opaque_key)) {
+ uint8_t handle[HWKEY_OPAQUE_HANDLE_MAX_SIZE] = {0};
+ uint32_t actual_size = sizeof(handle);
+ long rc = hwkey_get_keyslot_data(_state->hwkey_session,
+ HWCRYPTO_UNITTEST_OPAQUE_HANDLE_ID, handle,
+ &actual_size);
+
+ EXPECT_EQ(NO_ERROR, rc, "get hwcrypto-unittest opaque keybox");
+ EXPECT_LE(actual_size, HWKEY_OPAQUE_HANDLE_MAX_SIZE);
+ rc = strnlen((const char*)handle, HWKEY_OPAQUE_HANDLE_MAX_SIZE);
+ EXPECT_LT(rc, HWKEY_OPAQUE_HANDLE_MAX_SIZE,
+ "Unexpected opaque handle size");
+
+ uint8_t key_buf[sizeof(UNITTEST_KEYSLOT) - 1] = {0};
+ actual_size = sizeof(key_buf);
+ rc = hwkey_get_keyslot_data(_state->hwkey_session, (const char*)handle,
+ key_buf, &actual_size);
+ EXPECT_EQ(NO_ERROR, rc, "get hwcrypto-unittest opaque key failed");
+
+ rc = memcmp(UNITTEST_KEYSLOT, key_buf, sizeof(UNITTEST_KEYSLOT) - 1);
+ EXPECT_EQ(0, rc, "opaque key did not match expected value");
+}
+
+TEST_F(hwkey, DISABLED_WITHOUT_HWCRYPTO_UNITTEST(get_multiple_opaque_handles)) {
+ uint8_t handle1[HWKEY_OPAQUE_HANDLE_MAX_SIZE] = {0};
+ uint32_t actual_size = sizeof(handle1);
+ long rc = hwkey_get_keyslot_data(_state->hwkey_session,
+ HWCRYPTO_UNITTEST_OPAQUE_HANDLE_ID,
+ handle1, &actual_size);
+ EXPECT_EQ(NO_ERROR, rc, "get hwcrypto-unittest opaque keybox");
+ EXPECT_LE(actual_size, HWKEY_OPAQUE_HANDLE_MAX_SIZE);
+
+ uint8_t handle2[HWKEY_OPAQUE_HANDLE_MAX_SIZE] = {0};
+ actual_size = sizeof(handle2);
+ rc = hwkey_get_keyslot_data(_state->hwkey_session,
+ HWCRYPTO_UNITTEST_OPAQUE_HANDLE_NOACCESS_ID,
+ handle2, &actual_size);
+ EXPECT_EQ(NO_ERROR, rc, "get hwcrypto-unittest opaque keybox");
+ EXPECT_LE(actual_size, HWKEY_OPAQUE_HANDLE_MAX_SIZE);
+
+ rc = memcmp(handle1, handle2, HWKEY_OPAQUE_HANDLE_MAX_SIZE);
+ EXPECT_NE(0, rc, "opaque handles should not be the same");
+
+ uint8_t key_buf[sizeof(UNITTEST_KEYSLOT)] = {0};
+ actual_size = sizeof(key_buf);
+ rc = hwkey_get_keyslot_data(_state->hwkey_session, (const char*)handle1,
+ key_buf, &actual_size);
+ EXPECT_EQ(NO_ERROR, rc, "handle was not valid");
+ EXPECT_EQ(actual_size, sizeof(UNITTEST_KEYSLOT) - 1, "wrong key length");
+ rc = memcmp(UNITTEST_KEYSLOT, key_buf, sizeof(UNITTEST_KEYSLOT) - 1);
+ EXPECT_EQ(0, rc, "opaque key did not match expected value");
+
+ /* we are not allowed to retrieve key material for the NOACCESS handle */
+ memset(key_buf, 0, sizeof(UNITTEST_KEYSLOT));
+ actual_size = sizeof(key_buf);
+ rc = hwkey_get_keyslot_data(_state->hwkey_session, (const char*)handle2,
+ key_buf, &actual_size);
+ EXPECT_EQ(ERR_NOT_FOUND, rc,
+ "should not be able to retrieve key for second handle");
+
+ /*
+ * We need to reconnect to ensure that the tokens have been dropped and
+ * cleared.
+ */
+ hwkey_close(_state->hwkey_session);
+ int new_sess = hwkey_open();
+ ASSERT_GE(new_sess, 0);
+ _state->hwkey_session = (hwkey_session_t)new_sess;
+
+ /* Has the keyslot data been cleared? */
+ memset(key_buf, 0, sizeof(UNITTEST_KEYSLOT));
+ actual_size = sizeof(key_buf);
+ rc = hwkey_get_keyslot_data(_state->hwkey_session, (const char*)handle1,
+ key_buf, &actual_size);
+ EXPECT_EQ(ERR_NOT_FOUND, rc, "handle was still valid");
+
+ actual_size = sizeof(key_buf);
+ rc = hwkey_get_keyslot_data(_state->hwkey_session, (const char*)handle2,
+ key_buf, &actual_size);
+ EXPECT_EQ(ERR_NOT_FOUND, rc, "handle was still valid");
+
+test_abort:;
+}
+
+/*
+ * Make sure that attempting to get the same handle from multiple concurrent
+ * sessions doesn't break things.
+ */
+TEST_F(hwkey,
+ DISABLED_WITHOUT_HWCRYPTO_UNITTEST(opaque_handle_multiple_sessions)) {
+ uint8_t handle1[HWKEY_OPAQUE_HANDLE_MAX_SIZE] = {0};
+ uint32_t actual_size = sizeof(handle1);
+ long rc = hwkey_get_keyslot_data(_state->hwkey_session,
+ HWCRYPTO_UNITTEST_OPAQUE_HANDLE_ID,
+ handle1, &actual_size);
+ EXPECT_EQ(NO_ERROR, rc, "get hwcrypto-unittest opaque keybox");
+ EXPECT_LE(actual_size, HWKEY_OPAQUE_HANDLE_MAX_SIZE);
+
+ int new_sess = hwkey_open();
+ ASSERT_GE(new_sess, 0);
+
+ uint8_t handle2[HWKEY_OPAQUE_HANDLE_MAX_SIZE] = {0};
+ actual_size = sizeof(handle2);
+ rc = hwkey_get_keyslot_data(new_sess, HWCRYPTO_UNITTEST_OPAQUE_HANDLE_ID,
+ handle2, &actual_size);
+ EXPECT_EQ(ERR_ALREADY_EXISTS, rc, "retrieve same handle twice");
+
+ /* Fetch a new handle with a different keyslot from the second session */
+ actual_size = sizeof(handle2);
+ rc = hwkey_get_keyslot_data(new_sess, HWCRYPTO_UNITTEST_OPAQUE_HANDLE2_ID,
+ handle2, &actual_size);
+ EXPECT_EQ(NO_ERROR, rc, "get hwcrypto-unittest opaque keybox");
+ EXPECT_LE(actual_size, HWKEY_OPAQUE_HANDLE_MAX_SIZE);
+
+ uint8_t key_buf[sizeof(UNITTEST_KEYSLOT)] = {0};
+
+ /* Fetch the keys via the first session */
+ actual_size = sizeof(key_buf);
+ rc = hwkey_get_keyslot_data(_state->hwkey_session, (const char*)handle1,
+ key_buf, &actual_size);
+ EXPECT_EQ(NO_ERROR, rc, "handle was not valid");
+ EXPECT_EQ(actual_size, sizeof(UNITTEST_KEYSLOT) - 1, "wrong key length");
+ rc = memcmp(UNITTEST_KEYSLOT, key_buf, sizeof(UNITTEST_KEYSLOT) - 1);
+ EXPECT_EQ(0, rc, "opaque key did not match expected value");
+
+ memset(key_buf, 0, sizeof(UNITTEST_KEYSLOT));
+ actual_size = sizeof(key_buf);
+ rc = hwkey_get_keyslot_data(_state->hwkey_session, (const char*)handle2,
+ key_buf, &actual_size);
+ EXPECT_EQ(NO_ERROR, rc, "handle was not valid");
+ EXPECT_EQ(actual_size, sizeof(UNITTEST_KEYSLOT) - 1, "wrong key length");
+ rc = memcmp(UNITTEST_KEYSLOT, key_buf, sizeof(UNITTEST_KEYSLOT) - 1);
+ EXPECT_EQ(0, rc, "opaque key did not match expected value");
+
+ /* Fetch the same key via the second session */
+ memset(key_buf, 0, sizeof(UNITTEST_KEYSLOT));
+ actual_size = sizeof(key_buf);
+ rc = hwkey_get_keyslot_data(new_sess, (const char*)handle1, key_buf,
+ &actual_size);
+ EXPECT_EQ(NO_ERROR, rc, "handle was not valid");
+ EXPECT_EQ(actual_size, sizeof(UNITTEST_KEYSLOT) - 1, "wrong key length");
+ rc = memcmp(UNITTEST_KEYSLOT, key_buf, sizeof(UNITTEST_KEYSLOT) - 1);
+ EXPECT_EQ(0, rc, "opaque key did not match expected value");
+
+ memset(key_buf, 0, sizeof(UNITTEST_KEYSLOT));
+ actual_size = sizeof(key_buf);
+ rc = hwkey_get_keyslot_data(new_sess, (const char*)handle2, key_buf,
+ &actual_size);
+ EXPECT_EQ(NO_ERROR, rc, "handle was not valid");
+ EXPECT_EQ(actual_size, sizeof(UNITTEST_KEYSLOT) - 1, "wrong key length");
+ rc = memcmp(UNITTEST_KEYSLOT, key_buf, sizeof(UNITTEST_KEYSLOT) - 1);
+ EXPECT_EQ(0, rc, "opaque key did not match expected value");
+
+ hwkey_close(new_sess);
+
+ /* Has the keyslot data been cleared? */
+ actual_size = sizeof(key_buf);
+ rc = hwkey_get_keyslot_data(_state->hwkey_session, (const char*)handle1,
+ key_buf, &actual_size);
+ EXPECT_EQ(NO_ERROR, rc, "first session handle wasn't valid");
+
+ actual_size = sizeof(key_buf);
+ rc = hwkey_get_keyslot_data(_state->hwkey_session, (const char*)handle2,
+ key_buf, &actual_size);
+ EXPECT_EQ(ERR_NOT_FOUND, rc, "second session handle was still valid");
+
+ /* Disconnect the original session which retrieved the handle */
+ hwkey_close(_state->hwkey_session);
+ new_sess = hwkey_open();
+ ASSERT_GE(new_sess, 0);
+ _state->hwkey_session = (hwkey_session_t)new_sess;
+
+ actual_size = sizeof(key_buf);
+ rc = hwkey_get_keyslot_data(_state->hwkey_session, (const char*)handle1,
+ key_buf, &actual_size);
+ EXPECT_EQ(ERR_NOT_FOUND, rc, "handle was still valid");
+
+ actual_size = sizeof(key_buf);
+ rc = hwkey_get_keyslot_data(_state->hwkey_session, (const char*)handle2,
+ key_buf, &actual_size);
+ EXPECT_EQ(ERR_NOT_FOUND, rc, "handle was still valid");
+
+test_abort:;
+}
+
+TEST_F(hwkey, DISABLED_WITHOUT_HWCRYPTO_UNITTEST(try_empty_opaque_handle)) {
+ /* Reconnect just to make sure there is no spurious handles remaining. */
+ hwkey_close(_state->hwkey_session);
+ int new_sess = hwkey_open();
+ ASSERT_GE(new_sess, 0);
+ _state->hwkey_session = (hwkey_session_t)new_sess;
+
+ uint8_t key_buf[sizeof(UNITTEST_KEYSLOT) - 1] = {0};
+ uint32_t actual_size = sizeof(key_buf);
+ long rc = hwkey_get_keyslot_data(_state->hwkey_session, "", key_buf,
+ &actual_size);
+ EXPECT_EQ(ERR_NOT_FOUND, rc,
+ "retrieving a key with an empty access token succeeded");
+
+test_abort:;
+}
+
/*********************** HWRNG UNITTEST ***********************/
static uint32_t _hist[256];
diff --git a/hwcrypto/hwkey_srv.c b/hwcrypto/hwkey_srv.c
index 5ce917c..15589ed 100644
--- a/hwcrypto/hwkey_srv.c
+++ b/hwcrypto/hwkey_srv.c
@@ -17,6 +17,8 @@
#define TLOG_TAG "hwkey_srv"
#include <assert.h>
+#include <lk/list.h>
+#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
@@ -26,8 +28,10 @@
#include <interface/hwkey/hwkey.h>
#include <lib/tipc/tipc.h>
#include <openssl/evp.h>
+#include <openssl/mem.h>
#include <trusty_log.h>
+#include <hwcrypto/hwrng_dev.h>
#include <hwcrypto_consts.h>
#include "hwkey_srv_priv.h"
@@ -39,6 +43,30 @@
uuid_t uuid;
};
+/**
+ * An opaque key access token.
+ *
+ * Clients can retrieve an opaque access token as a handle to a key they are
+ * allowed to use but not read directly. This handle can then be passed to other
+ * crypto services which can use the token to retrieve the actual key from
+ * hwkey.
+ */
+typedef char access_token_t[HWKEY_OPAQUE_HANDLE_SIZE];
+
+struct opaque_handle_node {
+ const struct hwkey_keyslot* key_slot;
+ struct hwkey_chan_ctx* owner;
+ access_token_t token;
+ struct list_node node;
+};
+
+/*
+ * Global list of currently valid opaque handles. Each client may only have a
+ * single entry in this list for a given key slot, and this entry will be
+ * cleaned up when the connection it was created for is closed.
+ */
+static struct list_node opaque_handles = LIST_INITIAL_VALUE(opaque_handles);
+
static void hwkey_port_handler(const uevent_t* ev, void* priv);
static void hwkey_chan_handler(const uevent_t* ev, void* priv);
@@ -52,10 +80,33 @@
static unsigned int key_slot_cnt;
static const struct hwkey_keyslot* key_slots;
+static bool is_opaque_handle(const struct hwkey_keyslot* key_slot) {
+ assert(key_slot);
+ return key_slot->handler == get_key_handle;
+}
+
+static void delete_opaque_handle(struct opaque_handle_node* node) {
+ assert(node);
+
+ /* Zero out the access token just in case the memory is reused */
+ memset(node->token, 0, HWKEY_OPAQUE_HANDLE_SIZE);
+
+ list_delete(&node->node);
+ free(node);
+}
+
/*
* Close specified hwkey context
*/
static void hwkey_ctx_close(struct hwkey_chan_ctx* ctx) {
+ struct opaque_handle_node* entry;
+ struct opaque_handle_node* temp;
+ list_for_every_entry_safe(&opaque_handles, entry, temp,
+ struct opaque_handle_node, node) {
+ if (entry->owner == ctx) {
+ delete_opaque_handle(entry);
+ }
+ }
close(ctx->chan);
free(ctx);
}
@@ -72,6 +123,56 @@
rsp_data_len);
}
+static bool is_allowed_to_read_opaque_key(const uuid_t* uuid,
+ const struct hwkey_keyslot* slot) {
+ assert(slot);
+ const struct hwkey_opaque_handle_data* handle = slot->priv;
+ assert(handle);
+
+ for (size_t i = 0; i < handle->allowed_uuids_len; ++i) {
+ if (memcmp(handle->allowed_uuids[i], uuid, sizeof(uuid_t)) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static struct opaque_handle_node* find_opaque_handle_for_slot(
+ const struct hwkey_keyslot* slot) {
+ struct opaque_handle_node* entry;
+ list_for_every_entry(&opaque_handles, entry, struct opaque_handle_node,
+ node) {
+ if (entry->key_slot == slot) {
+ return entry;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * If a handle doesn't exist yet for the given slot, create and insert a new one
+ * in the global list.
+ */
+static uint32_t insert_handle_node(struct hwkey_chan_ctx* ctx,
+ const struct hwkey_keyslot* slot) {
+ struct opaque_handle_node* entry = find_opaque_handle_for_slot(slot);
+
+ if (!entry) {
+ entry = calloc(1, sizeof(struct opaque_handle_node));
+ if (!entry) {
+ TLOGE("Could not allocate new opaque_handle_node\n");
+ return HWKEY_ERR_GENERIC;
+ }
+
+ entry->owner = ctx;
+ entry->key_slot = slot;
+ list_add_tail(&opaque_handles, &entry->node);
+ }
+
+ return HWKEY_NO_ERROR;
+}
+
static uint32_t _handle_slots(struct hwkey_chan_ctx* ctx,
const char* slot_id,
const struct hwkey_keyslot* slots,
@@ -90,11 +191,21 @@
/* Check if the caller is allowed to get that key */
if (memcmp(&ctx->uuid, slots->uuid, sizeof(uuid_t)) == 0) {
if (slots->handler) {
+ if (is_opaque_handle(slots)) {
+ uint32_t rc = insert_handle_node(ctx, slots);
+ if (rc != HWKEY_NO_ERROR)
+ return rc;
+ }
return slots->handler(slots, kbuf, kbuf_len, klen);
}
}
}
- return HWKEY_ERR_NOT_FOUND;
+
+ /*
+ * We couldn't match a key ID, so try to treat the id as an opaque access
+ * handle
+ */
+ return get_opaque_key(&ctx->uuid, slot_id, kbuf, kbuf_len, klen);
}
uint32_t hwkey_derived_keyslot_handler(const struct hwkey_keyslot* slot,
@@ -379,6 +490,108 @@
key_slot_cnt = kcnt;
}
+static bool is_empty_token(const char* access_token) {
+ for (int i = 0; i < HWKEY_OPAQUE_HANDLE_SIZE; i++) {
+ if (access_token[i] != 0) {
+ assert(strnlen(access_token, HWKEY_OPAQUE_HANDLE_SIZE) ==
+ HWKEY_OPAQUE_HANDLE_SIZE - 1);
+ return false;
+ }
+ }
+ return true;
+}
+
+uint32_t get_key_handle(const struct hwkey_keyslot* slot,
+ uint8_t* kbuf,
+ size_t kbuf_len,
+ size_t* klen) {
+ assert(kbuf);
+ assert(klen);
+
+ const struct hwkey_opaque_handle_data* handle = slot->priv;
+ assert(handle);
+ assert(kbuf_len >= HWKEY_OPAQUE_HANDLE_SIZE);
+
+ struct opaque_handle_node* entry = find_opaque_handle_for_slot(slot);
+ /* _handle_slots should have already created an entry for this slot */
+ assert(entry);
+
+ if (!is_empty_token(entry->token)) {
+ /*
+ * We do not allow fetching a token again for the same slot again after
+ * the token is first created and returned
+ */
+ return HWKEY_ERR_ALREADY_EXISTS;
+ }
+
+ /*
+ * We want to generate a null-terminated opaque handle with no interior null
+ * bytes, so we generate extra randomness and only use the non-zero bytes.
+ */
+ uint8_t random_buf[HWKEY_OPAQUE_HANDLE_SIZE + 2];
+ while (1) {
+ int rc = hwrng_dev_get_rng_data(random_buf, sizeof(random_buf));
+ if (rc != NO_ERROR) {
+ /* Don't leave an empty entry if we couldn't generate a token */
+ delete_opaque_handle(entry);
+ return rc;
+ }
+
+ size_t token_offset = 0;
+ for (size_t i = 0; i < sizeof(random_buf) &&
+ token_offset < HWKEY_OPAQUE_HANDLE_SIZE - 1;
+ ++i) {
+ if (random_buf[i] != 0) {
+ entry->token[token_offset] = random_buf[i];
+ token_offset++;
+ }
+ }
+ if (token_offset == HWKEY_OPAQUE_HANDLE_SIZE - 1) {
+ break;
+ }
+ }
+
+ /* ensure that token is properly null-terminated */
+ assert(entry->token[HWKEY_OPAQUE_HANDLE_SIZE - 1] == 0);
+
+ memcpy(kbuf, entry->token, HWKEY_OPAQUE_HANDLE_SIZE);
+ *klen = HWKEY_OPAQUE_HANDLE_SIZE;
+
+ return HWKEY_NO_ERROR;
+}
+
+uint32_t get_opaque_key(const uuid_t* uuid,
+ const char* access_token,
+ uint8_t* kbuf,
+ size_t kbuf_len,
+ size_t* klen) {
+ struct opaque_handle_node* entry;
+ list_for_every_entry(&opaque_handles, entry, struct opaque_handle_node,
+ node) {
+ /* get_key_handle should never leave an empty token in the list */
+ assert(!is_empty_token(entry->token));
+
+ if (!is_allowed_to_read_opaque_key(uuid, entry->key_slot))
+ continue;
+
+ /*
+ * We are using a constant-time memcmp here to avoid side-channel
+ * leakage of the access token. Even if we trust the service that is
+ * allowed to retrieve this key, one of its clients may be trying to
+ * brute force the token, so this comparison must be constant-time.
+ */
+ if (CRYPTO_memcmp(entry->token, access_token,
+ HWKEY_OPAQUE_HANDLE_SIZE) == 0) {
+ const struct hwkey_opaque_handle_data* handle =
+ entry->key_slot->priv;
+ assert(handle);
+ return handle->retriever(entry->key_slot, kbuf, kbuf_len, klen);
+ }
+ }
+
+ return HWKEY_ERR_NOT_FOUND;
+}
+
/*
* Initialize HWKEY service
*/
diff --git a/hwcrypto/hwkey_srv_fake_provider.c b/hwcrypto/hwkey_srv_fake_provider.c
index 5873502..d970366 100644
--- a/hwcrypto/hwkey_srv_fake_provider.c
+++ b/hwcrypto/hwkey_srv_fake_provider.c
@@ -179,6 +179,28 @@
.encrypted_key_size_ptr = &_unittest_encrypted_key32_size,
.retriever = get_unittest_derived_key32,
};
+
+static const uuid_t* unittest_allowed_opaque_key_uuids[] = {
+ &hwcrypto_unittest_uuid,
+};
+
+static struct hwkey_opaque_handle_data unittest_opaque_handle_data = {
+ .allowed_uuids = unittest_allowed_opaque_key_uuids,
+ .allowed_uuids_len = countof(unittest_allowed_opaque_key_uuids),
+ .retriever = get_unittest_key32,
+};
+
+static struct hwkey_opaque_handle_data unittest_opaque_handle_data2 = {
+ .allowed_uuids = unittest_allowed_opaque_key_uuids,
+ .allowed_uuids_len = countof(unittest_allowed_opaque_key_uuids),
+ .retriever = get_unittest_key32,
+};
+
+static struct hwkey_opaque_handle_data unittest_opaque_handle_data_noaccess = {
+ .allowed_uuids = NULL,
+ .allowed_uuids_len = 0,
+ .retriever = get_unittest_key32,
+};
#endif /* WITH_HWCRYPTO_UNITTEST */
/*
@@ -461,6 +483,25 @@
.priv = &hwcrypto_unittest_derived_data,
.handler = hwkey_derived_keyslot_handler,
},
+ {
+ .uuid = &hwcrypto_unittest_uuid,
+ .key_id = "com.android.trusty.hwcrypto.unittest.opaque_handle",
+ .handler = get_key_handle,
+ .priv = &unittest_opaque_handle_data,
+ },
+ {
+ .uuid = &hwcrypto_unittest_uuid,
+ .key_id = "com.android.trusty.hwcrypto.unittest.opaque_handle2",
+ .handler = get_key_handle,
+ .priv = &unittest_opaque_handle_data2,
+ },
+ {
+ .uuid = &hwcrypto_unittest_uuid,
+ .key_id =
+ "com.android.trusty.hwcrypto.unittest.opaque_handle_noaccess",
+ .handler = get_key_handle,
+ .priv = &unittest_opaque_handle_data_noaccess,
+ },
#endif /* WITH_HWCRYPTO_UNITTEST */
};
diff --git a/hwcrypto/hwkey_srv_priv.h b/hwcrypto/hwkey_srv_priv.h
index f744ee2..3ea3a52 100644
--- a/hwcrypto/hwkey_srv_priv.h
+++ b/hwcrypto/hwkey_srv_priv.h
@@ -15,6 +15,7 @@
*/
#pragma once
+#include <interface/hwkey/hwkey.h>
#include <lk/compiler.h>
#include <sys/types.h>
#include <uapi/trusty_uuid.h>
@@ -55,6 +56,37 @@
*/
#define HWKEY_DERIVED_KEY_MAX_SIZE 32
+#define HWKEY_OPAQUE_HANDLE_SIZE 32
+STATIC_ASSERT(HWKEY_OPAQUE_HANDLE_SIZE <= HWKEY_OPAQUE_HANDLE_MAX_SIZE);
+
+/**
+ * struct hwkey_opaque_handle_data - Opaque handle data for keyslots that allow
+ * opaque usage in hwaes.
+ *
+ * Intended for use in the @hwkey_keyslot.priv field. The retriever function is
+ * equivalent to the generic &hwkey_keyslot->handler but is called only when a
+ * service allowed to unwrap opaque requests this handle.
+ *
+ * @token: The access token used as an opaque handle to
+ * reference this keyslot
+ * @allowed_uuids: Array of UUIDs that are allowed to retrieve the
+ * plaintext key corresponding to an opaque handle
+ * for this slot
+ * @allowed_uuids_len: Length of the @allowed_reader_uuids array
+ * @priv: Opaque pointer to keyslot-specific data
+ * @retriever: Keyslot-specific callback which retrieves the
+ * actual key corresponding to this opaque handle.
+ */
+struct hwkey_opaque_handle_data {
+ const uuid_t** allowed_uuids;
+ size_t allowed_uuids_len;
+ const void* priv;
+ uint32_t (*retriever)(const struct hwkey_keyslot* slot,
+ uint8_t* kbuf,
+ size_t kbuf_len,
+ size_t* klen);
+};
+
__BEGIN_CDECLS
/**
@@ -80,6 +112,29 @@
size_t kbuf_len,
size_t* klen);
+/**
+ * get_key_handle() - Handler for opaque keys
+ *
+ * Create and return an access token for a key slot. This key slot must contain
+ * a pointer to a &struct hwkey_opaque_handle_data in the &hwkey_keyslot.priv
+ * field.
+ */
+uint32_t get_key_handle(const struct hwkey_keyslot* slot,
+ uint8_t* kbuf,
+ size_t kbuf_len,
+ size_t* klen);
+
+/**
+ * get_opaque_key() - Get an opaque key given an access handle
+ *
+ * @access_token: pointer to an access_token_t
+ */
+uint32_t get_opaque_key(const uuid_t* uuid,
+ const char* access_token,
+ uint8_t* kbuf,
+ size_t kbuf_len,
+ size_t* klen);
+
void hwkey_init_srv_provider(void);
void hwkey_install_keys(const struct hwkey_keyslot* keys, unsigned int kcnt);