aboutsummaryrefslogtreecommitdiff
path: root/ks_volatile.c
diff options
context:
space:
mode:
Diffstat (limited to 'ks_volatile.c')
-rw-r--r--ks_volatile.c520
1 files changed, 472 insertions, 48 deletions
diff --git a/ks_volatile.c b/ks_volatile.c
index 00f656a..99ad68c 100644
--- a/ks_volatile.c
+++ b/ks_volatile.c
@@ -7,7 +7,7 @@
* to survive library exit, eg, for storing PKCS #11 session keys.
*
* Authors: Rob Austein
- * Copyright (c) 2015, NORDUnet A/S All rights reserved.
+ * Copyright (c) 2015-2016, NORDUnet A/S All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -37,106 +37,530 @@
*/
#include <string.h>
+#include <assert.h>
#include "hal.h"
#include "hal_internal.h"
+#define KEK_LENGTH (bitsToBytes(256))
+
+#ifndef STATIC_KS_VOLATILE_SLOTS
+#define STATIC_KS_VOLATILE_SLOTS HAL_STATIC_PKEY_STATE_BLOCKS
+#endif
+
+#ifndef STATIC_KS_VOLATILE_ATTRIBUTE_SPACE
+#define STATIC_KS_VOLATILE_ATTRIBUTE_SPACE 4096
+#endif
+
/*
- * Splitting the different keystore backends out into separate files
- * seemed like a good idea at the time, but the code is getting
- * somewhat repetitive. Might want to re-merge and conditionalize in
- * some other way. Deferred until we sort out ks_flash.c.
+ * In-memory keystore database. This should also be usable for
+ * mmap(), if and when we get around to rewriting that driver (and in
+ * which case this driver probably ought to be renamed ks_memory).
*/
+typedef struct {
+ hal_key_type_t type;
+ hal_curve_name_t curve;
+ hal_key_flags_t flags;
+ hal_client_handle_t client;
+ hal_session_handle_t session;
+ size_t der_len;
+ unsigned attributes_len;
+ uint8_t der[HAL_KS_WRAPPED_KEYSIZE + STATIC_KS_VOLATILE_ATTRIBUTE_SPACE];
+} ks_key_t;
+
+typedef struct {
+ hal_ks_index_t ksi;
+ ks_key_t *keys;
+} db_t;
+
+/*
+ * "Subclass" (well, what one can do in C) of hal_ks_t. This is
+ * separate from db_t primarily to simplify things like rewriting the
+ * old ks_mmap driver to piggy-back on the ks_volatile driver: we
+ * wouldn't want the hal_ks_t into the mmap()ed file.
+ */
+
+typedef struct {
+ hal_ks_t ks; /* Must be first */
+ db_t *db; /* Which memory-based keystore database */
+ int per_session; /* Whether objects are per-session */
+} ks_t;
+
/*
- * Use a one-element array here so that references can be pointer-based
- * as in the other implementations, to ease re-merge at some later date.
+ * If we also supported mmap, there would be a separate definition for
+ * HAL_KS_MMAP_SLOTS above, and the bulk of the code would be under a
+ * conditional testing whether either HAL_KS_*_SLOTS were nonzero.
*/
-static hal_ks_keydb_t db[1];
+#if STATIC_KS_VOLATILE_SLOTS > 0
+
+static ks_t volatile_ks;
+
+static inline ks_t *ks_to_ksv(hal_ks_t *ks)
+{
+ return (ks_t *) ks;
+}
/*
- * There's no good place to store the master key (KEK) in this volatile memory implementation.
- * We might be able to add a bit of protection doing things like using locked physical memory,
- * as gpg does, or obfuscating the KEK a bit to make it harder to pull out of a crash dump,
- * but, really, there's not a lot we can do against a determined opponant in this case.
+ * Check whether the current session can see a particular key. One
+ * might expect this to be based on whether the session matches, and
+ * indeed it would be in a sane world, but in the world of PKCS #11,
+ * keys belong to sessions, are visible to other sessions, and may
+ * even be modifiable by other sessions, but softly and silently
+ * vanish away when the original creating session is destroyed.
*
- * For now, we just go through the motions.
+ * In our terms, this means that visibility of session objects is
+ * determined only by the client handle, so taking the session handle
+ * as an argument here isn't really necessary, but we've flipflopped
+ * on that enough times that at least for now I'd prefer to leave the
+ * session handle here and not have to revise all the RPC calls again.
+ * Remove it at some later date and redo the RPC calls if we manage to
+ * avoid revising this yet again.
*/
-static uint8_t kekbuf[bitsToBytes(256)];
+static inline int key_visible_to_session(const ks_t * const ksv,
+ const hal_client_handle_t client,
+ const hal_session_handle_t session,
+ const ks_key_t * const k)
+{
+ return !ksv->per_session || client.handle == HAL_HANDLE_NONE || k->client.handle == client.handle;
+}
+
+static inline void *gnaw(uint8_t **mem, size_t *len, const size_t size)
+{
+ if (mem == NULL || *mem == NULL || len == NULL || size > *len)
+ return NULL;
+ void *ret = *mem;
+ *mem += size;
+ *len -= size;
+ return ret;
+}
+
+static hal_error_t ks_init(const hal_ks_driver_t * const driver,
+ const int per_session,
+ ks_t *ksv,
+ uint8_t *mem,
+ size_t len)
+{
+ if (ksv == NULL)
+ return HAL_ERROR_IMPOSSIBLE;
+
+ if (mem != NULL) {
+ memset(ksv, 0, sizeof(*ksv));
+ memset(mem, 0, len);
+
+ ksv->db = gnaw(&mem, &len, sizeof(*ksv->db));
+ ksv->db->ksi.index = gnaw(&mem, &len, sizeof(*ksv->db->ksi.index) * STATIC_KS_VOLATILE_SLOTS);
+ ksv->db->ksi.names = gnaw(&mem, &len, sizeof(*ksv->db->ksi.names) * STATIC_KS_VOLATILE_SLOTS);
+ ksv->db->keys = gnaw(&mem, &len, sizeof(*ksv->db->keys) * STATIC_KS_VOLATILE_SLOTS);
+ ksv->db->ksi.size = STATIC_KS_VOLATILE_SLOTS;
+ }
+
+ if (ksv->db == NULL ||
+ ksv->db->ksi.index == NULL ||
+ ksv->db->ksi.names == NULL ||
+ ksv->db->keys == NULL)
+ return HAL_ERROR_IMPOSSIBLE;
+
+ if (mem == NULL) {
+ memset(ksv->db->ksi.index, 0, sizeof(*ksv->db->ksi.index) * STATIC_KS_VOLATILE_SLOTS);
+ memset(ksv->db->ksi.names, 0, sizeof(*ksv->db->ksi.names) * STATIC_KS_VOLATILE_SLOTS);
+ memset(ksv->db->keys, 0, sizeof(*ksv->db->keys) * STATIC_KS_VOLATILE_SLOTS);
+ }
+
+ ksv->ks.driver = driver;
+ ksv->per_session = per_session;
+ ksv->db->ksi.used = 0;
-const hal_ks_keydb_t *hal_ks_get_keydb(void)
+ /*
+ * Set up keystore with empty index and full free list.
+ * Since this driver doesn't care about wear leveling,
+ * just populate the free list in block numerical order.
+ */
+
+ for (int i = 0; i < STATIC_KS_VOLATILE_SLOTS; i++)
+ ksv->db->ksi.index[i] = i;
+
+ return hal_ks_index_setup(&ksv->db->ksi);
+}
+
+static hal_error_t ks_volatile_init(const hal_ks_driver_t * const driver, const int alloc)
{
- return db;
+ const size_t len = (sizeof(*volatile_ks.db) +
+ sizeof(*volatile_ks.db->ksi.index) * STATIC_KS_VOLATILE_SLOTS +
+ sizeof(*volatile_ks.db->ksi.names) * STATIC_KS_VOLATILE_SLOTS +
+ sizeof(*volatile_ks.db->keys) * STATIC_KS_VOLATILE_SLOTS);
+
+ uint8_t *mem = NULL;
+
+ if (alloc && (mem = hal_allocate_static_memory(len)) == NULL)
+ return HAL_ERROR_ALLOCATION_FAILURE;
+
+ return ks_init(driver, 1, &volatile_ks, mem, len);
+}
+
+static hal_error_t ks_volatile_shutdown(const hal_ks_driver_t * const driver)
+{
+ if (volatile_ks.ks.driver != driver)
+ return HAL_ERROR_KEYSTORE_ACCESS;
+ return HAL_OK;
+}
+
+static hal_error_t ks_volatile_open(const hal_ks_driver_t * const driver,
+ hal_ks_t **ks)
+{
+ assert(driver != NULL && ks != NULL);
+ *ks = &volatile_ks.ks;
+ return HAL_OK;
+}
+
+static hal_error_t ks_volatile_close(hal_ks_t *ks)
+{
+ return HAL_OK;
+}
+
+static inline int acceptable_key_type(const hal_key_type_t type)
+{
+ switch (type) {
+ case HAL_KEY_TYPE_RSA_PRIVATE:
+ case HAL_KEY_TYPE_EC_PRIVATE:
+ case HAL_KEY_TYPE_RSA_PUBLIC:
+ case HAL_KEY_TYPE_EC_PUBLIC:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static hal_error_t ks_store(hal_ks_t *ks,
+ hal_pkey_slot_t *slot,
+ const uint8_t * const der, const size_t der_len)
+{
+ if (ks == NULL || slot == NULL || der == NULL || der_len == 0 || !acceptable_key_type(slot->type))
+ return HAL_ERROR_BAD_ARGUMENTS;
+
+ ks_t *ksv = ks_to_ksv(ks);
+ hal_error_t err;
+ unsigned b;
+
+ if (ksv->db == NULL)
+ return HAL_ERROR_KEYSTORE_ACCESS;
+
+ if ((err = hal_ks_index_add(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK)
+ return err;
+
+ uint8_t kek[KEK_LENGTH];
+ size_t kek_len;
+ ks_key_t k;
+
+ memset(&k, 0, sizeof(k));
+ k.der_len = sizeof(k.der);
+ k.type = slot->type;
+ k.curve = slot->curve;
+ k.flags = slot->flags;
+ k.client = slot->client_handle;
+ k.session = slot->session_handle;
+
+ if ((err = hal_mkm_get_kek(kek, &kek_len, sizeof(kek))) == HAL_OK)
+ err = hal_aes_keywrap(NULL, kek, kek_len, der, der_len, k.der, &k.der_len);
+
+ memset(kek, 0, sizeof(kek));
+
+ if (err == HAL_OK)
+ ksv->db->keys[b] = k;
+ else
+ (void) hal_ks_index_delete(&ksv->db->ksi, &slot->name, 0, NULL, &slot->hint);
+
+ return err;
+}
+
+static hal_error_t ks_fetch(hal_ks_t *ks,
+ hal_pkey_slot_t *slot,
+ uint8_t *der, size_t *der_len, const size_t der_max)
+{
+ if (ks == NULL || slot == NULL)
+ return HAL_ERROR_BAD_ARGUMENTS;
+
+ ks_t *ksv = ks_to_ksv(ks);
+ hal_error_t err;
+ unsigned b;
+
+ if (ksv->db == NULL)
+ return HAL_ERROR_KEYSTORE_ACCESS;
+
+ if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK)
+ return err;
+
+ const ks_key_t * const k = &ksv->db->keys[b];
+
+ if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, k))
+ return HAL_ERROR_KEY_NOT_FOUND;
+
+ slot->type = k->type;
+ slot->curve = k->curve;
+ slot->flags = k->flags;
+
+ if (der == NULL && der_len != NULL)
+ *der_len = k->der_len;
+
+ if (der != NULL) {
+
+ uint8_t kek[KEK_LENGTH];
+ size_t kek_len, der_len_;
+ hal_error_t err;
+
+ if (der_len == NULL)
+ der_len = &der_len_;
+
+ *der_len = der_max;
+
+ if ((err = hal_mkm_get_kek(kek, &kek_len, sizeof(kek))) == HAL_OK)
+ err = hal_aes_keyunwrap(NULL, kek, kek_len, k->der, k->der_len, der, der_len);
+
+ memset(kek, 0, sizeof(kek));
+
+ if (err != HAL_OK)
+ return err;
+ }
+
+ return HAL_OK;
}
-hal_error_t hal_ks_set_keydb(const hal_ks_key_t * const key,
- const int loc,
- const int updating)
+static hal_error_t ks_delete(hal_ks_t *ks,
+ hal_pkey_slot_t *slot)
{
- if (key == NULL || loc < 0 || loc >= sizeof(db->keys)/sizeof(*db->keys) || (!key->in_use != !updating))
+ if (ks == NULL || slot == NULL)
return HAL_ERROR_BAD_ARGUMENTS;
- db->keys[loc] = *key;
- db->keys[loc].in_use = 1;
+ ks_t *ksv = ks_to_ksv(ks);
+ hal_error_t err;
+ unsigned b;
+
+ if (ksv->db == NULL)
+ return HAL_ERROR_KEYSTORE_ACCESS;
+
+ if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK)
+ return err;
+
+ if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, &ksv->db->keys[b]))
+ return HAL_ERROR_KEY_NOT_FOUND;
+
+ if ((err = hal_ks_index_delete(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK)
+ return err;
+
+ memset(&ksv->db->keys[b], 0, sizeof(ksv->db->keys[b]));
+
return HAL_OK;
}
-hal_error_t hal_ks_del_keydb(const int loc)
+static hal_error_t ks_match(hal_ks_t *ks,
+ hal_client_handle_t client,
+ hal_session_handle_t session,
+ const hal_key_type_t type,
+ const hal_curve_name_t curve,
+ const hal_key_flags_t flags,
+ const hal_pkey_attribute_t *attributes,
+ const unsigned attributes_len,
+ hal_uuid_t *result,
+ unsigned *result_len,
+ const unsigned result_max,
+ const hal_uuid_t * const previous_uuid)
{
- if (loc < 0 || loc >= sizeof(db->keys)/sizeof(*db->keys))
+ if (ks == NULL || (attributes == NULL && attributes_len > 0) ||
+ result == NULL || result_len == NULL || previous_uuid == NULL)
return HAL_ERROR_BAD_ARGUMENTS;
- memset(&db->keys[loc], 0, sizeof(db->keys[loc]));
+ ks_t *ksv = ks_to_ksv(ks);
+
+ if (ksv->db == NULL)
+ return HAL_ERROR_KEYSTORE_ACCESS;
+
+ hal_error_t err;
+ int i = -1;
+
+ *result_len = 0;
+
+ err = hal_ks_index_find(&ksv->db->ksi, previous_uuid, 0, NULL, &i);
+
+ if (err == HAL_ERROR_KEY_NOT_FOUND)
+ i--;
+ else if (err != HAL_OK)
+ return err;
+
+ while (*result_len < result_max && ++i < ksv->db->ksi.used) {
+
+ unsigned b = ksv->db->ksi.index[i];
+
+ if (ksv->db->ksi.names[b].chunk > 0)
+ continue;
+
+ if (type != HAL_KEY_TYPE_NONE && type != ksv->db->keys[b].type)
+ continue;
+
+ if (curve != HAL_CURVE_NONE && curve != ksv->db->keys[b].curve)
+ continue;
+
+ if (!key_visible_to_session(ksv, client, session, &ksv->db->keys[b]))
+ continue;
+
+ if (attributes_len > 0) {
+ const ks_key_t * const k = &ksv->db->keys[b];
+ int ok = 1;
+
+ if (k->attributes_len == 0)
+ continue;
+
+ hal_pkey_attribute_t key_attrs[k->attributes_len];
+
+ if ((err = hal_ks_attribute_scan(k->der + k->der_len, sizeof(k->der) - k->der_len,
+ key_attrs, k->attributes_len, NULL)) != HAL_OK)
+ return err;
+
+ for (const hal_pkey_attribute_t *required = attributes;
+ ok && required < attributes + attributes_len; required++) {
+
+ hal_pkey_attribute_t *present = key_attrs;
+ while (ok && present->type != required->type)
+ ok = ++present < key_attrs + k->attributes_len;
+
+ if (ok)
+ ok = (present->length == required->length &&
+ !memcmp(present->value, required->value, present->length));
+ }
+
+ if (!ok)
+ continue;
+ }
+
+ result[*result_len] = ksv->db->ksi.names[b].name;
+ ++*result_len;
+ }
+
return HAL_OK;
}
-hal_error_t hal_ks_set_pin(const hal_user_t user,
- const hal_ks_pin_t * const pin)
+static hal_error_t ks_set_attributes(hal_ks_t *ks,
+ hal_pkey_slot_t *slot,
+ const hal_pkey_attribute_t *attributes,
+ const unsigned attributes_len)
{
- if (pin == NULL)
+ if (ks == NULL || slot == NULL || attributes == NULL || attributes_len == 0)
return HAL_ERROR_BAD_ARGUMENTS;
- hal_ks_pin_t *p = NULL;
+ ks_t *ksv = ks_to_ksv(ks);
+ hal_error_t err;
+ unsigned b;
+
+ if (ksv->db == NULL)
+ return HAL_ERROR_KEYSTORE_ACCESS;
+
+ if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK)
+ return err;
- switch (user) {
- case HAL_USER_WHEEL: p = &db->wheel_pin; break;
- case HAL_USER_SO: p = &db->so_pin; break;
- case HAL_USER_NORMAL: p = &db->user_pin; break;
- default: return HAL_ERROR_BAD_ARGUMENTS;
+ ks_key_t * const k = &ksv->db->keys[b];
+
+ if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, k))
+ return HAL_ERROR_KEY_NOT_FOUND;
+
+ hal_pkey_attribute_t attrs[k->attributes_len + attributes_len];
+ uint8_t *bytes = k->der + k->der_len;
+ size_t bytes_len = sizeof(k->der) - k->der_len;
+ size_t total_len;
+
+ if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, k->attributes_len, &total_len)) != HAL_OK)
+ return err;
+
+ for (const hal_pkey_attribute_t *a = attributes; a < attributes + attributes_len; a++) {
+ if (a->length == HAL_PKEY_ATTRIBUTE_NIL)
+ err = hal_ks_attribute_delete(bytes, bytes_len, attrs, &k->attributes_len, &total_len,
+ a->type);
+ else
+ err = hal_ks_attribute_insert(bytes, bytes_len, attrs, &k->attributes_len, &total_len,
+ a->type, a->value, a->length);
+ if (err != HAL_OK)
+ return err;
}
- *p = *pin;
return HAL_OK;
}
-hal_error_t hal_ks_get_kek(uint8_t *kek,
- size_t *kek_len,
- const size_t kek_max)
+static hal_error_t ks_get_attributes(hal_ks_t *ks,
+ hal_pkey_slot_t *slot,
+ hal_pkey_attribute_t *attributes,
+ const unsigned attributes_len,
+ uint8_t *attributes_buffer,
+ const size_t attributes_buffer_len)
{
- if (kek == NULL || kek_len == NULL || kek_max < bitsToBytes(128))
+ if (ks == NULL || slot == NULL || attributes == NULL || attributes_len == 0 ||
+ attributes_buffer == NULL)
return HAL_ERROR_BAD_ARGUMENTS;
+ ks_t *ksv = ks_to_ksv(ks);
hal_error_t err;
+ unsigned b;
- const size_t len = ((kek_max < bitsToBytes(192)) ? bitsToBytes(128) :
- (kek_max < bitsToBytes(256)) ? bitsToBytes(192) :
- bitsToBytes(256));
+ if (ksv->db == NULL)
+ return HAL_ERROR_KEYSTORE_ACCESS;
- uint8_t t = 0;
+ if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK)
+ return err;
+
+ const ks_key_t * const k = &ksv->db->keys[b];
- for (int i = 0; i < sizeof(kekbuf); i++)
- t |= kekbuf[i];
+ if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, k))
+ return HAL_ERROR_KEY_NOT_FOUND;
- if (t == 0 && (err = hal_rpc_get_random(kekbuf, sizeof(kekbuf))) != HAL_OK)
+ hal_pkey_attribute_t attrs[k->attributes_len > 0 ? k->attributes_len : 1];
+
+ if ((err = hal_ks_attribute_scan(k->der + k->der_len, sizeof(k->der) - k->der_len,
+ attrs, k->attributes_len, NULL)) != HAL_OK)
return err;
- memcpy(kek, kekbuf, len);
- *kek_len = len;
+ uint8_t *abuf = attributes_buffer;
+
+ for (int i = 0; i < attributes_len; i++) {
+ int j = 0;
+ while (j < k->attributes_len && attrs[j].type != attributes[i].type)
+ j++;
+ const int found = j < k->attributes_len;
+
+ if (attributes_buffer_len == 0) {
+ attributes[i].value = NULL;
+ attributes[i].length = found ? attrs[j].length : 0;
+ continue;
+ }
+
+ if (!found)
+ return HAL_ERROR_ATTRIBUTE_NOT_FOUND;
+
+ if (attrs[j].length > attributes_buffer + attributes_buffer_len - abuf)
+ return HAL_ERROR_RESULT_TOO_LONG;
+
+ memcpy(abuf, attrs[j].value, attrs[j].length);
+ attributes[i].value = abuf;
+ attributes[i].length = attrs[j].length;
+ abuf += attrs[j].length;
+ }
+
return HAL_OK;
}
+const hal_ks_driver_t hal_ks_volatile_driver[1] = {{
+ .init = ks_volatile_init,
+ .shutdown = ks_volatile_shutdown,
+ .open = ks_volatile_open,
+ .close = ks_volatile_close,
+ .store = ks_store,
+ .fetch = ks_fetch,
+ .delete = ks_delete,
+ .match = ks_match,
+ .set_attributes = ks_set_attributes,
+ .get_attributes = ks_get_attributes
+}};
+
+#endif /* STATIC_KS_VOLATILE_SLOTS > 0 */
+
/*
* Local variables:
* indent-tabs-mode: nil