From d008267960337e9e44b270b26555a7a894808746 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Mon, 24 Apr 2017 08:33:11 -0400 Subject: Clean up pkey slots and volatile keys on client logout. --- hal_internal.h | 21 ++++++++++++++++++ ks_volatile.c | 30 ++++++++++++++++++++++++- rpc_misc.c | 69 +++++++++++++++++++++++++++++++++++++++++++++------------- rpc_pkey.c | 28 ++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 16 deletions(-) diff --git a/hal_internal.h b/hal_internal.h index 56d0936..b20bff2 100644 --- a/hal_internal.h +++ b/hal_internal.h @@ -427,6 +427,12 @@ extern hal_error_t hal_mkm_flash_erase(const size_t len); #endif +/* + * Clean up pkey stuff that's tied to a particular client. + */ + +extern hal_error_t hal_pkey_client_cleanup(const hal_client_handle_t client); + /* * Keystore API for use by the pkey implementation. * @@ -520,6 +526,9 @@ struct hal_ks_driver { uint8_t *attributes_buffer, const size_t attributes_buffer_len); + hal_error_t (*client_cleanup)(hal_ks_t *ks, + const hal_client_handle_t client); + }; @@ -681,6 +690,18 @@ static inline hal_error_t hal_ks_get_attributes(hal_ks_t *ks, attributes_buffer, attributes_buffer_len); } +static inline hal_error_t hal_ks_client_cleanup(hal_ks_t *ks, + const hal_client_handle_t client) +{ + if (ks == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (ks->client_cleanup == NULL || client.handle == HAL_HANDLE_NONE) + return HAL_OK; + + return ks->driver->client_cleanup(ks, client); +} + /* * Keystore index. This is intended to be usable by both memory-based * (in-memory, mmap(), ...) keystores and keystores based on raw flash. diff --git a/ks_volatile.c b/ks_volatile.c index d565c60..363441a 100644 --- a/ks_volatile.c +++ b/ks_volatile.c @@ -614,6 +614,33 @@ static hal_error_t ks_get_attributes(hal_ks_t *ks, return err; } +static void ks_client_cleanup(hal_ks_t *ks, + hal_client_handle_t client) +{ + if (ks == NULL || client.handle = HAL_HANDLE_NONE) + return HAL_ERROR_BAD_ARGUMENTS; + + ks_t *ksv = ks_to_ksv(ks); + hal_error_t err = HAL_OK; + + hal_ks_lock(); + + for (int i = 0; i < ksv->db->ksi.used; i++) { + unsigned b = ksv->db->ksi.index[i]; + if (ksv->db->keys[b].client.handle == client.handle) { + int hint = i; + if ((err = hal_ks_index_delete(&ksv->db->ksi, &ksv->db->ksi.names[b].name, 0, NULL, &hint)) != HAL_OK) + goto done; + memset(&ksv->db->keys[b], 0, sizeof(ksv->db->keys[b])); + i--; + } + } + + done: + hal_ks_unlock(); + return err; +} + const hal_ks_driver_t hal_ks_volatile_driver[1] = {{ .init = ks_volatile_init, .shutdown = ks_volatile_shutdown, @@ -624,7 +651,8 @@ const hal_ks_driver_t hal_ks_volatile_driver[1] = {{ .delete = ks_delete, .match = ks_match, .set_attributes = ks_set_attributes, - .get_attributes = ks_get_attributes + .get_attributes = ks_get_attributes, + .client_cleanup = ks_client_cleanup }}; #endif /* STATIC_KS_VOLATILE_SLOTS > 0 */ diff --git a/rpc_misc.c b/rpc_misc.c index cf5e4a0..2fd743a 100644 --- a/rpc_misc.c +++ b/rpc_misc.c @@ -101,21 +101,47 @@ static client_slot_t client_handle[HAL_STATIC_CLIENT_STATE_BLOCKS]; * them. HAL_USER_NONE indicates an empty slot in the table. */ -static inline client_slot_t *alloc_slot(void) +static inline client_slot_t *alloc_slot(const hal_client_handle_t client, + const hal_user_t user) { client_slot_t *slot = NULL; hal_critical_section_start(); #if HAL_STATIC_CLIENT_STATE_BLOCKS > 0 + + for (int i = 0; slot == NULL && i < sizeof(client_handle)/sizeof(*client_handle); i++) + if (client_handle[i].logged_in != HAL_USER_NONE && client_handle[i].handle.handle == handle.handle) + slot = &client_handle[i]; + for (int i = 0; slot == NULL && i < sizeof(client_handle)/sizeof(*client_handle); i++) if (client_handle[i].logged_in == HAL_USER_NONE) slot = &client_handle[i]; + #endif + if (slot != NULL) { + slot->handle = client; + slot->logged_in = user; + } + hal_critical_section_end(); return slot; } +static inline void clear_slot(client_slot_t *slot) +{ + if (slot == NULL) + return; + + hal_pkey_client_cleanup(slot->handle); + + hal_critical_section_start(); + + memset(slot, 0, sizeof(*slot)); + + hal_critical_section_end(); +} + static inline client_slot_t *find_handle(const hal_client_handle_t handle) { client_slot_t *slot = NULL; @@ -158,14 +184,9 @@ static hal_error_t login(const hal_client_handle_t client, if (diff != 0) return HAL_ERROR_PIN_INCORRECT; - client_slot_t *slot = find_handle(client); - - if (slot == NULL && (slot = alloc_slot()) == NULL) + if (alloc_slot(client, user) == NULL) return HAL_ERROR_NO_CLIENT_SLOTS_AVAILABLE; - slot->handle = client; - slot->logged_in = user; - return HAL_OK; } @@ -184,21 +205,39 @@ static hal_error_t is_logged_in(const hal_client_handle_t client, static hal_error_t logout(const hal_client_handle_t client) { - client_slot_t *slot = find_handle(client); - - if (slot != NULL) - slot->logged_in = HAL_USER_NONE; - - return HAL_OK; + return clear_slot(find_handle(client)); } static hal_error_t logout_all(void) { + /* + * This is a bit inefficient, but it lets us keep the control + * structure simple. + */ + + client_slot_t *slot; + hal_error_t err; + + do { + slot = NULL; + #if HAL_STATIC_CLIENT_STATE_BLOCKS > 0 - for (int i = 0; i < sizeof(client_handle)/sizeof(*client_handle); i++) - client_handle[i].logged_in = HAL_USER_NONE; + + hal_critical_section_start(); + + for (int i = 0; slot == NULL && i < sizeof(client_handle)/sizeof(*client_handle); i++) + if (client_handle[i].logged_in != HAL_USER_NONE) + slot = &client_handle[i]; + + hal_critical_section_end(); + #endif + if ((err = clear_slot(slot)) != HAL_OK) + return err; + + } while (slot != NULL); + return HAL_OK; } diff --git a/rpc_pkey.c b/rpc_pkey.c index bdf8a7e..0da2410 100644 --- a/rpc_pkey.c +++ b/rpc_pkey.c @@ -128,6 +128,34 @@ static inline hal_pkey_slot_t *find_handle(const hal_pkey_handle_t handle) return slot; } +/* + * Clean up key state associated with a client. + */ + +hal_error_t hal_pkey_client_cleanup(const hal_client_handle_t client) +{ + if (client.handle == HAL_HANDLE_NONE) + return HAL_OK; + + hal_error_t err; + + if ((err = hal_ks_client_cleanup(hal_ks_volatile_driver, client)) != HAL_OK) + return err; + + if ((err = hal_ks_client_cleanup(hal_ks_flash_driver, client)) != HAL_OK) + return err; + + hal_critical_section_start(); + + for (int i = 0; i < sizeof(pkey_slot)/sizeof(*pkey_slot); i++) + if (pkey_slot[i].pkey_handle.handle == client.handle) + memset(&pkey_slot[i], 0, sizeof(pkey_slot[i])); + + hal_critical_section_end(); + + return HAL_OK; +} + /* * Access rules are a bit complicated, mostly due to PKCS #11. * -- cgit v1.2.3 From 4ee44177c6da04e210a52528763b2c96a8f3d824 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Mon, 24 Apr 2017 17:23:17 -0400 Subject: Call a portable entrenching tool a portable entrenching tool. --- hal_internal.h | 16 ++++++++-------- ks_volatile.c | 6 +++--- rpc_misc.c | 2 +- rpc_pkey.c | 10 ++++------ 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/hal_internal.h b/hal_internal.h index b20bff2..f6c31fe 100644 --- a/hal_internal.h +++ b/hal_internal.h @@ -428,10 +428,10 @@ extern hal_error_t hal_mkm_flash_erase(const size_t len); #endif /* - * Clean up pkey stuff that's tied to a particular client. + * Clean up pkey stuff that's tied to a particular client on logout. */ -extern hal_error_t hal_pkey_client_cleanup(const hal_client_handle_t client); +extern hal_error_t hal_pkey_logout(const hal_client_handle_t client); /* * Keystore API for use by the pkey implementation. @@ -526,8 +526,8 @@ struct hal_ks_driver { uint8_t *attributes_buffer, const size_t attributes_buffer_len); - hal_error_t (*client_cleanup)(hal_ks_t *ks, - const hal_client_handle_t client); + hal_error_t (*logout)(hal_ks_t *ks, + const hal_client_handle_t client); }; @@ -690,16 +690,16 @@ static inline hal_error_t hal_ks_get_attributes(hal_ks_t *ks, attributes_buffer, attributes_buffer_len); } -static inline hal_error_t hal_ks_client_cleanup(hal_ks_t *ks, - const hal_client_handle_t client) +static inline hal_error_t hal_ks_logout(hal_ks_t *ks, + const hal_client_handle_t client) { if (ks == NULL) return HAL_ERROR_BAD_ARGUMENTS; - if (ks->client_cleanup == NULL || client.handle == HAL_HANDLE_NONE) + if (ks->logout == NULL || client.handle == HAL_HANDLE_NONE) return HAL_OK; - return ks->driver->client_cleanup(ks, client); + return ks->driver->logout(ks, client); } /* diff --git a/ks_volatile.c b/ks_volatile.c index 363441a..29c3576 100644 --- a/ks_volatile.c +++ b/ks_volatile.c @@ -614,8 +614,8 @@ static hal_error_t ks_get_attributes(hal_ks_t *ks, return err; } -static void ks_client_cleanup(hal_ks_t *ks, - hal_client_handle_t client) +static void ks_logout(hal_ks_t *ks, + hal_client_handle_t client) { if (ks == NULL || client.handle = HAL_HANDLE_NONE) return HAL_ERROR_BAD_ARGUMENTS; @@ -652,7 +652,7 @@ const hal_ks_driver_t hal_ks_volatile_driver[1] = {{ .match = ks_match, .set_attributes = ks_set_attributes, .get_attributes = ks_get_attributes, - .client_cleanup = ks_client_cleanup + .logout = ks_logout }}; #endif /* STATIC_KS_VOLATILE_SLOTS > 0 */ diff --git a/rpc_misc.c b/rpc_misc.c index 2fd743a..4db6ed3 100644 --- a/rpc_misc.c +++ b/rpc_misc.c @@ -133,7 +133,7 @@ static inline void clear_slot(client_slot_t *slot) if (slot == NULL) return; - hal_pkey_client_cleanup(slot->handle); + hal_pkey_logout(slot->handle); hal_critical_section_start(); diff --git a/rpc_pkey.c b/rpc_pkey.c index 0da2410..165419e 100644 --- a/rpc_pkey.c +++ b/rpc_pkey.c @@ -129,20 +129,18 @@ static inline hal_pkey_slot_t *find_handle(const hal_pkey_handle_t handle) } /* - * Clean up key state associated with a client. + * Clean up key state associated with a client when logging out. */ -hal_error_t hal_pkey_client_cleanup(const hal_client_handle_t client) +hal_error_t hal_pkey_logout(const hal_client_handle_t client) { if (client.handle == HAL_HANDLE_NONE) return HAL_OK; hal_error_t err; - if ((err = hal_ks_client_cleanup(hal_ks_volatile_driver, client)) != HAL_OK) - return err; - - if ((err = hal_ks_client_cleanup(hal_ks_flash_driver, client)) != HAL_OK) + if ((err = hal_ks_logout(hal_ks_volatile_driver, client)) != HAL_OK || + (err = hal_ks_logout(hal_ks_flash_driver, client)) != HAL_OK) return err; hal_critical_section_start(); -- cgit v1.2.3 From 358b3803cdedad607cf649221d0b7e3ce66b45f2 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Tue, 25 Apr 2017 17:14:40 -0400 Subject: Shake dumb compile-time bugs out of new logout code. What I get for writing code while build and test environment is tied up with a multi-day run testing something else. --- hal_internal.h | 2 +- ks_volatile.c | 6 +++--- rpc_misc.c | 14 ++++++++++---- rpc_pkey.c | 19 +++++++++++++++++-- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/hal_internal.h b/hal_internal.h index f6c31fe..13c79e9 100644 --- a/hal_internal.h +++ b/hal_internal.h @@ -696,7 +696,7 @@ static inline hal_error_t hal_ks_logout(hal_ks_t *ks, if (ks == NULL) return HAL_ERROR_BAD_ARGUMENTS; - if (ks->logout == NULL || client.handle == HAL_HANDLE_NONE) + if (ks->driver->logout == NULL || client.handle == HAL_HANDLE_NONE) return HAL_OK; return ks->driver->logout(ks, client); diff --git a/ks_volatile.c b/ks_volatile.c index 29c3576..6a17e45 100644 --- a/ks_volatile.c +++ b/ks_volatile.c @@ -614,10 +614,10 @@ static hal_error_t ks_get_attributes(hal_ks_t *ks, return err; } -static void ks_logout(hal_ks_t *ks, - hal_client_handle_t client) +static hal_error_t ks_logout(hal_ks_t *ks, + hal_client_handle_t client) { - if (ks == NULL || client.handle = HAL_HANDLE_NONE) + if (ks == NULL || client.handle == HAL_HANDLE_NONE) return HAL_ERROR_BAD_ARGUMENTS; ks_t *ksv = ks_to_ksv(ks); diff --git a/rpc_misc.c b/rpc_misc.c index 4db6ed3..e9ff4c6 100644 --- a/rpc_misc.c +++ b/rpc_misc.c @@ -110,7 +110,8 @@ static inline client_slot_t *alloc_slot(const hal_client_handle_t client, #if HAL_STATIC_CLIENT_STATE_BLOCKS > 0 for (int i = 0; slot == NULL && i < sizeof(client_handle)/sizeof(*client_handle); i++) - if (client_handle[i].logged_in != HAL_USER_NONE && client_handle[i].handle.handle == handle.handle) + if (client_handle[i].logged_in != HAL_USER_NONE && + client_handle[i].handle.handle == client.handle) slot = &client_handle[i]; for (int i = 0; slot == NULL && i < sizeof(client_handle)/sizeof(*client_handle); i++) @@ -128,18 +129,23 @@ static inline client_slot_t *alloc_slot(const hal_client_handle_t client, return slot; } -static inline void clear_slot(client_slot_t *slot) +static inline hal_error_t clear_slot(client_slot_t *slot) { if (slot == NULL) - return; + return HAL_OK; + + hal_error_t err; - hal_pkey_logout(slot->handle); + if ((err = hal_pkey_logout(slot->handle)) != HAL_OK) + return err; hal_critical_section_start(); memset(slot, 0, sizeof(*slot)); hal_critical_section_end(); + + return HAL_OK; } static inline client_slot_t *find_handle(const hal_client_handle_t handle) diff --git a/rpc_pkey.c b/rpc_pkey.c index 165419e..5af6c0e 100644 --- a/rpc_pkey.c +++ b/rpc_pkey.c @@ -138,9 +138,24 @@ hal_error_t hal_pkey_logout(const hal_client_handle_t client) return HAL_OK; hal_error_t err; + hal_ks_t *ks; - if ((err = hal_ks_logout(hal_ks_volatile_driver, client)) != HAL_OK || - (err = hal_ks_logout(hal_ks_flash_driver, client)) != HAL_OK) + if ((err = hal_ks_open(hal_ks_volatile_driver, &ks)) != HAL_OK) + return err; + if ((err = hal_ks_logout(ks, client)) == HAL_OK) + err = hal_ks_close(ks); + else + (void) hal_ks_close(ks); + if (err != HAL_OK) + return err; + + if ((err = hal_ks_open(hal_ks_token_driver, &ks)) != HAL_OK) + return err; + if ((err = hal_ks_logout(ks, client)) == HAL_OK) + err = hal_ks_close(ks); + else + (void) hal_ks_close(ks); + if (err != HAL_OK) return err; hal_critical_section_start(); -- cgit v1.2.3 From 2b4dc660d9d10eed407851319bfe63d5f9c3acd1 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Mon, 22 May 2017 23:22:09 -0400 Subject: First pass on experimental one-size-fits-nobody keystore. Support for variable-length keystore objects significantly complicates the keystore implementation, including serious some serious code bloat and a complex recovery algorithm to deal with crashes or loss of power at exactly the wrong time. Perhaps we don't really need this? So this is an experiment to see whether we can replace variable-length keystore objects with fixed-length, perhaps with a compile time option to let us make the fixed object length be 8192 bytes instead of 4096 bytes when needed to hold things like large RSA keys. First pass on this is just throwing away nearly 1,000 lines of excessively complex code. The result probably won't even compile yet, but it's already significantly easier to read. --- hal.h | 3 - hal_internal.h | 40 +-- ks_flash.c | 788 +++++++-------------------------------------------------- ks_index.c | 175 +------------ ks_volatile.c | 3 - 5 files changed, 107 insertions(+), 902 deletions(-) diff --git a/hal.h b/hal.h index abcaf52..c89083c 100644 --- a/hal.h +++ b/hal.h @@ -158,9 +158,6 @@ DEFINE_HAL_ERROR(HAL_ERROR_ATTRIBUTE_NOT_FOUND, "Attribute not found") \ DEFINE_HAL_ERROR(HAL_ERROR_NO_KEY_INDEX_SLOTS, "No key index slots available") \ DEFINE_HAL_ERROR(HAL_ERROR_KSI_INDEX_UUID_MISORDERED, "Key index UUID misordered") \ - DEFINE_HAL_ERROR(HAL_ERROR_KSI_INDEX_CHUNK_ORPHANED, "Key index chunk orphaned") \ - DEFINE_HAL_ERROR(HAL_ERROR_KSI_INDEX_CHUNK_MISSING, "Key index chunk missing") \ - DEFINE_HAL_ERROR(HAL_ERROR_KSI_INDEX_CHUNK_OVERLAPS, "Key index chunk overlaps") \ DEFINE_HAL_ERROR(HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE, "Wrong block type in keystore") \ DEFINE_HAL_ERROR(HAL_ERROR_RPC_PROTOCOL_ERROR, "RPC protocol error") \ DEFINE_HAL_ERROR(HAL_ERROR_NOT_IMPLEMENTED, "Not implemented") \ diff --git a/hal_internal.h b/hal_internal.h index 3aadb48..aa31585 100644 --- a/hal_internal.h +++ b/hal_internal.h @@ -708,13 +708,6 @@ static inline hal_error_t hal_ks_get_attributes(hal_ks_t *ks, * support a simplistic form of wear leveling in flash-based keystores. * * Key names are kept in a separate array, indexed by block number. - * Key names here are a composite of the key's UUID and a "chunk" - * number; the latter allows storage of keys whose total size exceeds - * one block (whatever a block is). For the moment we keep the UUID - * and the chunk number in a single array, which may provide (very) - * slightly better performance due to reference locality in SDRAM, but - * this may change if we need to reclaim the space wasted by structure - * size rounding. * * The all-zeros UUID, which (by definition) cannot be a valid key * UUID, is reserved for the (non-key) block used to stash PINs and @@ -726,16 +719,11 @@ static inline hal_error_t hal_ks_get_attributes(hal_ks_t *ks, * portion of the API is only concerned with allocation and naming. */ -typedef struct { - hal_uuid_t name; /* Key name */ - uint8_t chunk; /* Key chunk number */ -} hal_ks_name_t; - typedef struct { unsigned size; /* Array length */ unsigned used; /* How many blocks are in use */ uint16_t *index; /* Index/freelist array */ - hal_ks_name_t *names; /* Keyname array */ + hal_uuid_t *names; /* Keyname array */ } hal_ks_index_t; /* @@ -755,27 +743,14 @@ extern hal_error_t hal_ks_index_setup(hal_ks_index_t *ksi); */ extern hal_error_t hal_ks_index_find(hal_ks_index_t *ksi, const hal_uuid_t * const name, - const unsigned chunk, unsigned *blockno, int *hint); -/* - * Find all the blocks in a key, return the block numbers. - */ -extern hal_error_t hal_ks_index_find_range(hal_ks_index_t *ksi, - const hal_uuid_t * const name, - const unsigned max_blocks, - unsigned *n_blocks, - unsigned *blocknos, - int *hint, - const int strict); - /* * Add a key block, return its block number. */ extern hal_error_t hal_ks_index_add(hal_ks_index_t *ksi, const hal_uuid_t * const name, - const unsigned chunk, unsigned *blockno, int *hint); @@ -784,21 +759,9 @@ extern hal_error_t hal_ks_index_add(hal_ks_index_t *ksi, */ extern hal_error_t hal_ks_index_delete(hal_ks_index_t *ksi, const hal_uuid_t * const name, - const unsigned chunk, unsigned *blockno, int *hint); -/* - * Delete all of blocks in a key, returning the block numbers. - */ - -extern hal_error_t hal_ks_index_delete_range(hal_ks_index_t *ksi, - const hal_uuid_t * const name, - const unsigned max_blocks, - unsigned *n_blocks, - unsigned *blocknos, - int *hint); - /* * Replace a key block with a new one, return new block number. * Name of block does not change. This is an optimization of @@ -807,7 +770,6 @@ extern hal_error_t hal_ks_index_delete_range(hal_ks_index_t *ksi, extern hal_error_t hal_ks_index_replace(hal_ks_index_t *ksi, const hal_uuid_t * const name, - const unsigned chunk, unsigned *blockno, int *hint); diff --git a/ks_flash.c b/ks_flash.c index 8aadc37..e073b84 100644 --- a/ks_flash.c +++ b/ks_flash.c @@ -70,7 +70,6 @@ typedef enum { BLOCK_TYPE_ERASED = 0xFF, /* Pristine erased block (candidate for reuse) */ BLOCK_TYPE_ZEROED = 0x00, /* Zeroed block (recently used) */ BLOCK_TYPE_KEY = 0x55, /* Block contains key material */ - BLOCK_TYPE_ATTR = 0x66, /* Block contains key attributes (overflow from key block) */ BLOCK_TYPE_PIN = 0xAA, /* Block contains PINs */ BLOCK_TYPE_UNKNOWN = -1, /* Internal code for "I have no clue what this is" */ } flash_block_type_t; @@ -93,8 +92,6 @@ typedef enum { typedef struct { uint8_t block_type; uint8_t block_status; - uint8_t total_chunks; - uint8_t this_chunk; hal_crc32_t crc; } flash_block_header_t; @@ -116,20 +113,6 @@ typedef struct { #define SIZEOF_FLASH_KEY_BLOCK_DER \ (KEYSTORE_SUBSECTOR_SIZE - offsetof(flash_key_block_t, der)) -/* - * Key attribute overflow block (attributes which don't fit in der field of key block). - */ - -typedef struct { - flash_block_header_t header; - hal_uuid_t name; - unsigned attributes_len; - uint8_t attributes[]; /* Must be last field -- C99 "flexible array member" */ -} flash_attributes_block_t; - -#define SIZEOF_FLASH_ATTRIBUTE_BLOCK_ATTRIBUTES \ - (KEYSTORE_SUBSECTOR_SIZE - offsetof(flash_attributes_block_t, attributes)) - /* * PIN block. Also includes space for backing up the KEK when * HAL_MKM_FLASH_BACKUP_KLUDGE is enabled. @@ -308,12 +291,6 @@ static hal_crc32_t calculate_block_crc(const flash_block_t * const block) crc = hal_crc32_update(crc, &block->header.block_type, sizeof(block->header.block_type)); - crc = hal_crc32_update(crc, &block->header.total_chunks, - sizeof(block->header.total_chunks)); - - crc = hal_crc32_update(crc, &block->header.this_chunk, - sizeof(block->header.this_chunk)); - crc = hal_crc32_update(crc, block->bytes + sizeof(flash_block_header_t), sizeof(*block) - sizeof(flash_block_header_t)); @@ -525,7 +502,7 @@ static hal_error_t block_write(const unsigned blockno, flash_block_t *block) */ static hal_error_t block_update(const unsigned b1, flash_block_t *block, - const hal_uuid_t * const uuid, const unsigned chunk, int *hint) + const hal_uuid_t * const uuid, int *hint) { if (block == NULL) return HAL_ERROR_IMPOSSIBLE; @@ -538,10 +515,10 @@ static hal_error_t block_update(const unsigned b1, flash_block_t *block, hal_error_t err; unsigned b2; - if ((err = block_deprecate(b1)) != HAL_OK || - (err = hal_ks_index_replace(&db.ksi, uuid, chunk, &b2, hint)) != HAL_OK || - (err = block_write(b2, block)) != HAL_OK || - (err = block_zero(b1)) != HAL_OK) + if ((err = block_deprecate(b1)) != HAL_OK || + (err = hal_ks_index_replace(&db.ksi, uuid, &b2, hint)) != HAL_OK || + (err = block_write(b2, block)) != HAL_OK || + (err = block_zero(b1)) != HAL_OK) return err; cache_mark_used(block, b2); @@ -702,7 +679,6 @@ static hal_error_t ks_init(const hal_ks_driver_t * const driver, const int alloc if (uuid != NULL) { db.ksi.names[i].name = *uuid; - db.ksi.names[i].chunk = block->header.this_chunk; db.ksi.index[n++] = i; } } @@ -755,173 +731,69 @@ static hal_error_t ks_init(const hal_ks_driver_t * const driver, const int alloc */ /* - * Deal with tombstones. These are blocks left behind when - * something bad (like a power failure) happened while we updating. - * The sequence of operations while updating is designed so that, - * barring a bug or a hardware failure, we should never lose data. - * - * For any tombstone we find, we start by looking for all the blocks - * with a matching UUID, then see what valid sequences we can - * construct from what we found. This basically works in reverse of - * the update sequence in ks_set_attributes(). - * - * If we can construct a valid sequence of live blocks, the complete - * update was written out, and we just need to finish zeroing the - * tombstones. - * - * Otherwise, if we can construct a complete sequence of tombstone - * blocks, the update failed before it was completely written, so we - * have to zero the incomplete sequence of live blocks then restore - * the tombstones. - * - * Otherwise, if the live and tombstone blocks taken together form a - * valid sequence, the update failed while deprecating the old live - * blocks, and none of the new data was written, so we need to restore - * the tombstones and leave the live blocks alone. - * - * If none of the above applies, we don't understand what happened, - * which is a symptom of either a bug or a hardware failure more - * serious than simple loss of power or reboot at an inconvenient - * time, so we error out to avoid accidental loss of data. + * Deal with tombstones, now that the index is sorted. Tombstones + * are blocks left behind when something bad (like a power failure) + * happened while we updating. There can be at most one tombstone + * and one live block for a given UUID. If we find no live block, + * we need to restore it from the tombstone, after which we need to + * zero the tombstone in either case. The sequence of operations + * while updating is designed so that, barring a bug or a hardware + * failure, we should never lose data. */ - for (int i = 0; i < NUM_FLASH_BLOCKS; i++) { + for (unsigned b_tomb = 0; b_tomb < NUM_FLASH_BLOCKS; b_tomb++) { - if (block_status[i] != BLOCK_STATUS_TOMBSTONE) + if (block_status[b_tomb] != BLOCK_STATUS_TOMBSTONE) continue; - hal_uuid_t name = db.ksi.names[i].name; - unsigned n_blocks; + hal_uuid_t name = db.ksi.names[b_tomb].name; + int where = -1; - if ((err = hal_ks_index_find_range(&db.ksi, &name, 0, &n_blocks, NULL, &where, 0)) != HAL_OK) + if ((err = hal_ks_index_find(&db.ksi, &name, NULL, &where)) != HAL_OK) goto done; - /* - * hal_ks_index_find_range does a binary search, not a linear search, - * so it may not return the first instance of a block with the given - * name and chunk=0. Search backwards to make sure we have all chunks. - */ - - while (where > 0 && !hal_uuid_cmp(&name, &db.ksi.names[db.ksi.index[where - 1]].name)) { - where--; - n_blocks++; - } - - /* - * Rather than calling hal_ks_index_find_range with an array pointer - * to get the list of matching blocks (because of the binary search - * issue), we're going to fondle the index directly. This is really - * not something to do in regular code, but this is error-recovery - * code. - */ - - int live_ok = 1, tomb_ok = 1, join_ok = 1; - unsigned n_live = 0, n_tomb = 0; - unsigned i_live = 0, i_tomb = 0; - - for (int j = 0; j < n_blocks; j++) { - unsigned b = db.ksi.index[where + j]; - switch (block_status[b]) { - case BLOCK_STATUS_LIVE: n_live++; break; - case BLOCK_STATUS_TOMBSTONE: n_tomb++; break; - default: err = HAL_ERROR_IMPOSSIBLE; goto done; - } - } - - uint16_t live_blocks[n_live], tomb_blocks[n_tomb]; - - for (int j = 0; j < n_blocks; j++) { - unsigned b = db.ksi.index[where + j]; - - if ((err = block_read(b, block)) != HAL_OK) - goto done; - - join_ok &= block->header.this_chunk == j && block->header.total_chunks == n_blocks; - - switch (block_status[b]) { - case BLOCK_STATUS_LIVE: - live_blocks[i_live] = b; - live_ok &= block->header.this_chunk == i_live++ && block->header.total_chunks == n_live; - break; - case BLOCK_STATUS_TOMBSTONE: - tomb_blocks[i_tomb] = b; - tomb_ok &= block->header.this_chunk == i_tomb++ && block->header.total_chunks == n_tomb; - break; - default: + if (b_tomb != db.ksi.index[where]) { + if (db.ksi.used > where + 1 && b_tomb == db.ksi.index[where + 1]) + where = where + 1; + else if (0 <= where - 1 && b_tomb == db.ksi.index[where - 1]) + where = where - 1; + else { err = HAL_ERROR_IMPOSSIBLE; goto done; } } - if (!live_ok && !tomb_ok && !join_ok) { - err = HAL_ERROR_KEYSTORE_LOST_DATA; + const int matches_next = where + 1 < db.ksi.used && !hal_uuid_cmp(&name, &db.ksi.names[db.ksi.index[where + 1]].name); + const int matches_prev = where - 1 >= 0 && !hal_uuid_cmp(&name, &db.ksi.names[db.ksi.index[where - 1]].name); + + if ((matches_prev && matches_next) || + (matches_prev && block_status[db.ksi.index[b_tomb - 1]] != BLOCK_STATUS_LIVE) || + (matches_next && block_status[db.ksi.index[b_tomb + 1]] != BLOCK_STATUS_LIVE)) { + err = HAL_ERROR_IMPOSSIBLE; goto done; } - /* - * If live_ok or tomb_ok, we have to zero out some blocks, and adjust - * the index. Again, don't fondle the index directly, outside of error - * recovery. - */ - - if (live_ok) { - for (int j = 0; j < n_tomb; j++) { - const unsigned b = tomb_blocks[j]; - if ((err = block_zero(b)) != HAL_OK) - goto done; - block_types[b] = BLOCK_TYPE_ZEROED; - block_status[b] = BLOCK_STATUS_UNKNOWN; - } - } - - else if (tomb_ok) { - for (int j = 0; j < n_live; j++) { - const unsigned b = live_blocks[j]; - if ((err = block_zero(b)) != HAL_OK) - goto done; - block_types[b] = BLOCK_TYPE_ZEROED; - block_status[b] = BLOCK_STATUS_UNKNOWN; - } - } - - if (live_ok) { - memcpy(&db.ksi.index[where], live_blocks, n_live * sizeof(*db.ksi.index)); - memmove(&db.ksi.index[where + n_live], &db.ksi.index[where + n_blocks], - (db.ksi.size - where - n_blocks) * sizeof(*db.ksi.index)); - memcpy(&db.ksi.index[db.ksi.size - n_tomb], tomb_blocks, n_tomb * sizeof(*db.ksi.index)); - db.ksi.used -= n_tomb; - n_blocks = n_live; + if (matches_prev || matches_next) { + memmove(&db.ksi.index[where], &db.ksi.index[where + 1], (db.ksi.size - where - 1) * sizeof(*db.ksi.index)); + db.ksi.index[db.ksi.size - 1] = b_tomb; } - else if (tomb_ok) { - memcpy(&db.ksi.index[where], tomb_blocks, n_tomb * sizeof(*db.ksi.index)); - memmove(&db.ksi.index[where + n_tomb], &db.ksi.index[where + n_blocks], - (db.ksi.size - where - n_blocks) * sizeof(*db.ksi.index)); - memcpy(&db.ksi.index[db.ksi.size - n_live], live_blocks, n_live * sizeof(*db.ksi.index)); - db.ksi.used -= n_live; - n_blocks = n_tomb; - } - - /* - * Restore tombstone blocks (tomb_ok or join_ok). - */ - - for (int j = 0; j < n_blocks; j++) { - int hint = where + j; - unsigned b1 = db.ksi.index[hint], b2; - if (block_status[b1] != BLOCK_STATUS_TOMBSTONE) - continue; - if ((err = block_read(b1, block)) != HAL_OK) + else { + unsigned b_live; + if ((err = block_read(b_tomb, block)) != HAL_OK) goto done; block->header.block_status = BLOCK_STATUS_LIVE; - if ((err = hal_ks_index_replace(&db.ksi, &name, j, &b2, &hint)) != HAL_OK || - (err = block_write(b2, block)) != HAL_OK) + if ((err = hal_ks_index_replace(&db.ksi, &name, &b_live, &where)) != HAL_OK || + (err = block_write(b_live, block)) != HAL_OK) goto done; - block_types[b1] = BLOCK_TYPE_ZEROED; - block_status[b1] = BLOCK_STATUS_UNKNOWN; - block_status[b2] = BLOCK_STATUS_LIVE; + block_status[b_live] = BLOCK_STATUS_LIVE; } + + if ((err = block_zero(b_tomb)) != HAL_OK) + goto done; + block_types[ b_tomb] = BLOCK_TYPE_ZEROED; + block_status[b_tomb] = BLOCK_STATUS_UNKNOWN; } /* @@ -954,8 +826,6 @@ static hal_error_t ks_init(const hal_ks_driver_t * const driver, const int alloc block->header.block_type = BLOCK_TYPE_PIN; block->header.block_status = BLOCK_STATUS_LIVE; - block->header.total_chunks = 1; - block->header.this_chunk = 0; block->pin.wheel_pin = db.wheel_pin = hal_last_gasp_pin; block->pin.so_pin = db.so_pin; @@ -1065,8 +935,6 @@ static hal_error_t ks_store(hal_ks_t *ks, block->header.block_type = BLOCK_TYPE_KEY; block->header.block_status = BLOCK_STATUS_LIVE; - block->header.total_chunks = 1; - block->header.this_chunk = 0; k->name = slot->name; k->type = slot->type; @@ -1208,28 +1076,19 @@ static hal_error_t ks_delete(hal_ks_t *ks, return err; } -static inline hal_error_t locate_attributes(flash_block_t *block, const unsigned chunk, +static inline hal_error_t locate_attributes(flash_block_t *block, uint8_t **bytes, size_t *bytes_len, unsigned **attrs_len) { if (block == NULL || bytes == NULL || bytes_len == NULL || attrs_len == NULL) return HAL_ERROR_IMPOSSIBLE; - if (chunk == 0) { - if (block_get_type(block) != BLOCK_TYPE_KEY) - return HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE; /* HAL_ERROR_KEY_NOT_FOUND */ - *attrs_len = &block->key.attributes_len; - *bytes = block->key.der + block->key.der_len; - *bytes_len = SIZEOF_FLASH_KEY_BLOCK_DER - block->key.der_len; - } - else { - if (block_get_type(block) != BLOCK_TYPE_ATTR) - return HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE; /* HAL_ERROR_KEY_NOT_FOUND */ - *attrs_len = &block->attr.attributes_len; - *bytes = block->attr.attributes; - *bytes_len = SIZEOF_FLASH_ATTRIBUTE_BLOCK_ATTRIBUTES; - } + if (block_get_type(block) != BLOCK_TYPE_KEY) + return HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE; + *attrs_len = &block->key.attributes_len; + *bytes = block->key.der + block->key.der_len; + *bytes_len = SIZEOF_FLASH_KEY_BLOCK_DER - block->key.der_len; return HAL_OK; } @@ -1252,10 +1111,8 @@ static hal_error_t ks_match(hal_ks_t *ks, result == NULL || result_len == NULL || previous_uuid == NULL) return HAL_ERROR_BAD_ARGUMENTS; - uint8_t need_attr[attributes_len > 0 ? attributes_len : 1]; hal_error_t err = HAL_OK; flash_block_t *block; - int possible = 0; int i = -1; hal_ks_lock(); @@ -1273,32 +1130,24 @@ static hal_error_t ks_match(hal_ks_t *ks, unsigned b = db.ksi.index[i]; - if (db.ksi.names[b].chunk == 0) - possible = 1; - - if (!possible) - continue; - if ((err = block_read_cached(b, &block)) != HAL_OK) goto done; - if (db.ksi.names[b].chunk == 0) { - memset(need_attr, 1, sizeof(need_attr)); - possible = ((type == HAL_KEY_TYPE_NONE || type == block->key.type) && - (curve == HAL_CURVE_NONE || curve == block->key.curve) && - ((flags ^ block->key.flags) & mask) == 0); - } - - if (!possible) + if ((type != HAL_KEY_TYPE_NONE && type != block->key.type) || + (curve != HAL_CURVE_NONE && curve != block->key.curve) || + ((flags ^ block->key.flags) & mask) != 0) continue; if (attributes_len > 0) { + uint8_t need_attr[attributes_len]; uint8_t *bytes = NULL; size_t bytes_len = 0; unsigned *attrs_len; + int possible = 1; - if ((err = locate_attributes(block, db.ksi.names[b].chunk, - &bytes, &bytes_len, &attrs_len)) != HAL_OK) + memset(need_attr, 1, sizeof(need_attr)); + + if ((err = locate_attributes(block, &bytes, &bytes_len, &attrs_len)) != HAL_OK) goto done; if (*attrs_len > 0) { @@ -1322,17 +1171,13 @@ static hal_error_t ks_match(hal_ks_t *ks, } } } - } - - if (!possible) - continue; - if (attributes_len > 0 && memchr(need_attr, 1, sizeof(need_attr)) != NULL) - continue; + if (!possible || memchr(need_attr, 1, sizeof(need_attr)) != NULL) + continue; + } result[*result_len] = db.ksi.names[b].name; ++*result_len; - possible = 0; } err = HAL_OK; @@ -1342,24 +1187,6 @@ static hal_error_t ks_match(hal_ks_t *ks, return err; } -/* - * This controls whether we include a separate code path for the - * common case where we can handle attribute setting via a single - * block update. It's a lot simpler when it works, but it's yet - * another code path, and enabling it leaves the slower full-blown - * algorithm less tested. - */ - -#ifndef KS_SET_ATTRIBUTES_SINGLE_BLOCK_UPDATE_FAST_PATH -#define KS_SET_ATTRIBUTES_SINGLE_BLOCK_UPDATE_FAST_PATH 0 -#endif - -/* - * ks_set_attributes() is much too long. Probably needs to be broken - * up into a collection of inline functions, even if most of them end - * up being called exactly once. - */ - static hal_error_t ks_set_attributes(hal_ks_t *ks, hal_pkey_slot_t *slot, const hal_pkey_attribute_t *attributes, @@ -1368,469 +1195,51 @@ static hal_error_t ks_set_attributes(hal_ks_t *ks, if (ks != &db.ks || slot == NULL || attributes == NULL || attributes_len == 0) return HAL_ERROR_BAD_ARGUMENTS; - /* - * Perform initial scan of the object to figure out the total - * attribute count and a few other parameters. - * - * If enabled, try to do everything as as a single-block update. If - * single block update fails, we MUST clear the modified block from - * the cache before doing anything else. - */ - - unsigned updated_attributes_len = attributes_len; hal_error_t err = HAL_OK; flash_block_t *block; - unsigned chunk = 0; unsigned b; hal_ks_lock(); { - - do { - int hint = slot->hint + chunk; - - if ((err = hal_ks_index_find(&db.ksi, &slot->name, chunk, &b, &hint)) != HAL_OK || - (err = block_read_cached(b, &block)) != HAL_OK) - goto done; - - if (block->header.this_chunk != chunk) { - err = HAL_ERROR_IMPOSSIBLE; - goto done; - } - - cache_mark_used(block, b); - - if (chunk == 0) - slot->hint = hint; - - uint8_t *bytes = NULL; - size_t bytes_len = 0; - unsigned *attrs_len; - - if ((err = locate_attributes(block, chunk, &bytes, &bytes_len, &attrs_len)) != HAL_OK) - goto done; - - updated_attributes_len += *attrs_len; - -#if KS_SET_ATTRIBUTES_SINGLE_BLOCK_UPDATE_FAST_PATH - - hal_pkey_attribute_t attrs[*attrs_len + attributes_len]; - size_t total; - - if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, &total)) != HAL_OK) - goto done; - - for (int i = 0; err == HAL_OK && i < attributes_len; i++) - if (attributes[i].length == HAL_PKEY_ATTRIBUTE_NIL) - err = hal_ks_attribute_delete(bytes, bytes_len, attrs, attrs_len, &total, - attributes[i].type); - else - err = hal_ks_attribute_insert(bytes, bytes_len, attrs, attrs_len, &total, - attributes[i].type, - attributes[i].value, - attributes[i].length); - - if (err != HAL_OK) - cache_release(block); - - if (err == HAL_ERROR_RESULT_TOO_LONG) - continue; - - if (err == HAL_OK) - err = block_update(b, block, &slot->name, chunk, &hint); - + if ((err = hal_ks_index_find(&db.ksi, &slot->name, &b, &slot->hint)) != HAL_OK || + (err = block_read_cached(b, &block)) != HAL_OK) goto done; -#endif /* KS_SET_ATTRIBUTES_SINGLE_BLOCK_UPDATE_FAST_PATH */ - - } while (++chunk < block->header.total_chunks); - - /* - * If we get here, we're on the slow path, which requires rewriting - * all the chunks in this object but which can also add or remove - * chunks from this object. We need to keep track of all the old - * chunks so we can zero them at the end, and because we can't zero - * them until we've written out the new chunks, we need enough free - * blocks to hold all the new chunks. - * - * Calculating all of this is extremely tedious, but flash writes - * are so much more expensive than anything else we do here that - * it's almost certainly worth it. - * - * We don't need the attribute values to compute the sizes, just the - * attribute sizes, so we scan all the existing blocks, build up a - * structure with the current attribute types and sizes, modify that - * according to our arguments, and compute the needed size. Once we - * have that, we can start rewriting existing blocks. We put all - * the new stuff at the end, which simplifies this slightly. - * - * In theory, this process never requires us to have more than two - * blocks in memory at the same time (source and destination when - * copying across chunk boundaries), but having enough cache buffers - * to keep the whole set in memory will almost certainly make this - * run faster. - */ - - hal_pkey_attribute_t updated_attributes[updated_attributes_len]; - const unsigned total_chunks_old = block->header.total_chunks; - size_t bytes_available = 0; - - updated_attributes_len = 0; - - /* - * Phase 0.1: Walk the old chunks to populate updated_attributes[]. - * This also initializes bytes_available, since we can only get that - * by reading old chunk zero. - */ - - for (chunk = 0; chunk < total_chunks_old; chunk++) { - int hint = slot->hint + chunk; - - if ((err = hal_ks_index_find(&db.ksi, &slot->name, chunk, &b, &hint)) != HAL_OK || - (err = block_read_cached(b, &block)) != HAL_OK) - goto done; - - if (block->header.this_chunk != chunk) { - err = HAL_ERROR_IMPOSSIBLE; - goto done; - } - - cache_mark_used(block, b); - - uint8_t *bytes = NULL; - size_t bytes_len = 0; - unsigned *attrs_len; - - if ((err = locate_attributes(block, chunk, &bytes, &bytes_len, &attrs_len)) != HAL_OK) - goto done; - - hal_pkey_attribute_t attrs[*attrs_len]; - size_t total; - - if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, &total)) != HAL_OK) - goto done; - - if (chunk == 0) - bytes_available = bytes_len; - - for (int i = 0; i < *attrs_len; i++) { - - if (updated_attributes_len >= sizeof(updated_attributes)/sizeof(*updated_attributes)) { - err = HAL_ERROR_IMPOSSIBLE; - goto done; - } - - updated_attributes[updated_attributes_len].type = attrs[i].type; - updated_attributes[updated_attributes_len].length = attrs[i].length; - updated_attributes[updated_attributes_len].value = NULL; - updated_attributes_len++; - } - } - - /* - * Phase 0.2: Merge new attributes into updated_attributes[]. - * For each new attribute type, mark any existing attributes of that - * type for deletion. Append new attributes to updated_attributes[]. - */ - - for (int i = 0; i < attributes_len; i++) { - - for (int j = 0; j < updated_attributes_len; j++) - if (updated_attributes[j].type == attributes[i].type) - updated_attributes[j].length = HAL_PKEY_ATTRIBUTE_NIL; - - if (updated_attributes_len >= sizeof(updated_attributes)/sizeof(*updated_attributes)) { - err = HAL_ERROR_IMPOSSIBLE; - goto done; - } - - updated_attributes[updated_attributes_len].type = attributes[i].type; - updated_attributes[updated_attributes_len].length = attributes[i].length; - updated_attributes[updated_attributes_len].value = attributes[i].value; - updated_attributes_len++; - } - - /* - * Phase 0.3: Prune trailing deletion actions: we don't need them to - * maintain synchronization with existing attributes, and doing so - * simplifies logic for updating the final new chunk. - */ - - while (updated_attributes_len > 0 && - updated_attributes[updated_attributes_len - 1].length == HAL_PKEY_ATTRIBUTE_NIL) - --updated_attributes_len; - - /* - * Phase 0.4: Figure out how many chunks all this will occupy. - */ - - chunk = 0; - - for (int i = 0; i < updated_attributes_len; i++) { - - if (updated_attributes[i].length == HAL_PKEY_ATTRIBUTE_NIL) - continue; - - const size_t needed = hal_ks_attribute_header_size + updated_attributes[i].length; - - if (needed > bytes_available) { - bytes_available = SIZEOF_FLASH_ATTRIBUTE_BLOCK_ATTRIBUTES; - chunk++; - } - - if (needed > bytes_available) { - err = HAL_ERROR_RESULT_TOO_LONG; - goto done; - } - - bytes_available -= needed; - } - - const unsigned total_chunks_new = chunk + 1; + cache_mark_used(block, b); - /* - * If there aren't enough free blocks, give up now, before changing anything. - */ + uint8_t *bytes = NULL; + size_t bytes_len = 0; + unsigned *attrs_len; - if (db.ksi.used + total_chunks_new > db.ksi.size) { - err = HAL_ERROR_NO_KEY_INDEX_SLOTS; + if ((err = locate_attributes(block, &bytes, &bytes_len, &attrs_len)) != HAL_OK) goto done; - } - - /* - * Phase 1: Deprecate all the old chunks, remember where they were. - */ - - unsigned old_blocks[total_chunks_old]; - - for (chunk = 0; chunk < total_chunks_old; chunk++) { - int hint = slot->hint + chunk; - if ((err = hal_ks_index_find(&db.ksi, &slot->name, chunk, &b, &hint)) != HAL_OK || - (err = block_deprecate(b)) != HAL_OK) - goto done; - old_blocks[chunk] = b; - } - - /* - * Phase 2: Write new chunks, copying attributes from old chunks or - * from attributes[], as needed. - */ - - { - hal_pkey_attribute_t old_attrs[updated_attributes_len], new_attrs[updated_attributes_len]; - unsigned *old_attrs_len = NULL, *new_attrs_len = NULL; - flash_block_t *old_block = NULL, *new_block = NULL; - uint8_t *old_bytes = NULL, *new_bytes = NULL; - size_t old_bytes_len = 0, new_bytes_len = 0; - unsigned old_chunk = 0, new_chunk = 0; - size_t old_total = 0, new_total = 0; - - int updated_attributes_i = 0, old_attrs_i = 0; - - uint32_t new_attr_type; - size_t new_attr_length; - const uint8_t *new_attr_value; - - while (updated_attributes_i < updated_attributes_len) { - - if (old_chunk >= total_chunks_old || new_chunk >= total_chunks_new) { - err = HAL_ERROR_IMPOSSIBLE; - goto done; - } - - /* - * If we've gotten as far as new data that comes from - * attributes[], we have it in hand and can just copy it. - */ - - if (updated_attributes_len - updated_attributes_i <= attributes_len) { - new_attr_type = updated_attributes[updated_attributes_i].type; - new_attr_length = updated_attributes[updated_attributes_i].length; - new_attr_value = updated_attributes[updated_attributes_i].value; - } - - /* - * Otherwise, we have to read it from an old block, which may in - * turn require reading in the next old block. - */ - else { + hal_pkey_attribute_t attrs[*attrs_len + attributes_len]; + size_t total; - if (old_block == NULL) { - - if ((err = block_read_cached(old_blocks[old_chunk], &old_block)) != HAL_OK) - goto done; - - if (old_block->header.this_chunk != old_chunk) { - err = HAL_ERROR_IMPOSSIBLE; - goto done; - } - - if ((err = locate_attributes(old_block, old_chunk, - &old_bytes, &old_bytes_len, &old_attrs_len)) != HAL_OK || - (err = hal_ks_attribute_scan(old_bytes, old_bytes_len, - old_attrs, *old_attrs_len, &old_total)) != HAL_OK) - goto done; - - old_attrs_i = 0; - } - - if (old_attrs_i >= *old_attrs_len) { - old_chunk++; - old_block = NULL; - continue; - } - - new_attr_type = old_attrs[old_attrs_i].type; - new_attr_length = old_attrs[old_attrs_i].length; - new_attr_value = old_attrs[old_attrs_i].value; - - if (new_attr_type != updated_attributes[updated_attributes_i].type) { - err = HAL_ERROR_IMPOSSIBLE; - goto done; - } - - old_attrs_i++; - } - - /* - * Unless this is a deletion, we should have something to write. - */ - - if (new_attr_length != HAL_PKEY_ATTRIBUTE_NIL && new_attr_value == NULL) { - err = HAL_ERROR_IMPOSSIBLE; - goto done; - } - - /* - * Initialize the new block if necessary. If it's the new chunk - * zero, we need to copy all the non-attribute data from the old - * chunk zero; otherwise, it's a new empty attribute block. - */ - - if (new_block == NULL) { - - new_block = cache_pick_lru(); - memset(new_block, 0xFF, sizeof(*new_block)); - - if (new_chunk == 0) { - flash_block_t *tmp_block; - if ((err = block_read_cached(old_blocks[0], &tmp_block)) != HAL_OK) - goto done; - if (tmp_block->header.this_chunk != 0) { - err = HAL_ERROR_IMPOSSIBLE; - goto done; - } - new_block->header.block_type = BLOCK_TYPE_KEY; - new_block->key.name = slot->name; - new_block->key.type = tmp_block->key.type; - new_block->key.curve = tmp_block->key.curve; - new_block->key.flags = tmp_block->key.flags; - new_block->key.der_len = tmp_block->key.der_len; - new_block->key.attributes_len = 0; - memcpy(new_block->key.der, tmp_block->key.der, tmp_block->key.der_len); - } - else { - new_block->header.block_type = BLOCK_TYPE_ATTR; - new_block->attr.name = slot->name; - new_block->attr.attributes_len = 0; - } - - new_block->header.block_status = BLOCK_STATUS_LIVE; - new_block->header.total_chunks = total_chunks_new; - new_block->header.this_chunk = new_chunk; - - if ((err = locate_attributes(new_block, new_chunk, - &new_bytes, &new_bytes_len, &new_attrs_len)) != HAL_OK) - goto done; - - new_total = 0; - } - - /* - * After all that setup, we finally get to write the frelling attribute. - */ - - if (new_attr_length != HAL_PKEY_ATTRIBUTE_NIL) - err = hal_ks_attribute_insert(new_bytes, new_bytes_len, new_attrs, new_attrs_len, &new_total, - new_attr_type, new_attr_value, new_attr_length); - - /* - * Figure out what to do next: immediately loop for next - * attribute, write current block, or bail out. - */ - - switch (err) { - case HAL_OK: - if (++updated_attributes_i < updated_attributes_len) - continue; - break; - case HAL_ERROR_RESULT_TOO_LONG: - if (new_chunk > 0 && new_attrs_len == 0) - goto done; - break; - default: - goto done; - } - - /* - * If we get here, either the current new block is full or we - * finished the last block, so we need to write it out. - */ - - int hint = slot->hint + new_chunk; - - if (new_chunk < total_chunks_old) - err = hal_ks_index_replace(&db.ksi, &slot->name, new_chunk, &b, &hint); - else - err = hal_ks_index_add( &db.ksi, &slot->name, new_chunk, &b, &hint); - - if (err != HAL_OK || (err = block_write(b, new_block)) != HAL_OK) - goto done; - - cache_mark_used(new_block, b); - - new_block = NULL; - new_chunk++; - } - - /* - * If number of blocks shrank, we need to clear trailing entries from the index. - */ - - for (old_chunk = total_chunks_new; old_chunk < total_chunks_old; old_chunk++) { - int hint = slot->hint + old_chunk; - - err = hal_ks_index_delete(&db.ksi, &slot->name, old_chunk, NULL, &hint); - - if (err != HAL_OK) - goto done; - } - - } - - /* - * Phase 3: Zero the old chunks we deprecated in phase 1. - */ - - for (chunk = 0; chunk < total_chunks_old; chunk++) - if ((err = block_zero(old_blocks[chunk])) != HAL_OK) - goto done; - - err = HAL_OK; + if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, &total)) != HAL_OK) + goto done; + for (int i = 0; err == HAL_OK && i < attributes_len; i++) + if (attributes[i].length == HAL_PKEY_ATTRIBUTE_NIL) + err = hal_ks_attribute_delete(bytes, bytes_len, attrs, attrs_len, &total, + attributes[i].type); + else + err = hal_ks_attribute_insert(bytes, bytes_len, attrs, attrs_len, &total, + attributes[i].type, + attributes[i].value, + attributes[i].length); + + if (err == HAL_OK) + err = block_update(b, block, &slot->name, &slot->hint); + else + cache_release(block); } done: hal_ks_unlock(); return err; - -#warning What happens if something goes wrong partway through this awful mess? - // We're left in a state with all the old blocks deprecated and - // (maybe) some of the new blocks current, need to clean that up. - // Would be nice if we could just reuse the keystore initialization - // code, but don't quite see how (yet?). } static hal_error_t ks_get_attributes(hal_ks_t *ks, @@ -1851,27 +1260,16 @@ static hal_error_t ks_get_attributes(hal_ks_t *ks, uint8_t *abuf = attributes_buffer; flash_block_t *block = NULL; - unsigned chunk = 0; - unsigned found = 0; hal_error_t err = HAL_OK; + unsigned found = 0; unsigned b; hal_ks_lock(); - do { - int hint = slot->hint + chunk; - - if ((err = hal_ks_index_find(&db.ksi, &slot->name, chunk, &b, &hint)) != HAL_OK || - (err = block_read_cached(b, &block)) != HAL_OK) - goto done; - - if (block->header.this_chunk != chunk) { - err = HAL_ERROR_IMPOSSIBLE; + { + if ((err = hal_ks_index_find(&db.ksi, &slot->name, &b, &slot->hint)) != HAL_OK || + (err = block_read_cached(b, &block)) != HAL_OK) goto done; - } - - if (chunk == 0) - slot->hint = hint; cache_mark_used(block, b); @@ -1879,7 +1277,7 @@ static hal_error_t ks_get_attributes(hal_ks_t *ks, size_t bytes_len = 0; unsigned *attrs_len; - if ((err = locate_attributes(block, chunk, &bytes, &bytes_len, &attrs_len)) != HAL_OK) + if ((err = locate_attributes(block, &bytes, &bytes_len, &attrs_len)) != HAL_OK) goto done; if (*attrs_len == 0) @@ -1917,7 +1315,7 @@ static hal_error_t ks_get_attributes(hal_ks_t *ks, abuf += attrs[j].length; } - } while (found < attributes_len && ++chunk < block->header.total_chunks); + }; if (found < attributes_len && attributes_buffer_len > 0) err = HAL_ERROR_ATTRIBUTE_NOT_FOUND; diff --git a/ks_index.c b/ks_index.c index c47451c..ebcb33b 100644 --- a/ks_index.c +++ b/ks_index.c @@ -38,22 +38,6 @@ #include "hal.h" #include "hal_internal.h" -/* - * Compare two hal_ks_name_t objects. - */ - -static inline int ks_name_cmp(const hal_ks_name_t * const name1, const hal_ks_name_t * const name2) -{ - assert(name1 != NULL && name2 != NULL); - - int cmp = hal_uuid_cmp(&name1->name, &name2->name); - - if (cmp == 0) - cmp = ((int) name1->chunk) - ((int) name2->chunk); - - return cmp; -} - /* * Find a block in the index, return true (found) or false (not found). * "where" indicates the name's position, or the position of the first free block. @@ -64,16 +48,13 @@ static inline int ks_name_cmp(const hal_ks_name_t * const name1, const hal_ks_na static int ks_find(const hal_ks_index_t * const ksi, const hal_uuid_t * const uuid, - const uint8_t chunk, const int * const hint, int *where) { assert(ksi != NULL && ksi->index != NULL && ksi->names != NULL && uuid != NULL && where != NULL); - const hal_ks_name_t name = { *uuid, chunk }; - if (hint != NULL && *hint >= 0 && *hint < ksi->used && - ks_name_cmp(&name, &ksi->names[ksi->index[*hint]]) == 0) { + hal_uuid_cmp(uuid, &ksi->names[ksi->index[*hint]]) == 0) { *where = *hint; return 1; } @@ -87,7 +68,7 @@ static int ks_find(const hal_ks_index_t * const ksi, *where = hi; return 0; } - const int cmp = ks_name_cmp(&name, &ksi->names[ksi->index[m]]); + const int cmp = hal_uuid_cmp(uuid, &ksi->names[ksi->index[m]]); if (cmp < 0) hi = m; else if (cmp > 0) @@ -115,11 +96,11 @@ static inline void ks_heapsift(hal_ks_index_t *ksi, int parent, const int end) const int left_child = parent * 2 + 1; const int right_child = parent * 2 + 2; int biggest = parent; - if (left_child <= end && ks_name_cmp(&ksi->names[ksi->index[biggest]], - &ksi->names[ksi->index[left_child]]) < 0) + if (left_child <= end && hal_uuid_cmp(&ksi->names[ksi->index[biggest]], + &ksi->names[ksi->index[left_child]]) < 0) biggest = left_child; - if (right_child <= end && ks_name_cmp(&ksi->names[ksi->index[biggest]], - &ksi->names[ksi->index[right_child]]) < 0) + if (right_child <= end && hal_uuid_cmp(&ksi->names[ksi->index[biggest]], + &ksi->names[ksi->index[right_child]]) < 0) biggest = right_child; if (biggest == parent) return; @@ -159,27 +140,10 @@ hal_error_t hal_ks_index_fsck(hal_ks_index_t *ksi) ksi->size == 0 || ksi->used > ksi->size) return HAL_ERROR_BAD_ARGUMENTS; - for (int i = 0; i < ksi->used; i++) { - - const int cmp = i == 0 ? -1 : hal_uuid_cmp(&ksi->names[ksi->index[i - 1]].name, - &ksi->names[ksi->index[i ]].name); - - const uint8_t prev_chunk = i == 0 ? 0 : ksi->names[ksi->index[i - 1]].chunk; - const uint8_t cur_chunk = ksi->names[ksi->index[i ]].chunk; - - if (cmp > 0) + for (int i = 1; i < ksi->used; i++) + if (hal_uuid_cmp(&ksi->names[ksi->index[i - 1]].name, &ksi->names[ksi->index[i]].name) >= 0) return HAL_ERROR_KSI_INDEX_UUID_MISORDERED; - if (cur_chunk > 0 && cmp != 0) - return HAL_ERROR_KSI_INDEX_CHUNK_ORPHANED; - - if (cur_chunk > 0 && prev_chunk + 1 < cur_chunk) - return HAL_ERROR_KSI_INDEX_CHUNK_MISSING; - - if (cur_chunk > 0 && prev_chunk + 1 > cur_chunk) - return HAL_ERROR_KSI_INDEX_CHUNK_OVERLAPS; - } - return HAL_OK; } @@ -205,12 +169,11 @@ hal_error_t hal_ks_index_setup(hal_ks_index_t *ksi) } /* - * Find a single block by name and chunk number. + * Find a single block by name. */ hal_error_t hal_ks_index_find(hal_ks_index_t *ksi, const hal_uuid_t * const name, - const unsigned chunk, unsigned *blockno, int *hint) { @@ -222,7 +185,7 @@ hal_error_t hal_ks_index_find(hal_ks_index_t *ksi, fsck(ksi); - int ok = ks_find(ksi, name, chunk, hint, &where); + int ok = ks_find(ksi, name, hint, &where); if (blockno != NULL) *blockno = ksi->index[where]; @@ -233,59 +196,12 @@ hal_error_t hal_ks_index_find(hal_ks_index_t *ksi, return ok ? HAL_OK : HAL_ERROR_KEY_NOT_FOUND; } -/* - * Find all blocks with the given name. - * If 'strict' is set, expect it to be a well-ordered set of chunks. - */ - -hal_error_t hal_ks_index_find_range(hal_ks_index_t *ksi, - const hal_uuid_t * const name, - const unsigned max_blocks, - unsigned *n_blocks, - unsigned *blocknos, - int *hint, - const int strict) -{ - if (ksi == NULL || ksi->index == NULL || ksi->names == NULL || - ksi->size == 0 || ksi->used > ksi->size || name == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - int where; - - fsck(ksi); - - if (!ks_find(ksi, name, 0, hint, &where)) - return HAL_ERROR_KEY_NOT_FOUND; - - int n = 0; - - for (int i = where; i < ksi->used && !hal_uuid_cmp(name, &ksi->names[ksi->index[i]].name); i++) { - if (strict && n != ksi->names[ksi->index[i]].chunk) - return HAL_ERROR_IMPOSSIBLE; - if (blocknos != NULL && n < max_blocks) - blocknos[n] = ksi->index[i]; - n++; - } - - if (n_blocks != NULL) - *n_blocks = n; - - if (hint != NULL) - *hint = where; - - if (blocknos != NULL && n > max_blocks) - return HAL_ERROR_RESULT_TOO_LONG; - - return HAL_OK; -} - /* * Add a single block to the index. */ hal_error_t hal_ks_index_add(hal_ks_index_t *ksi, const hal_uuid_t * const name, - const unsigned chunk, unsigned *blockno, int *hint) { @@ -300,7 +216,7 @@ hal_error_t hal_ks_index_add(hal_ks_index_t *ksi, fsck(ksi); - if (ks_find(ksi, name, chunk, hint, &where)) + if (ks_find(ksi, name, hint, &where)) return HAL_ERROR_KEY_NAME_IN_USE; /* @@ -313,7 +229,6 @@ hal_error_t hal_ks_index_add(hal_ks_index_t *ksi, memmove(&ksi->index[where + 1], &ksi->index[where], len); ksi->index[where] = b; ksi->names[b].name = *name; - ksi->names[b].chunk = chunk; if (blockno != NULL) *blockno = b; @@ -332,7 +247,6 @@ hal_error_t hal_ks_index_add(hal_ks_index_t *ksi, hal_error_t hal_ks_index_delete(hal_ks_index_t *ksi, const hal_uuid_t * const name, - const unsigned chunk, unsigned *blockno, int *hint) { @@ -344,7 +258,7 @@ hal_error_t hal_ks_index_delete(hal_ks_index_t *ksi, fsck(ksi); - if (ksi->used == 0 || !ks_find(ksi, name, chunk, hint, &where)) + if (ksi->used == 0 || !ks_find(ksi, name, hint, &where)) return HAL_ERROR_KEY_NOT_FOUND; /* @@ -369,74 +283,12 @@ hal_error_t hal_ks_index_delete(hal_ks_index_t *ksi, return HAL_OK; } -/* - * Delete all blocks with the given name. If blocknos is NULL, return a - * count of the matching blocks without deleting anything. - */ - -hal_error_t hal_ks_index_delete_range(hal_ks_index_t *ksi, - const hal_uuid_t * const name, - const unsigned max_blocks, - unsigned *n_blocks, - unsigned *blocknos, - int *hint) -{ - if (ksi == NULL || ksi->index == NULL || ksi->names == NULL || - ksi->size == 0 || ksi->used > ksi->size || name == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - int where; - - fsck(ksi); - - if (ksi->used == 0 || !ks_find(ksi, name, 0, hint, &where)) - return HAL_ERROR_KEY_NOT_FOUND; - - int n = 0; - - for (int i = where; i < ksi->used && !hal_uuid_cmp(name, &ksi->names[ksi->index[i]].name); i++) { - if (n != ksi->names[ksi->index[i]].chunk) - return HAL_ERROR_IMPOSSIBLE; - if (blocknos != NULL && n < max_blocks) - blocknos[n] = ksi->index[i]; - n++; - } - - if (n_blocks != NULL) - *n_blocks = n; - - /* - * Free the blocks and stuff them at the end of the free list. - */ - - if (blocknos != NULL) { - if (n > max_blocks) - return HAL_ERROR_RESULT_TOO_LONG; - const size_t len = (ksi->size - where - n) * sizeof(*ksi->index); - memmove(&ksi->index[where], &ksi->index[where + n], len); - ksi->used -= n; - for (int i = 0; i < n; i++) { - ksi->index[ksi->size - n + i] = blocknos[i]; - memset(&ksi->names[blocknos[i]], 0, sizeof(ksi->names[blocknos[i]])); - } - where = -1; - } - - if (hint != NULL) - *hint = where; - - fsck(ksi); - - return HAL_OK; -} - /* * Replace a single block in the index. */ hal_error_t hal_ks_index_replace(hal_ks_index_t *ksi, const hal_uuid_t * const name, - const unsigned chunk, unsigned *blockno, int *hint) { @@ -451,7 +303,7 @@ hal_error_t hal_ks_index_replace(hal_ks_index_t *ksi, fsck(ksi); - if (ksi->used == 0 || !ks_find(ksi, name, chunk, hint, &where)) + if (ksi->used == 0 || !ks_find(ksi, name, hint, &where)) return HAL_ERROR_KEY_NOT_FOUND; /* @@ -466,7 +318,6 @@ hal_error_t hal_ks_index_replace(hal_ks_index_t *ksi, ksi->index[ksi->size - 1] = b1; ksi->index[where] = b2; ksi->names[b2].name = *name; - ksi->names[b2].chunk = chunk; memset(&ksi->names[b1], 0, sizeof(ksi->names[b1])); if (blockno != NULL) diff --git a/ks_volatile.c b/ks_volatile.c index d565c60..f8bed1a 100644 --- a/ks_volatile.c +++ b/ks_volatile.c @@ -423,9 +423,6 @@ static hal_error_t ks_match(hal_ks_t *ks, 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; -- cgit v1.2.3 From d532e6bbcd63c550f91fc97446f6114f37d18bde Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Tue, 23 May 2017 00:05:49 -0400 Subject: Whack previous commit with club until it compiles. --- ks_flash.c | 85 ++++++++++++++++++++--------------------------------------- ks_index.c | 6 ++--- ks_volatile.c | 18 ++++++------- 3 files changed, 41 insertions(+), 68 deletions(-) diff --git a/ks_flash.c b/ks_flash.c index e073b84..803d81c 100644 --- a/ks_flash.c +++ b/ks_flash.c @@ -139,7 +139,6 @@ typedef union { uint8_t bytes[KEYSTORE_SUBSECTOR_SIZE]; flash_block_header_t header; flash_key_block_t key; - flash_attributes_block_t attr; flash_pin_block_t pin; } flash_block_t; @@ -330,7 +329,6 @@ static hal_error_t block_read(const unsigned blockno, flash_block_t *block) return HAL_OK; case BLOCK_TYPE_KEY: case BLOCK_TYPE_PIN: - case BLOCK_TYPE_ATTR: break; default: return HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE; @@ -483,7 +481,6 @@ static hal_error_t block_write(const unsigned blockno, flash_block_t *block) switch (block_get_type(block)) { case BLOCK_TYPE_KEY: case BLOCK_TYPE_PIN: - case BLOCK_TYPE_ATTR: block->header.crc = calculate_block_crc(block); break; default: @@ -501,8 +498,10 @@ static hal_error_t block_write(const unsigned blockno, flash_block_t *block) * Update one flash block, including zombie jamboree. */ -static hal_error_t block_update(const unsigned b1, flash_block_t *block, - const hal_uuid_t * const uuid, int *hint) +static hal_error_t block_update(const unsigned b1, + flash_block_t *block, + const hal_uuid_t * const uuid, + int *hint) { if (block == NULL) return HAL_ERROR_IMPOSSIBLE; @@ -648,7 +647,6 @@ static hal_error_t ks_init(const hal_ks_driver_t * const driver, const int alloc switch (block_types[i]) { case BLOCK_TYPE_KEY: case BLOCK_TYPE_PIN: - case BLOCK_TYPE_ATTR: block_status[i] = block_get_status(block); break; default: @@ -672,13 +670,12 @@ static hal_error_t ks_init(const hal_ks_driver_t * const driver, const int alloc switch (block_types[i]) { case BLOCK_TYPE_KEY: uuid = &block->key.name; break; - case BLOCK_TYPE_ATTR: uuid = &block->attr.name; break; case BLOCK_TYPE_PIN: uuid = &pin_uuid; break; default: /* Keep GCC happy */ break; } if (uuid != NULL) { - db.ksi.names[i].name = *uuid; + db.ksi.names[i] = *uuid; db.ksi.index[n++] = i; } } @@ -746,7 +743,7 @@ static hal_error_t ks_init(const hal_ks_driver_t * const driver, const int alloc if (block_status[b_tomb] != BLOCK_STATUS_TOMBSTONE) continue; - hal_uuid_t name = db.ksi.names[b_tomb].name; + hal_uuid_t name = db.ksi.names[b_tomb]; int where = -1; @@ -764,8 +761,8 @@ static hal_error_t ks_init(const hal_ks_driver_t * const driver, const int alloc } } - const int matches_next = where + 1 < db.ksi.used && !hal_uuid_cmp(&name, &db.ksi.names[db.ksi.index[where + 1]].name); - const int matches_prev = where - 1 >= 0 && !hal_uuid_cmp(&name, &db.ksi.names[db.ksi.index[where - 1]].name); + const int matches_next = where + 1 < db.ksi.used && !hal_uuid_cmp(&name, &db.ksi.names[db.ksi.index[where + 1]]); + const int matches_prev = where - 1 >= 0 && !hal_uuid_cmp(&name, &db.ksi.names[db.ksi.index[where - 1]]); if ((matches_prev && matches_next) || (matches_prev && block_status[db.ksi.index[b_tomb - 1]] != BLOCK_STATUS_LIVE) || @@ -831,7 +828,7 @@ static hal_error_t ks_init(const hal_ks_driver_t * const driver, const int alloc block->pin.so_pin = db.so_pin; block->pin.user_pin = db.user_pin; - if ((err = hal_ks_index_add(&db.ksi, &pin_uuid, 0, &b, NULL)) != HAL_OK) + if ((err = hal_ks_index_add(&db.ksi, &pin_uuid, &b, NULL)) != HAL_OK) goto done; cache_mark_used(block, b); @@ -926,7 +923,7 @@ static hal_error_t ks_store(hal_ks_t *ks, k = &block->key; - if ((err = hal_ks_index_add(&db.ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK) + if ((err = hal_ks_index_add(&db.ksi, &slot->name, &b, &slot->hint)) != HAL_OK) goto done; cache_mark_used(block, b); @@ -962,7 +959,7 @@ static hal_error_t ks_store(hal_ks_t *ks, memset(block, 0, sizeof(*block)); cache_release(block); - (void) hal_ks_index_delete(&db.ksi, &slot->name, 0, NULL, &slot->hint); + (void) hal_ks_index_delete(&db.ksi, &slot->name, NULL, &slot->hint); done: hal_ks_unlock(); @@ -982,8 +979,8 @@ static hal_error_t ks_fetch(hal_ks_t *ks, hal_ks_lock(); - if ((err = hal_ks_index_find(&db.ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK || - (err = block_read_cached(b, &block)) != HAL_OK) + if ((err = hal_ks_index_find(&db.ksi, &slot->name, &b, &slot->hint)) != HAL_OK || + (err = block_read_cached(b, &block)) != HAL_OK) goto done; if (block_get_type(block) != BLOCK_TYPE_KEY) { @@ -1031,45 +1028,19 @@ static hal_error_t ks_delete(hal_ks_t *ks, return HAL_ERROR_BAD_ARGUMENTS; hal_error_t err = HAL_OK; - unsigned n; + unsigned b; hal_ks_lock(); - { - /* - * Get the count of blocks to delete. - */ - - if ((err = hal_ks_index_delete_range(&db.ksi, &slot->name, 0, &n, NULL, &slot->hint)) != HAL_OK) - goto done; - - /* - * Then delete them. - */ - - unsigned b[n]; - - if ((err = hal_ks_index_delete_range(&db.ksi, &slot->name, n, NULL, b, &slot->hint)) != HAL_OK) - goto done; - - for (int i = 0; i < n; i++) - cache_release(cache_find_block(b[i])); + if ((err = hal_ks_index_delete(&db.ksi, &slot->name, &b, &slot->hint)) != HAL_OK) + goto done; - /* - * Zero the blocks, to mark them as recently used. - */ + cache_release(cache_find_block(b)); - for (int i = 0; i < n; i++) - if ((err = block_zero(b[i])) != HAL_OK) - goto done; - - /* - * Erase the first block in the free list. In case of restart, this - * puts the block back at the head of the free list. - */ + if ((err = block_zero(b)) != HAL_OK) + goto done; - err = block_erase_maybe(db.ksi.index[db.ksi.used]); - } + err = block_erase_maybe(db.ksi.index[db.ksi.used]); done: hal_ks_unlock(); @@ -1119,7 +1090,7 @@ static hal_error_t ks_match(hal_ks_t *ks, *result_len = 0; - err = hal_ks_index_find(&db.ksi, previous_uuid, 0, NULL, &i); + err = hal_ks_index_find(&db.ksi, previous_uuid, NULL, &i); if (err == HAL_ERROR_KEY_NOT_FOUND) i--; @@ -1176,7 +1147,7 @@ static hal_error_t ks_match(hal_ks_t *ks, continue; } - result[*result_len] = db.ksi.names[b].name; + result[*result_len] = db.ksi.names[b]; ++*result_len; } @@ -1280,8 +1251,10 @@ static hal_error_t ks_get_attributes(hal_ks_t *ks, if ((err = locate_attributes(block, &bytes, &bytes_len, &attrs_len)) != HAL_OK) goto done; - if (*attrs_len == 0) - continue; + if (*attrs_len == 0) { + err = HAL_ERROR_ATTRIBUTE_NOT_FOUND; + goto done; + } hal_pkey_attribute_t attrs[*attrs_len]; @@ -1428,8 +1401,8 @@ static hal_error_t fetch_pin_block(unsigned *b, flash_block_t **block) if (b == NULL) b = &b_; - if ((err = hal_ks_index_find(&db.ksi, &pin_uuid, 0, b, &hint)) != HAL_OK || - (err = block_read_cached(*b, block)) != HAL_OK) + if ((err = hal_ks_index_find(&db.ksi, &pin_uuid, b, &hint)) != HAL_OK || + (err = block_read_cached(*b, block)) != HAL_OK) return err; cache_mark_used(*block, *b); @@ -1458,7 +1431,7 @@ static hal_error_t update_pin_block(const unsigned b, block->pin = *new_data; - return block_update(b, block, &pin_uuid, 0, &hint); + return block_update(b, block, &pin_uuid, &hint); } /* diff --git a/ks_index.c b/ks_index.c index ebcb33b..806394a 100644 --- a/ks_index.c +++ b/ks_index.c @@ -141,7 +141,7 @@ hal_error_t hal_ks_index_fsck(hal_ks_index_t *ksi) return HAL_ERROR_BAD_ARGUMENTS; for (int i = 1; i < ksi->used; i++) - if (hal_uuid_cmp(&ksi->names[ksi->index[i - 1]].name, &ksi->names[ksi->index[i]].name) >= 0) + if (hal_uuid_cmp(&ksi->names[ksi->index[i - 1]], &ksi->names[ksi->index[i]]) >= 0) return HAL_ERROR_KSI_INDEX_UUID_MISORDERED; return HAL_OK; @@ -228,7 +228,7 @@ hal_error_t hal_ks_index_add(hal_ks_index_t *ksi, const uint16_t b = ksi->index[ksi->used++]; memmove(&ksi->index[where + 1], &ksi->index[where], len); ksi->index[where] = b; - ksi->names[b].name = *name; + ksi->names[b] = *name; if (blockno != NULL) *blockno = b; @@ -317,7 +317,7 @@ hal_error_t hal_ks_index_replace(hal_ks_index_t *ksi, memmove(&ksi->index[ksi->used], &ksi->index[ksi->used + 1], len); ksi->index[ksi->size - 1] = b1; ksi->index[where] = b2; - ksi->names[b2].name = *name; + ksi->names[b2] = *name; memset(&ksi->names[b1], 0, sizeof(ksi->names[b1])); if (blockno != NULL) diff --git a/ks_volatile.c b/ks_volatile.c index f8bed1a..2dcb599 100644 --- a/ks_volatile.c +++ b/ks_volatile.c @@ -261,7 +261,7 @@ static hal_error_t ks_store(hal_ks_t *ks, goto done; } - if ((err = hal_ks_index_add(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK) + if ((err = hal_ks_index_add(&ksv->db->ksi, &slot->name, &b, &slot->hint)) != HAL_OK) goto done; uint8_t kek[KEK_LENGTH]; @@ -284,7 +284,7 @@ static hal_error_t ks_store(hal_ks_t *ks, if (err == HAL_OK) ksv->db->keys[b] = k; else - (void) hal_ks_index_delete(&ksv->db->ksi, &slot->name, 0, NULL, &slot->hint); + (void) hal_ks_index_delete(&ksv->db->ksi, &slot->name, NULL, &slot->hint); done: hal_ks_unlock(); @@ -309,7 +309,7 @@ static hal_error_t ks_fetch(hal_ks_t *ks, goto done; } - if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK) + if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, &b, &slot->hint)) != HAL_OK) goto done; const ks_key_t * const k = &ksv->db->keys[b]; @@ -364,7 +364,7 @@ static hal_error_t ks_delete(hal_ks_t *ks, goto done; } - if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK) + if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, &b, &slot->hint)) != HAL_OK) goto done; if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, &ksv->db->keys[b])) { @@ -372,7 +372,7 @@ static hal_error_t ks_delete(hal_ks_t *ks, goto done; } - if ((err = hal_ks_index_delete(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK) + if ((err = hal_ks_index_delete(&ksv->db->ksi, &slot->name, &b, &slot->hint)) != HAL_OK) goto done; memset(&ksv->db->keys[b], 0, sizeof(ksv->db->keys[b])); @@ -412,7 +412,7 @@ static hal_error_t ks_match(hal_ks_t *ks, *result_len = 0; - err = hal_ks_index_find(&ksv->db->ksi, previous_uuid, 0, NULL, &i); + err = hal_ks_index_find(&ksv->db->ksi, previous_uuid, NULL, &i); if (err == HAL_ERROR_KEY_NOT_FOUND) i--; @@ -464,7 +464,7 @@ static hal_error_t ks_match(hal_ks_t *ks, continue; } - result[*result_len] = ksv->db->ksi.names[b].name; + result[*result_len] = ksv->db->ksi.names[b]; ++*result_len; } @@ -495,7 +495,7 @@ static hal_error_t ks_set_attributes(hal_ks_t *ks, goto done; } - if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK) + if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, &b, &slot->hint)) != HAL_OK) goto done; ks_key_t * const k = &ksv->db->keys[b]; @@ -556,7 +556,7 @@ static hal_error_t ks_get_attributes(hal_ks_t *ks, goto done; } - if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK) + if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, &b, &slot->hint)) != HAL_OK) goto done; const ks_key_t * const k = &ksv->db->keys[b]; -- cgit v1.2.3 From dc8c7d92118541bba8d6f76f75a5661416055fb8 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Tue, 23 May 2017 00:57:29 -0400 Subject: Goodbye ancient mmap()-based keystore. The Novena-era mmap()-based keystore is far enough out of date that it's not worth maintaining (and we haven't been doing so): if we ever need one again, it would be easier to rewrite it from scratch. --- Makefile | 16 +---- hal_internal.h | 5 +- ks_mmap.c | 180 --------------------------------------------------------- ks_volatile.c | 6 +- 4 files changed, 8 insertions(+), 199 deletions(-) delete mode 100644 ks_mmap.c diff --git a/Makefile b/Makefile index 829c895..5ba5d32 100644 --- a/Makefile +++ b/Makefile @@ -41,10 +41,9 @@ LIB = libhal.a # Error checking on known control options, some of which allow the user entirely too much rope. -USAGE := "usage: ${MAKE} [IO_BUS=eim|i2c|fmc] [RPC_MODE=none|server|client-simple|client-mixed] [KS=mmap|flash] [RPC_TRANSPORT=none|loopback|serial|daemon] [MODEXP_CORE=no|yes] [HASH_CORES=no|yes] [ECDSA_CORES=no|yes]" +USAGE := "usage: ${MAKE} [IO_BUS=eim|i2c|fmc] [RPC_MODE=none|server|client-simple|client-mixed] [RPC_TRANSPORT=none|loopback|serial|daemon] [MODEXP_CORE=no|yes] [HASH_CORES=no|yes] [ECDSA_CORES=no|yes]" IO_BUS ?= none -KS ?= flash RPC_MODE ?= none RPC_TRANSPORT ?= none MODEXP_CORE ?= yes @@ -54,7 +53,6 @@ ECDSA_CORES ?= yes ifeq (,$(and \ $(filter none eim i2c fmc ,${IO_BUS}),\ $(filter none server client-simple client-mixed ,${RPC_MODE}),\ - $(filter mmap flash ,${KS}),\ $(filter none loopback serial daemon ,${RPC_TRANSPORT}),\ $(filter no yes ,${MODEXP_CORE}),\ $(filter no yes ,${HASH_CORES}),\ @@ -62,7 +60,7 @@ ifeq (,$(and \ $(error ${USAGE}) endif -$(info Building libhal with configuration IO_BUS=${IO_BUS} RPC_MODE=${RPC_MODE} KS=${KS} RPC_TRANSPORT=${RPC_TRANSPORT} MODEXP_CORE=${MODEXP_CORE} HASH_CORES=${HASH_CORES} ECDSA_CORES=${ECDSA_CORES}) +$(info Building libhal with configuration IO_BUS=${IO_BUS} RPC_MODE=${RPC_MODE} RPC_TRANSPORT=${RPC_TRANSPORT} MODEXP_CORE=${MODEXP_CORE} HASH_CORES=${HASH_CORES} ECDSA_CORES=${ECDSA_CORES}) # Whether the RSA code should use the ModExp | ModExpS6 | ModExpA7 core. @@ -138,16 +136,8 @@ endif # In the new world, all keystores are on the server side, and the # volatile keystore is always present, to support things like PKCS #11 # "session" objects. -# -# The mmap keystore hasn't been rewritten for the new API yet. - -KS_OBJ = ks_index.o ks_attribute.o ks_volatile.o -ifeq "${KS}" "mmap" - KS_OBJ += ks_mmap.o -else ifeq "${KS}" "flash" - KS_OBJ += ks_flash.o mkm.o -endif +KS_OBJ = ks_index.o ks_attribute.o ks_volatile.o ks_flash.o mkm.o # RPC_MODE = none | server | client-simple | client-mixed # none: Build without RPC client, use cores directly. diff --git a/hal_internal.h b/hal_internal.h index aa31585..eee2eab 100644 --- a/hal_internal.h +++ b/hal_internal.h @@ -689,9 +689,8 @@ static inline hal_error_t hal_ks_get_attributes(hal_ks_t *ks, /* * Keystore index. This is intended to be usable by both memory-based - * (in-memory, mmap(), ...) keystores and keystores based on raw flash. - * Some of the features aren't really necessary for memory-based keystores, - * but should be harmless. + * and flash-based keystores. Some of the features aren't really + * necessary for memory-based keystores, but should be harmless. * * General approach is multiple arrays, all but one of which are * indexed by "block" numbers, where a block number might be a slot in diff --git a/ks_mmap.c b/ks_mmap.c deleted file mode 100644 index 066e93e..0000000 --- a/ks_mmap.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * ks_mmap.c - * --------- - * Keystore implementation over POSIX mmap(). - * - * Authors: Rob Austein - * Copyright (c) 2015, 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 - * met: - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name of the NORDUnet nor the names of its contributors may - * be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include - -#include "hal.h" -#include "hal_internal.h" - -#ifndef HAL_KS_MMAP_FILE -#define HAL_KS_MMAP_FILE ".cryptech_hal_keystore" -#endif - -#ifndef MAP_FILE -#define MAP_FILE 0 -#endif - -/* - * Storing the KEK in with the keys it's protecting is a bad idea, but we have no better - * place to put it (real protection requires dedicated hardware, which we don't have here). - */ - -#define KEKBUF_LEN (bitsToBytes(256)) - -static hal_ks_keydb_t *db; -static uint8_t *kekbuf; - -const hal_ks_keydb_t *hal_ks_get_keydb(void) -{ - if (db != NULL) - return db; - - const char * const env = getenv("CRYPTECH_KEYSTORE"); - const char * const home = getenv("HOME"); - const char * const base = HAL_KS_MMAP_FILE; - const long pagemask = sysconf(_SC_PAGESIZE) - 1; - const size_t len = (sizeof(hal_ks_keydb_t) + KEKBUF_LEN + pagemask) & ~pagemask; - - char fn_[strlen(base) + (home == NULL ? 0 : strlen(home)) + 2]; - const char *fn = fn_; - int fd; - - if (pagemask < 0) - return NULL; - - if (env != NULL) - fn = env; - else if (home == NULL) - fn = base; - else - strcat(strcat(strcpy(fn_, home), "/"), base); - - if ((fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0600)) >= 0) { - uint8_t zeros[len]; - memset(zeros, 0, sizeof(zeros)); - (void) write(fd, zeros, sizeof(zeros)); - } - else if (errno == EEXIST) { - fd = open(fn, O_RDWR | O_CREAT, 0600); - } - - if (fd >= 0 && (db = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0)) != NULL) - kekbuf = (uint8_t *) (db + 1); - - (void) close(fd); - - return db; -} - -hal_error_t hal_ks_set_keydb(const hal_ks_key_t * const key, - const int loc, - const int updating) -{ - if (key == NULL || loc < 0 || loc >= sizeof(db->keys)/sizeof(*db->keys) || (!key->in_use != !updating)) - return HAL_ERROR_BAD_ARGUMENTS; - - db->keys[loc] = *key; - db->keys[loc].in_use = 1; - return HAL_OK; -} - -hal_error_t hal_ks_del_keydb(const int loc) -{ - if (loc < 0 || loc >= sizeof(db->keys)/sizeof(*db->keys)) - return HAL_ERROR_BAD_ARGUMENTS; - - db->keys[loc].in_use = 0; - memset(&db->keys[loc], 0, sizeof(db->keys[loc])); - return HAL_OK; -} - -hal_error_t hal_set_pin(const hal_user_t user, - const hal_ks_pin_t * const pin) -{ - if (pin == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - hal_ks_pin_t *p = NULL; - - 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; - } - - *p = *pin; - return HAL_OK; -} - -hal_error_t hal_mkm_get_kek(uint8_t *kek, - size_t *kek_len, - const size_t kek_max) -{ - if (kek == NULL || kek_len == NULL || kek_max < bitsToBytes(128)) - return HAL_ERROR_BAD_ARGUMENTS; - - if (kekbuf == NULL) - return HAL_ERROR_IMPOSSIBLE; - - hal_error_t err; - - const size_t len = ((kek_max < bitsToBytes(192)) ? bitsToBytes(128) : - (kek_max < bitsToBytes(256)) ? bitsToBytes(192) : - bitsToBytes(256)); - - uint8_t t = 0; - - for (int i = 0; i < KEKBUF_LEN; i++) - t |= kekbuf[i]; - - if (t == 0 && (err = hal_rpc_get_random(kekbuf, sizeof(KEKBUF_LEN))) != HAL_OK) - return err; - - memcpy(kek, kekbuf, len); - *kek_len = len; - return HAL_OK; -} - -/* - * Local variables: - * indent-tabs-mode: nil - * End: - */ diff --git a/ks_volatile.c b/ks_volatile.c index 2dcb599..515a8e8 100644 --- a/ks_volatile.c +++ b/ks_volatile.c @@ -53,9 +53,9 @@ #endif /* - * 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). + * In-memory keystore database. This is a bit more complicated than + * necessary because originally I though we would want to continue + * supporting an mmap()-based keystore as well. Needs cleaning up. */ typedef struct { -- cgit v1.2.3 From 7c61f43d516dff9f1047d1c08a9bb778cb8edc68 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Wed, 24 May 2017 21:44:56 -0400 Subject: Checkpoint, not expected to work yet, includes a lot of notes. --- ks.c | 1131 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ks.h | 241 ++++++++++++++ 2 files changed, 1372 insertions(+) create mode 100644 ks.c create mode 100644 ks.h diff --git a/ks.c b/ks.c new file mode 100644 index 0000000..c107eb6 --- /dev/null +++ b/ks.c @@ -0,0 +1,1131 @@ +/* + * ks.c + * ---- + * Keystore, generic parts anyway. This is internal within libhal. + * + * Copyright (c) 2015-2017, 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 + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "hal.h" +#include "hal_internal.h" +#include "ks.h" + +/* + * Find a block in the index, return true (found) or false (not found). + * "where" indicates the name's position, or the position of the first free block. + * + * NB: This does NOT return a block number, it returns an index into + * ks->index[]. + */ + +static int ks_find(const hal_ks_t * const ks, + const hal_uuid_t * const uuid, + const int * const hint, + int *where) +{ + if (ks == NULL || ks->index == NULL || ks->names == NULL || uuid == NULL || where == NULL) + return 0; + + if (hint != NULL && *hint >= 0 && *hint < ks->used && + hal_uuid_cmp(uuid, &ks->names[ks->index[*hint]]) == 0) { + *where = *hint; + return 1; + } + + int lo = -1; + int hi = ks->used; + + for (;;) { + int m = (lo + hi) / 2; + if (hi == 0 || m == lo) { + *where = hi; + return 0; + } + const int cmp = hal_uuid_cmp(uuid, &ks->names[ks->index[m]]); + if (cmp < 0) + hi = m; + else if (cmp > 0) + lo = m; + else { + *where = m; + return 1; + } + } +} + +/* + * Heapsort the index. We only need to do this on setup, for other + * operations we're just inserting or deleting a single entry in an + * already-ordered array, which is just a search problem. If we were + * really crunched for space, we could use an insertion sort here, but + * heapsort is easy and works well with data already in place. + */ + +static inline void ks_heapsift(hal_ks_t *ks, int parent, const int end) +{ + if (ks == NULL || ks->index == NULL || ks->names == NULL || parent < 0 || end < parent) + return; + for (;;) { + const int left_child = parent * 2 + 1; + const int right_child = parent * 2 + 2; + int biggest = parent; + if (left_child <= end && hal_uuid_cmp(&ks->names[ks->index[biggest]], + &ks->names[ks->index[left_child]]) < 0) + biggest = left_child; + if (right_child <= end && hal_uuid_cmp(&ks->names[ks->index[biggest]], + &ks->names[ks->index[right_child]]) < 0) + biggest = right_child; + if (biggest == parent) + return; + const uint16_t tmp = ks->index[biggest]; + ks->index[biggest] = ks->index[parent]; + ks->index[parent] = tmp; + parent = biggest; + } +} + +static inline void ks_heapsort(hal_ks_t *ks) +{ + if (ks == NULL || ks->index == NULL || ks->names == NULL) + return; + if (ks->used < 2) + return; + for (int i = (ks->used - 2) / 2; i >= 0; i--) + ks_heapsift(ks, i, ks->used - 1); + for (int i = ks->used - 1; i > 0; i--) { + const uint16_t tmp = ks->index[i]; + ks->index[i] = ks->index[0]; + ks->index[0] = tmp; + ks_heapsift(ks, 0, i - 1); + } +} + +/* + * Perform a consistency check on the index. + */ + +#define fsck(_ks) \ + do { hal_error_t _err = hal_ks_index_fsck(_ks); if (_err != HAL_OK) return _err; } while (0) + + +hal_error_t hal_ks_index_fsck(hal_ks_t *ks) +{ + if (ks == NULL || ks->index == NULL || ks->names == NULL || + ks->size == 0 || ks->used > ks->size) + return HAL_ERROR_BAD_ARGUMENTS; + + for (int i = 1; i < ks->used; i++) + if (hal_uuid_cmp(&ks->names[ks->index[i - 1]], &ks->names[ks->index[i]]) >= 0) + return HAL_ERROR_KS_INDEX_UUID_MISORDERED; + + return HAL_OK; +} + +/* + * Find a single block by name. + */ + +hal_error_t hal_ks_index_find(hal_ks_t *ks, + const hal_uuid_t * const name, + unsigned *blockno, + int *hint) +{ + if (ks == NULL || ks->index == NULL || ks->names == NULL || + ks->size == 0 || ks->used > ks->size || name == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + int where; + + fsck(ks); + + int ok = ks_find(ks, name, hint, &where); + + if (blockno != NULL) + *blockno = ks->index[where]; + + if (hint != NULL) + *hint = where; + + return ok ? HAL_OK : HAL_ERROR_KEY_NOT_FOUND; +} + +/* + * Add a single block to the index. + */ + +hal_error_t hal_ks_index_add(hal_ks_t *ks, + const hal_uuid_t * const name, + unsigned *blockno, + int *hint) +{ + if (ks == NULL || ks->index == NULL || ks->names == NULL || + ks->size == 0 || ks->used > ks->size || name == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (ks->used == ks->size) + return HAL_ERROR_NO_KEY_INDEX_SLOTS; + + int where; + + fsck(ks); + + if (ks_find(ks, name, hint, &where)) + return HAL_ERROR_KEY_NAME_IN_USE; + + /* + * Grab first block on free list, which makes room to slide the + * index up by one slot so we can insert the new block number. + */ + + const size_t len = (ks->used - where) * sizeof(*ks->index); + const uint16_t b = ks->index[ks->used++]; + memmove(&ks->index[where + 1], &ks->index[where], len); + ks->index[where] = b; + ks->names[b] = *name; + + if (blockno != NULL) + *blockno = b; + + if (hint != NULL) + *hint = where; + + fsck(ks); + + return HAL_OK; +} + +/* + * Delete a single block from the index. + */ + +hal_error_t hal_ks_index_delete(hal_ks_t *ks, + const hal_uuid_t * const name, + unsigned *blockno, + int *hint) +{ + if (ks == NULL || ks->index == NULL || ks->names == NULL || + ks->size == 0 || ks->used > ks->size || name == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + int where; + + fsck(ks); + + if (ks->used == 0 || !ks_find(ks, name, hint, &where)) + return HAL_ERROR_KEY_NOT_FOUND; + + /* + * Free the block and stuff it at the end of the free list. + */ + + const size_t len = (ks->size - where - 1) * sizeof(*ks->index); + const uint16_t b = ks->index[where]; + memmove(&ks->index[where], &ks->index[where + 1], len); + ks->index[ks->size - 1] = b; + ks->used--; + memset(&ks->names[b], 0, sizeof(ks->names[b])); + + if (blockno != NULL) + *blockno = b; + + if (hint != NULL) + *hint = where; + + fsck(ks); + + return HAL_OK; +} + +/* + * Replace a single block in the index. + */ + +hal_error_t hal_ks_index_replace(hal_ks_t *ks, + const hal_uuid_t * const name, + unsigned *blockno, + int *hint) +{ + if (ks == NULL || ks->index == NULL || ks->names == NULL || + ks->size == 0 || ks->used > ks->size || name == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (ks->used == ks->size) + return HAL_ERROR_NO_KEY_INDEX_SLOTS; + + int where; + + fsck(ks); + + if (ks->used == 0 || !ks_find(ks, name, hint, &where)) + return HAL_ERROR_KEY_NOT_FOUND; + + /* + * Grab first block from free list, slide free list down, put old + * block at end of free list and replace old block with new block. + */ + + const size_t len = (ks->size - ks->used - 1) * sizeof(*ks->index); + const uint16_t b1 = ks->index[where]; + const uint16_t b2 = ks->index[ks->used]; + memmove(&ks->index[ks->used], &ks->index[ks->used + 1], len); + ks->index[ks->size - 1] = b1; + ks->index[where] = b2; + ks->names[b2] = *name; + memset(&ks->names[b1], 0, sizeof(ks->names[b1])); + + if (blockno != NULL) + *blockno = b2; + + if (hint != NULL) + *hint = where; + + fsck(ks); + + return HAL_OK; +} + +/* + * Pick unused or least-recently-used slot in our in-memory cache. + * + * Updating lru values is caller's problem: if caller is using a cache + * slot as a temporary buffer and there's no point in caching the + * result, leave the lru values alone and the right thing will happen. + */ + +static inline ks_block_t *cache_pick_lru(hal_ks_t *ks) +{ + uint32_t best_delta = 0; + int best_index = 0; + + for (int i = 0; i < ks->cache_size; i++) { + + if (ks->cache[i].blockno == ~0) + return &ks->cache[i].block; + + const unsigned delta = ks->cache_lru - ks->cache[i].lru; + if (delta > best_delta) { + best_delta = delta; + best_index = i; + } + + } + + ks->cache[best_index].blockno = ~0; + return &ks->cache[best_index].block; +} + +/* + * Find a block in our in-memory cache; return block or NULL if not present. + */ + +static inline ks_block_t *cache_find_block(const hal_ks_t * const ks, const unsigned blockno) +{ + for (int i = 0; i < ks->cache_size; i++) + if (ks->cache[i].blockno == blockno) + return &ks->cache[i].block; + return NULL; +} + +/* + * Mark a block in our in-memory cache as being in current use. + */ + +static inline void cache_mark_used(hal_ks_t *ks, const ks_block_t * const block, const unsigned blockno) +{ + for (int i = 0; i < ks->cache_size; i++) { + if (&ks->cache[i].block == block) { + ks->cache[i].blockno = blockno; + ks->cache[i].lru = ++ks->cache_lru; + return; + } + } +} + +/* + * Release a block from the in-memory cache. + */ + +static inline void cache_release(hal_ks_t *ks, const ks_block_t * const block) +{ + if (block != NULL) + cache_mark_used(block, ~0); +} + +/* + * Generate CRC-32 for a block. + * + * This function needs to understand the structure of + * ks_block_header_t, so that it can skip over fields that + * shouldn't be included in the CRC. + */ + +static hal_crc32_t calculate_block_crc(const ks_block_t * const block) +{ + hal_crc32_t crc = hal_crc32_init(); + + if (block != NULL) { + + crc = hal_crc32_update(crc, &block->header.block_type, + sizeof(block->header.block_type)); + + crc = hal_crc32_update(crc, + block->bytes + sizeof(ks_block_header_t), + sizeof(*block) - sizeof(ks_block_header_t)); + } + + return hal_crc32_finalize(crc); +} + +/* + * Read a block using the cache. Marking the block as used is left + * for the caller, so we can avoid blowing out the cache when we + * perform a ks_match() operation. + */ + +static hal_error_t block_read_cached(hal_ks_t *ks, const unsigned blockno, ks_block_t **block) +{ + if (block == NULL) + return HAL_ERROR_IMPOSSIBLE; + + if ((*block = cache_find_block(ks, blockno)) != NULL) + return HAL_OK; + + if ((*block = cache_pick_lru(ks)) == NULL) + return HAL_ERROR_IMPOSSIBLE; + + return block_read(ks, blockno, *block); +} + +/* + * Update one block, including zombie jamboree. + */ + +static hal_error_t block_update(hal_ks_t *ks, + const unsigned b1, + ks_block_t *block, + const hal_uuid_t * const uuid, + int *hint) +{ + if (block == NULL) + return HAL_ERROR_IMPOSSIBLE; + + if (ks->used == ks->size) + return HAL_ERROR_NO_KEY_INDEX_SLOTS; + + cache_release(block); + + hal_error_t err; + unsigned b2; + + if ((err = block_deprecate(ks, b1)) != HAL_OK || + (err = hal_ks_index_replace(ks, uuid, &b2, hint)) != HAL_OK || + (err = block_write(ks, b2, block)) != HAL_OK || + (err = block_zero(ks, b1)) != HAL_OK) + return err; + + cache_mark_used(ks, block, b2); + + /* + * Erase the first block in the free list. In case of restart, this + * puts the block back at the head of the free list. + */ + + return block_erase_maybe(ks, ks->index[ks->used]); +} + +/* + * Initialize keystore. This includes various tricky bits, some of + * which attempt to preserve the free list ordering across reboots, to + * improve our simplistic attempt at wear leveling, others attempt to + * recover from unclean shutdown. + */ + +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; +} + +#warning Call ks_alloc_common() and ks_init_common() while holding hal_ks_lock(); ! + +hal_error_t ks_alloc_common(hal_ks_t *ks, const unsigned ks_blocks, const unsigned cache_blocks) +{ + /* + * We allocate a single big chunk of memory rather than three + * smaller chunks to make it atomic. We need all three, so this way + * either all succeed or all fail. + */ + + size_t len = (sizeof(*ks->index) * ks_blocks + + sizeof(*ks->names) * ks_blocks + + sizeof(*ks->cache) * cache_blocks); + + uint8_t *mem = hal_allocate_static_memory(len); + + if (mem == NULL) + return HAL_ERROR_ALLOCATION_FAILURE; + + memset(ks, 0, sizeof(*ks)); + memset(mem, 0, len); + + ks->index = gnaw(&mem, &len, sizeof(*ks->index) * ks_blocks); + ks->names = gnaw(&mem, &len, sizeof(*ks->names) * ks_blocks); + ks->cache = gnaw(&mem, &len, sizeof(*ks->cache) * cache_blocks); + + ks->size = ks_blocks; + ks->cache_size = cache_blocks; + + return HAL_OK; +} + +hal_error_t ks_init_common(hal_ks_t *ks, const hal_ks_driver_t * const driver) +{ + if (ks->index == NULL || ks->names == NULL || ks->cache == NULL) + return HAL_ERROR_IMPOSSIBLE; + + ks->used = 0; + + for (int i = 0; i < ks->cache_size; i++) + ks->cache[i].blockno = ~0; + + /* + * Scan existing content of keystore to figure out what we've got. + * This gets a bit involved due to the need to recover from things + * like power failures at inconvenient times. + */ + + ks_block_type_t block_types[ks->size]; + ks_block_status_t block_status[ks->size]; + ks_block_t *block = cache_pick_lru(ks); + int first_erased = -1; + hal_error_t err; + uint16_t n = 0; + + if (block == NULL) + return HAL_ERROR_IMPOSSIBLE; + + for (int i = 0; i < ks->size; i++) { + + /* + * Read one block. If the CRC is bad or the block type is + * unknown, it's old data we don't understand, something we were + * writing when we crashed, or bad flash; in any of these cases, + * we want the block to end up near the end of the free list. + */ + + err = block_read(ks, i, block); + + if (err == HAL_ERROR_KEYSTORE_BAD_CRC || err == HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE) + block_types[i] = BLOCK_TYPE_UNKNOWN; + + else if (err == HAL_OK) + block_types[i] = block_get_type(block); + + else + return err; + + switch (block_types[i]) { + case BLOCK_TYPE_KEY: + case BLOCK_TYPE_PIN: + block_status[i] = block_get_status(block); + break; + default: + block_status[i] = BLOCK_STATUS_UNKNOWN; + } + + /* + * First erased block we see is head of the free list. + */ + + if (block_types[i] == BLOCK_TYPE_ERASED && first_erased < 0) + first_erased = i; + + /* + * If it's a valid data block, include it in the index. We remove + * tombstones (if any) below, for now it's easiest to include them + * in the index, so we can look them up by name if we must. + */ + + const hal_uuid_t *uuid = NULL; + + switch (block_types[i]) { + case BLOCK_TYPE_KEY: uuid = &block->key.name; break; + case BLOCK_TYPE_PIN: uuid = &pin_uuid; break; + default: /* Keep GCC happy */ break; + } + + if (uuid != NULL) { + ks->names[i] = *uuid; + ks->index[n++] = i; + } + } + + ks->used = n; + + if (ks->used > ks->size) + return HAL_ERROR_IMPOSSIBLE; + + /* + * At this point we've built the (unsorted) index from all the valid + * blocks. Now we need to insert free and unrecognized blocks into + * the free list in our preferred order. It's possible that there's + * a better way to do this than linear scan, but this is just + * integer comparisons in a fairly small data set, so it's probably + * not worth trying to optimize. + */ + + if (n < ks->size) + for (int i = 0; i < ks->size; i++) + if (block_types[i] == BLOCK_TYPE_ERASED) + ks->index[n++] = i; + + if (n < ks->size) + for (int i = first_erased; i < ks->size; i++) + if (block_types[i] == BLOCK_TYPE_ZEROED) + ks->index[n++] = i; + + if (n < ks->size) + for (int i = 0; i < first_erased; i++) + if (block_types[i] == BLOCK_TYPE_ZEROED) + ks->index[n++] = i; + + if (n < ks->size) + for (int i = 0; i < ks->size; i++) + if (block_types[i] == BLOCK_TYPE_UNKNOWN) + ks->index[n++] = i; + + if (ks->used > ks->size) + return HAL_ERROR_IMPOSSIBLE; + + /* + * Sort the index, then deal with tombstones. Tombstones are blocks + * left behind when something bad (like a power failure) happened + * while we updating. There can be at most one tombstone and one + * live block for a given UUID. If we find no live block, we need + * to restore it from the tombstone, after which we need to zero the + * tombstone in either case. The sequence of operations while + * updating is designed so that, barring a bug or a hardware + * failure, we should never lose data. + */ + + ks_heapsort(ks); + + for (unsigned b_tomb = 0; b_tomb < ks->size; b_tomb++) { + + if (block_status[b_tomb] != BLOCK_STATUS_TOMBSTONE) + continue; + + hal_uuid_t name = ks->names[b_tomb]; + + int where = -1; + + if ((err = hal_ks_index_find(ks, &name, NULL, &where)) != HAL_OK) + return err; + + if (b_tomb != ks->index[where]) { + if (ks->used > where + 1 && b_tomb == ks->index[where + 1]) + where = where + 1; + else if (0 <= where - 1 && b_tomb == ks->index[where - 1]) + where = where - 1; + else + return HAL_ERROR_IMPOSSIBLE; + } + + const int matches_next = where + 1 < ks->used && !hal_uuid_cmp(&name, &ks->names[ks->index[where + 1]]); + const int matches_prev = where - 1 >= 0 && !hal_uuid_cmp(&name, &ks->names[ks->index[where - 1]]); + + if ((matches_prev && matches_next) || + (matches_prev && block_status[ks->index[b_tomb - 1]] != BLOCK_STATUS_LIVE) || + (matches_next && block_status[ks->index[b_tomb + 1]] != BLOCK_STATUS_LIVE)) + return HAL_ERROR_IMPOSSIBLE; + + if (matches_prev || matches_next) { + memmove(&ks->index[where], &ks->index[where + 1], (ks->size - where - 1) * sizeof(*ks->index)); + ks->index[ks->size - 1] = b_tomb; + } + + else { + unsigned b_live; + if ((err = block_read(ks, b_tomb, block)) != HAL_OK) + return err; + block->header.block_status = BLOCK_STATUS_LIVE; + if ((err = hal_ks_index_replace(ks, &name, &b_live, &where)) != HAL_OK || + (err = block_write(ks, b_live, block)) != HAL_OK) + return err; + block_status[b_live] = BLOCK_STATUS_LIVE; + } + + if ((err = block_zero(ks, b_tomb)) != HAL_OK) + return err; + block_types[ b_tomb] = BLOCK_TYPE_ZEROED; + block_status[b_tomb] = BLOCK_STATUS_UNKNOWN; + } + + /* + * Erase first block on free list if it's not already erased. + */ + + if (ks->used < ks->size && + (err = block_erase_maybe(ks, ks->index[ks->used])) != HAL_OK) + return err; + + /* + * And we're finally done. + */ + + ks->driver = driver; + + 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; + } +} + +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; + + hal_error_t err = HAL_OK; + ks_block_t *block; + flash_key_block_t *k; + uint8_t kek[KEK_LENGTH]; + size_t kek_len; + unsigned b; + + hal_ks_lock(); + + if ((block = cache_pick_lru(ks)) == NULL) { + err = HAL_ERROR_IMPOSSIBLE; + goto done; + } + + k = &block->key; + + if ((err = hal_ks_index_add(ks, &slot->name, &b, &slot->hint)) != HAL_OK) + goto done; + + cache_mark_used(ks, block, b); + + memset(block, 0xFF, sizeof(*block)); + + block->header.block_type = BLOCK_TYPE_KEY; + block->header.block_status = BLOCK_STATUS_LIVE; + + k->name = slot->name; + k->type = slot->type; + k->curve = slot->curve; + k->flags = slot->flags; + k->der_len = SIZEOF_FLASH_KEY_BLOCK_DER; + k->attributes_len = 0; + + if (ks->used < ks->size) + err = block_erase_maybe(ks, ks->index[ks->used]); + + if (err == HAL_OK) + err = hal_mkm_get_kek(kek, &kek_len, sizeof(kek)); + + if (err == 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) + err = block_write(ks, b, block); + + if (err == HAL_OK) + goto done; + + memset(block, 0, sizeof(*block)); + cache_release(ks, block); + (void) hal_ks_index_delete(ks, &slot->name, NULL, &slot->hint); + + done: + hal_ks_unlock(); + 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; + + hal_error_t err = HAL_OK; + ks_block_t *block; + unsigned b; + + hal_ks_lock(); + + if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || + (err = block_read_cached(ks, b, &block)) != HAL_OK) + goto done; + + if (block_get_type(block) != BLOCK_TYPE_KEY) { + err = HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE; /* HAL_ERROR_KEY_NOT_FOUND */ + goto done; + } + + cache_mark_used(ks, block, b); + + flash_key_block_t *k = &block->key; + + 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)); + } + + done: + hal_ks_unlock(); + return err; +} + +static hal_error_t ks_delete(hal_ks_t *ks, + hal_pkey_slot_t *slot) +{ + if (ks == NULL || slot == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + hal_error_t err = HAL_OK; + unsigned b; + + hal_ks_lock(); + + if ((err = hal_ks_index_delete(ks, &slot->name, &b, &slot->hint)) != HAL_OK) + goto done; + + cache_release(ks, cache_find_block(ks, b)); + + if ((err = block_zero(ks, b)) != HAL_OK) + goto done; + + err = block_erase_maybe(ks, ks->index[ks->used]); + + done: + hal_ks_unlock(); + return err; +} + +static inline hal_error_t locate_attributes(ks_block_t *block, + uint8_t **bytes, size_t *bytes_len, + unsigned **attrs_len) +{ + if (block == NULL || bytes == NULL || bytes_len == NULL || attrs_len == NULL) + return HAL_ERROR_IMPOSSIBLE; + + + if (block_get_type(block) != BLOCK_TYPE_KEY) + return HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE; + *attrs_len = &block->key.attributes_len; + *bytes = block->key.der + block->key.der_len; + *bytes_len = SIZEOF_FLASH_KEY_BLOCK_DER - block->key.der_len; + + return HAL_OK; +} + +static hal_error_t ks_match(hal_ks_t *ks, + const hal_client_handle_t client, + const hal_session_handle_t session, + const hal_key_type_t type, + const hal_curve_name_t curve, + const hal_key_flags_t mask, + 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 (ks == NULL || (attributes == NULL && attributes_len > 0) || + result == NULL || result_len == NULL || previous_uuid == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + hal_error_t err = HAL_OK; + ks_block_t *block; + int i = -1; + + hal_ks_lock(); + + *result_len = 0; + + err = hal_ks_index_find(ks, previous_uuid, NULL, &i); + + if (err == HAL_ERROR_KEY_NOT_FOUND) + i--; + else if (err != HAL_OK) + goto done; + + while (*result_len < result_max && ++i < ks->used) { + + unsigned b = ks->index[i]; + + if ((err = block_read_cached(ks, b, &block)) != HAL_OK) + goto done; + + if ((type != HAL_KEY_TYPE_NONE && type != block->key.type) || + (curve != HAL_CURVE_NONE && curve != block->key.curve) || + ((flags ^ block->key.flags) & mask) != 0) + continue; + + if (attributes_len > 0) { + uint8_t need_attr[attributes_len]; + uint8_t *bytes = NULL; + size_t bytes_len = 0; + unsigned *attrs_len; + int possible = 1; + + memset(need_attr, 1, sizeof(need_attr)); + + if ((err = locate_attributes(block, &bytes, &bytes_len, &attrs_len)) != HAL_OK) + goto done; + + if (*attrs_len > 0) { + hal_pkey_attribute_t attrs[*attrs_len]; + + if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, NULL)) != HAL_OK) + goto done; + + for (int j = 0; possible && j < attributes_len; j++) { + + if (!need_attr[j]) + continue; + + for (hal_pkey_attribute_t *a = attrs; a < attrs + *attrs_len; a++) { + if (a->type != attributes[j].type) + continue; + need_attr[j] = 0; + possible = (a->length == attributes[j].length && + !memcmp(a->value, attributes[j].value, a->length)); + break; + } + } + } + + if (!possible || memchr(need_attr, 1, sizeof(need_attr)) != NULL) + continue; + } + + result[*result_len] = ks->names[b]; + ++*result_len; + } + + err = HAL_OK; + + done: + hal_ks_unlock(); + return err; +} + +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 (ks == NULL || slot == NULL || attributes == NULL || attributes_len == 0) + return HAL_ERROR_BAD_ARGUMENTS; + + hal_error_t err = HAL_OK; + ks_block_t *block; + unsigned b; + + hal_ks_lock(); + + { + if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || + (err = block_read_cached(ks, b, &block)) != HAL_OK) + goto done; + + cache_mark_used(ks, block, b); + + uint8_t *bytes = NULL; + size_t bytes_len = 0; + unsigned *attrs_len; + + if ((err = locate_attributes(block, &bytes, &bytes_len, &attrs_len)) != HAL_OK) + goto done; + + hal_pkey_attribute_t attrs[*attrs_len + attributes_len]; + size_t total; + + if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, &total)) != HAL_OK) + goto done; + + for (int i = 0; err == HAL_OK && i < attributes_len; i++) + if (attributes[i].length == HAL_PKEY_ATTRIBUTE_NIL) + err = hal_ks_attribute_delete(bytes, bytes_len, attrs, attrs_len, &total, + attributes[i].type); + else + err = hal_ks_attribute_insert(bytes, bytes_len, attrs, attrs_len, &total, + attributes[i].type, + attributes[i].value, + attributes[i].length); + + if (err == HAL_OK) + err = block_update(ks, b, block, &slot->name, &slot->hint); + else + cache_release(ks, block); + } + + done: + hal_ks_unlock(); + return err; +} + +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 (ks == NULL || slot == NULL || attributes == NULL || attributes_len == 0 || + attributes_buffer == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + for (int i = 0; i < attributes_len; i++) { + attributes[i].length = 0; + attributes[i].value = NULL; + } + + uint8_t *abuf = attributes_buffer; + ks_block_t *block = NULL; + hal_error_t err = HAL_OK; + unsigned found = 0; + unsigned b; + + hal_ks_lock(); + + { + if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || + (err = block_read_cached(ks, b, &block)) != HAL_OK) + goto done; + + cache_mark_used(ks, block, b); + + uint8_t *bytes = NULL; + size_t bytes_len = 0; + unsigned *attrs_len; + + if ((err = locate_attributes(block, &bytes, &bytes_len, &attrs_len)) != HAL_OK) + goto done; + + if (*attrs_len == 0) { + err = HAL_ERROR_ATTRIBUTE_NOT_FOUND; + goto done; + } + + hal_pkey_attribute_t attrs[*attrs_len]; + + if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, NULL)) != HAL_OK) + goto done; + + for (int i = 0; i < attributes_len; i++) { + + if (attributes[i].length > 0) + continue; + + int j = 0; + while (j < *attrs_len && attrs[j].type != attributes[i].type) + j++; + if (j >= *attrs_len) + continue; + found++; + + attributes[i].length = attrs[j].length; + + if (attributes_buffer_len == 0) + continue; + + if (attrs[j].length > attributes_buffer + attributes_buffer_len - abuf) { + err = HAL_ERROR_RESULT_TOO_LONG; + goto done; + } + + memcpy(abuf, attrs[j].value, attrs[j].length); + attributes[i].value = abuf; + abuf += attrs[j].length; + } + + }; + + if (found < attributes_len && attributes_buffer_len > 0) + err = HAL_ERROR_ATTRIBUTE_NOT_FOUND; + else + err = HAL_OK; + + done: + hal_ks_unlock(); + return err; +} + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ diff --git a/ks.h b/ks.h new file mode 100644 index 0000000..ff6d382 --- /dev/null +++ b/ks.h @@ -0,0 +1,241 @@ +// Notes towards unified keystore code (drivers become just low-level +// "disk" I/O and perhaps a bit of local init/shutdown). +// +// Most of the structure definitions in ks_flash.c and ks_volatile.c +// become common and go in ks.h (or wherever, but probably be enough +// stuff that separate .h file might be easier to read). +// +// We already have +// +// typedef struct hal_ks hal_ks_t; +// +// which we "subclass" to get ks_t (ks_volatile) and db_t (ks_flash). +// We can move more common stuff there. +// +// flash_block_t (etc) becomes ks_block_t (etc) as these data +// structures will be used by all keystores, not just flash. +// +// We might want to fold hal_ks_index_t into hal_ks_t as everything +// will be using it. Then again, it's relatively harmless as it is, a +// bit more verbose trading for a bit more isolation. Probably go for +// less verbose, for readability. +// +// Each keystore will still have some weird private stuff, like the +// RAM for the keys themselves in the volatile case and the PIN stuff +// in the flash case. +// +// The ks_flash cache, however, probably wants to become common code. +// Yes we could get a bit more efficient if we skipped caching in the +// volatile case, but that's not our bottleneck and there are some +// cases where the code relies on knowing that mucking with the cache +// copy is harmless until we write the block to "disk", don't want to +// mess with that, so keep the flash model for volatile. Cache size +// will need to become another hal_ks_t field. +// +// Don't remember exactly where we're doing the "subclassing" casts, +// should be easy enough to find...except that ks_flash is mostly +// ignoring that argument and using the static db variable directly. +// ks_volatile may be closer to write on this point, as it already had +// ks_to_ksv(). But most of the code will be in a driver-agnostic +// ks.c (or whatever) and will be calling functions that care through +// the driver, maybe this doesn't matter very much. +// +// Tedious though it sounds, might be simplest just to check each +// function in ks_*.c to see whether it moves to ks.[ch] or becomes +// something called by the new lower-level driver API. Need a sketch +// of the lower-level driver API, chicken and egg there but probably +// is init(), shutdown(), block_read(), block_deprecate(), +// block_zero(), block_erase(), block_erase_maybe(), block-write(). +// Possible that some of these don't really need to be driver, was +// mostly basing this on which things in ks_flash touch flash +// directly-ish via the keystore_*() functions. +// +// Would be nice if we can make the API regular enough (inline +// functions?) that user need not really care which functions are +// driver-specific and which are layered on top, but that may be +// impractical (or silly). +// +// Hmm, hal_ks_open() and hal_ks_close() don't quite fit new model, +// what was I thinking there? Not much, existing implementations just +// use that to get back a (hal_ks_t*), so really just checking the +// binding between driver and keystore object. +// +// I think this boils down to another instance of the confusion +// between what in Python would be Keystore.__new__() and +// Keystore.__init__(). This even sort of fits with the weird `alloc` +// parameter in ks_init(). +// +// Maybe we can trust C memory initialization enough to use a zeroed +// static variable as test for whether a keystore has been +// initialized, and just have the low-level (driver) methods check +// that and fail if trying to use an uninitialized keystore? +// +// Pythonesque view might be the right way to handle ks_init(0 and +// ks_shutdown() too: in most cases we have inline functions which +// call the driver function, but for these methods the subclass needs +// to extend the abstract method, which translates, in C, to the +// generic method calling the driver method of the same name at the +// right time. Not quite what Python does but close enough. + + +#ifndef _KS_H_ +#define _KS_H_ + +#include "hal.h" +#include "hal_internal.h" + +/* + * Size of a keystore "block". + * + * This must be an integer multiple of the flash subsector size, among + * other reasons because that's the minimum erasable unit. + */ + +#ifndef HAL_KS_BLOCK_SIZE +#define HAL_KS_BLOCK_SIZE (KEYSTORE_SUBSECTOR_SIZE * 1) +#endif + +/* + * Known block states. + * + * C does not guarantee any particular representation for enums, so + * including enums directly in the block header isn't safe. Instead, + * we use an access method which casts when reading from the header. + * Writing to the header isn't a problem, because C does guarantee + * that enum is compatible with *some* integer type, it just doesn't + * specify which one. + */ + +typedef enum { + KS_BLOCK_TYPE_ERASED = 0xFF, /* Pristine erased block (candidate for reuse) */ + KS_BLOCK_TYPE_ZEROED = 0x00, /* Zeroed block (recently used) */ + KS_BLOCK_TYPE_KEY = 0x55, /* Block contains key material */ + KS_BLOCK_TYPE_PIN = 0xAA, /* Block contains PINs */ + KS_BLOCK_TYPE_UNKNOWN = -1, /* Internal code for "I have no clue what this is" */ +} ks_block_type_t; + +/* + * Block status. + */ + +typedef enum { + KS_BLOCK_STATUS_LIVE = 0x66, /* This is a live block */ + KS_BLOCK_STATUS_TOMBSTONE = 0x44, /* This is a tombstone left behind during an update */ + KS_BLOCK_STATUS_UNKNOWN = -1, /* Internal code for "I have no clue what this is" */ +} ks_block_status_t; + +/* + * Common header for all keystore block types. + * A few of these fields are deliberately omitted from the CRC. + */ + +typedef struct { + uint8_t block_type; + uint8_t block_status; + hal_crc32_t crc; +} ks_block_header_t; + +/* + * Key block. Tail end of "der" field (after der_len) used for attributes. + */ + +typedef struct { + ks_block_header_t header; + hal_uuid_t name; + hal_key_type_t type; + hal_curve_name_t curve; + hal_key_flags_t flags; + size_t der_len; + unsigned attributes_len; + uint8_t der[]; /* Must be last field -- C99 "flexible array member" */ +} ks_blockkey_block_t; + +#define SIZEOF_KS_BLOCKKEY_BLOCK_DER \ + (HAL_KS_BLOCK_SIZE - offsetof(ks_blockkey_block_t, der)) + +/* + * PIN block. Also includes space for backing up the KEK when + * HAL_MKM_FLASH_BACKUP_KLUDGE is enabled. + */ + +typedef struct { + ks_block_header_t header; + hal_ks_pin_t wheel_pin; + hal_ks_pin_t so_pin; + hal_ks_pin_t user_pin; +#if HAL_MKM_FLASH_BACKUP_KLUDGE + uint32_t kek_set; + uint8_t kek[KEK_LENGTH]; +#endif +} ks_blockpin_block_t; + +#define FLASH_KEK_SET 0x33333333 + +/* + * One keystore block. + */ + +typedef union { + uint8_t bytes[HAL_KS_BLOCK_SIZE]; + ks_block_header_t header; + ks_blockkey_block_t key; + ks_blockpin_block_t pin; +} ks_block_t; + +/* + * In-memory cache. + */ + +typedef struct { + unsigned blockno; + unsigned lru; + ks_block_t block; +} ks_cache_block_t; + +/* + * Medium-specific driver and in-memory database. + * + * The top-level structure is a static variable; the arrays are + * allocated at runtime using hal_allocate_static_memory() because + * they can get kind of large. + * + * Driver-specific stuff is handled by a form of subclassing: the + * driver embeds the hal_ks_t structure at the head of whatever else + * it needs, and performs (controlled, type-safe) casts as needed. + */ + +typedef struct hal_ks_driver hal_ks_driver_t; +typedef struct hal_ks hal_ks_t; + +struct hal_ks { + const hal_ks_driver_t *driver; + unsigned size; /* Blocks in keystore */ + unsigned used; /* How many blocks are in use */ + uint16_t *index; /* Index/freelist array */ + hal_uuid_t *names; /* Keyname array */ + unsigned cache_lru; /* Cache LRU counter */ + unsigned cache_size; /* Size (how many blocks) in cache */ + ks_cache_block_t *cache; /* Cache */ + int per_session; /* Whether objects have per-session semantics (PKCS #11, sigh) */ +}; + +struct hal_ks_driver { + hal_error_t (*init) (hal_ks_t *, const int alloc); + hal_error_t (*shutdown) (hal_ks_t *); + hal_error_t (*read) (hal_ks_t *, const unsigned blockno, ks_block_t *); + hal_error_t (*write) (hal_ks_t *, const unsigned blockno, ks_block_t *) + hal_error_t (*deprecate) (hal_ks_t *, const unsigned blockno); + hal_error_t (*zero) (hal_ks_t *, const unsigned blockno); + hal_error_t (*erase) (hal_ks_t *, const unsigned blockno); + hal_error_t (*erase_maybe) (hal_ks_t *, const unsigned blockno); + hal_error_t (*get_owner) (hal_ks_t *, const unsigned blockno, hal_client_handle_t *, hal_session_handle_t *); + hal_error_t (*set_owner) (hal_ks_t *, const unsigned blockno, const hal_client_handle_t, const hal_session_handle_t); +}; + +#endif /* _KS_H_ */ + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ -- cgit v1.2.3 From c1b19879e13e5717867c73b3273b0fbdeea88c01 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Wed, 24 May 2017 22:36:55 -0400 Subject: Type name cleanup, key visibility. --- ks.c | 181 ++++++++++++++++++++++++++++++++++++++++++++----------------------- ks.h | 168 +++++++++++++++++++++++--------------------------------------- 2 files changed, 180 insertions(+), 169 deletions(-) diff --git a/ks.c b/ks.c index c107eb6..89babc2 100644 --- a/ks.c +++ b/ks.c @@ -321,7 +321,7 @@ hal_error_t hal_ks_index_replace(hal_ks_t *ks, * result, leave the lru values alone and the right thing will happen. */ -static inline ks_block_t *cache_pick_lru(hal_ks_t *ks) +static inline hal_ks_block_t *cache_pick_lru(hal_ks_t *ks) { uint32_t best_delta = 0; int best_index = 0; @@ -347,7 +347,7 @@ static inline ks_block_t *cache_pick_lru(hal_ks_t *ks) * Find a block in our in-memory cache; return block or NULL if not present. */ -static inline ks_block_t *cache_find_block(const hal_ks_t * const ks, const unsigned blockno) +static inline hal_ks_block_t *cache_find_block(const hal_ks_t * const ks, const unsigned blockno) { for (int i = 0; i < ks->cache_size; i++) if (ks->cache[i].blockno == blockno) @@ -359,7 +359,7 @@ static inline ks_block_t *cache_find_block(const hal_ks_t * const ks, const unsi * Mark a block in our in-memory cache as being in current use. */ -static inline void cache_mark_used(hal_ks_t *ks, const ks_block_t * const block, const unsigned blockno) +static inline void cache_mark_used(hal_ks_t *ks, const hal_ks_block_t * const block, const unsigned blockno) { for (int i = 0; i < ks->cache_size; i++) { if (&ks->cache[i].block == block) { @@ -374,7 +374,7 @@ static inline void cache_mark_used(hal_ks_t *ks, const ks_block_t * const block, * Release a block from the in-memory cache. */ -static inline void cache_release(hal_ks_t *ks, const ks_block_t * const block) +static inline void cache_release(hal_ks_t *ks, const hal_ks_block_t * const block) { if (block != NULL) cache_mark_used(block, ~0); @@ -383,12 +383,12 @@ static inline void cache_release(hal_ks_t *ks, const ks_block_t * const block) /* * Generate CRC-32 for a block. * - * This function needs to understand the structure of - * ks_block_header_t, so that it can skip over fields that + * This function needs to understand the structure of the + * hal_ks_block_header_t, so that it can skip over fields that * shouldn't be included in the CRC. */ -static hal_crc32_t calculate_block_crc(const ks_block_t * const block) +static hal_crc32_t calculate_block_crc(const hal_ks_block_t * const block) { hal_crc32_t crc = hal_crc32_init(); @@ -398,8 +398,8 @@ static hal_crc32_t calculate_block_crc(const ks_block_t * const block) sizeof(block->header.block_type)); crc = hal_crc32_update(crc, - block->bytes + sizeof(ks_block_header_t), - sizeof(*block) - sizeof(ks_block_header_t)); + block->bytes + sizeof(hal_ks_block_header_t), + sizeof(*block) - sizeof(hal_ks_block_header_t)); } return hal_crc32_finalize(crc); @@ -408,10 +408,10 @@ static hal_crc32_t calculate_block_crc(const ks_block_t * const block) /* * Read a block using the cache. Marking the block as used is left * for the caller, so we can avoid blowing out the cache when we - * perform a ks_match() operation. + * perform a hal_ks_match() operation. */ -static hal_error_t block_read_cached(hal_ks_t *ks, const unsigned blockno, ks_block_t **block) +static hal_error_t block_read_cached(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t **block) { if (block == NULL) return HAL_ERROR_IMPOSSIBLE; @@ -431,7 +431,7 @@ static hal_error_t block_read_cached(hal_ks_t *ks, const unsigned blockno, ks_bl static hal_error_t block_update(hal_ks_t *ks, const unsigned b1, - ks_block_t *block, + hal_ks_block_t *block, const hal_uuid_t * const uuid, int *hint) { @@ -479,9 +479,9 @@ static inline void *gnaw(uint8_t **mem, size_t *len, const size_t size) return ret; } -#warning Call ks_alloc_common() and ks_init_common() while holding hal_ks_lock(); ! +#warning Call hal_ks_alloc_common() and hal_ks_init_common() while holding hal_ks_lock(); ! -hal_error_t ks_alloc_common(hal_ks_t *ks, const unsigned ks_blocks, const unsigned cache_blocks) +hal_error_t hal_ks_alloc_common(hal_ks_t *ks, const unsigned ks_blocks, const unsigned cache_blocks) { /* * We allocate a single big chunk of memory rather than three @@ -511,7 +511,7 @@ hal_error_t ks_alloc_common(hal_ks_t *ks, const unsigned ks_blocks, const unsign return HAL_OK; } -hal_error_t ks_init_common(hal_ks_t *ks, const hal_ks_driver_t * const driver) +hal_error_t hal_ks_init_common(hal_ks_t *ks, const hal_ks_driver_t * const driver) { if (ks->index == NULL || ks->names == NULL || ks->cache == NULL) return HAL_ERROR_IMPOSSIBLE; @@ -527,9 +527,9 @@ hal_error_t ks_init_common(hal_ks_t *ks, const hal_ks_driver_t * const driver) * like power failures at inconvenient times. */ - ks_block_type_t block_types[ks->size]; - ks_block_status_t block_status[ks->size]; - ks_block_t *block = cache_pick_lru(ks); + hal_ks_block_type_t block_types[ks->size]; + hal_ks_block_status_t block_status[ks->size]; + hal_ks_block_t *block = cache_pick_lru(ks); int first_erased = -1; hal_error_t err; uint16_t n = 0; @@ -711,6 +711,10 @@ hal_error_t ks_init_common(hal_ks_t *ks, const hal_ks_driver_t * const driver) return HAL_OK; } +/* + * Test whether we like a particular key type. + */ + static inline int acceptable_key_type(const hal_key_type_t type) { switch (type) { @@ -724,15 +728,56 @@ static inline int acceptable_key_type(const hal_key_type_t type) } } -hal_error_t ks_store(hal_ks_t *ks, - hal_pkey_slot_t *slot, - const uint8_t * const der, const size_t der_len) +/* + * Test 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. + * + * 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 inline hal_error_t key_visible(const hal_ks_t * const ks, + const hal_client_handle_t client, + const hal_session_handle_t session, + const unsigned blockno) +{ + if (ks == NULL) + return HAL_ERROR_IMPOSSIBLE; + + if (!ks->per_session) + return HAL_OK; + + hal_error_t err; + + if ((err = hal_ks_test_owner(ks, client, session)) != HAL_OK) + return err; + + err = hal_rpc_is_logged_in(client, HAL_USER_WHEEL); + + if (err == HAL_ERROR_FORBIDDEN) + err = HAL_ERROR_KEY_NOT_FOUND; + + return err; +} + +hal_error_t hal_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; hal_error_t err = HAL_OK; - ks_block_t *block; + hal_ks_block_t *block; flash_key_block_t *k; uint8_t kek[KEK_LENGTH]; size_t kek_len; @@ -778,6 +823,9 @@ hal_error_t ks_store(hal_ks_t *ks, if (err == HAL_OK) err = block_write(ks, b, block); + if (err == HAL_OK) + err = hal_ks_set_owner(ks, b, slot->client_handle, slot->session_handle); + if (err == HAL_OK) goto done; @@ -790,21 +838,22 @@ hal_error_t ks_store(hal_ks_t *ks, 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) +hal_error_t hal_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; hal_error_t err = HAL_OK; - ks_block_t *block; + hal_ks_block_t *block; unsigned b; hal_ks_lock(); - if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || - (err = block_read_cached(ks, b, &block)) != HAL_OK) + if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || + (err = key_visible(ks, slot->client_handle, slot->session_handle, b)) != HAL_OK || + (err = block_read_cached(ks, b, &block)) != HAL_OK) goto done; if (block_get_type(block) != BLOCK_TYPE_KEY) { @@ -845,8 +894,8 @@ static hal_error_t ks_fetch(hal_ks_t *ks, return err; } -static hal_error_t ks_delete(hal_ks_t *ks, - hal_pkey_slot_t *slot) +hal_error_t hal_ks_delete(hal_ks_t *ks, + hal_pkey_slot_t *slot) { if (ks == NULL || slot == NULL) return HAL_ERROR_BAD_ARGUMENTS; @@ -856,7 +905,8 @@ static hal_error_t ks_delete(hal_ks_t *ks, hal_ks_lock(); - if ((err = hal_ks_index_delete(ks, &slot->name, &b, &slot->hint)) != HAL_OK) + if ((err = hal_ks_index_delete(ks, &slot->name, &b, &slot->hint)) != HAL_OK || + (err = key_visible(ks, slot->client_handle, slot->session_handle, b)) != HAL_OK) goto done; cache_release(ks, cache_find_block(ks, b)); @@ -871,7 +921,7 @@ static hal_error_t ks_delete(hal_ks_t *ks, return err; } -static inline hal_error_t locate_attributes(ks_block_t *block, +static inline hal_error_t locate_attributes(hal_ks_block_t *block, uint8_t **bytes, size_t *bytes_len, unsigned **attrs_len) { @@ -888,26 +938,26 @@ static inline hal_error_t locate_attributes(ks_block_t *block, return HAL_OK; } -static hal_error_t ks_match(hal_ks_t *ks, - const hal_client_handle_t client, - const hal_session_handle_t session, - const hal_key_type_t type, - const hal_curve_name_t curve, - const hal_key_flags_t mask, - 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) +hal_error_t hal_ks_match(hal_ks_t *ks, + const hal_client_handle_t client, + const hal_session_handle_t session, + const hal_key_type_t type, + const hal_curve_name_t curve, + const hal_key_flags_t mask, + 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 (ks == NULL || (attributes == NULL && attributes_len > 0) || result == NULL || result_len == NULL || previous_uuid == NULL) return HAL_ERROR_BAD_ARGUMENTS; hal_error_t err = HAL_OK; - ks_block_t *block; + hal_ks_block_t *block; int i = -1; hal_ks_lock(); @@ -928,6 +978,11 @@ static hal_error_t ks_match(hal_ks_t *ks, if ((err = block_read_cached(ks, b, &block)) != HAL_OK) goto done; + if ((err = key_visible(ks, client, session, b)) == HAL_ERROR_KEY_NOT_FOUND) + continue; + else if (err != HAL_OK) + goto done; + if ((type != HAL_KEY_TYPE_NONE && type != block->key.type) || (curve != HAL_CURVE_NONE && curve != block->key.curve) || ((flags ^ block->key.flags) & mask) != 0) @@ -982,23 +1037,24 @@ static hal_error_t ks_match(hal_ks_t *ks, return err; } -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) +hal_error_t hal_ks_set_attributes(hal_ks_t *ks, + hal_pkey_slot_t *slot, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len) { if (ks == NULL || slot == NULL || attributes == NULL || attributes_len == 0) return HAL_ERROR_BAD_ARGUMENTS; hal_error_t err = HAL_OK; - ks_block_t *block; + hal_ks_block_t *block; unsigned b; hal_ks_lock(); { - if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || - (err = block_read_cached(ks, b, &block)) != HAL_OK) + if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || + (err = key_visibile(ks, slot->client_handle, slot->session_handle, b)) != HAL_OK || + (err = block_read_cached(ks, b, &block)) != HAL_OK) goto done; cache_mark_used(ks, block, b); @@ -1037,12 +1093,12 @@ static hal_error_t ks_set_attributes(hal_ks_t *ks, return err; } -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) +hal_error_t hal_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 (ks == NULL || slot == NULL || attributes == NULL || attributes_len == 0 || attributes_buffer == NULL) @@ -1054,7 +1110,7 @@ static hal_error_t ks_get_attributes(hal_ks_t *ks, } uint8_t *abuf = attributes_buffer; - ks_block_t *block = NULL; + hal_ks_block_t *block = NULL; hal_error_t err = HAL_OK; unsigned found = 0; unsigned b; @@ -1062,8 +1118,9 @@ static hal_error_t ks_get_attributes(hal_ks_t *ks, hal_ks_lock(); { - if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || - (err = block_read_cached(ks, b, &block)) != HAL_OK) + if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || + (err = key_visibile(ks, slot->client_handle, slot->session_handle, b)) != HAL_OK || + (err = block_read_cached(ks, b, &block)) != HAL_OK) goto done; cache_mark_used(ks, block, b); diff --git a/ks.h b/ks.h index ff6d382..3db2e66 100644 --- a/ks.h +++ b/ks.h @@ -1,82 +1,36 @@ -// Notes towards unified keystore code (drivers become just low-level -// "disk" I/O and perhaps a bit of local init/shutdown). -// -// Most of the structure definitions in ks_flash.c and ks_volatile.c -// become common and go in ks.h (or wherever, but probably be enough -// stuff that separate .h file might be easier to read). -// -// We already have -// -// typedef struct hal_ks hal_ks_t; -// -// which we "subclass" to get ks_t (ks_volatile) and db_t (ks_flash). -// We can move more common stuff there. -// -// flash_block_t (etc) becomes ks_block_t (etc) as these data -// structures will be used by all keystores, not just flash. -// -// We might want to fold hal_ks_index_t into hal_ks_t as everything -// will be using it. Then again, it's relatively harmless as it is, a -// bit more verbose trading for a bit more isolation. Probably go for -// less verbose, for readability. -// -// Each keystore will still have some weird private stuff, like the -// RAM for the keys themselves in the volatile case and the PIN stuff -// in the flash case. -// -// The ks_flash cache, however, probably wants to become common code. -// Yes we could get a bit more efficient if we skipped caching in the -// volatile case, but that's not our bottleneck and there are some -// cases where the code relies on knowing that mucking with the cache -// copy is harmless until we write the block to "disk", don't want to -// mess with that, so keep the flash model for volatile. Cache size -// will need to become another hal_ks_t field. -// -// Don't remember exactly where we're doing the "subclassing" casts, -// should be easy enough to find...except that ks_flash is mostly -// ignoring that argument and using the static db variable directly. -// ks_volatile may be closer to write on this point, as it already had -// ks_to_ksv(). But most of the code will be in a driver-agnostic -// ks.c (or whatever) and will be calling functions that care through -// the driver, maybe this doesn't matter very much. -// -// Tedious though it sounds, might be simplest just to check each -// function in ks_*.c to see whether it moves to ks.[ch] or becomes -// something called by the new lower-level driver API. Need a sketch -// of the lower-level driver API, chicken and egg there but probably -// is init(), shutdown(), block_read(), block_deprecate(), -// block_zero(), block_erase(), block_erase_maybe(), block-write(). -// Possible that some of these don't really need to be driver, was -// mostly basing this on which things in ks_flash touch flash -// directly-ish via the keystore_*() functions. -// -// Would be nice if we can make the API regular enough (inline -// functions?) that user need not really care which functions are -// driver-specific and which are layered on top, but that may be -// impractical (or silly). -// -// Hmm, hal_ks_open() and hal_ks_close() don't quite fit new model, -// what was I thinking there? Not much, existing implementations just -// use that to get back a (hal_ks_t*), so really just checking the -// binding between driver and keystore object. -// -// I think this boils down to another instance of the confusion -// between what in Python would be Keystore.__new__() and -// Keystore.__init__(). This even sort of fits with the weird `alloc` -// parameter in ks_init(). -// -// Maybe we can trust C memory initialization enough to use a zeroed -// static variable as test for whether a keystore has been -// initialized, and just have the low-level (driver) methods check -// that and fail if trying to use an uninitialized keystore? -// -// Pythonesque view might be the right way to handle ks_init(0 and -// ks_shutdown() too: in most cases we have inline functions which -// call the driver function, but for these methods the subclass needs -// to extend the abstract method, which translates, in C, to the -// generic method calling the driver method of the same name at the -// right time. Not quite what Python does but close enough. - +/* + * ks.h + * ---- + * Keystore, generic parts anyway. This is internal within libhal. + * + * Copyright (c) 2015-2017, 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 + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ #ifndef _KS_H_ #define _KS_H_ @@ -107,22 +61,22 @@ */ typedef enum { - KS_BLOCK_TYPE_ERASED = 0xFF, /* Pristine erased block (candidate for reuse) */ - KS_BLOCK_TYPE_ZEROED = 0x00, /* Zeroed block (recently used) */ - KS_BLOCK_TYPE_KEY = 0x55, /* Block contains key material */ - KS_BLOCK_TYPE_PIN = 0xAA, /* Block contains PINs */ - KS_BLOCK_TYPE_UNKNOWN = -1, /* Internal code for "I have no clue what this is" */ -} ks_block_type_t; + HAL_KS_BLOCK_TYPE_ERASED = 0xFF, /* Pristine erased block (candidate for reuse) */ + HAL_KS_BLOCK_TYPE_ZEROED = 0x00, /* Zeroed block (recently used) */ + HAL_KS_BLOCK_TYPE_KEY = 0x55, /* Block contains key material */ + HAL_KS_BLOCK_TYPE_PIN = 0xAA, /* Block contains PINs */ + HAL_KS_BLOCK_TYPE_UNKNOWN = -1, /* Internal code for "I have no clue what this is" */ +} hal_ks_block_type_t; /* * Block status. */ typedef enum { - KS_BLOCK_STATUS_LIVE = 0x66, /* This is a live block */ - KS_BLOCK_STATUS_TOMBSTONE = 0x44, /* This is a tombstone left behind during an update */ - KS_BLOCK_STATUS_UNKNOWN = -1, /* Internal code for "I have no clue what this is" */ -} ks_block_status_t; + HAL_KS_BLOCK_STATUS_LIVE = 0x66, /* This is a live block */ + HAL_KS_BLOCK_STATUS_TOMBSTONE = 0x44, /* This is a tombstone left behind during an update */ + HAL_KS_BLOCK_STATUS_UNKNOWN = -1, /* Internal code for "I have no clue what this is" */ +} hal_ks_block_status_t; /* * Common header for all keystore block types. @@ -133,14 +87,14 @@ typedef struct { uint8_t block_type; uint8_t block_status; hal_crc32_t crc; -} ks_block_header_t; +} hal_ks_block_header_t; /* * Key block. Tail end of "der" field (after der_len) used for attributes. */ typedef struct { - ks_block_header_t header; + hal_ks_block_header_t header; hal_uuid_t name; hal_key_type_t type; hal_curve_name_t curve; @@ -148,10 +102,10 @@ typedef struct { size_t der_len; unsigned attributes_len; uint8_t der[]; /* Must be last field -- C99 "flexible array member" */ -} ks_blockkey_block_t; +} hal_ks_blockkey_block_t; #define SIZEOF_KS_BLOCKKEY_BLOCK_DER \ - (HAL_KS_BLOCK_SIZE - offsetof(ks_blockkey_block_t, der)) + (HAL_KS_BLOCK_SIZE - offsetof(hal_ks_blockkey_block_t, der)) /* * PIN block. Also includes space for backing up the KEK when @@ -159,7 +113,7 @@ typedef struct { */ typedef struct { - ks_block_header_t header; + hal_ks_block_header_t header; hal_ks_pin_t wheel_pin; hal_ks_pin_t so_pin; hal_ks_pin_t user_pin; @@ -167,7 +121,7 @@ typedef struct { uint32_t kek_set; uint8_t kek[KEK_LENGTH]; #endif -} ks_blockpin_block_t; +} hal_ks_blockpin_block_t; #define FLASH_KEK_SET 0x33333333 @@ -176,11 +130,11 @@ typedef struct { */ typedef union { - uint8_t bytes[HAL_KS_BLOCK_SIZE]; - ks_block_header_t header; - ks_blockkey_block_t key; - ks_blockpin_block_t pin; -} ks_block_t; + uint8_t bytes[HAL_KS_BLOCK_SIZE]; + hal_ks_block_header_t header; + hal_ks_blockkey_block_t key; + hal_ks_blockpin_block_t pin; +} hal_ks_block_t; /* * In-memory cache. @@ -189,8 +143,8 @@ typedef union { typedef struct { unsigned blockno; unsigned lru; - ks_block_t block; -} ks_cache_block_t; + hal_ks_block_t block; +} hal_ks_cache_block_t; /* * Medium-specific driver and in-memory database. @@ -215,21 +169,21 @@ struct hal_ks { hal_uuid_t *names; /* Keyname array */ unsigned cache_lru; /* Cache LRU counter */ unsigned cache_size; /* Size (how many blocks) in cache */ - ks_cache_block_t *cache; /* Cache */ + hal_ks_cache_block_t *cache; /* Cache */ int per_session; /* Whether objects have per-session semantics (PKCS #11, sigh) */ }; struct hal_ks_driver { hal_error_t (*init) (hal_ks_t *, const int alloc); hal_error_t (*shutdown) (hal_ks_t *); - hal_error_t (*read) (hal_ks_t *, const unsigned blockno, ks_block_t *); - hal_error_t (*write) (hal_ks_t *, const unsigned blockno, ks_block_t *) + hal_error_t (*read) (hal_ks_t *, const unsigned blockno, hal_ks_block_t *); + hal_error_t (*write) (hal_ks_t *, const unsigned blockno, hal_ks_block_t *) hal_error_t (*deprecate) (hal_ks_t *, const unsigned blockno); hal_error_t (*zero) (hal_ks_t *, const unsigned blockno); hal_error_t (*erase) (hal_ks_t *, const unsigned blockno); hal_error_t (*erase_maybe) (hal_ks_t *, const unsigned blockno); - hal_error_t (*get_owner) (hal_ks_t *, const unsigned blockno, hal_client_handle_t *, hal_session_handle_t *); - hal_error_t (*set_owner) (hal_ks_t *, const unsigned blockno, const hal_client_handle_t, const hal_session_handle_t); + hal_error_t (*set_owner) (hal_ks_t *, const unsigned blockno, const hal_client_handle_t, const hal_session_handle_t); + hal_error_t (*test_owner) (hal_ks_t *, const unsigned blockno, const hal_client_handle_t, const hal_session_handle_t); }; #endif /* _KS_H_ */ -- cgit v1.2.3 From b9565626187cca926c21120786ec575c59f06a05 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Thu, 25 May 2017 01:05:10 -0400 Subject: Fix up ks driver calls and inline wrappers. --- ks.c | 40 ++++++++++++++++++++-------------------- ks.h | 43 +++++++++++++++++++++++++++++++------------ 2 files changed, 51 insertions(+), 32 deletions(-) diff --git a/ks.c b/ks.c index 89babc2..a93cbe1 100644 --- a/ks.c +++ b/ks.c @@ -422,7 +422,7 @@ static hal_error_t block_read_cached(hal_ks_t *ks, const unsigned blockno, hal_k if ((*block = cache_pick_lru(ks)) == NULL) return HAL_ERROR_IMPOSSIBLE; - return block_read(ks, blockno, *block); + return hal_ks_block_read(ks, blockno, *block); } /* @@ -446,10 +446,10 @@ static hal_error_t block_update(hal_ks_t *ks, hal_error_t err; unsigned b2; - if ((err = block_deprecate(ks, b1)) != HAL_OK || + if ((err = hal_ks_block_deprecate(ks, b1)) != HAL_OK || (err = hal_ks_index_replace(ks, uuid, &b2, hint)) != HAL_OK || - (err = block_write(ks, b2, block)) != HAL_OK || - (err = block_zero(ks, b1)) != HAL_OK) + (err = hal_ks_block_write(ks, b2, block)) != HAL_OK || + (err = hal_ks_block_zero(ks, b1)) != HAL_OK) return err; cache_mark_used(ks, block, b2); @@ -459,7 +459,7 @@ static hal_error_t block_update(hal_ks_t *ks, * puts the block back at the head of the free list. */ - return block_erase_maybe(ks, ks->index[ks->used]); + return hal_ks_block_erase_maybe(ks, ks->index[ks->used]); } /* @@ -546,7 +546,7 @@ hal_error_t hal_ks_init_common(hal_ks_t *ks, const hal_ks_driver_t * const drive * we want the block to end up near the end of the free list. */ - err = block_read(ks, i, block); + err = hal_ks_block_read(ks, i, block); if (err == HAL_ERROR_KEYSTORE_BAD_CRC || err == HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE) block_types[i] = BLOCK_TYPE_UNKNOWN; @@ -679,16 +679,16 @@ hal_error_t hal_ks_init_common(hal_ks_t *ks, const hal_ks_driver_t * const drive else { unsigned b_live; - if ((err = block_read(ks, b_tomb, block)) != HAL_OK) + if ((err = hal_ks_block_read(ks, b_tomb, block)) != HAL_OK) return err; block->header.block_status = BLOCK_STATUS_LIVE; if ((err = hal_ks_index_replace(ks, &name, &b_live, &where)) != HAL_OK || - (err = block_write(ks, b_live, block)) != HAL_OK) + (err = hal_ks_block_write(ks, b_live, block)) != HAL_OK) return err; block_status[b_live] = BLOCK_STATUS_LIVE; } - if ((err = block_zero(ks, b_tomb)) != HAL_OK) + if ((err = hal_ks_block_zero(ks, b_tomb)) != HAL_OK) return err; block_types[ b_tomb] = BLOCK_TYPE_ZEROED; block_status[b_tomb] = BLOCK_STATUS_UNKNOWN; @@ -699,7 +699,7 @@ hal_error_t hal_ks_init_common(hal_ks_t *ks, const hal_ks_driver_t * const drive */ if (ks->used < ks->size && - (err = block_erase_maybe(ks, ks->index[ks->used])) != HAL_OK) + (err = hal_ks_block_erase_maybe(ks, ks->index[ks->used])) != HAL_OK) return err; /* @@ -758,7 +758,7 @@ static inline hal_error_t key_visible(const hal_ks_t * const ks, hal_error_t err; - if ((err = hal_ks_test_owner(ks, client, session)) != HAL_OK) + if ((err = hal_ks_block_test_owner(ks, client, session)) != HAL_OK) return err; err = hal_rpc_is_logged_in(client, HAL_USER_WHEEL); @@ -810,7 +810,7 @@ hal_error_t hal_ks_store(hal_ks_t *ks, k->attributes_len = 0; if (ks->used < ks->size) - err = block_erase_maybe(ks, ks->index[ks->used]); + err = hal_ks_block_erase_maybe(ks, ks->index[ks->used]); if (err == HAL_OK) err = hal_mkm_get_kek(kek, &kek_len, sizeof(kek)); @@ -821,10 +821,10 @@ hal_error_t hal_ks_store(hal_ks_t *ks, memset(kek, 0, sizeof(kek)); if (err == HAL_OK) - err = block_write(ks, b, block); + err = hal_ks_block_write(ks, b, block); if (err == HAL_OK) - err = hal_ks_set_owner(ks, b, slot->client_handle, slot->session_handle); + err = hal_ks_block_set_owner(ks, b, slot->client_handle, slot->session_handle); if (err == HAL_OK) goto done; @@ -853,7 +853,7 @@ hal_error_t hal_ks_fetch(hal_ks_t *ks, if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || (err = key_visible(ks, slot->client_handle, slot->session_handle, b)) != HAL_OK || - (err = block_read_cached(ks, b, &block)) != HAL_OK) + (err = hal_ks_block_read_cached(ks, b, &block)) != HAL_OK) goto done; if (block_get_type(block) != BLOCK_TYPE_KEY) { @@ -911,10 +911,10 @@ hal_error_t hal_ks_delete(hal_ks_t *ks, cache_release(ks, cache_find_block(ks, b)); - if ((err = block_zero(ks, b)) != HAL_OK) + if ((err = hal_ks_block_zero(ks, b)) != HAL_OK) goto done; - err = block_erase_maybe(ks, ks->index[ks->used]); + err = hal_ks_block_erase_maybe(ks, ks->index[ks->used]); done: hal_ks_unlock(); @@ -975,7 +975,7 @@ hal_error_t hal_ks_match(hal_ks_t *ks, unsigned b = ks->index[i]; - if ((err = block_read_cached(ks, b, &block)) != HAL_OK) + if ((err = hal_ks_block_read_cached(ks, b, &block)) != HAL_OK) goto done; if ((err = key_visible(ks, client, session, b)) == HAL_ERROR_KEY_NOT_FOUND) @@ -1054,7 +1054,7 @@ hal_error_t hal_ks_set_attributes(hal_ks_t *ks, { if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || (err = key_visibile(ks, slot->client_handle, slot->session_handle, b)) != HAL_OK || - (err = block_read_cached(ks, b, &block)) != HAL_OK) + (err = hal_ks_block_read_cached(ks, b, &block)) != HAL_OK) goto done; cache_mark_used(ks, block, b); @@ -1120,7 +1120,7 @@ hal_error_t hal_ks_get_attributes(hal_ks_t *ks, { if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || (err = key_visibile(ks, slot->client_handle, slot->session_handle, b)) != HAL_OK || - (err = block_read_cached(ks, b, &block)) != HAL_OK) + (err = hal_ks_block_read_cached(ks, b, &block)) != HAL_OK) goto done; cache_mark_used(ks, block, b); diff --git a/ks.h b/ks.h index 3db2e66..2f48738 100644 --- a/ks.h +++ b/ks.h @@ -173,18 +173,37 @@ struct hal_ks { int per_session; /* Whether objects have per-session semantics (PKCS #11, sigh) */ }; -struct hal_ks_driver { - hal_error_t (*init) (hal_ks_t *, const int alloc); - hal_error_t (*shutdown) (hal_ks_t *); - hal_error_t (*read) (hal_ks_t *, const unsigned blockno, hal_ks_block_t *); - hal_error_t (*write) (hal_ks_t *, const unsigned blockno, hal_ks_block_t *) - hal_error_t (*deprecate) (hal_ks_t *, const unsigned blockno); - hal_error_t (*zero) (hal_ks_t *, const unsigned blockno); - hal_error_t (*erase) (hal_ks_t *, const unsigned blockno); - hal_error_t (*erase_maybe) (hal_ks_t *, const unsigned blockno); - hal_error_t (*set_owner) (hal_ks_t *, const unsigned blockno, const hal_client_handle_t, const hal_session_handle_t); - hal_error_t (*test_owner) (hal_ks_t *, const unsigned blockno, const hal_client_handle_t, const hal_session_handle_t); -}; +#define KS_DRIVER_END_LIST +#define KS_DRIVER_METHODS \ + KS_DRIVER_METHOD(read, hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block) \ + KS_DRIVER_METHOD(write, hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block) \ + KS_DRIVER_METHOD(deprecate, hal_ks_t *ks, const unsigned blockno) \ + KS_DRIVER_METHOD(zero, hal_ks_t *ks, const unsigned blockno) \ + KS_DRIVER_METHOD(erase, hal_ks_t *ks, const unsigned blockno) \ + KS_DRIVER_METHOD(erase_maybe, hal_ks_t *ks, const unsigned blockno) \ + KS_DRIVER_METHOD(set_owner, hal_ks_t *ks, const unsigned blockno, \ + const hal_client_handle_t client, const hal_session_handle_t session) \ + KS_DRIVER_METHOD(test_owner, hal_ks_t *ks, const unsigned blockno, \ + const hal_client_handle_t client, const hal_session_handle_t session) \ + KS_DRIVER_END_LIST + +#define KS_DRIVER_METHOD(_name_, ...) hal_error_t (*_name_)(__VA_ARGS__) +struct hal_ks_driver { KS_DRIVER_METHODS }; +#undef KS_DRIVER_METHOD + +#define KS_DRIVER_METHOD(_name_, ...) \ + static inline hal_error_t hal_ks_block_##_name_(__VA_ARGS__) \ + { \ + return \ + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : \ + ks->driver->_name_ == NULL ? HAL_ERROR_NOT_IMPLEMENTED : \ + ks->driver->_name_(__VA_ARGS__); \ + } +KS_DRIVER_METHODS +#undef KS_DRIVER_METHOD + +#undef KS_DRIVER_METHODS +#undef KS_DRIVER_END_LIST #endif /* _KS_H_ */ -- cgit v1.2.3 From 5eccb3e6d7c27149a0092de48eb21baa495879cb Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Thu, 25 May 2017 11:18:39 -0400 Subject: Checkpoint while refactoring. Almost certainly will not compile. --- Makefile | 2 +- core.c | 2 +- hal_internal.h | 348 ++----------------- ks.c | 275 +-------------- ks.h | 113 +++++- ks_flash.c | 1056 +++----------------------------------------------------- ks_index.c | 218 ++++++------ ks_volatile.c | 607 ++++++-------------------------- 8 files changed, 395 insertions(+), 2226 deletions(-) diff --git a/Makefile b/Makefile index 5ba5d32..e688837 100644 --- a/Makefile +++ b/Makefile @@ -137,7 +137,7 @@ endif # volatile keystore is always present, to support things like PKCS #11 # "session" objects. -KS_OBJ = ks_index.o ks_attribute.o ks_volatile.o ks_flash.o mkm.o +KS_OBJ = ks.o ks_index.o ks_attribute.o ks_volatile.o ks_flash.o mkm.o # RPC_MODE = none | server | client-simple | client-mixed # none: Build without RPC client, use cores directly. diff --git a/core.c b/core.c index 1c247f0..8e9f2b2 100644 --- a/core.c +++ b/core.c @@ -245,7 +245,7 @@ hal_error_t hal_core_alloc(const char *name, hal_core_t **pcore) hal_task_yield(); else break; - } + } return err; } diff --git a/hal_internal.h b/hal_internal.h index eee2eab..89dfbbb 100644 --- a/hal_internal.h +++ b/hal_internal.h @@ -473,338 +473,30 @@ typedef struct { */ } hal_pkey_slot_t; -typedef struct hal_ks_driver hal_ks_driver_t; - -typedef struct hal_ks hal_ks_t; - -struct hal_ks_driver { - - hal_error_t (*init)(const hal_ks_driver_t * const driver, - const int alloc); - - hal_error_t (*shutdown)(const hal_ks_driver_t * const driver); - - hal_error_t (*open)(const hal_ks_driver_t * const driver, - hal_ks_t **ks); - - hal_error_t (*close)(hal_ks_t *ks); - - hal_error_t (*store)(hal_ks_t *ks, - hal_pkey_slot_t *slot, - const uint8_t * const der, const size_t der_len); - - hal_error_t (*fetch)(hal_ks_t *ks, - hal_pkey_slot_t *slot, - uint8_t *der, size_t *der_len, const size_t der_max); - - hal_error_t (*delete)(hal_ks_t *ks, - hal_pkey_slot_t *slot); - - hal_error_t (*match)(hal_ks_t *ks, - const hal_client_handle_t client, - const hal_session_handle_t session, - const hal_key_type_t type, - const hal_curve_name_t curve, - const hal_key_flags_t mask, - 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); - - hal_error_t (*set_attributes)(hal_ks_t *ks, - hal_pkey_slot_t *slot, - const hal_pkey_attribute_t *attributes, - const unsigned attributes_len); - - hal_error_t (*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); - -}; - - -struct hal_ks { - const hal_ks_driver_t *driver; - /* - * Any other common portions of hal_ks_t go here. - */ - - /* - * Driver-specific stuff is handled by a form of subclassing: - * driver module embeds this structure at the head of whatever - * else it needs, and performs casts as needed. - */ -}; - -extern const hal_ks_driver_t - hal_ks_volatile_driver[1], - hal_ks_token_driver[1]; - -static inline hal_error_t hal_ks_init(const hal_ks_driver_t * const driver, - const int alloc) -{ - if (driver == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - if (driver->init == NULL) - return HAL_ERROR_NOT_IMPLEMENTED; - - return driver->init(driver, alloc); -} - -static inline hal_error_t hal_ks_shutdown(const hal_ks_driver_t * const driver) -{ - if (driver == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - if (driver->shutdown == NULL) - return HAL_ERROR_NOT_IMPLEMENTED; - - return driver->shutdown(driver); -} - -static inline hal_error_t hal_ks_open(const hal_ks_driver_t * const driver, - hal_ks_t **ks) -{ - if (driver == NULL || ks == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - if (driver->open == NULL) - return HAL_ERROR_NOT_IMPLEMENTED; - - return driver->open(driver, ks); -} - -static inline hal_error_t hal_ks_close(hal_ks_t *ks) -{ - if (ks == NULL || ks->driver == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - if (ks->driver->close == NULL) - return HAL_ERROR_NOT_IMPLEMENTED; - - return ks->driver->close(ks); -} - -static inline hal_error_t hal_ks_store(hal_ks_t *ks, - hal_pkey_slot_t *slot, - const uint8_t * const der, const size_t der_len) -{ - if (ks == NULL || ks->driver == NULL || slot == NULL || der == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - if (ks->driver->store == NULL) - return HAL_ERROR_NOT_IMPLEMENTED; - - return ks->driver->store(ks, slot, der, der_len); -} - -static inline hal_error_t hal_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 || ks->driver == NULL || slot == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - if (ks->driver->fetch == NULL) - return HAL_ERROR_NOT_IMPLEMENTED; - - return ks->driver->fetch(ks, slot, der, der_len, der_max); -} - -static inline hal_error_t hal_ks_delete(hal_ks_t *ks, - hal_pkey_slot_t *slot) -{ - if (ks == NULL || ks->driver == NULL || slot == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - if (ks->driver->delete == NULL) - return HAL_ERROR_NOT_IMPLEMENTED; - - return ks->driver->delete(ks, slot); -} - -static inline hal_error_t hal_ks_match(hal_ks_t *ks, - const hal_client_handle_t client, - const hal_session_handle_t session, - const hal_key_type_t type, - const hal_curve_name_t curve, - const hal_key_flags_t mask, - 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 (ks == NULL || ks->driver == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - if (ks->driver->match == NULL) - return HAL_ERROR_NOT_IMPLEMENTED; - - return ks->driver->match(ks, client, session, type, curve, mask, flags, attributes, attributes_len, - result, result_len, result_max, previous_uuid); -} - -static inline hal_error_t hal_ks_set_attributes(hal_ks_t *ks, - hal_pkey_slot_t *slot, - const hal_pkey_attribute_t *attributes, - const unsigned attributes_len) -{ - if (ks == NULL || ks->driver == NULL || slot == NULL || - attributes == NULL || attributes_len == 0) - return HAL_ERROR_BAD_ARGUMENTS; - - if (ks->driver->set_attributes == NULL) - return HAL_ERROR_NOT_IMPLEMENTED; - - return ks->driver->set_attributes(ks, slot, attributes, attributes_len); -} - -static inline hal_error_t hal_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 (ks == NULL || ks->driver == NULL || slot == NULL || - attributes == NULL || attributes_len == 0) - return HAL_ERROR_BAD_ARGUMENTS; - - if (ks->driver->get_attributes == NULL) - return HAL_ERROR_NOT_IMPLEMENTED; - - return ks->driver->get_attributes(ks, slot, attributes, attributes_len, - attributes_buffer, attributes_buffer_len); -} - /* - * Keystore index. This is intended to be usable by both memory-based - * and flash-based keystores. Some of the features aren't really - * necessary for memory-based keystores, but should be harmless. - * - * General approach is multiple arrays, all but one of which are - * indexed by "block" numbers, where a block number might be a slot in - * yet another static array, the number of a flash sub-sector, or - * whatever is the appropriate unit for holding one keystore record. - * - * The index array contains nothing but flags and block numbers, and - * is deliberately a small data structure so that moving data around - * within it is relatively cheap. - * - * The index array is divided into two portions: the index proper, and - * the free queue. The index proper is ordered according to the names - * (UUIDs) of the corresponding blocks; the free queue is a FIFO, to - * support a simplistic form of wear leveling in flash-based keystores. - * - * Key names are kept in a separate array, indexed by block number. - * - * The all-zeros UUID, which (by definition) cannot be a valid key - * UUID, is reserved for the (non-key) block used to stash PINs and - * other small data which aren't really part of the keystore proper - * but are kept with it because the keystore is the flash we have. - * - * Note that this API deliberately says nothing about how the keys - * themselves are stored, that's up to the keystore driver. This - * portion of the API is only concerned with allocation and naming. - */ - -typedef struct { - unsigned size; /* Array length */ - unsigned used; /* How many blocks are in use */ - uint16_t *index; /* Index/freelist array */ - hal_uuid_t *names; /* Keyname array */ -} hal_ks_index_t; - -/* - * Finish setting up key index. Caller must populate index, free - * list, and name array. - * - * This function checks a few things then sorts the index proper. - * - * If driver cares about wear leveling, driver must supply the free - * list in the desired order (FIFO); figuring out what that order is a - * problem for the keystore driver. - */ -extern hal_error_t hal_ks_index_setup(hal_ks_index_t *ksi); - -/* - * Find a key block, return its block number. - */ -extern hal_error_t hal_ks_index_find(hal_ks_index_t *ksi, - const hal_uuid_t * const name, - unsigned *blockno, - int *hint); - -/* - * Add a key block, return its block number. - */ -extern hal_error_t hal_ks_index_add(hal_ks_index_t *ksi, - const hal_uuid_t * const name, - unsigned *blockno, - int *hint); - -/* - * Delete a key block, returns its block number (driver may need it). + * Keystore is an opaque type, we just pass pointers. */ -extern hal_error_t hal_ks_index_delete(hal_ks_index_t *ksi, - const hal_uuid_t * const name, - unsigned *blockno, - int *hint); - -/* - * Replace a key block with a new one, return new block number. - * Name of block does not change. This is an optimization of - * a delete immediately followed by an add for the same name. - */ - -extern hal_error_t hal_ks_index_replace(hal_ks_index_t *ksi, - const hal_uuid_t * const name, - unsigned *blockno, - int *hint); -/* - * Check the index for errors. At least for the moment, this just - * reports errors, it doesn't attempt to fix them. - */ - -extern hal_error_t hal_ks_index_fsck(hal_ks_index_t *ksi); - -/* - * Keystore attribute utilities, for use by keystore drivers. - */ +typedef struct hal_ks hal_ks_t; -extern const size_t hal_ks_attribute_header_size; - -extern hal_error_t hal_ks_attribute_scan(const uint8_t * const bytes, - const size_t bytes_len, - hal_pkey_attribute_t *attributes, - const unsigned attributes_len, - size_t *total_len); - -extern hal_error_t hal_ks_attribute_delete(uint8_t *bytes, - const size_t bytes_len, - hal_pkey_attribute_t *attributes, - unsigned *attributes_len, - size_t *total_len, - const uint32_t type); - -extern hal_error_t hal_ks_attribute_insert(uint8_t *bytes, const size_t bytes_len, - hal_pkey_attribute_t *attributes, - unsigned *attributes_len, - size_t *total_len, - const uint32_t type, - const uint8_t * const value, - const size_t value_len); +#error Minor re-think needed on keystore init + +// I like current hal_ks_*_init() setup method, but how does anything +// get a handle on the keystore? Make the keystores global variables? +// Right now they're hidden in larger, driver-specific structures, but +// it would be easy enough to break them out. Have already forgotten +// how the old API handled this, except that it involved an init +// method via the driver. Init is going to be special in any case, +// since we can't dispatch through a driver pointer in the keystore +// object, so either we expose the keystore and the driver or we +// expose the keystore and the init function. The latter may be +// simpler. +// +// Another variation would be to keep the current nesting, add global +// pointer variables for the keystores, and have the init functions +// set the pointers. Only real advantage there is that it would give +// us an easy test for whether the keystore had been initialized...but +// we already have several of those, not clear what value another adds. /* * RPC lowest-level send and receive routines. These are blocking, and diff --git a/ks.c b/ks.c index a93cbe1..f0c71cc 100644 --- a/ks.c +++ b/ks.c @@ -40,277 +40,17 @@ #include "ks.h" /* - * Find a block in the index, return true (found) or false (not found). - * "where" indicates the name's position, or the position of the first free block. - * - * NB: This does NOT return a block number, it returns an index into - * ks->index[]. - */ - -static int ks_find(const hal_ks_t * const ks, - const hal_uuid_t * const uuid, - const int * const hint, - int *where) -{ - if (ks == NULL || ks->index == NULL || ks->names == NULL || uuid == NULL || where == NULL) - return 0; - - if (hint != NULL && *hint >= 0 && *hint < ks->used && - hal_uuid_cmp(uuid, &ks->names[ks->index[*hint]]) == 0) { - *where = *hint; - return 1; - } - - int lo = -1; - int hi = ks->used; - - for (;;) { - int m = (lo + hi) / 2; - if (hi == 0 || m == lo) { - *where = hi; - return 0; - } - const int cmp = hal_uuid_cmp(uuid, &ks->names[ks->index[m]]); - if (cmp < 0) - hi = m; - else if (cmp > 0) - lo = m; - else { - *where = m; - return 1; - } - } -} - -/* - * Heapsort the index. We only need to do this on setup, for other - * operations we're just inserting or deleting a single entry in an - * already-ordered array, which is just a search problem. If we were - * really crunched for space, we could use an insertion sort here, but - * heapsort is easy and works well with data already in place. - */ - -static inline void ks_heapsift(hal_ks_t *ks, int parent, const int end) -{ - if (ks == NULL || ks->index == NULL || ks->names == NULL || parent < 0 || end < parent) - return; - for (;;) { - const int left_child = parent * 2 + 1; - const int right_child = parent * 2 + 2; - int biggest = parent; - if (left_child <= end && hal_uuid_cmp(&ks->names[ks->index[biggest]], - &ks->names[ks->index[left_child]]) < 0) - biggest = left_child; - if (right_child <= end && hal_uuid_cmp(&ks->names[ks->index[biggest]], - &ks->names[ks->index[right_child]]) < 0) - biggest = right_child; - if (biggest == parent) - return; - const uint16_t tmp = ks->index[biggest]; - ks->index[biggest] = ks->index[parent]; - ks->index[parent] = tmp; - parent = biggest; - } -} - -static inline void ks_heapsort(hal_ks_t *ks) -{ - if (ks == NULL || ks->index == NULL || ks->names == NULL) - return; - if (ks->used < 2) - return; - for (int i = (ks->used - 2) / 2; i >= 0; i--) - ks_heapsift(ks, i, ks->used - 1); - for (int i = ks->used - 1; i > 0; i--) { - const uint16_t tmp = ks->index[i]; - ks->index[i] = ks->index[0]; - ks->index[0] = tmp; - ks_heapsift(ks, 0, i - 1); - } -} - -/* - * Perform a consistency check on the index. + * Type safe casts. */ -#define fsck(_ks) \ - do { hal_error_t _err = hal_ks_index_fsck(_ks); if (_err != HAL_OK) return _err; } while (0) - - -hal_error_t hal_ks_index_fsck(hal_ks_t *ks) +static inline ks_block_type_t block_get_type(const ks_block_t * const block) { - if (ks == NULL || ks->index == NULL || ks->names == NULL || - ks->size == 0 || ks->used > ks->size) - return HAL_ERROR_BAD_ARGUMENTS; - - for (int i = 1; i < ks->used; i++) - if (hal_uuid_cmp(&ks->names[ks->index[i - 1]], &ks->names[ks->index[i]]) >= 0) - return HAL_ERROR_KS_INDEX_UUID_MISORDERED; - - return HAL_OK; + return block == NULL ? HAL_KS_BLOCK_TYPE_UNKNOWN : (ks_block_type_t) block->header.block_type; } -/* - * Find a single block by name. - */ - -hal_error_t hal_ks_index_find(hal_ks_t *ks, - const hal_uuid_t * const name, - unsigned *blockno, - int *hint) +static inline ks_block_status_t block_get_status(const ks_block_t * const block) { - if (ks == NULL || ks->index == NULL || ks->names == NULL || - ks->size == 0 || ks->used > ks->size || name == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - int where; - - fsck(ks); - - int ok = ks_find(ks, name, hint, &where); - - if (blockno != NULL) - *blockno = ks->index[where]; - - if (hint != NULL) - *hint = where; - - return ok ? HAL_OK : HAL_ERROR_KEY_NOT_FOUND; -} - -/* - * Add a single block to the index. - */ - -hal_error_t hal_ks_index_add(hal_ks_t *ks, - const hal_uuid_t * const name, - unsigned *blockno, - int *hint) -{ - if (ks == NULL || ks->index == NULL || ks->names == NULL || - ks->size == 0 || ks->used > ks->size || name == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - if (ks->used == ks->size) - return HAL_ERROR_NO_KEY_INDEX_SLOTS; - - int where; - - fsck(ks); - - if (ks_find(ks, name, hint, &where)) - return HAL_ERROR_KEY_NAME_IN_USE; - - /* - * Grab first block on free list, which makes room to slide the - * index up by one slot so we can insert the new block number. - */ - - const size_t len = (ks->used - where) * sizeof(*ks->index); - const uint16_t b = ks->index[ks->used++]; - memmove(&ks->index[where + 1], &ks->index[where], len); - ks->index[where] = b; - ks->names[b] = *name; - - if (blockno != NULL) - *blockno = b; - - if (hint != NULL) - *hint = where; - - fsck(ks); - - return HAL_OK; -} - -/* - * Delete a single block from the index. - */ - -hal_error_t hal_ks_index_delete(hal_ks_t *ks, - const hal_uuid_t * const name, - unsigned *blockno, - int *hint) -{ - if (ks == NULL || ks->index == NULL || ks->names == NULL || - ks->size == 0 || ks->used > ks->size || name == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - int where; - - fsck(ks); - - if (ks->used == 0 || !ks_find(ks, name, hint, &where)) - return HAL_ERROR_KEY_NOT_FOUND; - - /* - * Free the block and stuff it at the end of the free list. - */ - - const size_t len = (ks->size - where - 1) * sizeof(*ks->index); - const uint16_t b = ks->index[where]; - memmove(&ks->index[where], &ks->index[where + 1], len); - ks->index[ks->size - 1] = b; - ks->used--; - memset(&ks->names[b], 0, sizeof(ks->names[b])); - - if (blockno != NULL) - *blockno = b; - - if (hint != NULL) - *hint = where; - - fsck(ks); - - return HAL_OK; -} - -/* - * Replace a single block in the index. - */ - -hal_error_t hal_ks_index_replace(hal_ks_t *ks, - const hal_uuid_t * const name, - unsigned *blockno, - int *hint) -{ - if (ks == NULL || ks->index == NULL || ks->names == NULL || - ks->size == 0 || ks->used > ks->size || name == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - if (ks->used == ks->size) - return HAL_ERROR_NO_KEY_INDEX_SLOTS; - - int where; - - fsck(ks); - - if (ks->used == 0 || !ks_find(ks, name, hint, &where)) - return HAL_ERROR_KEY_NOT_FOUND; - - /* - * Grab first block from free list, slide free list down, put old - * block at end of free list and replace old block with new block. - */ - - const size_t len = (ks->size - ks->used - 1) * sizeof(*ks->index); - const uint16_t b1 = ks->index[where]; - const uint16_t b2 = ks->index[ks->used]; - memmove(&ks->index[ks->used], &ks->index[ks->used + 1], len); - ks->index[ks->size - 1] = b1; - ks->index[where] = b2; - ks->names[b2] = *name; - memset(&ks->names[b1], 0, sizeof(ks->names[b1])); - - if (blockno != NULL) - *blockno = b2; - - if (hint != NULL) - *hint = where; - - fsck(ks); - - return HAL_OK; + return block == NULL ? HAL_KS_BLOCK_STATUS_UNKNOWN : (ks_block_status_t) block->header.block_status; } /* @@ -641,7 +381,8 @@ hal_error_t hal_ks_init_common(hal_ks_t *ks, const hal_ks_driver_t * const drive * failure, we should never lose data. */ - ks_heapsort(ks); + if ((err = hal_ks_index_heapsort(ks)) != HAL_OK) + return err; for (unsigned b_tomb = 0; b_tomb < ks->size; b_tomb++) { @@ -666,7 +407,7 @@ hal_error_t hal_ks_init_common(hal_ks_t *ks, const hal_ks_driver_t * const drive const int matches_next = where + 1 < ks->used && !hal_uuid_cmp(&name, &ks->names[ks->index[where + 1]]); const int matches_prev = where - 1 >= 0 && !hal_uuid_cmp(&name, &ks->names[ks->index[where - 1]]); - + if ((matches_prev && matches_next) || (matches_prev && block_status[ks->index[b_tomb - 1]] != BLOCK_STATUS_LIVE) || (matches_next && block_status[ks->index[b_tomb + 1]] != BLOCK_STATUS_LIVE)) diff --git a/ks.h b/ks.h index 2f48738..b24f3c0 100644 --- a/ks.h +++ b/ks.h @@ -147,20 +147,45 @@ typedef struct { } hal_ks_cache_block_t; /* - * Medium-specific driver and in-memory database. + * Keystore object. hal_internal.h typedefs this to hal_ks_t. * - * The top-level structure is a static variable; the arrays are - * allocated at runtime using hal_allocate_static_memory() because - * they can get kind of large. + * We expect this to be a static variable, but we expect the arrays in + * it to be allocated at runtime using hal_allocate_static_memory() + * because they can get kind of large. * * Driver-specific stuff is handled by a form of subclassing: the * driver embeds the hal_ks_t structure at the head of whatever else * it needs, and performs (controlled, type-safe) casts as needed. + * + * Core of this is the keystore index. This is intended to be usable + * by both memory-based and flash-based keystores. Some of the + * features aren't necessary for memory-based keystores, but should be + * harmless, and let us keep the drivers simple. + * + * General approach is multiple arrays, all but one of which are + * indexed by "block" numbers, where a block number might be a slot in + * yet another static array, the number of a flash sub-sector, or + * whatever is the appropriate unit for holding one keystore record. + * + * The index array only contains block numbers. This is a small data + * structure so that moving data within it is relatively cheap. + * + * The index array is divided into two portions: the index proper, and + * the free queue. The index proper is ordered according to the names + * (UUIDs) of the corresponding blocks; the free queue is a FIFO, to + * support a simplistic form of wear leveling in flash-based keystores. + * + * Key names are kept in a separate array, indexed by block number. + * + * The all-zeros UUID, which (by definition) cannot be a valid key + * UUID, is reserved for the (non-key) block used to stash PINs and + * other small data which aren't really part of the keystore proper + * but are kept with it because the keystore is the flash we have. + * + * Note that this API deliberately says nothing about how the keys + * themselves are stored, that's up to the keystore driver. */ -typedef struct hal_ks_driver hal_ks_driver_t; -typedef struct hal_ks hal_ks_t; - struct hal_ks { const hal_ks_driver_t *driver; unsigned size; /* Blocks in keystore */ @@ -173,6 +198,19 @@ struct hal_ks { int per_session; /* Whether objects have per-session semantics (PKCS #11, sigh) */ }; +/* + * Keystore driver. This is just a dispatch vector for low-level + * keystore operations, and the code is very repetitive. We opt for + * expressing this in a terse form via C macros over expressing it + * as huge chunks of repetitive code: both are difficult to read, but + * the terse form has the advantage of fitting in a single screen. + * The KS_DRIVER_METHODS macro is the protein, the rest is just the + * machinery to expand the method definitions into a struct of typed + * function pointers and a set of static inline wrapper functions. + */ + +typedef struct hal_ks_driver hal_ks_driver_t; + #define KS_DRIVER_END_LIST #define KS_DRIVER_METHODS \ KS_DRIVER_METHOD(read, hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block) \ @@ -187,7 +225,7 @@ struct hal_ks { const hal_client_handle_t client, const hal_session_handle_t session) \ KS_DRIVER_END_LIST -#define KS_DRIVER_METHOD(_name_, ...) hal_error_t (*_name_)(__VA_ARGS__) +#define KS_DRIVER_METHOD(_name_, ...) hal_error_t (*_name_)(__VA_ARGS__); struct hal_ks_driver { KS_DRIVER_METHODS }; #undef KS_DRIVER_METHOD @@ -207,6 +245,65 @@ KS_DRIVER_METHODS #endif /* _KS_H_ */ +/* + * Keystore utilities. Some or all of these may end up static within ks.c. + */ + +extern hal_error_t hal_ks_alloc_common(hal_ks_t *ks, + const unsigned ks_blocks, + const unsigned cache_blocks); + +extern hal_error_t hal_ks_init_common(hal_ks_t *ks, + const hal_ks_driver_t * const driver); + +extern hal_error_t hal_ks_index_heapsort(hal_ks_t *ks); + +extern hal_error_t hal_ks_index_find(hal_ks_t *ks, + const hal_uuid_t * const name, + unsigned *blockno, + int *hint); + +extern hal_error_t hal_ks_index_add(hal_ks_t *ks, + const hal_uuid_t * const name, + unsigned *blockno, + int *hint); + +extern hal_error_t hal_ks_index_delete(hal_ks_t *ks, + const hal_uuid_t * const name, + unsigned *blockno, + int *hint); + +extern hal_error_t hal_ks_index_replace(hal_ks_t *ks, + const hal_uuid_t * const name, + unsigned *blockno, + int *hint); + +extern hal_error_t hal_ks_index_fsck(hal_ks_t *ks); + +extern const size_t hal_ks_attribute_header_size; + +extern hal_error_t hal_ks_attribute_scan(const uint8_t * const bytes, + const size_t bytes_len, + hal_pkey_attribute_t *attributes, + const unsigned attributes_len, + size_t *total_len); + +extern hal_error_t hal_ks_attribute_delete(uint8_t *bytes, + const size_t bytes_len, + hal_pkey_attribute_t *attributes, + unsigned *attributes_len, + size_t *total_len, + const uint32_t type); + +extern hal_error_t hal_ks_attribute_insert(uint8_t *bytes, const size_t bytes_len, + hal_pkey_attribute_t *attributes, + unsigned *attributes_len, + size_t *total_len, + const uint32_t type, + const uint8_t * const value, + const size_t value_len); + + /* * Local variables: * indent-tabs-mode: nil diff --git a/ks_flash.c b/ks_flash.c index 803d81c..e5a83ea 100644 --- a/ks_flash.c +++ b/ks_flash.c @@ -4,7 +4,7 @@ * Keystore implementation in flash memory. * * Authors: Rob Austein, Fredrik Thulin - * Copyright (c) 2015-2016, NORDUnet A/S All rights reserved. + * Copyright (c) 2015-2017, 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 @@ -48,6 +48,7 @@ #include "hal.h" #include "hal_internal.h" +#include "ks.h" #include "last_gasp_pin_internal.h" @@ -55,125 +56,18 @@ #include "stm-keystore.h" #undef HAL_OK -/* - * Known block states. - * - * C does not guarantee any particular representation for enums, so - * including enums directly in the block header isn't safe. Instead, - * we use an access method which casts when reading from the header. - * Writing to the header isn't a problem, because C does guarantee - * that enum is compatible with *some* integer type, it just doesn't - * specify which one. - */ - -typedef enum { - BLOCK_TYPE_ERASED = 0xFF, /* Pristine erased block (candidate for reuse) */ - BLOCK_TYPE_ZEROED = 0x00, /* Zeroed block (recently used) */ - BLOCK_TYPE_KEY = 0x55, /* Block contains key material */ - BLOCK_TYPE_PIN = 0xAA, /* Block contains PINs */ - BLOCK_TYPE_UNKNOWN = -1, /* Internal code for "I have no clue what this is" */ -} flash_block_type_t; - -/* - * Block status. - */ - -typedef enum { - BLOCK_STATUS_LIVE = 0x66, /* This is a live flash block */ - BLOCK_STATUS_TOMBSTONE = 0x44, /* This is a tombstone left behind during an update */ - BLOCK_STATUS_UNKNOWN = -1, /* Internal code for "I have no clue what this is" */ -} flash_block_status_t; - -/* - * Common header for all flash block types. - * A few of these fields are deliberately omitted from the CRC. - */ - -typedef struct { - uint8_t block_type; - uint8_t block_status; - hal_crc32_t crc; -} flash_block_header_t; - -/* - * Key block. Tail end of "der" field (after der_len) used for attributes. - */ - -typedef struct { - flash_block_header_t header; - hal_uuid_t name; - hal_key_type_t type; - hal_curve_name_t curve; - hal_key_flags_t flags; - size_t der_len; - unsigned attributes_len; - uint8_t der[]; /* Must be last field -- C99 "flexible array member" */ -} flash_key_block_t; - -#define SIZEOF_FLASH_KEY_BLOCK_DER \ - (KEYSTORE_SUBSECTOR_SIZE - offsetof(flash_key_block_t, der)) - -/* - * PIN block. Also includes space for backing up the KEK when - * HAL_MKM_FLASH_BACKUP_KLUDGE is enabled. - */ - -typedef struct { - flash_block_header_t header; - hal_ks_pin_t wheel_pin; - hal_ks_pin_t so_pin; - hal_ks_pin_t user_pin; -#if HAL_MKM_FLASH_BACKUP_KLUDGE - uint32_t kek_set; - uint8_t kek[KEK_LENGTH]; -#endif -} flash_pin_block_t; - -#define FLASH_KEK_SET 0x33333333 - -/* - * One flash block. - */ - -typedef union { - uint8_t bytes[KEYSTORE_SUBSECTOR_SIZE]; - flash_block_header_t header; - flash_key_block_t key; - flash_pin_block_t pin; -} flash_block_t; - -/* - * In-memory cache. - */ - -typedef struct { - unsigned blockno; - uint32_t lru; - flash_block_t block; -} cache_block_t; - -/* - * In-memory database. - * - * The top-level structure is a static variable; the arrays are allocated at runtime - * using hal_allocate_static_memory() because they can get kind of large. - */ - #ifndef KS_FLASH_CACHE_SIZE #define KS_FLASH_CACHE_SIZE 4 #endif #define NUM_FLASH_BLOCKS KEYSTORE_NUM_SUBSECTORS -typedef struct { +static struct db { hal_ks_t ks; /* Must be first (C "subclassing") */ - hal_ks_index_t ksi; hal_ks_pin_t wheel_pin; hal_ks_pin_t so_pin; hal_ks_pin_t user_pin; - uint32_t cache_lru; - cache_block_t *cache; -} db_t; +} db; /* * PIN block gets the all-zeros UUID, which will never be returned by @@ -182,119 +76,6 @@ typedef struct { const static hal_uuid_t pin_uuid = {{0}}; -/* - * The in-memory database structure itself is small, but the arrays it - * points to are large enough that they come from SDRAM allocated at - * startup. - */ - -static db_t db; - -/* - * Type safe casts. - */ - -static inline flash_block_type_t block_get_type(const flash_block_t * const block) -{ - assert(block != NULL); - return (flash_block_type_t) block->header.block_type; -} - -static inline flash_block_status_t block_get_status(const flash_block_t * const block) -{ - assert(block != NULL); - return (flash_block_status_t) block->header.block_status; -} - -/* - * Pick unused or least-recently-used slot in our in-memory cache. - * - * Updating lru values is caller's problem: if caller is using a cache - * slot as a temporary buffer and there's no point in caching the - * result, leave the lru values alone and the right thing will happen. - */ - -static inline flash_block_t *cache_pick_lru(void) -{ - uint32_t best_delta = 0; - int best_index = 0; - - for (int i = 0; i < KS_FLASH_CACHE_SIZE; i++) { - - if (db.cache[i].blockno == ~0) - return &db.cache[i].block; - - const uint32_t delta = db.cache_lru - db.cache[i].lru; - if (delta > best_delta) { - best_delta = delta; - best_index = i; - } - - } - - db.cache[best_index].blockno = ~0; - return &db.cache[best_index].block; -} - -/* - * Find a block in our in-memory cache; return block or NULL if not present. - */ - -static inline flash_block_t *cache_find_block(const unsigned blockno) -{ - for (int i = 0; i < KS_FLASH_CACHE_SIZE; i++) - if (db.cache[i].blockno == blockno) - return &db.cache[i].block; - return NULL; -} - -/* - * Mark a block in our in-memory cache as being in current use. - */ - -static inline void cache_mark_used(const flash_block_t * const block, const unsigned blockno) -{ - for (int i = 0; i < KS_FLASH_CACHE_SIZE; i++) { - if (&db.cache[i].block == block) { - db.cache[i].blockno = blockno; - db.cache[i].lru = ++db.cache_lru; - return; - } - } -} - -/* - * Release a block from the in-memory cache. - */ - -static inline void cache_release(const flash_block_t * const block) -{ - if (block != NULL) - cache_mark_used(block, ~0); -} - -/* - * Generate CRC-32 for a block. - * - * This function needs to understand the structure of - * flash_block_header_t, so that it can skip over fields that - * shouldn't be included in the CRC. - */ - -static hal_crc32_t calculate_block_crc(const flash_block_t * const block) -{ - assert(block != NULL); - - hal_crc32_t crc = hal_crc32_init(); - - crc = hal_crc32_update(crc, &block->header.block_type, - sizeof(block->header.block_type)); - - crc = hal_crc32_update(crc, block->bytes + sizeof(flash_block_header_t), - sizeof(*block) - sizeof(flash_block_header_t)); - - return hal_crc32_finalize(crc); -} /* * Calculate offset of the block in the flash address space. @@ -312,9 +93,9 @@ static inline uint32_t block_offset(const unsigned blockno) * first page before reading the rest of the block. */ -static hal_error_t block_read(const unsigned blockno, flash_block_t *block) +static hal_error_t block_read(hal_k_t *ks, const unsigned blockno, ks_block_t *block) { - if (block == NULL || blockno >= NUM_FLASH_BLOCKS || sizeof(*block) != KEYSTORE_SUBSECTOR_SIZE) + if (ks != &db.ks || block == NULL || blockno >= NUM_FLASH_BLOCKS || sizeof(*block) != KEYSTORE_SUBSECTOR_SIZE) return HAL_ERROR_IMPOSSIBLE; /* Sigh, magic numeric return codes */ @@ -354,35 +135,15 @@ static hal_error_t block_read(const unsigned blockno, flash_block_t *block) return HAL_OK; } -/* - * Read a block using the cache. Marking the block as used is left - * for the caller, so we can avoid blowing out the cache when we - * perform a ks_match() operation. - */ - -static hal_error_t block_read_cached(const unsigned blockno, flash_block_t **block) -{ - if (block == NULL) - return HAL_ERROR_IMPOSSIBLE; - - if ((*block = cache_find_block(blockno)) != NULL) - return HAL_OK; - - if ((*block = cache_pick_lru()) == NULL) - return HAL_ERROR_IMPOSSIBLE; - - return block_read(blockno, *block); -} - /* * Convert a live block into a tombstone. Caller is responsible for * making sure that the block being converted is valid; since we don't * need to update the CRC for this, we just modify the first page. */ -static hal_error_t block_deprecate(const unsigned blockno) +static hal_error_t block_deprecate(hal_k_t *ks, const unsigned blockno) { - if (blockno >= NUM_FLASH_BLOCKS) + if (ks != &db.ks || blockno >= NUM_FLASH_BLOCKS) return HAL_ERROR_IMPOSSIBLE; uint8_t page[KEYSTORE_PAGE_SIZE]; @@ -406,9 +167,9 @@ static hal_error_t block_deprecate(const unsigned blockno) * Zero (not erase) a flash block. Just need to zero the first page. */ -static hal_error_t block_zero(const unsigned blockno) +static hal_error_t block_zero(hal_k_t *ks, const unsigned blockno) { - if (blockno >= NUM_FLASH_BLOCKS) + if (ks != &db.ks || blockno >= NUM_FLASH_BLOCKS) return HAL_ERROR_IMPOSSIBLE; uint8_t page[KEYSTORE_PAGE_SIZE] = {0}; @@ -424,9 +185,9 @@ static hal_error_t block_zero(const unsigned blockno) * Erase a flash block. Also see block_erase_maybe(), below. */ -static hal_error_t block_erase(const unsigned blockno) +static hal_error_t block_erase(hal_k_t *ks, const unsigned blockno) { - if (blockno >= NUM_FLASH_BLOCKS) + if (ks != &db.ks || blockno >= NUM_FLASH_BLOCKS) return HAL_ERROR_IMPOSSIBLE; /* Sigh, magic numeric return codes */ @@ -446,9 +207,9 @@ static hal_error_t block_erase(const unsigned blockno) * leak information about, eg, key length, so we do constant time. */ -static hal_error_t block_erase_maybe(const unsigned blockno) +static hal_error_t block_erase_maybe(hal_k_t *ks, const unsigned blockno) { - if (blockno >= NUM_FLASH_BLOCKS) + if (ks != &db.ks || blockno >= NUM_FLASH_BLOCKS) return HAL_ERROR_IMPOSSIBLE; uint8_t mask = 0xFF; @@ -468,9 +229,9 @@ static hal_error_t block_erase_maybe(const unsigned blockno) * Write a flash block, calculating CRC when appropriate. */ -static hal_error_t block_write(const unsigned blockno, flash_block_t *block) +static hal_error_t block_write(hal_k_t *ks, const unsigned blockno, ks_block_t *block) { - if (block == NULL || blockno >= NUM_FLASH_BLOCKS || sizeof(*block) != KEYSTORE_SUBSECTOR_SIZE) + if (ks != &db.ks || block == NULL || blockno >= NUM_FLASH_BLOCKS || sizeof(*block) != KEYSTORE_SUBSECTOR_SIZE) return HAL_ERROR_IMPOSSIBLE; hal_error_t err = block_erase_maybe(blockno); @@ -495,308 +256,66 @@ static hal_error_t block_write(const unsigned blockno, flash_block_t *block) } /* - * Update one flash block, including zombie jamboree. + * The token keystore doesn't implement per-session objects, so these are no-ops. */ -static hal_error_t block_update(const unsigned b1, - flash_block_t *block, - const hal_uuid_t * const uuid, - int *hint) +static hal_error_t block_set_owner(hal_ks_t *ks, + const unsigned blockno, + const hal_client_handle_t client, + const hal_session_handle_t session) { - if (block == NULL) - return HAL_ERROR_IMPOSSIBLE; - - if (db.ksi.used == db.ksi.size) - return HAL_ERROR_NO_KEY_INDEX_SLOTS; - - cache_release(block); - - hal_error_t err; - unsigned b2; - - if ((err = block_deprecate(b1)) != HAL_OK || - (err = hal_ks_index_replace(&db.ksi, uuid, &b2, hint)) != HAL_OK || - (err = block_write(b2, block)) != HAL_OK || - (err = block_zero(b1)) != HAL_OK) - return err; - - cache_mark_used(block, b2); - - /* - * Erase the first block in the free list. In case of restart, this - * puts the block back at the head of the free list. - */ + return HAL_OK; +} - return block_erase_maybe(db.ksi.index[db.ksi.used]); +static hal_error_t block_test_owner(hal_ks_t *ks, const + unsigned blockno, + const hal_client_handle_t client, + const hal_session_handle_t session) +{ + return HAL_OK; } /* * Forward reference. */ -static hal_error_t fetch_pin_block(unsigned *b, flash_block_t **block); +static hal_error_t fetch_pin_block(unsigned *b, ks_block_t **block); /* - * Initialize keystore. This includes various tricky bits, some of - * which attempt to preserve the free list ordering across reboots, to - * improve our simplistic attempt at wear leveling, others attempt to - * recover from unclean shutdown. + * Initialize keystore. */ -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 const hal_ks_driver_t hal_ks_token_driver[1] = {{ + .read = block_read, + .write = block_write, + .deprecate = block_deprecate, + .zero = block_zero, + .erase = block_erase, + .erase_maybe = block_erase_maybe, + .set_owner = block_set_owner, + .test_owner = block_test_owner +}}; -static hal_error_t ks_init(const hal_ks_driver_t * const driver, const int alloc) +hal_error_t hal_ks_token_init(const int alloc) { hal_error_t err = HAL_OK; hal_ks_lock(); - /* - * Initialize the in-memory database. - */ - - if (alloc) { - - size_t len = (sizeof(*db.ksi.index) * NUM_FLASH_BLOCKS + - sizeof(*db.ksi.names) * NUM_FLASH_BLOCKS + - sizeof(*db.cache) * KS_FLASH_CACHE_SIZE); - - /* - * This is done as a single large allocation, rather than 3 smaller - * allocations, to make it atomic - we need all 3, so either all - * succeed or all fail. - */ - - uint8_t *mem = hal_allocate_static_memory(len); - - if (mem == NULL) { - err = HAL_ERROR_ALLOCATION_FAILURE; - goto done; - } - - memset(&db, 0, sizeof(db)); - memset(mem, 0, len); - - db.ksi.index = gnaw(&mem, &len, sizeof(*db.ksi.index) * NUM_FLASH_BLOCKS); - db.ksi.names = gnaw(&mem, &len, sizeof(*db.ksi.names) * NUM_FLASH_BLOCKS); - db.cache = gnaw(&mem, &len, sizeof(*db.cache) * KS_FLASH_CACHE_SIZE); - db.ksi.size = NUM_FLASH_BLOCKS; - } - - else { - memset(&db.wheel_pin, 0, sizeof(db.wheel_pin)); - memset(&db.so_pin, 0, sizeof(db.so_pin)); - memset(&db.user_pin, 0, sizeof(db.user_pin)); - } - - db.ksi.used = 0; - - if (db.ksi.index == NULL || db.ksi.names == NULL || db.cache == NULL) { - err = HAL_ERROR_IMPOSSIBLE; - goto done; - } - - for (int i = 0; i < KS_FLASH_CACHE_SIZE; i++) - db.cache[i].blockno = ~0; - - /* - * Scan existing content of flash to figure out what we've got. - * This gets a bit involved due to the need to recover from things - * like power failures at inconvenient times. - */ - - flash_block_type_t block_types[NUM_FLASH_BLOCKS]; - flash_block_status_t block_status[NUM_FLASH_BLOCKS]; - flash_block_t *block = cache_pick_lru(); - int first_erased = -1; - uint16_t n = 0; - - if (block == NULL) { - err = HAL_ERROR_IMPOSSIBLE; + if (alloc && (err = hal_ks_alloc_common(&db.ks, NUM_FLASH_BLOCKS, KS_FLASH_CACHE_SIZE)) != HAL_OK) goto done; - } - - for (int i = 0; i < NUM_FLASH_BLOCKS; i++) { - - /* - * Read one block. If the CRC is bad or the block type is - * unknown, it's old data we don't understand, something we were - * writing when we crashed, or bad flash; in any of these cases, - * we want the block to end up near the end of the free list. - */ - - err = block_read(i, block); - if (err == HAL_ERROR_KEYSTORE_BAD_CRC || err == HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE) - block_types[i] = BLOCK_TYPE_UNKNOWN; - - else if (err == HAL_OK) - block_types[i] = block_get_type(block); - - else - goto done; - - switch (block_types[i]) { - case BLOCK_TYPE_KEY: - case BLOCK_TYPE_PIN: - block_status[i] = block_get_status(block); - break; - default: - block_status[i] = BLOCK_STATUS_UNKNOWN; - } - - /* - * First erased block we see is head of the free list. - */ - - if (block_types[i] == BLOCK_TYPE_ERASED && first_erased < 0) - first_erased = i; - - /* - * If it's a valid data block, include it in the index. We remove - * tombstones (if any) below, for now it's easiest to include them - * in the index, so we can look them up by name if we must. - */ - - const hal_uuid_t *uuid = NULL; - - switch (block_types[i]) { - case BLOCK_TYPE_KEY: uuid = &block->key.name; break; - case BLOCK_TYPE_PIN: uuid = &pin_uuid; break; - default: /* Keep GCC happy */ break; - } - - if (uuid != NULL) { - db.ksi.names[i] = *uuid; - db.ksi.index[n++] = i; - } - } - - db.ksi.used = n; - - assert(db.ksi.used <= db.ksi.size); - - /* - * At this point we've built the (unsorted) index from all the valid - * blocks. Now we need to insert free and unrecognized blocks into - * the free list in our preferred order. It's possible that there's - * a better way to do this than linear scan, but this is just - * integer comparisons in a fairly small data set, so it's probably - * not worth trying to optimize. - */ - - if (n < db.ksi.size) - for (int i = 0; i < NUM_FLASH_BLOCKS; i++) - if (block_types[i] == BLOCK_TYPE_ERASED) - db.ksi.index[n++] = i; - - if (n < db.ksi.size) - for (int i = first_erased; i < NUM_FLASH_BLOCKS; i++) - if (block_types[i] == BLOCK_TYPE_ZEROED) - db.ksi.index[n++] = i; - - if (n < db.ksi.size) - for (int i = 0; i < first_erased; i++) - if (block_types[i] == BLOCK_TYPE_ZEROED) - db.ksi.index[n++] = i; - - if (n < db.ksi.size) - for (int i = 0; i < NUM_FLASH_BLOCKS; i++) - if (block_types[i] == BLOCK_TYPE_UNKNOWN) - db.ksi.index[n++] = i; - - assert(n == db.ksi.size); - - /* - * Initialize the index. - */ - - if ((err = hal_ks_index_setup(&db.ksi)) != HAL_OK) + if ((err = hal_ks_init_common(ks, hal_ks_token_driver)) != HAL_OK) goto done; - /* - * We might want to call hal_ks_index_fsck() here, if we can figure - * out some safe set of recovery actions we can take. - */ - - /* - * Deal with tombstones, now that the index is sorted. Tombstones - * are blocks left behind when something bad (like a power failure) - * happened while we updating. There can be at most one tombstone - * and one live block for a given UUID. If we find no live block, - * we need to restore it from the tombstone, after which we need to - * zero the tombstone in either case. The sequence of operations - * while updating is designed so that, barring a bug or a hardware - * failure, we should never lose data. - */ - - for (unsigned b_tomb = 0; b_tomb < NUM_FLASH_BLOCKS; b_tomb++) { - - if (block_status[b_tomb] != BLOCK_STATUS_TOMBSTONE) - continue; - - hal_uuid_t name = db.ksi.names[b_tomb]; - - int where = -1; - - if ((err = hal_ks_index_find(&db.ksi, &name, NULL, &where)) != HAL_OK) - goto done; - - if (b_tomb != db.ksi.index[where]) { - if (db.ksi.used > where + 1 && b_tomb == db.ksi.index[where + 1]) - where = where + 1; - else if (0 <= where - 1 && b_tomb == db.ksi.index[where - 1]) - where = where - 1; - else { - err = HAL_ERROR_IMPOSSIBLE; - goto done; - } - } - - const int matches_next = where + 1 < db.ksi.used && !hal_uuid_cmp(&name, &db.ksi.names[db.ksi.index[where + 1]]); - const int matches_prev = where - 1 >= 0 && !hal_uuid_cmp(&name, &db.ksi.names[db.ksi.index[where - 1]]); - - if ((matches_prev && matches_next) || - (matches_prev && block_status[db.ksi.index[b_tomb - 1]] != BLOCK_STATUS_LIVE) || - (matches_next && block_status[db.ksi.index[b_tomb + 1]] != BLOCK_STATUS_LIVE)) { - err = HAL_ERROR_IMPOSSIBLE; - goto done; - } - - if (matches_prev || matches_next) { - memmove(&db.ksi.index[where], &db.ksi.index[where + 1], (db.ksi.size - where - 1) * sizeof(*db.ksi.index)); - db.ksi.index[db.ksi.size - 1] = b_tomb; - } - - else { - unsigned b_live; - if ((err = block_read(b_tomb, block)) != HAL_OK) - goto done; - block->header.block_status = BLOCK_STATUS_LIVE; - if ((err = hal_ks_index_replace(&db.ksi, &name, &b_live, &where)) != HAL_OK || - (err = block_write(b_live, block)) != HAL_OK) - goto done; - block_status[b_live] = BLOCK_STATUS_LIVE; - } - - if ((err = block_zero(b_tomb)) != HAL_OK) - goto done; - block_types[ b_tomb] = BLOCK_TYPE_ZEROED; - block_status[b_tomb] = BLOCK_STATUS_UNKNOWN; - } - /* * Fetch or create the PIN block. */ + memset(&db.wheel_pin, 0, sizeof(db.wheel_pin)); + memset(&db.so_pin, 0, sizeof(db.so_pin)); + memset(&db.user_pin, 0, sizeof(db.user_pin)); + err = fetch_pin_block(NULL, &block); if (err == HAL_OK) { @@ -841,316 +360,6 @@ static hal_error_t ks_init(const hal_ks_driver_t * const driver, const int alloc goto done; } - /* - * Erase first block on free list if it's not already erased. - */ - - if (db.ksi.used < db.ksi.size && - (err = block_erase_maybe(db.ksi.index[db.ksi.used])) != HAL_OK) - goto done; - - /* - * And we're finally done. - */ - - db.ks.driver = driver; - - err = HAL_OK; - - done: - hal_ks_unlock(); - return err; -} - -static hal_error_t ks_shutdown(const hal_ks_driver_t * const driver) -{ - if (db.ks.driver != driver) - return HAL_ERROR_KEYSTORE_ACCESS; - return HAL_OK; -} - -static hal_error_t ks_open(const hal_ks_driver_t * const driver, - hal_ks_t **ks) -{ - if (driver != hal_ks_token_driver || ks == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - *ks = &db.ks; - return HAL_OK; -} - -static hal_error_t ks_close(hal_ks_t *ks) -{ - if (ks != NULL && ks != &db.ks) - return HAL_ERROR_BAD_ARGUMENTS; - - 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 != &db.ks || slot == NULL || der == NULL || der_len == 0 || !acceptable_key_type(slot->type)) - return HAL_ERROR_BAD_ARGUMENTS; - - hal_error_t err = HAL_OK; - flash_block_t *block; - flash_key_block_t *k; - uint8_t kek[KEK_LENGTH]; - size_t kek_len; - unsigned b; - - hal_ks_lock(); - - if ((block = cache_pick_lru()) == NULL) { - err = HAL_ERROR_IMPOSSIBLE; - goto done; - } - - k = &block->key; - - if ((err = hal_ks_index_add(&db.ksi, &slot->name, &b, &slot->hint)) != HAL_OK) - goto done; - - cache_mark_used(block, b); - - memset(block, 0xFF, sizeof(*block)); - - block->header.block_type = BLOCK_TYPE_KEY; - block->header.block_status = BLOCK_STATUS_LIVE; - - k->name = slot->name; - k->type = slot->type; - k->curve = slot->curve; - k->flags = slot->flags; - k->der_len = SIZEOF_FLASH_KEY_BLOCK_DER; - k->attributes_len = 0; - - if (db.ksi.used < db.ksi.size) - err = block_erase_maybe(db.ksi.index[db.ksi.used]); - - if (err == HAL_OK) - err = hal_mkm_get_kek(kek, &kek_len, sizeof(kek)); - - if (err == 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) - err = block_write(b, block); - - if (err == HAL_OK) - goto done; - - memset(block, 0, sizeof(*block)); - cache_release(block); - (void) hal_ks_index_delete(&db.ksi, &slot->name, NULL, &slot->hint); - - done: - hal_ks_unlock(); - 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 != &db.ks || slot == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - hal_error_t err = HAL_OK; - flash_block_t *block; - unsigned b; - - hal_ks_lock(); - - if ((err = hal_ks_index_find(&db.ksi, &slot->name, &b, &slot->hint)) != HAL_OK || - (err = block_read_cached(b, &block)) != HAL_OK) - goto done; - - if (block_get_type(block) != BLOCK_TYPE_KEY) { - err = HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE; /* HAL_ERROR_KEY_NOT_FOUND */ - goto done; - } - - cache_mark_used(block, b); - - flash_key_block_t *k = &block->key; - - 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)); - } - - done: - hal_ks_unlock(); - return err; -} - -static hal_error_t ks_delete(hal_ks_t *ks, - hal_pkey_slot_t *slot) -{ - if (ks != &db.ks || slot == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - hal_error_t err = HAL_OK; - unsigned b; - - hal_ks_lock(); - - if ((err = hal_ks_index_delete(&db.ksi, &slot->name, &b, &slot->hint)) != HAL_OK) - goto done; - - cache_release(cache_find_block(b)); - - if ((err = block_zero(b)) != HAL_OK) - goto done; - - err = block_erase_maybe(db.ksi.index[db.ksi.used]); - - done: - hal_ks_unlock(); - return err; -} - -static inline hal_error_t locate_attributes(flash_block_t *block, - uint8_t **bytes, size_t *bytes_len, - unsigned **attrs_len) -{ - if (block == NULL || bytes == NULL || bytes_len == NULL || attrs_len == NULL) - return HAL_ERROR_IMPOSSIBLE; - - - if (block_get_type(block) != BLOCK_TYPE_KEY) - return HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE; - *attrs_len = &block->key.attributes_len; - *bytes = block->key.der + block->key.der_len; - *bytes_len = SIZEOF_FLASH_KEY_BLOCK_DER - block->key.der_len; - - return HAL_OK; -} - -static hal_error_t ks_match(hal_ks_t *ks, - const hal_client_handle_t client, - const hal_session_handle_t session, - const hal_key_type_t type, - const hal_curve_name_t curve, - const hal_key_flags_t mask, - 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 (ks == NULL || (attributes == NULL && attributes_len > 0) || - result == NULL || result_len == NULL || previous_uuid == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - hal_error_t err = HAL_OK; - flash_block_t *block; - int i = -1; - - hal_ks_lock(); - - *result_len = 0; - - err = hal_ks_index_find(&db.ksi, previous_uuid, NULL, &i); - - if (err == HAL_ERROR_KEY_NOT_FOUND) - i--; - else if (err != HAL_OK) - goto done; - - while (*result_len < result_max && ++i < db.ksi.used) { - - unsigned b = db.ksi.index[i]; - - if ((err = block_read_cached(b, &block)) != HAL_OK) - goto done; - - if ((type != HAL_KEY_TYPE_NONE && type != block->key.type) || - (curve != HAL_CURVE_NONE && curve != block->key.curve) || - ((flags ^ block->key.flags) & mask) != 0) - continue; - - if (attributes_len > 0) { - uint8_t need_attr[attributes_len]; - uint8_t *bytes = NULL; - size_t bytes_len = 0; - unsigned *attrs_len; - int possible = 1; - - memset(need_attr, 1, sizeof(need_attr)); - - if ((err = locate_attributes(block, &bytes, &bytes_len, &attrs_len)) != HAL_OK) - goto done; - - if (*attrs_len > 0) { - hal_pkey_attribute_t attrs[*attrs_len]; - - if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, NULL)) != HAL_OK) - goto done; - - for (int j = 0; possible && j < attributes_len; j++) { - - if (!need_attr[j]) - continue; - - for (hal_pkey_attribute_t *a = attrs; a < attrs + *attrs_len; a++) { - if (a->type != attributes[j].type) - continue; - need_attr[j] = 0; - possible = (a->length == attributes[j].length && - !memcmp(a->value, attributes[j].value, a->length)); - break; - } - } - } - - if (!possible || memchr(need_attr, 1, sizeof(need_attr)) != NULL) - continue; - } - - result[*result_len] = db.ksi.names[b]; - ++*result_len; - } - err = HAL_OK; done: @@ -1158,161 +367,6 @@ static hal_error_t ks_match(hal_ks_t *ks, return err; } -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 (ks != &db.ks || slot == NULL || attributes == NULL || attributes_len == 0) - return HAL_ERROR_BAD_ARGUMENTS; - - hal_error_t err = HAL_OK; - flash_block_t *block; - unsigned b; - - hal_ks_lock(); - - { - if ((err = hal_ks_index_find(&db.ksi, &slot->name, &b, &slot->hint)) != HAL_OK || - (err = block_read_cached(b, &block)) != HAL_OK) - goto done; - - cache_mark_used(block, b); - - uint8_t *bytes = NULL; - size_t bytes_len = 0; - unsigned *attrs_len; - - if ((err = locate_attributes(block, &bytes, &bytes_len, &attrs_len)) != HAL_OK) - goto done; - - hal_pkey_attribute_t attrs[*attrs_len + attributes_len]; - size_t total; - - if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, &total)) != HAL_OK) - goto done; - - for (int i = 0; err == HAL_OK && i < attributes_len; i++) - if (attributes[i].length == HAL_PKEY_ATTRIBUTE_NIL) - err = hal_ks_attribute_delete(bytes, bytes_len, attrs, attrs_len, &total, - attributes[i].type); - else - err = hal_ks_attribute_insert(bytes, bytes_len, attrs, attrs_len, &total, - attributes[i].type, - attributes[i].value, - attributes[i].length); - - if (err == HAL_OK) - err = block_update(b, block, &slot->name, &slot->hint); - else - cache_release(block); - } - - done: - hal_ks_unlock(); - return err; -} - -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 (ks != &db.ks || slot == NULL || attributes == NULL || attributes_len == 0 || - attributes_buffer == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - for (int i = 0; i < attributes_len; i++) { - attributes[i].length = 0; - attributes[i].value = NULL; - } - - uint8_t *abuf = attributes_buffer; - flash_block_t *block = NULL; - hal_error_t err = HAL_OK; - unsigned found = 0; - unsigned b; - - hal_ks_lock(); - - { - if ((err = hal_ks_index_find(&db.ksi, &slot->name, &b, &slot->hint)) != HAL_OK || - (err = block_read_cached(b, &block)) != HAL_OK) - goto done; - - cache_mark_used(block, b); - - uint8_t *bytes = NULL; - size_t bytes_len = 0; - unsigned *attrs_len; - - if ((err = locate_attributes(block, &bytes, &bytes_len, &attrs_len)) != HAL_OK) - goto done; - - if (*attrs_len == 0) { - err = HAL_ERROR_ATTRIBUTE_NOT_FOUND; - goto done; - } - - hal_pkey_attribute_t attrs[*attrs_len]; - - if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, NULL)) != HAL_OK) - goto done; - - for (int i = 0; i < attributes_len; i++) { - - if (attributes[i].length > 0) - continue; - - int j = 0; - while (j < *attrs_len && attrs[j].type != attributes[i].type) - j++; - if (j >= *attrs_len) - continue; - found++; - - attributes[i].length = attrs[j].length; - - if (attributes_buffer_len == 0) - continue; - - if (attrs[j].length > attributes_buffer + attributes_buffer_len - abuf) { - err = HAL_ERROR_RESULT_TOO_LONG; - goto done; - } - - memcpy(abuf, attrs[j].value, attrs[j].length); - attributes[i].value = abuf; - abuf += attrs[j].length; - } - - }; - - if (found < attributes_len && attributes_buffer_len > 0) - err = HAL_ERROR_ATTRIBUTE_NOT_FOUND; - else - err = HAL_OK; - - done: - hal_ks_unlock(); - return err; -} - -const hal_ks_driver_t hal_ks_token_driver[1] = {{ - .init = ks_init, - .shutdown = ks_shutdown, - .open = ks_open, - .close = ks_close, - .store = ks_store, - .fetch = ks_fetch, - .delete = ks_delete, - .match = ks_match, - .set_attributes = ks_set_attributes, - .get_attributes = ks_get_attributes -}}; - /* * The remaining functions aren't really part of the keystore API per se, * but they all involve non-key data which we keep in the keystore @@ -1331,7 +385,7 @@ const hal_ks_driver_t hal_ks_token_driver[1] = {{ void hal_ks_init_read_only_pins_only(void) { unsigned b, best_seen = ~0; - flash_block_t block[1]; + ks_block_t block[1]; hal_ks_lock(); @@ -1389,7 +443,7 @@ hal_error_t hal_get_pin(const hal_user_t user, * should always sort to first slot in the index. */ -static hal_error_t fetch_pin_block(unsigned *b, flash_block_t **block) +static hal_error_t fetch_pin_block(unsigned *b, ks_block_t **block) { if (block == NULL) return HAL_ERROR_IMPOSSIBLE; @@ -1421,7 +475,7 @@ static hal_error_t fetch_pin_block(unsigned *b, flash_block_t **block) */ static hal_error_t update_pin_block(const unsigned b, - flash_block_t *block, + ks_block_t *block, const flash_pin_block_t * const new_data) { if (block == NULL || new_data == NULL || block_get_type(block) != BLOCK_TYPE_PIN) @@ -1444,7 +498,7 @@ hal_error_t hal_set_pin(const hal_user_t user, if (pin == NULL) return HAL_ERROR_BAD_ARGUMENTS; - flash_block_t *block; + ks_block_t *block; hal_error_t err; unsigned b; @@ -1495,7 +549,7 @@ hal_error_t hal_mkm_flash_read_no_lock(uint8_t *buf, const size_t len) if (buf != NULL && len != KEK_LENGTH) return HAL_ERROR_MASTERKEY_BAD_LENGTH; - flash_block_t *block; + ks_block_t *block; hal_error_t err; unsigned b; @@ -1527,7 +581,7 @@ hal_error_t hal_mkm_flash_write(const uint8_t * const buf, const size_t len) if (len != KEK_LENGTH) return HAL_ERROR_MASTERKEY_BAD_LENGTH; - flash_block_t *block; + ks_block_t *block; hal_error_t err; unsigned b; @@ -1553,7 +607,7 @@ hal_error_t hal_mkm_flash_erase(const size_t len) if (len != KEK_LENGTH) return HAL_ERROR_MASTERKEY_BAD_LENGTH; - flash_block_t *block; + ks_block_t *block; hal_error_t err; unsigned b; diff --git a/ks_index.c b/ks_index.c index 806394a..ed22cfb 100644 --- a/ks_index.c +++ b/ks_index.c @@ -3,7 +3,7 @@ * ---------- * Keystore index API. This is internal within libhal. * - * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * Copyright (c) 2016-2017, 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 @@ -32,35 +32,37 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include -#include #include "hal.h" #include "hal_internal.h" +#include "ks.h" /* * Find a block in the index, return true (found) or false (not found). * "where" indicates the name's position, or the position of the first free block. * * NB: This does NOT return a block number, it returns an index into - * ksi->index[]. + * ks->index[]. */ -static int ks_find(const hal_ks_index_t * const ksi, - const hal_uuid_t * const uuid, +static int ks_find(const hal_ks_t * const ks, + const hal_uuid_t * const uuid, const int * const hint, - int *where) + int *where) { - assert(ksi != NULL && ksi->index != NULL && ksi->names != NULL && uuid != NULL && where != NULL); + if (ks == NULL || ks->index == NULL || ks->names == NULL || uuid == NULL || where == NULL) + return 0; - if (hint != NULL && *hint >= 0 && *hint < ksi->used && - hal_uuid_cmp(uuid, &ksi->names[ksi->index[*hint]]) == 0) { + if (hint != NULL && *hint >= 0 && *hint < ks->used && + hal_uuid_cmp(uuid, &ks->names[ks->index[*hint]]) == 0) { *where = *hint; return 1; } int lo = -1; - int hi = ksi->used; + int hi = ks->used; for (;;) { int m = (lo + hi) / 2; @@ -68,7 +70,7 @@ static int ks_find(const hal_ks_index_t * const ksi, *where = hi; return 0; } - const int cmp = hal_uuid_cmp(uuid, &ksi->names[ksi->index[m]]); + const int cmp = hal_uuid_cmp(uuid, &ks->names[ks->index[m]]); if (cmp < 0) hi = m; else if (cmp > 0) @@ -88,82 +90,72 @@ static int ks_find(const hal_ks_index_t * const ksi, * heapsort is easy and works well with data already in place. */ -static inline void ks_heapsift(hal_ks_index_t *ksi, int parent, const int end) +static inline hal_error_t ks_heapsift(hal_ks_t *ks, int parent, const int end) { - assert(ksi != NULL && ksi->index != NULL && ksi->names != NULL && - parent >= 0 && end >= parent); + if (ks == NULL || ks->index == NULL || ks->names == NULL || parent < 0 || end < parent) + return HAL_ERROR_IMPOSSIBLE; + for (;;) { const int left_child = parent * 2 + 1; const int right_child = parent * 2 + 2; int biggest = parent; - if (left_child <= end && hal_uuid_cmp(&ksi->names[ksi->index[biggest]], - &ksi->names[ksi->index[left_child]]) < 0) + if (left_child <= end && hal_uuid_cmp(&ks->names[ks->index[biggest]], + &ks->names[ks->index[left_child]]) < 0) biggest = left_child; - if (right_child <= end && hal_uuid_cmp(&ksi->names[ksi->index[biggest]], - &ksi->names[ksi->index[right_child]]) < 0) + if (right_child <= end && hal_uuid_cmp(&ks->names[ks->index[biggest]], + &ks->names[ks->index[right_child]]) < 0) biggest = right_child; if (biggest == parent) - return; - const uint16_t tmp = ksi->index[biggest]; - ksi->index[biggest] = ksi->index[parent]; - ksi->index[parent] = tmp; + return HAL_OK; + const uint16_t tmp = ks->index[biggest]; + ks->index[biggest] = ks->index[parent]; + ks->index[parent] = tmp; parent = biggest; } } -static inline void ks_heapsort(hal_ks_index_t *ksi) +hal_ks_error_t hal_ks_index_heapsort(hal_ks_t *ks) { - assert(ksi != NULL && ksi->index != NULL && ksi->names != NULL); - if (ksi->used < 2) - return; - for (int i = (ksi->used - 2) / 2; i >= 0; i--) - ks_heapsift(ksi, i, ksi->used - 1); - for (int i = ksi->used - 1; i > 0; i--) { - const uint16_t tmp = ksi->index[i]; - ksi->index[i] = ksi->index[0]; - ksi->index[0] = tmp; - ks_heapsift(ksi, 0, i - 1); - } -} - -/* - * Perform a consistency check on the index. - */ + if (ks == NULL || ks->index == NULL || ks->names == NULL) + return HAL_ERROR_IMPOSSIBLE; -#define fsck(_ksi) \ - do { hal_error_t _err = hal_ks_index_fsck(_ksi); if (_err != HAL_OK) return _err; } while (0) + if (ks->used < 2) + return HAL_OK; + hal_error_t err; -hal_error_t hal_ks_index_fsck(hal_ks_index_t *ksi) -{ - if (ksi == NULL || ksi->index == NULL || ksi->names == NULL || - ksi->size == 0 || ksi->used > ksi->size) - return HAL_ERROR_BAD_ARGUMENTS; + for (int i = (ks->used - 2) / 2; i >= 0; i--) + if ((err = ks_heapsift(ks, i, ks->used - 1)) != HAL_OK) + return err; - for (int i = 1; i < ksi->used; i++) - if (hal_uuid_cmp(&ksi->names[ksi->index[i - 1]], &ksi->names[ksi->index[i]]) >= 0) - return HAL_ERROR_KSI_INDEX_UUID_MISORDERED; + for (int i = ks->used - 1; i > 0; i--) { + const uint16_t tmp = ks->index[i]; + ks->index[i] = ks->index[0]; + ks->index[0] = tmp; + if ((err = ks_heapsift(ks, 0, i - 1)) != HAL_OK) + return err; + } return HAL_OK; } /* - * Set up the index. Only setup task we have at the moment is sorting the index. + * Perform a consistency check on the index. */ -hal_error_t hal_ks_index_setup(hal_ks_index_t *ksi) +#define fsck(_ks) \ + do { hal_error_t _err = hal_ks_index_fsck(_ks); if (_err != HAL_OK) return _err; } while (0) + + +hal_error_t hal_ks_index_fsck(hal_ks_t *ks) { - if (ksi == NULL || ksi->index == NULL || ksi->names == NULL || - ksi->size == 0 || ksi->used > ksi->size) + if (ks == NULL || ks->index == NULL || ks->names == NULL || + ks->size == 0 || ks->used > ks->size) return HAL_ERROR_BAD_ARGUMENTS; - ks_heapsort(ksi); - - /* - * One might think we should fsck here, but errors in the index - * at this point probably relate to errors in the supplied data, - * which only the driver knows how to clean up. - */ + for (int i = 1; i < ks->used; i++) + if (hal_uuid_cmp(&ks->names[ks->index[i - 1]], &ks->names[ks->index[i]]) >= 0) + return HAL_ERROR_KS_INDEX_UUID_MISORDERED; return HAL_OK; } @@ -172,23 +164,23 @@ hal_error_t hal_ks_index_setup(hal_ks_index_t *ksi) * Find a single block by name. */ -hal_error_t hal_ks_index_find(hal_ks_index_t *ksi, - const hal_uuid_t * const name, - unsigned *blockno, +hal_error_t hal_ks_index_find(hal_ks_t *ks, + const hal_uuid_t * const name, + unsigned *blockno, int *hint) { - if (ksi == NULL || ksi->index == NULL || ksi->names == NULL || - ksi->size == 0 || ksi->used > ksi->size || name == NULL) + if (ks == NULL || ks->index == NULL || ks->names == NULL || + ks->size == 0 || ks->used > ks->size || name == NULL) return HAL_ERROR_BAD_ARGUMENTS; int where; - fsck(ksi); + fsck(ks); - int ok = ks_find(ksi, name, hint, &where); + int ok = ks_find(ks, name, hint, &where); if (blockno != NULL) - *blockno = ksi->index[where]; + *blockno = ks->index[where]; if (hint != NULL) *hint = where; @@ -200,23 +192,23 @@ hal_error_t hal_ks_index_find(hal_ks_index_t *ksi, * Add a single block to the index. */ -hal_error_t hal_ks_index_add(hal_ks_index_t *ksi, - const hal_uuid_t * const name, - unsigned *blockno, +hal_error_t hal_ks_index_add(hal_ks_t *ks, + const hal_uuid_t * const name, + unsigned *blockno, int *hint) { - if (ksi == NULL || ksi->index == NULL || ksi->names == NULL || - ksi->size == 0 || ksi->used > ksi->size || name == NULL) + if (ks == NULL || ks->index == NULL || ks->names == NULL || + ks->size == 0 || ks->used > ks->size || name == NULL) return HAL_ERROR_BAD_ARGUMENTS; - if (ksi->used == ksi->size) + if (ks->used == ks->size) return HAL_ERROR_NO_KEY_INDEX_SLOTS; int where; - fsck(ksi); + fsck(ks); - if (ks_find(ksi, name, hint, &where)) + if (ks_find(ks, name, hint, &where)) return HAL_ERROR_KEY_NAME_IN_USE; /* @@ -224,11 +216,11 @@ hal_error_t hal_ks_index_add(hal_ks_index_t *ksi, * index up by one slot so we can insert the new block number. */ - const size_t len = (ksi->used - where) * sizeof(*ksi->index); - const uint16_t b = ksi->index[ksi->used++]; - memmove(&ksi->index[where + 1], &ksi->index[where], len); - ksi->index[where] = b; - ksi->names[b] = *name; + const size_t len = (ks->used - where) * sizeof(*ks->index); + const uint16_t b = ks->index[ks->used++]; + memmove(&ks->index[where + 1], &ks->index[where], len); + ks->index[where] = b; + ks->names[b] = *name; if (blockno != NULL) *blockno = b; @@ -236,7 +228,7 @@ hal_error_t hal_ks_index_add(hal_ks_index_t *ksi, if (hint != NULL) *hint = where; - fsck(ksi); + fsck(ks); return HAL_OK; } @@ -245,32 +237,32 @@ hal_error_t hal_ks_index_add(hal_ks_index_t *ksi, * Delete a single block from the index. */ -hal_error_t hal_ks_index_delete(hal_ks_index_t *ksi, - const hal_uuid_t * const name, - unsigned *blockno, +hal_error_t hal_ks_index_delete(hal_ks_t *ks, + const hal_uuid_t * const name, + unsigned *blockno, int *hint) { - if (ksi == NULL || ksi->index == NULL || ksi->names == NULL || - ksi->size == 0 || ksi->used > ksi->size || name == NULL) + if (ks == NULL || ks->index == NULL || ks->names == NULL || + ks->size == 0 || ks->used > ks->size || name == NULL) return HAL_ERROR_BAD_ARGUMENTS; int where; - fsck(ksi); + fsck(ks); - if (ksi->used == 0 || !ks_find(ksi, name, hint, &where)) + if (ks->used == 0 || !ks_find(ks, name, hint, &where)) return HAL_ERROR_KEY_NOT_FOUND; /* * Free the block and stuff it at the end of the free list. */ - const size_t len = (ksi->size - where - 1) * sizeof(*ksi->index); - const uint16_t b = ksi->index[where]; - memmove(&ksi->index[where], &ksi->index[where + 1], len); - ksi->index[ksi->size - 1] = b; - ksi->used--; - memset(&ksi->names[b], 0, sizeof(ksi->names[b])); + const size_t len = (ks->size - where - 1) * sizeof(*ks->index); + const uint16_t b = ks->index[where]; + memmove(&ks->index[where], &ks->index[where + 1], len); + ks->index[ks->size - 1] = b; + ks->used--; + memset(&ks->names[b], 0, sizeof(ks->names[b])); if (blockno != NULL) *blockno = b; @@ -278,32 +270,34 @@ hal_error_t hal_ks_index_delete(hal_ks_index_t *ksi, if (hint != NULL) *hint = where; - fsck(ksi); + fsck(ks); return HAL_OK; } /* - * Replace a single block in the index. + * Replace a single block with a new one, return new block number. + * Name of block does not change. This is an optimization of a delete + * immediately followed by an add for the same name. */ -hal_error_t hal_ks_index_replace(hal_ks_index_t *ksi, +hal_error_t hal_ks_index_replace(hal_ks_t *ks, const hal_uuid_t * const name, unsigned *blockno, int *hint) { - if (ksi == NULL || ksi->index == NULL || ksi->names == NULL || - ksi->size == 0 || ksi->used > ksi->size || name == NULL) + if (ks == NULL || ks->index == NULL || ks->names == NULL || + ks->size == 0 || ks->used > ks->size || name == NULL) return HAL_ERROR_BAD_ARGUMENTS; - if (ksi->used == ksi->size) + if (ks->used == ks->size) return HAL_ERROR_NO_KEY_INDEX_SLOTS; int where; - fsck(ksi); + fsck(ks); - if (ksi->used == 0 || !ks_find(ksi, name, hint, &where)) + if (ks->used == 0 || !ks_find(ks, name, hint, &where)) return HAL_ERROR_KEY_NOT_FOUND; /* @@ -311,14 +305,14 @@ hal_error_t hal_ks_index_replace(hal_ks_index_t *ksi, * block at end of free list and replace old block with new block. */ - const size_t len = (ksi->size - ksi->used - 1) * sizeof(*ksi->index); - const uint16_t b1 = ksi->index[where]; - const uint16_t b2 = ksi->index[ksi->used]; - memmove(&ksi->index[ksi->used], &ksi->index[ksi->used + 1], len); - ksi->index[ksi->size - 1] = b1; - ksi->index[where] = b2; - ksi->names[b2] = *name; - memset(&ksi->names[b1], 0, sizeof(ksi->names[b1])); + const size_t len = (ks->size - ks->used - 1) * sizeof(*ks->index); + const uint16_t b1 = ks->index[where]; + const uint16_t b2 = ks->index[ks->used]; + memmove(&ks->index[ks->used], &ks->index[ks->used + 1], len); + ks->index[ks->size - 1] = b1; + ks->index[where] = b2; + ks->names[b2] = *name; + memset(&ks->names[b1], 0, sizeof(ks->names[b1])); if (blockno != NULL) *blockno = b2; @@ -326,7 +320,7 @@ hal_error_t hal_ks_index_replace(hal_ks_index_t *ksi, if (hint != NULL) *hint = where; - fsck(ksi); + fsck(ks); return HAL_OK; } diff --git a/ks_volatile.c b/ks_volatile.c index 515a8e8..e9a0ef4 100644 --- a/ks_volatile.c +++ b/ks_volatile.c @@ -41,591 +41,182 @@ #include "hal.h" #include "hal_internal.h" - -#define KEK_LENGTH (bitsToBytes(256)) +#include "ks.h" #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 +#ifndef KS_VOLATILE_CACHE_SIZE +#define KS_VOLATILE_CACHE_SIZE 4 #endif -/* - * In-memory keystore database. This is a bit more complicated than - * necessary because originally I though we would want to continue - * supporting an mmap()-based keystore as well. Needs cleaning up. - */ - 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; + hal_ks_block_t block; +} volatile_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 { +static struct db { 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; + volatile_key_t *keys; +} db; /* - * 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. + * Read a block. CRC probably not necessary for RAM. */ -#if STATIC_KS_VOLATILE_SLOTS > 0 +static hal_error_t block_read(hal_k_t *ks, const unsigned blockno, ks_block_t *block) +{ + if (ks != &db.ks || db.keys == NULL || block == NULL || blockno >= ks->size) + return HAL_ERROR_IMPOSSIBLE; -static ks_t volatile_ks; + memcpy(block, &db.keys[blockno].block, sizeof(*block)); -static inline ks_t *ks_to_ksv(hal_ks_t *ks) -{ - return (ks_t *) ks; + return HAL_OK; } /* - * 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. - * - * 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. + * Convert a live block into a tombstone. */ -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) +static hal_error_t block_deprecate(hal_k_t *ks, const unsigned blockno) { - return (!ksv->per_session || - client.handle == HAL_HANDLE_NONE || - k->client.handle == client.handle || - hal_rpc_is_logged_in(client, HAL_USER_WHEEL) == HAL_OK); -} - -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) + if (ks != &db.ks || db.keys == NULL || blockno >= ks->size) 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; - - /* - * 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; + db.keys[blockno].block.header->block_status = BLOCK_STATUS_TOMBSTONE; - return hal_ks_index_setup(&ksv->db->ksi); + return HAL_OK; } -static hal_error_t ks_volatile_init(const hal_ks_driver_t * const driver, const int alloc) -{ - hal_error_t err = HAL_OK; - - hal_ks_lock(); - - 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; +/* + * Zero (not erase) a flash block. + */ - if (alloc && (mem = hal_allocate_static_memory(len)) == NULL) - err = HAL_ERROR_ALLOCATION_FAILURE; - else - err = ks_init(driver, 1, &volatile_ks, mem, len); +static hal_error_t block_zero(hal_k_t *ks, const unsigned blockno) +{ + if (ks != &db.ks || db.keys == NULL || blockno >= ks->size) + return HAL_ERROR_IMPOSSIBLE; - hal_ks_unlock(); - return err; -} + memset(db.keys[blockno].block, 0x00, sizeof(db.keys[blockno].block)); + db.keys[blockno].client.handle = HAL_HANDLE_NONE; + db.keys[blockno].session.handle = HAL_HANDLE_NONE; -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; -} +/* + * Erase a flash block. + */ -static hal_error_t ks_volatile_close(hal_ks_t *ks) +static hal_error_t block_erase(hal_k_t *ks, const unsigned blockno) { + if (ks != &db.ks || db.keys == NULL || blockno >= ks->size) + return HAL_ERROR_IMPOSSIBLE; + + memset(db.keys[blockno].block, 0xFF, sizeof(db.keys[blockno].block)); + db.keys[blockno].client.handle = HAL_HANDLE_NONE; + db.keys[blockno].session.handle = HAL_HANDLE_NONE; + 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; - } -} +/* + * Write a flash block. CRC probably not necessary for RAM. + */ -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) +static hal_error_t block_write(hal_k_t *ks, const unsigned blockno, ks_block_t *block) { - if (ks == NULL || slot == NULL || der == NULL || der_len == 0 || !acceptable_key_type(slot->type)) - return HAL_ERROR_BAD_ARGUMENTS; + if (ks != &db.ks || db.keys == NULL || block == NULL || blockno >= ks->size) + return HAL_ERROR_IMPOSSIBLE; - ks_t *ksv = ks_to_ksv(ks); - hal_error_t err = HAL_OK; - unsigned b; + memcpy(&db.keys[blockno].block, block, sizeof(*block)); - hal_ks_lock(); + return HAL_OK; +} - if (ksv->db == NULL) { - err = HAL_ERROR_KEYSTORE_ACCESS; - goto done; - } +/* + * Set key ownership. + */ - if ((err = hal_ks_index_add(&ksv->db->ksi, &slot->name, &b, &slot->hint)) != HAL_OK) - goto done; +static hal_error_t block_set_owner(hal_ks_t *ks, + const unsigned blockno, + const hal_client_handle_t client, + const hal_session_handle_t session) +{ + if (ks != &db.ks || db.keys == NULL || blockno >= ks->size) + return HAL_ERROR_IMPOSSIBLE; - uint8_t kek[KEK_LENGTH]; - size_t kek_len; - ks_key_t k; + db.keys[blockno].client = client; + db.keys[blockno].session = session; - 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; + return HAL_OK; +} - 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); +/* + * Test key ownership. + */ - memset(kek, 0, sizeof(kek)); +static hal_error_t block_test_owner(hal_ks_t *ks, const + unsigned blockno, + const hal_client_handle_t client, + const hal_session_handle_t session) +{ + if (ks != &db.ks || db.keys == NULL || blockno >= ks->size) + return HAL_ERROR_IMPOSSIBLE; - if (err == HAL_OK) - ksv->db->keys[b] = k; + if (db.keys[blockno].client.handle == client.handle && + db.keys[blockno].session.handle == session.handle) + return HAL_OK; else - (void) hal_ks_index_delete(&ksv->db->ksi, &slot->name, NULL, &slot->hint); - - done: - hal_ks_unlock(); - return err; + return HAL_ERROR_KEY_NOT_FOUND; } -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 = HAL_OK; - unsigned b; - - hal_ks_lock(); - - if (ksv->db == NULL) { - err = HAL_ERROR_KEYSTORE_ACCESS; - goto done; - } - - if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, &b, &slot->hint)) != HAL_OK) - goto done; - - const ks_key_t * const k = &ksv->db->keys[b]; - - if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, k)) { - err = HAL_ERROR_KEY_NOT_FOUND; - goto done; - } - - 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_; - - 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)); - } +/* + * Initialize keystore. + */ - done: - hal_ks_unlock(); - return err; -} +static const hal_ks_driver_t hal_ks_volatile_driver[1] = {{ + .read = block_read, + .write = block_write, + .deprecate = block_deprecate, + .zero = block_zero, + .erase = block_erase, + .erase_maybe = block_erase, /* sic */ + .set_owner = block_set_owner, + .test_owner = block_test_owner +}}; -static hal_error_t ks_delete(hal_ks_t *ks, - hal_pkey_slot_t *slot) + hal_error_t hal_ks_volatile_init(const int alloc) { - if (ks == NULL || slot == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - ks_t *ksv = ks_to_ksv(ks); hal_error_t err = HAL_OK; - unsigned b; hal_ks_lock(); - if (ksv->db == NULL) { - err = HAL_ERROR_KEYSTORE_ACCESS; - goto done; - } - - if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, &b, &slot->hint)) != HAL_OK) - goto done; - if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, &ksv->db->keys[b])) { - err = HAL_ERROR_KEY_NOT_FOUND; + if (alloc && (err = hal_ks_alloc_common(&db.ks, STATIC_KS_VOLATILE_SLOTS, KS_VOLATILE_CACHE_SIZE)) != HAL_OK) goto done; - } - if ((err = hal_ks_index_delete(&ksv->db->ksi, &slot->name, &b, &slot->hint)) != HAL_OK) + if ((err = hal_ks_init_common(&db.ks, hal_ks_volatile_driver)) != HAL_OK) goto done; - memset(&ksv->db->keys[b], 0, sizeof(ksv->db->keys[b])); - - done: - hal_ks_unlock(); - return err; -} - -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 mask, - 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 (ks == NULL || (attributes == NULL && attributes_len > 0) || - result == NULL || result_len == NULL || previous_uuid == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - ks_t *ksv = ks_to_ksv(ks); - - if (ksv->db == NULL) - return HAL_ERROR_KEYSTORE_ACCESS; - - hal_error_t err = HAL_OK; - int i = -1; - - hal_ks_lock(); - - *result_len = 0; - - err = hal_ks_index_find(&ksv->db->ksi, previous_uuid, NULL, &i); - - if (err == HAL_ERROR_KEY_NOT_FOUND) - i--; - else if (err != HAL_OK) + if (alloc && (db.keys = hal_allocate_static_memory(sizeof(*db.keys) * db.ks.size)) == NULL) { + err = HAL_ERROR_ALLOCATION_FAILURE; goto done; - - while (*result_len < result_max && ++i < ksv->db->ksi.used) { - - unsigned b = ksv->db->ksi.index[i]; - - 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 (((flags ^ ksv->db->keys[b].flags) & mask) != 0) - 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) - goto done; - - 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]; - ++*result_len; - } - - err = HAL_OK; - - done: - hal_ks_unlock(); - return err; -} - -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 (ks == NULL || slot == NULL || attributes == NULL || attributes_len == 0) - return HAL_ERROR_BAD_ARGUMENTS; - - ks_t *ksv = ks_to_ksv(ks); - hal_error_t err = HAL_OK; - unsigned b; - - hal_ks_lock(); - - { - if (ksv->db == NULL) { - err = HAL_ERROR_KEYSTORE_ACCESS; - goto done; - } - - if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, &b, &slot->hint)) != HAL_OK) - goto done; - - ks_key_t * const k = &ksv->db->keys[b]; - - if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, k)) { - err = HAL_ERROR_KEY_NOT_FOUND; - goto done; - } - - 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) - goto done; - - 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) - goto done; - } - - err = HAL_OK; - } - done: - hal_ks_unlock(); - return err; -} - -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 (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 = HAL_OK; - unsigned b; - - hal_ks_lock(); - - { - if (ksv->db == NULL) { - err = HAL_ERROR_KEYSTORE_ACCESS; - goto done; - } - - if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, &b, &slot->hint)) != HAL_OK) - goto done; - - const ks_key_t * const k = &ksv->db->keys[b]; - - if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, k)) { - err = HAL_ERROR_KEY_NOT_FOUND; - goto done; - } - - 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) + for (unsigned b = 0; b < db.ks.size; i++) + if ((err = block_erase(&db.ks, b)) != HAL_OK) goto done; - 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) { - err = HAL_ERROR_ATTRIBUTE_NOT_FOUND; - goto done; - } - - if (attrs[j].length > attributes_buffer + attributes_buffer_len - abuf) { - err = HAL_ERROR_RESULT_TOO_LONG; - goto done; - } - - memcpy(abuf, attrs[j].value, attrs[j].length); - attributes[i].value = abuf; - attributes[i].length = attrs[j].length; - abuf += attrs[j].length; - } - - err = HAL_OK; - - } + err = HAL_OK; done: hal_ks_unlock(); return err; } -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 -- cgit v1.2.3 From f59533ee9807832ea5ca7dd5492592c8991a9f34 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Sun, 28 May 2017 12:11:31 -0400 Subject: Further keystore cleanup and consolidation. Still not yet expected to compile, much less run, but getting closer. --- Makefile | 4 +- hal_internal.h | 20 +- ks.c | 26 ++- ks.h | 7 +- ks_flash.c | 638 ------------------------------------------------------- ks_token.c | 661 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ks_volatile.c | 143 +++++++------ 7 files changed, 769 insertions(+), 730 deletions(-) delete mode 100644 ks_flash.c create mode 100644 ks_token.c diff --git a/Makefile b/Makefile index e688837..f3a5979 100644 --- a/Makefile +++ b/Makefile @@ -137,7 +137,7 @@ endif # volatile keystore is always present, to support things like PKCS #11 # "session" objects. -KS_OBJ = ks.o ks_index.o ks_attribute.o ks_volatile.o ks_flash.o mkm.o +KS_OBJ = ks.o ks_index.o ks_attribute.o ks_volatile.o ks_token.o mkm.o # RPC_MODE = none | server | client-simple | client-mixed # none: Build without RPC client, use cores directly. @@ -268,7 +268,7 @@ asn1.o rsa.o ecdsa.o: asn1_internal.h ecdsa.o: ecdsa_curves.h novena-eim.o hal_io_eim.o: novena-eim.h slip.o rpc_client_serial.o rpc_server_serial.o: slip_internal.h -ks_flash.o: last_gasp_pin_internal.h +ks_token.o: last_gasp_pin_internal.h last_gasp_pin_internal.h: ./utils/last_gasp_default_pin >$@ diff --git a/hal_internal.h b/hal_internal.h index 89dfbbb..667c5a4 100644 --- a/hal_internal.h +++ b/hal_internal.h @@ -479,24 +479,8 @@ typedef struct { typedef struct hal_ks hal_ks_t; -#error Minor re-think needed on keystore init - -// I like current hal_ks_*_init() setup method, but how does anything -// get a handle on the keystore? Make the keystores global variables? -// Right now they're hidden in larger, driver-specific structures, but -// it would be easy enough to break them out. Have already forgotten -// how the old API handled this, except that it involved an init -// method via the driver. Init is going to be special in any case, -// since we can't dispatch through a driver pointer in the keystore -// object, so either we expose the keystore and the driver or we -// expose the keystore and the init function. The latter may be -// simpler. -// -// Another variation would be to keep the current nesting, add global -// pointer variables for the keystores, and have the init functions -// set the pointers. Only real advantage there is that it would give -// us an easy test for whether the keystore had been initialized...but -// we already have several of those, not clear what value another adds. +extern hal_ks_t * const hal_ks_token; +extern hal_ks_t * const hal_ks_volatile; /* * RPC lowest-level send and receive routines. These are blocking, and diff --git a/ks.c b/ks.c index f0c71cc..3d1ae61 100644 --- a/ks.c +++ b/ks.c @@ -221,24 +221,31 @@ static inline void *gnaw(uint8_t **mem, size_t *len, const size_t size) #warning Call hal_ks_alloc_common() and hal_ks_init_common() while holding hal_ks_lock(); ! -hal_error_t hal_ks_alloc_common(hal_ks_t *ks, const unsigned ks_blocks, const unsigned cache_blocks) +hal_error_t hal_ks_alloc_common(hal_ks_t *ks, + const unsigned ks_blocks, + const unsigned cache_blocks, + void **extra, + const size_t extra_len) { /* - * We allocate a single big chunk of memory rather than three - * smaller chunks to make it atomic. We need all three, so this way - * either all succeed or all fail. + * We allocate a single big chunk of memory to make it atomic. We + * need all three of our blocks, so this way either all succeed or + * all fail; we allow our caller to piggyback its own memory needs + * (if any) on ours for the same reason. */ size_t len = (sizeof(*ks->index) * ks_blocks + sizeof(*ks->names) * ks_blocks + - sizeof(*ks->cache) * cache_blocks); + sizeof(*ks->cache) * cache_blocks + + extra_len); uint8_t *mem = hal_allocate_static_memory(len); if (mem == NULL) return HAL_ERROR_ALLOCATION_FAILURE; - memset(ks, 0, sizeof(*ks)); + memset(((uint8_t *) ks) + sizeof(hal_ks_driver_t), 0, + sizeof(hal_ks_t) - sizeof(hal_ks_driver_t)); memset(mem, 0, len); ks->index = gnaw(&mem, &len, sizeof(*ks->index) * ks_blocks); @@ -248,10 +255,13 @@ hal_error_t hal_ks_alloc_common(hal_ks_t *ks, const unsigned ks_blocks, const un ks->size = ks_blocks; ks->cache_size = cache_blocks; + if (extra != NULL) + *extra = mem; + return HAL_OK; } -hal_error_t hal_ks_init_common(hal_ks_t *ks, const hal_ks_driver_t * const driver) +hal_error_t hal_ks_init_common(hal_ks_t *ks) { if (ks->index == NULL || ks->names == NULL || ks->cache == NULL) return HAL_ERROR_IMPOSSIBLE; @@ -447,8 +457,6 @@ hal_error_t hal_ks_init_common(hal_ks_t *ks, const hal_ks_driver_t * const drive * And we're finally done. */ - ks->driver = driver; - return HAL_OK; } diff --git a/ks.h b/ks.h index b24f3c0..29965cc 100644 --- a/ks.h +++ b/ks.h @@ -186,8 +186,10 @@ typedef struct { * themselves are stored, that's up to the keystore driver. */ +typedef struct hal_ks_driver hal_ks_driver_t; + struct hal_ks { - const hal_ks_driver_t *driver; + const hal_ks_driver_t *driver;/* Must be first */ unsigned size; /* Blocks in keystore */ unsigned used; /* How many blocks are in use */ uint16_t *index; /* Index/freelist array */ @@ -209,10 +211,9 @@ struct hal_ks { * function pointers and a set of static inline wrapper functions. */ -typedef struct hal_ks_driver hal_ks_driver_t; - #define KS_DRIVER_END_LIST #define KS_DRIVER_METHODS \ + KS_DRIVER_METHOD(init, hal_ks_t *ks, const int alloc) \ KS_DRIVER_METHOD(read, hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block) \ KS_DRIVER_METHOD(write, hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block) \ KS_DRIVER_METHOD(deprecate, hal_ks_t *ks, const unsigned blockno) \ diff --git a/ks_flash.c b/ks_flash.c deleted file mode 100644 index e5a83ea..0000000 --- a/ks_flash.c +++ /dev/null @@ -1,638 +0,0 @@ -/* - * ks_flash.c - * ---------- - * Keystore implementation in flash memory. - * - * Authors: Rob Austein, Fredrik Thulin - * Copyright (c) 2015-2017, 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 - * met: - * - Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name of the NORDUnet nor the names of its contributors may - * be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * This keystore driver operates over bare flash, versus over a flash file - * system or flash translation layer. The block size is large enough to - * hold an AES-keywrapped 4096-bit RSA key. Any remaining space in the key - * block may be used to store attributes (opaque TLV blobs). If the - * attributes overflow the key block, additional blocks may be added, but - * no attribute may exceed the block size. - */ - -#include -#include -#include - -#include "hal.h" -#include "hal_internal.h" -#include "ks.h" - -#include "last_gasp_pin_internal.h" - -#define HAL_OK CMIS_HAL_OK -#include "stm-keystore.h" -#undef HAL_OK - -#ifndef KS_FLASH_CACHE_SIZE -#define KS_FLASH_CACHE_SIZE 4 -#endif - -#define NUM_FLASH_BLOCKS KEYSTORE_NUM_SUBSECTORS - -static struct db { - hal_ks_t ks; /* Must be first (C "subclassing") */ - hal_ks_pin_t wheel_pin; - hal_ks_pin_t so_pin; - hal_ks_pin_t user_pin; -} db; - -/* - * PIN block gets the all-zeros UUID, which will never be returned by - * the UUID generation code (by definition -- it's not a version 4 UUID). - */ - -const static hal_uuid_t pin_uuid = {{0}}; - - -/* - * Calculate offset of the block in the flash address space. - */ - -static inline uint32_t block_offset(const unsigned blockno) -{ - return blockno * KEYSTORE_SUBSECTOR_SIZE; -} - -/* - * Read a flash block. - * - * Flash read on the Alpha is slow enough that it pays to check the - * first page before reading the rest of the block. - */ - -static hal_error_t block_read(hal_k_t *ks, const unsigned blockno, ks_block_t *block) -{ - if (ks != &db.ks || block == NULL || blockno >= NUM_FLASH_BLOCKS || sizeof(*block) != KEYSTORE_SUBSECTOR_SIZE) - return HAL_ERROR_IMPOSSIBLE; - - /* Sigh, magic numeric return codes */ - if (keystore_read_data(block_offset(blockno), - block->bytes, - KEYSTORE_PAGE_SIZE) != 1) - return HAL_ERROR_KEYSTORE_ACCESS; - - switch (block_get_type(block)) { - case BLOCK_TYPE_ERASED: - case BLOCK_TYPE_ZEROED: - return HAL_OK; - case BLOCK_TYPE_KEY: - case BLOCK_TYPE_PIN: - break; - default: - return HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE; - } - - switch (block_get_status(block)) { - case BLOCK_STATUS_LIVE: - case BLOCK_STATUS_TOMBSTONE: - break; - default: - return HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE; - } - - /* Sigh, magic numeric return codes */ - if (keystore_read_data(block_offset(blockno) + KEYSTORE_PAGE_SIZE, - block->bytes + KEYSTORE_PAGE_SIZE, - sizeof(*block) - KEYSTORE_PAGE_SIZE) != 1) - return HAL_ERROR_KEYSTORE_ACCESS; - - if (calculate_block_crc(block) != block->header.crc) - return HAL_ERROR_KEYSTORE_BAD_CRC; - - return HAL_OK; -} - -/* - * Convert a live block into a tombstone. Caller is responsible for - * making sure that the block being converted is valid; since we don't - * need to update the CRC for this, we just modify the first page. - */ - -static hal_error_t block_deprecate(hal_k_t *ks, const unsigned blockno) -{ - if (ks != &db.ks || blockno >= NUM_FLASH_BLOCKS) - return HAL_ERROR_IMPOSSIBLE; - - uint8_t page[KEYSTORE_PAGE_SIZE]; - flash_block_header_t *header = (void *) page; - uint32_t offset = block_offset(blockno); - - /* Sigh, magic numeric return codes */ - if (keystore_read_data(offset, page, sizeof(page)) != 1) - return HAL_ERROR_KEYSTORE_ACCESS; - - header->block_status = BLOCK_STATUS_TOMBSTONE; - - /* Sigh, magic numeric return codes */ - if (keystore_write_data(offset, page, sizeof(page)) != 1) - return HAL_ERROR_KEYSTORE_ACCESS; - - return HAL_OK; -} - -/* - * Zero (not erase) a flash block. Just need to zero the first page. - */ - -static hal_error_t block_zero(hal_k_t *ks, const unsigned blockno) -{ - if (ks != &db.ks || blockno >= NUM_FLASH_BLOCKS) - return HAL_ERROR_IMPOSSIBLE; - - uint8_t page[KEYSTORE_PAGE_SIZE] = {0}; - - /* Sigh, magic numeric return codes */ - if (keystore_write_data(block_offset(blockno), page, sizeof(page)) != 1) - return HAL_ERROR_KEYSTORE_ACCESS; - - return HAL_OK; -} - -/* - * Erase a flash block. Also see block_erase_maybe(), below. - */ - -static hal_error_t block_erase(hal_k_t *ks, const unsigned blockno) -{ - if (ks != &db.ks || blockno >= NUM_FLASH_BLOCKS) - return HAL_ERROR_IMPOSSIBLE; - - /* Sigh, magic numeric return codes */ - if (keystore_erase_subsector(blockno) != 1) - return HAL_ERROR_KEYSTORE_ACCESS; - - return HAL_OK; -} - -/* - * Erase a flash block if it hasn't already been erased. - * May not be necessary, trying to avoid unnecessary wear. - * - * Unclear whether there's any sane reason why this needs to be - * constant time, given how slow erasure is. But side channel attacks - * can be tricky things, and it's theoretically possible that we could - * leak information about, eg, key length, so we do constant time. - */ - -static hal_error_t block_erase_maybe(hal_k_t *ks, const unsigned blockno) -{ - if (ks != &db.ks || blockno >= NUM_FLASH_BLOCKS) - return HAL_ERROR_IMPOSSIBLE; - - uint8_t mask = 0xFF; - - for (uint32_t a = block_offset(blockno); a < block_offset(blockno + 1); a += KEYSTORE_PAGE_SIZE) { - uint8_t page[KEYSTORE_PAGE_SIZE]; - if (keystore_read_data(a, page, sizeof(page)) != 1) - return HAL_ERROR_KEYSTORE_ACCESS; - for (int i = 0; i < KEYSTORE_PAGE_SIZE; i++) - mask &= page[i]; - } - - return mask == 0xFF ? HAL_OK : block_erase(blockno); -} - -/* - * Write a flash block, calculating CRC when appropriate. - */ - -static hal_error_t block_write(hal_k_t *ks, const unsigned blockno, ks_block_t *block) -{ - if (ks != &db.ks || block == NULL || blockno >= NUM_FLASH_BLOCKS || sizeof(*block) != KEYSTORE_SUBSECTOR_SIZE) - return HAL_ERROR_IMPOSSIBLE; - - hal_error_t err = block_erase_maybe(blockno); - - if (err != HAL_OK) - return err; - - switch (block_get_type(block)) { - case BLOCK_TYPE_KEY: - case BLOCK_TYPE_PIN: - block->header.crc = calculate_block_crc(block); - break; - default: - break; - } - - /* Sigh, magic numeric return codes */ - if (keystore_write_data(block_offset(blockno), block->bytes, sizeof(*block)) != 1) - return HAL_ERROR_KEYSTORE_ACCESS; - - return HAL_OK; -} - -/* - * The token keystore doesn't implement per-session objects, so these are no-ops. - */ - -static hal_error_t block_set_owner(hal_ks_t *ks, - const unsigned blockno, - const hal_client_handle_t client, - const hal_session_handle_t session) -{ - return HAL_OK; -} - -static hal_error_t block_test_owner(hal_ks_t *ks, const - unsigned blockno, - const hal_client_handle_t client, - const hal_session_handle_t session) -{ - return HAL_OK; -} - -/* - * Forward reference. - */ - -static hal_error_t fetch_pin_block(unsigned *b, ks_block_t **block); - -/* - * Initialize keystore. - */ - -static const hal_ks_driver_t hal_ks_token_driver[1] = {{ - .read = block_read, - .write = block_write, - .deprecate = block_deprecate, - .zero = block_zero, - .erase = block_erase, - .erase_maybe = block_erase_maybe, - .set_owner = block_set_owner, - .test_owner = block_test_owner -}}; - -hal_error_t hal_ks_token_init(const int alloc) -{ - hal_error_t err = HAL_OK; - - hal_ks_lock(); - - if (alloc && (err = hal_ks_alloc_common(&db.ks, NUM_FLASH_BLOCKS, KS_FLASH_CACHE_SIZE)) != HAL_OK) - goto done; - - if ((err = hal_ks_init_common(ks, hal_ks_token_driver)) != HAL_OK) - goto done; - - /* - * Fetch or create the PIN block. - */ - - memset(&db.wheel_pin, 0, sizeof(db.wheel_pin)); - memset(&db.so_pin, 0, sizeof(db.so_pin)); - memset(&db.user_pin, 0, sizeof(db.user_pin)); - - err = fetch_pin_block(NULL, &block); - - if (err == HAL_OK) { - db.wheel_pin = block->pin.wheel_pin; - db.so_pin = block->pin.so_pin; - db.user_pin = block->pin.user_pin; - } - - else if (err != HAL_ERROR_KEY_NOT_FOUND) - goto done; - - else { - /* - * We found no PIN block, so create one, with the user and so PINs - * cleared and the wheel PIN set to the last-gasp value. The - * last-gasp WHEEL PIN is a terrible answer, but we need some kind - * of bootstrapping mechanism when all else fails. If you have a - * better suggestion, we'd love to hear it. - */ - - unsigned b; - - memset(block, 0xFF, sizeof(*block)); - - block->header.block_type = BLOCK_TYPE_PIN; - block->header.block_status = BLOCK_STATUS_LIVE; - - block->pin.wheel_pin = db.wheel_pin = hal_last_gasp_pin; - block->pin.so_pin = db.so_pin; - block->pin.user_pin = db.user_pin; - - if ((err = hal_ks_index_add(&db.ksi, &pin_uuid, &b, NULL)) != HAL_OK) - goto done; - - cache_mark_used(block, b); - - err = block_write(b, block); - - cache_release(block); - - if (err != HAL_OK) - goto done; - } - - err = HAL_OK; - - done: - hal_ks_unlock(); - return err; -} - -/* - * The remaining functions aren't really part of the keystore API per se, - * but they all involve non-key data which we keep in the keystore - * because it's the flash we've got. - */ - -/* - * Special bonus init routine used only by the bootloader, so that it - * can read PINs set by the main firmware. Yes, this is a kludge. We - * could of course call the real ks_init() routine instead, but it's - * slow, and we don't want to allow anything that would modify the - * flash here, so having a special entry point for this kludge is - * simplest, overall. Sigh. - */ - -void hal_ks_init_read_only_pins_only(void) -{ - unsigned b, best_seen = ~0; - ks_block_t block[1]; - - hal_ks_lock(); - - for (b = 0; b < NUM_FLASH_BLOCKS; b++) { - if (block_read(b, block) != HAL_OK || block_get_type(block) != BLOCK_TYPE_PIN) - continue; - best_seen = b; - if (block_get_status(block) == BLOCK_STATUS_LIVE) - break; - } - - if (b != best_seen && best_seen != ~0 && block_read(best_seen, block) != HAL_OK) - best_seen = ~0; - - if (best_seen == ~0) { - memset(block, 0xFF, sizeof(*block)); - block->pin.wheel_pin = hal_last_gasp_pin; - } - - db.wheel_pin = block->pin.wheel_pin; - db.so_pin = block->pin.so_pin; - db.user_pin = block->pin.user_pin; - - hal_ks_unlock(); -} - -/* - * Fetch PIN. This is always cached, so just returned cached value. - */ - -hal_error_t hal_get_pin(const hal_user_t user, - const hal_ks_pin_t **pin) -{ - if (pin == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - hal_error_t err = HAL_OK; - - hal_ks_lock(); - - switch (user) { - case HAL_USER_WHEEL: *pin = &db.wheel_pin; break; - case HAL_USER_SO: *pin = &db.so_pin; break; - case HAL_USER_NORMAL: *pin = &db.user_pin; break; - default: err = HAL_ERROR_BAD_ARGUMENTS; - } - - hal_ks_unlock(); - - return err; -} - -/* - * Fetch PIN block. hint = 0 because we know that the all-zeros UUID - * should always sort to first slot in the index. - */ - -static hal_error_t fetch_pin_block(unsigned *b, ks_block_t **block) -{ - if (block == NULL) - return HAL_ERROR_IMPOSSIBLE; - - hal_error_t err; - int hint = 0; - unsigned b_; - - if (b == NULL) - b = &b_; - - if ((err = hal_ks_index_find(&db.ksi, &pin_uuid, b, &hint)) != HAL_OK || - (err = block_read_cached(*b, block)) != HAL_OK) - return err; - - cache_mark_used(*block, *b); - - if (block_get_type(*block) != BLOCK_TYPE_PIN) - return HAL_ERROR_IMPOSSIBLE; - - return HAL_OK; -} - -/* - * Update the PIN block. This block should always be present, but we - * have to do the zombie jamboree to make sure we write the new PIN - * block before destroying the old one. hint = 0 because we know that - * the all-zeros UUID should always sort to first slot in the index. - */ - -static hal_error_t update_pin_block(const unsigned b, - ks_block_t *block, - const flash_pin_block_t * const new_data) -{ - if (block == NULL || new_data == NULL || block_get_type(block) != BLOCK_TYPE_PIN) - return HAL_ERROR_IMPOSSIBLE; - - int hint = 0; - - block->pin = *new_data; - - return block_update(b, block, &pin_uuid, &hint); -} - -/* - * Change a PIN. - */ - -hal_error_t hal_set_pin(const hal_user_t user, - const hal_ks_pin_t * const pin) -{ - if (pin == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - ks_block_t *block; - hal_error_t err; - unsigned b; - - hal_ks_lock(); - - if ((err = fetch_pin_block(&b, &block)) != HAL_OK) - goto done; - - flash_pin_block_t new_data = block->pin; - hal_ks_pin_t *dp, *bp; - - switch (user) { - case HAL_USER_WHEEL: bp = &new_data.wheel_pin; dp = &db.wheel_pin; break; - case HAL_USER_SO: bp = &new_data.so_pin; dp = &db.so_pin; break; - case HAL_USER_NORMAL: bp = &new_data.user_pin; dp = &db.user_pin; break; - default: err = HAL_ERROR_BAD_ARGUMENTS; goto done; - } - - const hal_ks_pin_t old_pin = *dp; - *dp = *bp = *pin; - - if ((err = update_pin_block(b, block, &new_data)) != HAL_OK) - *dp = old_pin; - - done: - hal_ks_unlock(); - return err; -} - -#if HAL_MKM_FLASH_BACKUP_KLUDGE - -/* - * Horrible insecure kludge in lieu of a battery for the MKM. - * - * API here is a little strange: all calls pass a length parameter, - * but any length other than the compiled in constant just returns an - * immediate error, there's no notion of buffer max length vs buffer - * used length, querying for the size of buffer really needed, or - * anything like that. - * - * We might want to rewrite this some day, if we don't replace it with - * a battery first. For now we just preserve the API as we found it - * while re-implementing it on top of the new keystore. - */ - -hal_error_t hal_mkm_flash_read_no_lock(uint8_t *buf, const size_t len) -{ - if (buf != NULL && len != KEK_LENGTH) - return HAL_ERROR_MASTERKEY_BAD_LENGTH; - - ks_block_t *block; - hal_error_t err; - unsigned b; - - if ((err = fetch_pin_block(&b, &block)) != HAL_OK) - return err; - - if (block->pin.kek_set != FLASH_KEK_SET) - return HAL_ERROR_MASTERKEY_NOT_SET; - - if (buf != NULL) - memcpy(buf, block->pin.kek, len); - - return HAL_OK; -} - -hal_error_t hal_mkm_flash_read(uint8_t *buf, const size_t len) -{ - hal_ks_lock(); - const hal_error_t err = hal_mkm_flash_read_no_lock(buf, len); - hal_ks_unlock(); - return err; -} - -hal_error_t hal_mkm_flash_write(const uint8_t * const buf, const size_t len) -{ - if (buf == NULL) - return HAL_ERROR_BAD_ARGUMENTS; - - if (len != KEK_LENGTH) - return HAL_ERROR_MASTERKEY_BAD_LENGTH; - - ks_block_t *block; - hal_error_t err; - unsigned b; - - hal_ks_lock(); - - if ((err = fetch_pin_block(&b, &block)) != HAL_OK) - goto done; - - flash_pin_block_t new_data = block->pin; - - new_data.kek_set = FLASH_KEK_SET; - memcpy(new_data.kek, buf, len); - - err = update_pin_block(b, block, &new_data); - - done: - hal_ks_unlock(); - return err; -} - -hal_error_t hal_mkm_flash_erase(const size_t len) -{ - if (len != KEK_LENGTH) - return HAL_ERROR_MASTERKEY_BAD_LENGTH; - - ks_block_t *block; - hal_error_t err; - unsigned b; - - hal_ks_lock(); - - if ((err = fetch_pin_block(&b, &block)) != HAL_OK) - goto done; - - flash_pin_block_t new_data = block->pin; - - new_data.kek_set = FLASH_KEK_SET; - memset(new_data.kek, 0, len); - - err = update_pin_block(b, block, &new_data); - - done: - hal_ks_unlock(); - return err; -} - -#endif /* HAL_MKM_FLASH_BACKUP_KLUDGE */ - - -/* - * Local variables: - * indent-tabs-mode: nil - * End: - */ diff --git a/ks_token.c b/ks_token.c new file mode 100644 index 0000000..cc25ca5 --- /dev/null +++ b/ks_token.c @@ -0,0 +1,661 @@ +/* + * ks_token.c + * ---------- + * Keystore implementation in flash memory. + * + * Authors: Rob Austein, Fredrik Thulin + * Copyright (c) 2015-2017, 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 + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This keystore driver operates over bare flash, versus over a flash file + * system or flash translation layer. The block size is large enough to + * hold an AES-keywrapped 4096-bit RSA key. Any remaining space in the key + * block may be used to store attributes (opaque TLV blobs). If the + * attributes overflow the key block, additional blocks may be added, but + * no attribute may exceed the block size. + */ + +#include +#include +#include + +#include "hal.h" +#include "hal_internal.h" +#include "ks.h" + +#include "last_gasp_pin_internal.h" + +#define HAL_OK CMIS_HAL_OK +#include "stm-keystore.h" +#undef HAL_OK + +#ifndef KS_TOKEN_CACHE_SIZE +#define KS_TOKEN_CACHE_SIZE 4 +#endif + +#define NUM_FLASH_BLOCKS KEYSTORE_NUM_SUBSECTORS + +/* + * Keystore database. + */ + +typedef struct { + hal_ks_t ks; /* Must be first (C "subclassing") */ + hal_ks_pin_t wheel_pin; + hal_ks_pin_t so_pin; + hal_ks_pin_t user_pin; +} ks_token_db_t; + +/* + * This is a bit silly, but it's safe enough, and it lets us avoid a + * nasty mess of forward references. + */ + +#define db ((ks_token_db_t * const) hal_ks_token) + +/* + * PIN block gets the all-zeros UUID, which will never be returned by + * the UUID generation code (by definition -- it's not a version 4 UUID). + */ + +const static hal_uuid_t pin_uuid = {{0}}; + +/* + * Calculate offset of the block in the flash address space. + */ + +static inline uint32_t ks_token_offset(const unsigned blockno) +{ + return blockno * KEYSTORE_SUBSECTOR_SIZE; +} + +/* + * Read a flash block. + * + * Flash read on the Alpha is slow enough that it pays to check the + * first page before reading the rest of the block. + */ + +static hal_error_t ks_token_read(hal_k_t *ks, const unsigned blockno, ks_block_t *block) +{ + if (ks != hal_ks_token || block == NULL || blockno >= NUM_FLASH_BLOCKS || sizeof(*block) != KEYSTORE_SUBSECTOR_SIZE) + return HAL_ERROR_IMPOSSIBLE; + + /* Sigh, magic numeric return codes */ + if (keystore_read_data(block_offset(blockno), + block->bytes, + KEYSTORE_PAGE_SIZE) != 1) + return HAL_ERROR_KEYSTORE_ACCESS; + + switch (block_get_type(block)) { + case HAL_KS_BLOCK_TYPE_ERASED: + case HAL_KS_BLOCK_TYPE_ZEROED: + return HAL_OK; + case HAL_KS_BLOCK_TYPE_KEY: + case HAL_KS_BLOCK_TYPE_PIN: + break; + default: + return HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE; + } + + switch (block_get_status(block)) { + case HAL_KS_BLOCK_STATUS_LIVE: + case HAL_KS_BLOCK_STATUS_TOMBSTONE: + break; + default: + return HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE; + } + + /* Sigh, magic numeric return codes */ + if (keystore_read_data(block_offset(blockno) + KEYSTORE_PAGE_SIZE, + block->bytes + KEYSTORE_PAGE_SIZE, + sizeof(*block) - KEYSTORE_PAGE_SIZE) != 1) + return HAL_ERROR_KEYSTORE_ACCESS; + + if (calculate_block_crc(block) != block->header.crc) + return HAL_ERROR_KEYSTORE_BAD_CRC; + + return HAL_OK; +} + +/* + * Convert a live block into a tombstone. Caller is responsible for + * making sure that the block being converted is valid; since we don't + * need to update the CRC for this, we just modify the first page. + */ + +static hal_error_t ks_token_deprecate(hal_k_t *ks, const unsigned blockno) +{ + if (ks != hal_ks_token || blockno >= NUM_FLASH_BLOCKS) + return HAL_ERROR_IMPOSSIBLE; + + uint8_t page[KEYSTORE_PAGE_SIZE]; + flash_block_header_t *header = (void *) page; + uint32_t offset = ks_token_offset(blockno); + + /* Sigh, magic numeric return codes */ + if (keystore_read_data(offset, page, sizeof(page)) != 1) + return HAL_ERROR_KEYSTORE_ACCESS; + + header->block_status = HAL_KS_BLOCK_STATUS_TOMBSTONE; + + /* Sigh, magic numeric return codes */ + if (keystore_write_data(offset, page, sizeof(page)) != 1) + return HAL_ERROR_KEYSTORE_ACCESS; + + return HAL_OK; +} + +/* + * Zero (not erase) a flash block. Just need to zero the first page. + */ + +static hal_error_t ks_token_zero(hal_k_t *ks, const unsigned blockno) +{ + if (ks != hal_ks_token || blockno >= NUM_FLASH_BLOCKS) + return HAL_ERROR_IMPOSSIBLE; + + uint8_t page[KEYSTORE_PAGE_SIZE] = {0}; + + /* Sigh, magic numeric return codes */ + if (keystore_write_data(block_offset(blockno), page, sizeof(page)) != 1) + return HAL_ERROR_KEYSTORE_ACCESS; + + return HAL_OK; +} + +/* + * Erase a flash block. Also see ks_token_erase_maybe(), below. + */ + +static hal_error_t ks_token_erase(hal_k_t *ks, const unsigned blockno) +{ + if (ks != hal_ks_token || blockno >= NUM_FLASH_BLOCKS) + return HAL_ERROR_IMPOSSIBLE; + + /* Sigh, magic numeric return codes */ + if (keystore_erase_subsector(blockno) != 1) + return HAL_ERROR_KEYSTORE_ACCESS; + + return HAL_OK; +} + +/* + * Erase a flash block if it hasn't already been erased. + * May not be necessary, trying to avoid unnecessary wear. + * + * Unclear whether there's any sane reason why this needs to be + * constant time, given how slow erasure is. But side channel attacks + * can be tricky things, and it's theoretically possible that we could + * leak information about, eg, key length, so we do constant time. + */ + +static hal_error_t ks_token_erase_maybe(hal_k_t *ks, const unsigned blockno) +{ + if (ks != hal_ks_token || blockno >= NUM_FLASH_BLOCKS) + return HAL_ERROR_IMPOSSIBLE; + + uint8_t mask = 0xFF; + + for (uint32_t a = ks_token_offset(blockno); a < ks_token_offset(blockno + 1); a += KEYSTORE_PAGE_SIZE) { + uint8_t page[KEYSTORE_PAGE_SIZE]; + if (keystore_read_data(a, page, sizeof(page)) != 1) + return HAL_ERROR_KEYSTORE_ACCESS; + for (int i = 0; i < KEYSTORE_PAGE_SIZE; i++) + mask &= page[i]; + } + + return mask == 0xFF ? HAL_OK : ks_token_erase(blockno); +} + +/* + * Write a flash block, calculating CRC when appropriate. + */ + +static hal_error_t ks_token_write(hal_k_t *ks, const unsigned blockno, ks_block_t *block) +{ + if (ks != hal_ks_token || block == NULL || blockno >= NUM_FLASH_BLOCKS || sizeof(*block) != KEYSTORE_SUBSECTOR_SIZE) + return HAL_ERROR_IMPOSSIBLE; + + hal_error_t err = ks_token_erase_maybe(blockno); + + if (err != HAL_OK) + return err; + + switch (block_get_type(block)) { + case HAL_KS_BLOCK_TYPE_KEY: + case HAL_KS_BLOCK_TYPE_PIN: + block->header.crc = calculate_block_crc(block); + break; + default: + break; + } + + /* Sigh, magic numeric return codes */ + if (keystore_write_data(block_offset(blockno), block->bytes, sizeof(*block)) != 1) + return HAL_ERROR_KEYSTORE_ACCESS; + + return HAL_OK; +} + +/* + * The token keystore doesn't implement per-session objects, so these are no-ops. + */ + +static hal_error_t ks_token_set_owner(hal_ks_t *ks, + const unsigned blockno, + const hal_client_handle_t client, + const hal_session_handle_t session) +{ + return HAL_OK; +} + +static hal_error_t ks_token_test_owner(hal_ks_t *ks, const + unsigned blockno, + const hal_client_handle_t client, + const hal_session_handle_t session) +{ + return HAL_OK; +} + +/* + * Forward reference. + */ + +static hal_error_t fetch_pin_block(unsigned *b, ks_block_t **block); + +/* + * Initialize keystore. + */ + +static hal_error_t ks_token_init(hal_ks_t *ks, const int alloc) +{ + if (ks != hal_ks_token) + return HAL_ERROR_IMPOSSIBLE; + + hal_error_t err = HAL_OK; + + hal_ks_lock(); + + if (alloc && (err = hal_ks_alloc_common(ks, NUM_FLASH_BLOCKS, KS_TOKEN_CACHE_SIZE, NULL, 0)) != HAL_OK) + goto done; + + if ((err = hal_ks_init_common(ks)) != HAL_OK) + goto done; + + /* + * Fetch or create the PIN block. + */ + + memset(&db->wheel_pin, 0, sizeof(db->wheel_pin)); + memset(&db->so_pin, 0, sizeof(db->so_pin)); + memset(&db->user_pin, 0, sizeof(db->user_pin)); + + err = fetch_pin_block(NULL, &block); + + if (err == HAL_OK) { + db->wheel_pin = block->pin.wheel_pin; + db->so_pin = block->pin.so_pin; + db->user_pin = block->pin.user_pin; + } + + else if (err != HAL_ERROR_KEY_NOT_FOUND) + goto done; + + else { + /* + * We found no PIN block, so create one, with the user and so PINs + * cleared and the wheel PIN set to the last-gasp value. The + * last-gasp WHEEL PIN is a terrible answer, but we need some kind + * of bootstrapping mechanism when all else fails. If you have a + * better suggestion, we'd love to hear it. + */ + + unsigned b; + + memset(block, 0xFF, sizeof(*block)); + + block->header.block_type = HAL_KS_BLOCK_TYPE_PIN; + block->header.block_status = HAL_KS_BLOCK_STATUS_LIVE; + + block->pin.wheel_pin = db->wheel_pin = hal_last_gasp_pin; + block->pin.so_pin = db->so_pin; + block->pin.user_pin = db->user_pin; + + if ((err = hal_ks_index_add(ks, &pin_uuid, &b, NULL)) != HAL_OK) + goto done; + + cache_mark_used(block, b); + + err = ks_token_write(b, block); + + cache_release(block); + + if (err != HAL_OK) + goto done; + } + + err = HAL_OK; + + done: + hal_ks_unlock(); + return err; +} + +/* + * Dispatch vector and keystore definition, now that we've defined all + * the driver functions. + */ + +static const hal_ks_driver_t ks_token_driver = { + .init = ks_token_init, + .read = ks_token_read, + .write = ks_token_write, + .deprecate = ks_token_deprecate, + .zero = ks_token_zero, + .erase = ks_token_erase, + .erase_maybe = ks_token_erase_maybe, + .set_owner = ks_token_set_owner, + .test_owner = ks_token_test_owner +}; + +static ks_token_db_t _db = { .ks.driver = &ks_token_driver }; + +hal_ks_t * const hal_ks_token = &_db.ks; + +/* + * The remaining functions aren't really part of the keystore API per se, + * but they all involve non-key data which we keep in the keystore + * because it's the flash we've got. + */ + +/* + * Special bonus init routine used only by the bootloader, so that it + * can read PINs set by the main firmware. Yes, this is a kludge. We + * could of course call the real ks_init() routine instead, but it's + * slow, and we don't want to allow anything that would modify the + * flash here, so having a special entry point for this kludge is + * simplest, overall. Sigh. + */ + +void hal_ks_init_read_only_pins_only(void) +{ + unsigned b, best_seen = ~0; + ks_block_t block[1]; + + hal_ks_lock(); + + for (b = 0; b < NUM_FLASH_BLOCKS; b++) { + if (block_read(b, block) != HAL_OK || ks_token_get_type(block) != HAL_KS_BLOCK_TYPE_PIN) + continue; + best_seen = b; + if (block_get_status(block) == HAL_KS_BLOCK_STATUS_LIVE) + break; + } + + if (b != best_seen && best_seen != ~0 && ks_token_read(best_seen, block) != HAL_OK) + best_seen = ~0; + + if (best_seen == ~0) { + memset(block, 0xFF, sizeof(*block)); + block->pin.wheel_pin = hal_last_gasp_pin; + } + + db->wheel_pin = block->pin.wheel_pin; + db->so_pin = block->pin.so_pin; + db->user_pin = block->pin.user_pin; + + hal_ks_unlock(); +} + +/* + * Fetch PIN. This is always cached, so just returned cached value. + */ + +hal_error_t hal_get_pin(const hal_user_t user, + const hal_ks_pin_t **pin) +{ + if (pin == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + hal_error_t err = HAL_OK; + + hal_ks_lock(); + + switch (user) { + case HAL_USER_WHEEL: *pin = &db->wheel_pin; break; + case HAL_USER_SO: *pin = &db->so_pin; break; + case HAL_USER_NORMAL: *pin = &db->user_pin; break; + default: err = HAL_ERROR_BAD_ARGUMENTS; + } + + hal_ks_unlock(); + + return err; +} + +/* + * Fetch PIN block. hint = 0 because we know that the all-zeros UUID + * should always sort to first slot in the index. + */ + +static hal_error_t fetch_pin_block(unsigned *b, ks_block_t **block) +{ + if (block == NULL) + return HAL_ERROR_IMPOSSIBLE; + + hal_error_t err; + int hint = 0; + unsigned b_; + + if (b == NULL) + b = &b_; + + if ((err = hal_ks_index_find(hal_ks_token, &pin_uuid, b, &hint)) != HAL_OK || + (err = ks_token_read_cached(*b, block)) != HAL_OK) + return err; + + cache_mark_used(*block, *b); + + if (block_get_type(*block) != HAL_KS_BLOCK_TYPE_PIN) + return HAL_ERROR_IMPOSSIBLE; + + return HAL_OK; +} + +/* + * Update the PIN block. This block should always be present, but we + * have to do the zombie jamboree to make sure we write the new PIN + * block before destroying the old one. hint = 0 because we know that + * the all-zeros UUID should always sort to first slot in the index. + */ + +static hal_error_t update_pin_block(const unsigned b, + ks_block_t *block, + const flash_pin_block_t * const new_data) +{ + if (block == NULL || new_data == NULL || ks_token_get_type(block) != HAL_KS_BLOCK_TYPE_PIN) + return HAL_ERROR_IMPOSSIBLE; + + int hint = 0; + + block->pin = *new_data; + + return ks_token_update(b, block, &pin_uuid, &hint); +} + +/* + * Change a PIN. + */ + +hal_error_t hal_set_pin(const hal_user_t user, + const hal_ks_pin_t * const pin) +{ + if (pin == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + ks_block_t *block; + hal_error_t err; + unsigned b; + + hal_ks_lock(); + + if ((err = fetch_pin_block(&b, &block)) != HAL_OK) + goto done; + + flash_pin_block_t new_data = block->pin; + hal_ks_pin_t *dp, *bp; + + switch (user) { + case HAL_USER_WHEEL: bp = &new_data.wheel_pin; dp = &db->wheel_pin; break; + case HAL_USER_SO: bp = &new_data.so_pin; dp = &db->so_pin; break; + case HAL_USER_NORMAL: bp = &new_data.user_pin; dp = &db->user_pin; break; + default: err = HAL_ERROR_BAD_ARGUMENTS; goto done; + } + + const hal_ks_pin_t old_pin = *dp; + *dp = *bp = *pin; + + if ((err = update_pin_block(b, block, &new_data)) != HAL_OK) + *dp = old_pin; + + done: + hal_ks_unlock(); + return err; +} + +#if HAL_MKM_FLASH_BACKUP_KLUDGE + +/* + * Horrible insecure kludge in lieu of a battery for the MKM. + * + * API here is a little strange: all calls pass a length parameter, + * but any length other than the compiled in constant just returns an + * immediate error, there's no notion of buffer max length vs buffer + * used length, querying for the size of buffer really needed, or + * anything like that. + * + * We might want to rewrite this some day, if we don't replace it with + * a battery first. For now we just preserve the API as we found it + * while re-implementing it on top of the new keystore. + */ + +hal_error_t hal_mkm_flash_read_no_lock(uint8_t *buf, const size_t len) +{ + if (buf != NULL && len != KEK_LENGTH) + return HAL_ERROR_MASTERKEY_BAD_LENGTH; + + ks_block_t *block; + hal_error_t err; + unsigned b; + + if ((err = fetch_pin_block(&b, &block)) != HAL_OK) + return err; + + if (block->pin.kek_set != FLASH_KEK_SET) + return HAL_ERROR_MASTERKEY_NOT_SET; + + if (buf != NULL) + memcpy(buf, block->pin.kek, len); + + return HAL_OK; +} + +hal_error_t hal_mkm_flash_read(uint8_t *buf, const size_t len) +{ + hal_ks_lock(); + const hal_error_t err = hal_mkm_flash_read_no_lock(buf, len); + hal_ks_unlock(); + return err; +} + +hal_error_t hal_mkm_flash_write(const uint8_t * const buf, const size_t len) +{ + if (buf == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (len != KEK_LENGTH) + return HAL_ERROR_MASTERKEY_BAD_LENGTH; + + ks_block_t *block; + hal_error_t err; + unsigned b; + + hal_ks_lock(); + + if ((err = fetch_pin_block(&b, &block)) != HAL_OK) + goto done; + + flash_pin_block_t new_data = block->pin; + + new_data.kek_set = FLASH_KEK_SET; + memcpy(new_data.kek, buf, len); + + err = update_pin_block(b, block, &new_data); + + done: + hal_ks_unlock(); + return err; +} + +hal_error_t hal_mkm_flash_erase(const size_t len) +{ + if (len != KEK_LENGTH) + return HAL_ERROR_MASTERKEY_BAD_LENGTH; + + ks_block_t *block; + hal_error_t err; + unsigned b; + + hal_ks_lock(); + + if ((err = fetch_pin_block(&b, &block)) != HAL_OK) + goto done; + + flash_pin_block_t new_data = block->pin; + + new_data.kek_set = FLASH_KEK_SET; + memset(new_data.kek, 0, len); + + err = update_pin_block(b, block, &new_data); + + done: + hal_ks_unlock(); + return err; +} + +#endif /* HAL_MKM_FLASH_BACKUP_KLUDGE */ + + +/* + * Local variables: + * indent-tabs-mode: nil + * End: + */ diff --git a/ks_volatile.c b/ks_volatile.c index e9a0ef4..7e1a5f2 100644 --- a/ks_volatile.c +++ b/ks_volatile.c @@ -51,27 +51,38 @@ #define KS_VOLATILE_CACHE_SIZE 4 #endif +/* + * Keystore database. + */ + typedef struct { hal_client_handle_t client; hal_session_handle_t session; hal_ks_block_t block; -} volatile_key_t; +} ks_volatile_key_t; -static struct db { +typedef struct { hal_ks_t ks; /* Must be first */ - volatile_key_t *keys; -} db; + ks_volatile_key_t *keys; +} ks_volatile_db_t; + +/* + * This is a bit silly, but it's safe enough, and it lets us avoid a + * nasty mess of forward references. + */ + +#define db ((ks_volatile_db_t * const) hal_ks_volatile) /* * Read a block. CRC probably not necessary for RAM. */ -static hal_error_t block_read(hal_k_t *ks, const unsigned blockno, ks_block_t *block) +static hal_error_t ks_volatile_read(hal_k_t *ks, const unsigned blockno, ks_block_t *block) { - if (ks != &db.ks || db.keys == NULL || block == NULL || blockno >= ks->size) + if (ks != hal_ks_volatile || db->keys == NULL || block == NULL || blockno >= ks->size) return HAL_ERROR_IMPOSSIBLE; - memcpy(block, &db.keys[blockno].block, sizeof(*block)); + memcpy(block, &db->keys[blockno].block, sizeof(*block)); return HAL_OK; } @@ -80,12 +91,12 @@ static hal_error_t block_read(hal_k_t *ks, const unsigned blockno, ks_block_t *b * Convert a live block into a tombstone. */ -static hal_error_t block_deprecate(hal_k_t *ks, const unsigned blockno) +static hal_error_t ks_volatile_deprecate(hal_k_t *ks, const unsigned blockno) { - if (ks != &db.ks || db.keys == NULL || blockno >= ks->size) + if (ks != hal_ks_volatile || db->keys == NULL || blockno >= ks->size) return HAL_ERROR_IMPOSSIBLE; - db.keys[blockno].block.header->block_status = BLOCK_STATUS_TOMBSTONE; + db->keys[blockno].block.header->block_status = BLOCK_STATUS_TOMBSTONE; return HAL_OK; } @@ -94,14 +105,14 @@ static hal_error_t block_deprecate(hal_k_t *ks, const unsigned blockno) * Zero (not erase) a flash block. */ -static hal_error_t block_zero(hal_k_t *ks, const unsigned blockno) +static hal_error_t ks_volatile_zero(hal_k_t *ks, const unsigned blockno) { - if (ks != &db.ks || db.keys == NULL || blockno >= ks->size) + if (ks != hal_ks_volatile || db->keys == NULL || blockno >= ks->size) return HAL_ERROR_IMPOSSIBLE; - memset(db.keys[blockno].block, 0x00, sizeof(db.keys[blockno].block)); - db.keys[blockno].client.handle = HAL_HANDLE_NONE; - db.keys[blockno].session.handle = HAL_HANDLE_NONE; + memset(db->keys[blockno].block, 0x00, sizeof(db->keys[blockno].block)); + db->keys[blockno].client.handle = HAL_HANDLE_NONE; + db->keys[blockno].session.handle = HAL_HANDLE_NONE; return HAL_OK; } @@ -110,14 +121,14 @@ static hal_error_t block_zero(hal_k_t *ks, const unsigned blockno) * Erase a flash block. */ -static hal_error_t block_erase(hal_k_t *ks, const unsigned blockno) +static hal_error_t ks_volatile_erase(hal_k_t *ks, const unsigned blockno) { - if (ks != &db.ks || db.keys == NULL || blockno >= ks->size) + if (ks != hal_ks_volatile || db->keys == NULL || blockno >= ks->size) return HAL_ERROR_IMPOSSIBLE; - memset(db.keys[blockno].block, 0xFF, sizeof(db.keys[blockno].block)); - db.keys[blockno].client.handle = HAL_HANDLE_NONE; - db.keys[blockno].session.handle = HAL_HANDLE_NONE; + memset(db->keys[blockno].block, 0xFF, sizeof(db->keys[blockno].block)); + db->keys[blockno].client.handle = HAL_HANDLE_NONE; + db->keys[blockno].session.handle = HAL_HANDLE_NONE; return HAL_OK; } @@ -126,12 +137,12 @@ static hal_error_t block_erase(hal_k_t *ks, const unsigned blockno) * Write a flash block. CRC probably not necessary for RAM. */ -static hal_error_t block_write(hal_k_t *ks, const unsigned blockno, ks_block_t *block) +static hal_error_t ks_volatile_write(hal_k_t *ks, const unsigned blockno, ks_block_t *block) { - if (ks != &db.ks || db.keys == NULL || block == NULL || blockno >= ks->size) + if (ks != hal_ks_volatile || db->keys == NULL || block == NULL || blockno >= ks->size) return HAL_ERROR_IMPOSSIBLE; - memcpy(&db.keys[blockno].block, block, sizeof(*block)); + memcpy(&db->keys[blockno].block, block, sizeof(*block)); return HAL_OK; } @@ -140,16 +151,16 @@ static hal_error_t block_write(hal_k_t *ks, const unsigned blockno, ks_block_t * * Set key ownership. */ -static hal_error_t block_set_owner(hal_ks_t *ks, - const unsigned blockno, - const hal_client_handle_t client, - const hal_session_handle_t session) +static hal_error_t ks_volatile_set_owner(hal_ks_t *ks, + const unsigned blockno, + const hal_client_handle_t client, + const hal_session_handle_t session) { - if (ks != &db.ks || db.keys == NULL || blockno >= ks->size) + if (ks != hal_ks_volatile || db->keys == NULL || blockno >= ks->size) return HAL_ERROR_IMPOSSIBLE; - db.keys[blockno].client = client; - db.keys[blockno].session = session; + db->keys[blockno].client = client; + db->keys[blockno].session = session; return HAL_OK; } @@ -158,16 +169,16 @@ static hal_error_t block_set_owner(hal_ks_t *ks, * Test key ownership. */ -static hal_error_t block_test_owner(hal_ks_t *ks, const - unsigned blockno, - const hal_client_handle_t client, - const hal_session_handle_t session) +static hal_error_t ks_volatile_test_owner(hal_ks_t *ks, const + unsigned blockno, + const hal_client_handle_t client, + const hal_session_handle_t session) { - if (ks != &db.ks || db.keys == NULL || blockno >= ks->size) + if (ks != hal_ks_volatile || db->keys == NULL || blockno >= ks->size) return HAL_ERROR_IMPOSSIBLE; - if (db.keys[blockno].client.handle == client.handle && - db.keys[blockno].session.handle == session.handle) + if (db->keys[blockno].client.handle == client.handle && + db->keys[blockno].session.handle == session.handle) return HAL_OK; else return HAL_ERROR_KEY_NOT_FOUND; @@ -177,37 +188,28 @@ static hal_error_t block_test_owner(hal_ks_t *ks, const * Initialize keystore. */ -static const hal_ks_driver_t hal_ks_volatile_driver[1] = {{ - .read = block_read, - .write = block_write, - .deprecate = block_deprecate, - .zero = block_zero, - .erase = block_erase, - .erase_maybe = block_erase, /* sic */ - .set_owner = block_set_owner, - .test_owner = block_test_owner -}}; - - hal_error_t hal_ks_volatile_init(const int alloc) +static hal_error_t ks_volatile_init(hal_ks_t *ks, const int alloc) { + if (ks != hal_ks_volatile) + return HAL_ERROR_IMPOSSIBLE; + hal_error_t err = HAL_OK; + void *mem = NULL; hal_ks_lock(); + if (alloc) { + if ((err = hal_ks_alloc_common(ks, STATIC_KS_VOLATILE_SLOTS, KS_VOLATILE_CACHE_SIZE, + &mem, sizeof(*db->keys) * STATIC_KS_VOLATILE_SLOTS)) != HAL_OK) + goto done; + db->keys = mem; + } - if (alloc && (err = hal_ks_alloc_common(&db.ks, STATIC_KS_VOLATILE_SLOTS, KS_VOLATILE_CACHE_SIZE)) != HAL_OK) - goto done; - - if ((err = hal_ks_init_common(&db.ks, hal_ks_volatile_driver)) != HAL_OK) - goto done; - - if (alloc && (db.keys = hal_allocate_static_memory(sizeof(*db.keys) * db.ks.size)) == NULL) { - err = HAL_ERROR_ALLOCATION_FAILURE; + if ((err = hal_ks_init_common(ks)) != HAL_OK) goto done; - } - for (unsigned b = 0; b < db.ks.size; i++) - if ((err = block_erase(&db.ks, b)) != HAL_OK) + for (unsigned b = 0; b < db->ks.size; i++) + if ((err = block_erase(ks, b)) != HAL_OK) goto done; err = HAL_OK; @@ -217,6 +219,27 @@ static const hal_ks_driver_t hal_ks_volatile_driver[1] = {{ return err; } +/* + * Dispatch vector and keystore definition, now that we've defined all + * the driver functions. + */ + +static const hal_ks_driver_t hal_ks_volatile_driver = { + .init = ks_volatile_init, + .read = ks_volatile_read, + .write = ks_volatile_write, + .deprecate = ks_volatile_deprecate, + .zero = ks_volatile_zero, + .erase = ks_volatile_erase, + .erase_maybe = ks_volatile_erase, /* sic */ + .set_owner = ks_volatile_set_owner, + .test_owner = ks_volatile_test_owner +}; + +static ks_volatile_db_t _db = { .ks.driver = &ks_volatile_driver }; + +hal_ks_t * const hal_ks_volatile = &_db.ks; + /* * Local variables: * indent-tabs-mode: nil -- cgit v1.2.3 From 2caa6c72640877abc5f3572c4d926a23ff672ab1 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Sun, 28 May 2017 16:11:25 -0400 Subject: Almost compiles. Need to refactor init sequence slightly (again), this time to humor the bootloader, which has its own special read-only view of the PIN block in the token keystore. --- cryptech/libhal.py | 5 +- hal.h | 2 +- hal_internal.h | 44 +++++++++++- ks.c | 141 ++++++++++++++++++------------------ ks.h | 205 +++++++++++++++++++++++++++++++++++++++-------------- ks_index.c | 2 +- ks_token.c | 107 ++++++++++++++-------------- ks_volatile.c | 30 ++++---- rpc_pkey.c | 118 ++++++------------------------ rpc_server.c | 10 ++- 10 files changed, 359 insertions(+), 305 deletions(-) diff --git a/cryptech/libhal.py b/cryptech/libhal.py index 0c6b3f6..39fa826 100644 --- a/cryptech/libhal.py +++ b/cryptech/libhal.py @@ -117,10 +117,7 @@ HALError.define(HAL_ERROR_KEYSTORE_LOST_DATA = "Keystore appears to have HALError.define(HAL_ERROR_BAD_ATTRIBUTE_LENGTH = "Bad attribute length") HALError.define(HAL_ERROR_ATTRIBUTE_NOT_FOUND = "Attribute not found") HALError.define(HAL_ERROR_NO_KEY_INDEX_SLOTS = "No key index slots available") -HALError.define(HAL_ERROR_KSI_INDEX_UUID_MISORDERED = "Key index UUID misordered") -HALError.define(HAL_ERROR_KSI_INDEX_CHUNK_ORPHANED = "Key index chunk orphaned") -HALError.define(HAL_ERROR_KSI_INDEX_CHUNK_MISSING = "Key index chunk missing") -HALError.define(HAL_ERROR_KSI_INDEX_CHUNK_OVERLAPS = "Key index chunk overlaps") +HALError.define(HAL_ERROR_KS_INDEX_UUID_MISORDERED = "Key index UUID misordered") HALError.define(HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE = "Wrong block type in keystore") HALError.define(HAL_ERROR_RPC_PROTOCOL_ERROR = "RPC protocol error") HALError.define(HAL_ERROR_NOT_IMPLEMENTED = "Not implemented") diff --git a/hal.h b/hal.h index a3ea1dd..47ebe25 100644 --- a/hal.h +++ b/hal.h @@ -157,7 +157,7 @@ DEFINE_HAL_ERROR(HAL_ERROR_BAD_ATTRIBUTE_LENGTH, "Bad attribute length") \ DEFINE_HAL_ERROR(HAL_ERROR_ATTRIBUTE_NOT_FOUND, "Attribute not found") \ DEFINE_HAL_ERROR(HAL_ERROR_NO_KEY_INDEX_SLOTS, "No key index slots available") \ - DEFINE_HAL_ERROR(HAL_ERROR_KSI_INDEX_UUID_MISORDERED, "Key index UUID misordered") \ + DEFINE_HAL_ERROR(HAL_ERROR_KS_INDEX_UUID_MISORDERED, "Key index UUID misordered") \ DEFINE_HAL_ERROR(HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE, "Wrong block type in keystore") \ DEFINE_HAL_ERROR(HAL_ERROR_RPC_PROTOCOL_ERROR, "RPC protocol error") \ DEFINE_HAL_ERROR(HAL_ERROR_NOT_IMPLEMENTED, "Not implemented") \ diff --git a/hal_internal.h b/hal_internal.h index 667c5a4..e998ae3 100644 --- a/hal_internal.h +++ b/hal_internal.h @@ -397,8 +397,6 @@ extern hal_error_t hal_get_pin(const hal_user_t user, extern hal_error_t hal_set_pin(const hal_user_t user, const hal_ks_pin_t * const pin); -extern void hal_ks_init_read_only_pins_only(void); - /* * Master key memory (MKM) and key-encryption-key (KEK). * @@ -482,6 +480,48 @@ typedef struct hal_ks hal_ks_t; extern hal_ks_t * const hal_ks_token; extern hal_ks_t * const hal_ks_volatile; +extern hal_error_t hal_ks_init(hal_ks_t *ks, + const int alloc); + +extern void hal_ks_init_read_only_pins_only(void); + +extern hal_error_t hal_ks_store(hal_ks_t *ks, + hal_pkey_slot_t *slot, + const uint8_t * const der, const size_t der_len); + +extern hal_error_t hal_ks_fetch(hal_ks_t *ks, + hal_pkey_slot_t *slot, + uint8_t *der, size_t *der_len, const size_t der_max); + +extern hal_error_t hal_ks_delete(hal_ks_t *ks, + hal_pkey_slot_t *slot); + +extern hal_error_t hal_ks_match(hal_ks_t *ks, + const hal_client_handle_t client, + const hal_session_handle_t session, + const hal_key_type_t type, + const hal_curve_name_t curve, + const hal_key_flags_t mask, + 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); + +extern hal_error_t hal_ks_set_attributes(hal_ks_t *ks, + hal_pkey_slot_t *slot, + const hal_pkey_attribute_t *attributes, + const unsigned attributes_len); + +extern hal_error_t hal_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); + /* * RPC lowest-level send and receive routines. These are blocking, and * transport-specific (sockets, USB). diff --git a/ks.c b/ks.c index 3d1ae61..a0a4de7 100644 --- a/ks.c +++ b/ks.c @@ -40,18 +40,11 @@ #include "ks.h" /* - * Type safe casts. + * PIN block gets the all-zeros UUID, which will never be returned by + * the UUID generation code (by definition -- it's not a version 4 UUID). */ -static inline ks_block_type_t block_get_type(const ks_block_t * const block) -{ - return block == NULL ? HAL_KS_BLOCK_TYPE_UNKNOWN : (ks_block_type_t) block->header.block_type; -} - -static inline ks_block_status_t block_get_status(const ks_block_t * const block) -{ - return block == NULL ? HAL_KS_BLOCK_STATUS_UNKNOWN : (ks_block_status_t) block->header.block_status; -} +const hal_uuid_t hal_ks_pin_uuid = {{0}}; /* * Pick unused or least-recently-used slot in our in-memory cache. @@ -61,7 +54,7 @@ static inline ks_block_status_t block_get_status(const ks_block_t * const block) * result, leave the lru values alone and the right thing will happen. */ -static inline hal_ks_block_t *cache_pick_lru(hal_ks_t *ks) +hal_ks_block_t *hal_ks_cache_pick_lru(hal_ks_t *ks) { uint32_t best_delta = 0; int best_index = 0; @@ -87,7 +80,7 @@ static inline hal_ks_block_t *cache_pick_lru(hal_ks_t *ks) * Find a block in our in-memory cache; return block or NULL if not present. */ -static inline hal_ks_block_t *cache_find_block(const hal_ks_t * const ks, const unsigned blockno) +hal_ks_block_t *hal_ks_cache_find_block(const hal_ks_t * const ks, const unsigned blockno) { for (int i = 0; i < ks->cache_size; i++) if (ks->cache[i].blockno == blockno) @@ -99,7 +92,7 @@ static inline hal_ks_block_t *cache_find_block(const hal_ks_t * const ks, const * Mark a block in our in-memory cache as being in current use. */ -static inline void cache_mark_used(hal_ks_t *ks, const hal_ks_block_t * const block, const unsigned blockno) +void hal_ks_cache_mark_used(hal_ks_t *ks, const hal_ks_block_t * const block, const unsigned blockno) { for (int i = 0; i < ks->cache_size; i++) { if (&ks->cache[i].block == block) { @@ -114,10 +107,10 @@ static inline void cache_mark_used(hal_ks_t *ks, const hal_ks_block_t * const bl * Release a block from the in-memory cache. */ -static inline void cache_release(hal_ks_t *ks, const hal_ks_block_t * const block) +void hal_ks_cache_release(hal_ks_t *ks, const hal_ks_block_t * const block) { if (block != NULL) - cache_mark_used(block, ~0); + hal_ks_cache_mark_used(ks, block, ~0); } /* @@ -128,7 +121,7 @@ static inline void cache_release(hal_ks_t *ks, const hal_ks_block_t * const bloc * shouldn't be included in the CRC. */ -static hal_crc32_t calculate_block_crc(const hal_ks_block_t * const block) +hal_crc32_t hal_ks_block_calculate_crc(const hal_ks_block_t * const block) { hal_crc32_t crc = hal_crc32_init(); @@ -151,15 +144,15 @@ static hal_crc32_t calculate_block_crc(const hal_ks_block_t * const block) * perform a hal_ks_match() operation. */ -static hal_error_t block_read_cached(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t **block) +hal_error_t hal_ks_block_read_cached(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t **block) { if (block == NULL) return HAL_ERROR_IMPOSSIBLE; - if ((*block = cache_find_block(ks, blockno)) != NULL) + if ((*block = hal_ks_cache_find_block(ks, blockno)) != NULL) return HAL_OK; - if ((*block = cache_pick_lru(ks)) == NULL) + if ((*block = hal_ks_cache_pick_lru(ks)) == NULL) return HAL_ERROR_IMPOSSIBLE; return hal_ks_block_read(ks, blockno, *block); @@ -169,11 +162,11 @@ static hal_error_t block_read_cached(hal_ks_t *ks, const unsigned blockno, hal_k * Update one block, including zombie jamboree. */ -static hal_error_t block_update(hal_ks_t *ks, - const unsigned b1, - hal_ks_block_t *block, - const hal_uuid_t * const uuid, - int *hint) +hal_error_t hal_ks_block_update(hal_ks_t *ks, + const unsigned b1, + hal_ks_block_t *block, + const hal_uuid_t * const uuid, + int *hint) { if (block == NULL) return HAL_ERROR_IMPOSSIBLE; @@ -181,7 +174,7 @@ static hal_error_t block_update(hal_ks_t *ks, if (ks->used == ks->size) return HAL_ERROR_NO_KEY_INDEX_SLOTS; - cache_release(block); + hal_ks_cache_release(ks, block); hal_error_t err; unsigned b2; @@ -192,7 +185,7 @@ static hal_error_t block_update(hal_ks_t *ks, (err = hal_ks_block_zero(ks, b1)) != HAL_OK) return err; - cache_mark_used(ks, block, b2); + hal_ks_cache_mark_used(ks, block, b2); /* * Erase the first block in the free list. In case of restart, this @@ -209,6 +202,14 @@ static hal_error_t block_update(hal_ks_t *ks, * recover from unclean shutdown. */ +hal_error_t hal_ks_init(hal_ks_t *ks, const int alloc) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->init == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->init(ks, alloc); +} + static inline void *gnaw(uint8_t **mem, size_t *len, const size_t size) { if (mem == NULL || *mem == NULL || len == NULL || size > *len) @@ -219,8 +220,6 @@ static inline void *gnaw(uint8_t **mem, size_t *len, const size_t size) return ret; } -#warning Call hal_ks_alloc_common() and hal_ks_init_common() while holding hal_ks_lock(); ! - hal_error_t hal_ks_alloc_common(hal_ks_t *ks, const unsigned ks_blocks, const unsigned cache_blocks, @@ -279,7 +278,7 @@ hal_error_t hal_ks_init_common(hal_ks_t *ks) hal_ks_block_type_t block_types[ks->size]; hal_ks_block_status_t block_status[ks->size]; - hal_ks_block_t *block = cache_pick_lru(ks); + hal_ks_block_t *block = hal_ks_cache_pick_lru(ks); int first_erased = -1; hal_error_t err; uint16_t n = 0; @@ -299,28 +298,28 @@ hal_error_t hal_ks_init_common(hal_ks_t *ks) err = hal_ks_block_read(ks, i, block); if (err == HAL_ERROR_KEYSTORE_BAD_CRC || err == HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE) - block_types[i] = BLOCK_TYPE_UNKNOWN; + block_types[i] = HAL_KS_BLOCK_TYPE_UNKNOWN; else if (err == HAL_OK) - block_types[i] = block_get_type(block); + block_types[i] = hal_ks_block_get_type(block); else return err; switch (block_types[i]) { - case BLOCK_TYPE_KEY: - case BLOCK_TYPE_PIN: - block_status[i] = block_get_status(block); + case HAL_KS_BLOCK_TYPE_KEY: + case HAL_KS_BLOCK_TYPE_PIN: + block_status[i] = hal_ks_block_get_status(block); break; default: - block_status[i] = BLOCK_STATUS_UNKNOWN; + block_status[i] = HAL_KS_BLOCK_STATUS_UNKNOWN; } /* * First erased block we see is head of the free list. */ - if (block_types[i] == BLOCK_TYPE_ERASED && first_erased < 0) + if (block_types[i] == HAL_KS_BLOCK_TYPE_ERASED && first_erased < 0) first_erased = i; /* @@ -332,8 +331,8 @@ hal_error_t hal_ks_init_common(hal_ks_t *ks) const hal_uuid_t *uuid = NULL; switch (block_types[i]) { - case BLOCK_TYPE_KEY: uuid = &block->key.name; break; - case BLOCK_TYPE_PIN: uuid = &pin_uuid; break; + case HAL_KS_BLOCK_TYPE_KEY: uuid = &block->key.name; break; + case HAL_KS_BLOCK_TYPE_PIN: uuid = &hal_ks_pin_uuid; break; default: /* Keep GCC happy */ break; } @@ -359,22 +358,22 @@ hal_error_t hal_ks_init_common(hal_ks_t *ks) if (n < ks->size) for (int i = 0; i < ks->size; i++) - if (block_types[i] == BLOCK_TYPE_ERASED) + if (block_types[i] == HAL_KS_BLOCK_TYPE_ERASED) ks->index[n++] = i; if (n < ks->size) for (int i = first_erased; i < ks->size; i++) - if (block_types[i] == BLOCK_TYPE_ZEROED) + if (block_types[i] == HAL_KS_BLOCK_TYPE_ZEROED) ks->index[n++] = i; if (n < ks->size) for (int i = 0; i < first_erased; i++) - if (block_types[i] == BLOCK_TYPE_ZEROED) + if (block_types[i] == HAL_KS_BLOCK_TYPE_ZEROED) ks->index[n++] = i; if (n < ks->size) for (int i = 0; i < ks->size; i++) - if (block_types[i] == BLOCK_TYPE_UNKNOWN) + if (block_types[i] == HAL_KS_BLOCK_TYPE_UNKNOWN) ks->index[n++] = i; if (ks->used > ks->size) @@ -396,7 +395,7 @@ hal_error_t hal_ks_init_common(hal_ks_t *ks) for (unsigned b_tomb = 0; b_tomb < ks->size; b_tomb++) { - if (block_status[b_tomb] != BLOCK_STATUS_TOMBSTONE) + if (block_status[b_tomb] != HAL_KS_BLOCK_STATUS_TOMBSTONE) continue; hal_uuid_t name = ks->names[b_tomb]; @@ -419,8 +418,8 @@ hal_error_t hal_ks_init_common(hal_ks_t *ks) const int matches_prev = where - 1 >= 0 && !hal_uuid_cmp(&name, &ks->names[ks->index[where - 1]]); if ((matches_prev && matches_next) || - (matches_prev && block_status[ks->index[b_tomb - 1]] != BLOCK_STATUS_LIVE) || - (matches_next && block_status[ks->index[b_tomb + 1]] != BLOCK_STATUS_LIVE)) + (matches_prev && block_status[ks->index[b_tomb - 1]] != HAL_KS_BLOCK_STATUS_LIVE) || + (matches_next && block_status[ks->index[b_tomb + 1]] != HAL_KS_BLOCK_STATUS_LIVE)) return HAL_ERROR_IMPOSSIBLE; if (matches_prev || matches_next) { @@ -432,17 +431,17 @@ hal_error_t hal_ks_init_common(hal_ks_t *ks) unsigned b_live; if ((err = hal_ks_block_read(ks, b_tomb, block)) != HAL_OK) return err; - block->header.block_status = BLOCK_STATUS_LIVE; + block->header.block_status = HAL_KS_BLOCK_STATUS_LIVE; if ((err = hal_ks_index_replace(ks, &name, &b_live, &where)) != HAL_OK || (err = hal_ks_block_write(ks, b_live, block)) != HAL_OK) return err; - block_status[b_live] = BLOCK_STATUS_LIVE; + block_status[b_live] = HAL_KS_BLOCK_STATUS_LIVE; } if ((err = hal_ks_block_zero(ks, b_tomb)) != HAL_OK) return err; - block_types[ b_tomb] = BLOCK_TYPE_ZEROED; - block_status[b_tomb] = BLOCK_STATUS_UNKNOWN; + block_types[ b_tomb] = HAL_KS_BLOCK_TYPE_ZEROED; + block_status[b_tomb] = HAL_KS_BLOCK_STATUS_UNKNOWN; } /* @@ -494,7 +493,7 @@ static inline int acceptable_key_type(const hal_key_type_t type) * avoid revising this yet again. */ -static inline hal_error_t key_visible(const hal_ks_t * const ks, +static inline hal_error_t key_visible(hal_ks_t * const ks, const hal_client_handle_t client, const hal_session_handle_t session, const unsigned blockno) @@ -507,7 +506,7 @@ static inline hal_error_t key_visible(const hal_ks_t * const ks, hal_error_t err; - if ((err = hal_ks_block_test_owner(ks, client, session)) != HAL_OK) + if ((err = hal_ks_block_test_owner(ks, blockno, client, session)) != HAL_OK) return err; err = hal_rpc_is_logged_in(client, HAL_USER_WHEEL); @@ -527,14 +526,14 @@ hal_error_t hal_ks_store(hal_ks_t *ks, hal_error_t err = HAL_OK; hal_ks_block_t *block; - flash_key_block_t *k; + hal_ks_key_block_t *k; uint8_t kek[KEK_LENGTH]; size_t kek_len; unsigned b; hal_ks_lock(); - if ((block = cache_pick_lru(ks)) == NULL) { + if ((block = hal_ks_cache_pick_lru(ks)) == NULL) { err = HAL_ERROR_IMPOSSIBLE; goto done; } @@ -544,18 +543,18 @@ hal_error_t hal_ks_store(hal_ks_t *ks, if ((err = hal_ks_index_add(ks, &slot->name, &b, &slot->hint)) != HAL_OK) goto done; - cache_mark_used(ks, block, b); + hal_ks_cache_mark_used(ks, block, b); memset(block, 0xFF, sizeof(*block)); - block->header.block_type = BLOCK_TYPE_KEY; - block->header.block_status = BLOCK_STATUS_LIVE; + block->header.block_type = HAL_KS_BLOCK_TYPE_KEY; + block->header.block_status = HAL_KS_BLOCK_STATUS_LIVE; k->name = slot->name; k->type = slot->type; k->curve = slot->curve; k->flags = slot->flags; - k->der_len = SIZEOF_FLASH_KEY_BLOCK_DER; + k->der_len = SIZEOF_KS_KEY_BLOCK_DER; k->attributes_len = 0; if (ks->used < ks->size) @@ -579,7 +578,7 @@ hal_error_t hal_ks_store(hal_ks_t *ks, goto done; memset(block, 0, sizeof(*block)); - cache_release(ks, block); + hal_ks_cache_release(ks, block); (void) hal_ks_index_delete(ks, &slot->name, NULL, &slot->hint); done: @@ -605,14 +604,14 @@ hal_error_t hal_ks_fetch(hal_ks_t *ks, (err = hal_ks_block_read_cached(ks, b, &block)) != HAL_OK) goto done; - if (block_get_type(block) != BLOCK_TYPE_KEY) { + if (hal_ks_block_get_type(block) != HAL_KS_BLOCK_TYPE_KEY) { err = HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE; /* HAL_ERROR_KEY_NOT_FOUND */ goto done; } - cache_mark_used(ks, block, b); + hal_ks_cache_mark_used(ks, block, b); - flash_key_block_t *k = &block->key; + hal_ks_key_block_t *k = &block->key; slot->type = k->type; slot->curve = k->curve; @@ -658,7 +657,7 @@ hal_error_t hal_ks_delete(hal_ks_t *ks, (err = key_visible(ks, slot->client_handle, slot->session_handle, b)) != HAL_OK) goto done; - cache_release(ks, cache_find_block(ks, b)); + hal_ks_cache_release(ks, hal_ks_cache_find_block(ks, b)); if ((err = hal_ks_block_zero(ks, b)) != HAL_OK) goto done; @@ -678,11 +677,11 @@ static inline hal_error_t locate_attributes(hal_ks_block_t *block, return HAL_ERROR_IMPOSSIBLE; - if (block_get_type(block) != BLOCK_TYPE_KEY) + if (hal_ks_block_get_type(block) != HAL_KS_BLOCK_TYPE_KEY) return HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE; *attrs_len = &block->key.attributes_len; *bytes = block->key.der + block->key.der_len; - *bytes_len = SIZEOF_FLASH_KEY_BLOCK_DER - block->key.der_len; + *bytes_len = SIZEOF_KS_KEY_BLOCK_DER - block->key.der_len; return HAL_OK; } @@ -802,11 +801,11 @@ hal_error_t hal_ks_set_attributes(hal_ks_t *ks, { if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || - (err = key_visibile(ks, slot->client_handle, slot->session_handle, b)) != HAL_OK || + (err = key_visible(ks, slot->client_handle, slot->session_handle, b)) != HAL_OK || (err = hal_ks_block_read_cached(ks, b, &block)) != HAL_OK) goto done; - cache_mark_used(ks, block, b); + hal_ks_cache_mark_used(ks, block, b); uint8_t *bytes = NULL; size_t bytes_len = 0; @@ -832,9 +831,9 @@ hal_error_t hal_ks_set_attributes(hal_ks_t *ks, attributes[i].length); if (err == HAL_OK) - err = block_update(ks, b, block, &slot->name, &slot->hint); + err = hal_ks_block_update(ks, b, block, &slot->name, &slot->hint); else - cache_release(ks, block); + hal_ks_cache_release(ks, block); } done: @@ -868,11 +867,11 @@ hal_error_t hal_ks_get_attributes(hal_ks_t *ks, { if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || - (err = key_visibile(ks, slot->client_handle, slot->session_handle, b)) != HAL_OK || - (err = hal_ks_block_read_cached(ks, b, &block)) != HAL_OK) + (err = key_visible(ks, slot->client_handle, slot->session_handle, b)) != HAL_OK || + (err = hal_ks_block_read_cached(ks, b, &block)) != HAL_OK) goto done; - cache_mark_used(ks, block, b); + hal_ks_cache_mark_used(ks, block, b); uint8_t *bytes = NULL; size_t bytes_len = 0; diff --git a/ks.h b/ks.h index 29965cc..6db0bd7 100644 --- a/ks.h +++ b/ks.h @@ -46,9 +46,16 @@ */ #ifndef HAL_KS_BLOCK_SIZE -#define HAL_KS_BLOCK_SIZE (KEYSTORE_SUBSECTOR_SIZE * 1) +#define HAL_KS_BLOCK_SIZE (4096) #endif +/* + * PIN block gets the all-zeros UUID, which will never be returned by + * the UUID generation code (by definition -- it's not a version 4 UUID). + */ + +const hal_uuid_t hal_ks_pin_uuid; + /* * Known block states. * @@ -94,7 +101,7 @@ typedef struct { */ typedef struct { - hal_ks_block_header_t header; + hal_ks_block_header_t header; hal_uuid_t name; hal_key_type_t type; hal_curve_name_t curve; @@ -102,10 +109,10 @@ typedef struct { size_t der_len; unsigned attributes_len; uint8_t der[]; /* Must be last field -- C99 "flexible array member" */ -} hal_ks_blockkey_block_t; +} hal_ks_key_block_t; -#define SIZEOF_KS_BLOCKKEY_BLOCK_DER \ - (HAL_KS_BLOCK_SIZE - offsetof(hal_ks_blockkey_block_t, der)) +#define SIZEOF_KS_KEY_BLOCK_DER \ + (HAL_KS_BLOCK_SIZE - offsetof(hal_ks_key_block_t, der)) /* * PIN block. Also includes space for backing up the KEK when @@ -113,7 +120,7 @@ typedef struct { */ typedef struct { - hal_ks_block_header_t header; + hal_ks_block_header_t header; hal_ks_pin_t wheel_pin; hal_ks_pin_t so_pin; hal_ks_pin_t user_pin; @@ -121,7 +128,7 @@ typedef struct { uint32_t kek_set; uint8_t kek[KEK_LENGTH]; #endif -} hal_ks_blockpin_block_t; +} hal_ks_pin_block_t; #define FLASH_KEK_SET 0x33333333 @@ -130,10 +137,10 @@ typedef struct { */ typedef union { - uint8_t bytes[HAL_KS_BLOCK_SIZE]; + uint8_t bytes[HAL_KS_BLOCK_SIZE]; hal_ks_block_header_t header; - hal_ks_blockkey_block_t key; - hal_ks_blockpin_block_t pin; + hal_ks_key_block_t key; + hal_ks_pin_block_t pin; } hal_ks_block_t; /* @@ -143,7 +150,7 @@ typedef union { typedef struct { unsigned blockno; unsigned lru; - hal_ks_block_t block; + hal_ks_block_t block; } hal_ks_cache_block_t; /* @@ -201,50 +208,112 @@ struct hal_ks { }; /* - * Keystore driver. This is just a dispatch vector for low-level - * keystore operations, and the code is very repetitive. We opt for - * expressing this in a terse form via C macros over expressing it - * as huge chunks of repetitive code: both are difficult to read, but - * the terse form has the advantage of fitting in a single screen. - * The KS_DRIVER_METHODS macro is the protein, the rest is just the - * machinery to expand the method definitions into a struct of typed - * function pointers and a set of static inline wrapper functions. + * Keystore driver. */ -#define KS_DRIVER_END_LIST -#define KS_DRIVER_METHODS \ - KS_DRIVER_METHOD(init, hal_ks_t *ks, const int alloc) \ - KS_DRIVER_METHOD(read, hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block) \ - KS_DRIVER_METHOD(write, hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block) \ - KS_DRIVER_METHOD(deprecate, hal_ks_t *ks, const unsigned blockno) \ - KS_DRIVER_METHOD(zero, hal_ks_t *ks, const unsigned blockno) \ - KS_DRIVER_METHOD(erase, hal_ks_t *ks, const unsigned blockno) \ - KS_DRIVER_METHOD(erase_maybe, hal_ks_t *ks, const unsigned blockno) \ - KS_DRIVER_METHOD(set_owner, hal_ks_t *ks, const unsigned blockno, \ - const hal_client_handle_t client, const hal_session_handle_t session) \ - KS_DRIVER_METHOD(test_owner, hal_ks_t *ks, const unsigned blockno, \ - const hal_client_handle_t client, const hal_session_handle_t session) \ - KS_DRIVER_END_LIST - -#define KS_DRIVER_METHOD(_name_, ...) hal_error_t (*_name_)(__VA_ARGS__); -struct hal_ks_driver { KS_DRIVER_METHODS }; -#undef KS_DRIVER_METHOD - -#define KS_DRIVER_METHOD(_name_, ...) \ - static inline hal_error_t hal_ks_block_##_name_(__VA_ARGS__) \ - { \ - return \ - ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : \ - ks->driver->_name_ == NULL ? HAL_ERROR_NOT_IMPLEMENTED : \ - ks->driver->_name_(__VA_ARGS__); \ - } -KS_DRIVER_METHODS -#undef KS_DRIVER_METHOD - -#undef KS_DRIVER_METHODS -#undef KS_DRIVER_END_LIST +struct hal_ks_driver { + hal_error_t (*init) (hal_ks_t *ks, const int alloc); + hal_error_t (*read) (hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block); + hal_error_t (*write) (hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block); + hal_error_t (*deprecate) (hal_ks_t *ks, const unsigned blockno); + hal_error_t (*zero) (hal_ks_t *ks, const unsigned blockno); + hal_error_t (*erase) (hal_ks_t *ks, const unsigned blockno); + hal_error_t (*erase_maybe) (hal_ks_t *ks, const unsigned blockno); + hal_error_t (*set_owner) (hal_ks_t *ks, const unsigned blockno, + const hal_client_handle_t client, const hal_session_handle_t session); + hal_error_t (*test_owner) (hal_ks_t *ks, const unsigned blockno, + const hal_client_handle_t client, const hal_session_handle_t session); +}; -#endif /* _KS_H_ */ +/* + * Wrappers around keystore driver methods. + * + * hal_ks_init() is missing here because we expose it to the rest of libhal. + */ + +static inline hal_error_t hal_ks_block_read(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->read == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->read(ks, blockno, block); +} + +static inline hal_error_t hal_ks_block_write(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->write == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->write(ks, blockno, block); +} + +static inline hal_error_t hal_ks_block_deprecate(hal_ks_t *ks, const unsigned blockno) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->deprecate == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->deprecate(ks, blockno); +} + +static inline hal_error_t hal_ks_block_zero(hal_ks_t *ks, const unsigned blockno) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->zero == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->zero(ks, blockno); +} + +static inline hal_error_t hal_ks_block_erase(hal_ks_t *ks, const unsigned blockno) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->erase == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->erase(ks, blockno); +} + +static inline hal_error_t hal_ks_block_erase_maybe(hal_ks_t *ks, const unsigned blockno) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->erase_maybe == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->erase_maybe(ks, blockno); +} + +static inline hal_error_t hal_ks_block_set_owner(hal_ks_t *ks, const unsigned blockno, + const hal_client_handle_t client, + const hal_session_handle_t session) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->set_owner == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->set_owner(ks, blockno, client, session); +} + +static inline hal_error_t hal_ks_block_test_owner(hal_ks_t *ks, const unsigned blockno, + const hal_client_handle_t client, + const hal_session_handle_t session) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->test_owner == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->test_owner(ks, blockno, client, session); +} + +/* + * Type safe casts. + */ + +static inline hal_ks_block_type_t hal_ks_block_get_type(const hal_ks_block_t * const block) +{ + return block == NULL ? HAL_KS_BLOCK_TYPE_UNKNOWN : + (hal_ks_block_type_t) block->header.block_type; +} + +static inline hal_ks_block_status_t hal_ks_block_get_status(const hal_ks_block_t * const block) +{ + return block == NULL ? HAL_KS_BLOCK_STATUS_UNKNOWN : + (hal_ks_block_status_t) block->header.block_status; +} /* * Keystore utilities. Some or all of these may end up static within ks.c. @@ -252,10 +321,13 @@ KS_DRIVER_METHODS extern hal_error_t hal_ks_alloc_common(hal_ks_t *ks, const unsigned ks_blocks, - const unsigned cache_blocks); + const unsigned cache_blocks, + void **extra, + const size_t extra_len); -extern hal_error_t hal_ks_init_common(hal_ks_t *ks, - const hal_ks_driver_t * const driver); +extern hal_error_t hal_ks_init_common(hal_ks_t *ks); + +extern hal_crc32_t hal_ks_block_calculate_crc(const hal_ks_block_t * const block); extern hal_error_t hal_ks_index_heapsort(hal_ks_t *ks); @@ -304,6 +376,29 @@ extern hal_error_t hal_ks_attribute_insert(uint8_t *bytes, const size_t bytes_le const uint8_t * const value, const size_t value_len); +extern hal_ks_block_t *hal_ks_cache_pick_lru(hal_ks_t *ks); + +extern hal_ks_block_t *hal_ks_cache_find_block(const hal_ks_t * const ks, + const unsigned blockno); + +extern void hal_ks_cache_mark_used(hal_ks_t *ks, + const hal_ks_block_t * const block, + const unsigned blockno); + +extern void hal_ks_cache_release(hal_ks_t *ks, + const hal_ks_block_t * const block); + +extern hal_error_t hal_ks_block_read_cached(hal_ks_t *ks, + const unsigned blockno, + hal_ks_block_t **block); + +extern hal_error_t hal_ks_block_update(hal_ks_t *ks, + const unsigned b1, + hal_ks_block_t *block, + const hal_uuid_t * const uuid, + int *hint); + +#endif /* _KS_H_ */ /* * Local variables: diff --git a/ks_index.c b/ks_index.c index ed22cfb..644aecf 100644 --- a/ks_index.c +++ b/ks_index.c @@ -114,7 +114,7 @@ static inline hal_error_t ks_heapsift(hal_ks_t *ks, int parent, const int end) } } -hal_ks_error_t hal_ks_index_heapsort(hal_ks_t *ks) +hal_error_t hal_ks_index_heapsort(hal_ks_t *ks) { if (ks == NULL || ks->index == NULL || ks->names == NULL) return HAL_ERROR_IMPOSSIBLE; diff --git a/ks_token.c b/ks_token.c index cc25ca5..c2ebee2 100644 --- a/ks_token.c +++ b/ks_token.c @@ -62,6 +62,10 @@ #define NUM_FLASH_BLOCKS KEYSTORE_NUM_SUBSECTORS +#if HAL_KS_BLOCK_SIZE % KEYSTORE_SUBSECTOR_SIZE != 0 +#error Keystore block size is not a multiple of flash subsector size +#endif + /* * Keystore database. */ @@ -80,13 +84,6 @@ typedef struct { #define db ((ks_token_db_t * const) hal_ks_token) -/* - * PIN block gets the all-zeros UUID, which will never be returned by - * the UUID generation code (by definition -- it's not a version 4 UUID). - */ - -const static hal_uuid_t pin_uuid = {{0}}; - /* * Calculate offset of the block in the flash address space. */ @@ -103,18 +100,18 @@ static inline uint32_t ks_token_offset(const unsigned blockno) * first page before reading the rest of the block. */ -static hal_error_t ks_token_read(hal_k_t *ks, const unsigned blockno, ks_block_t *block) +static hal_error_t ks_token_read(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block) { if (ks != hal_ks_token || block == NULL || blockno >= NUM_FLASH_BLOCKS || sizeof(*block) != KEYSTORE_SUBSECTOR_SIZE) return HAL_ERROR_IMPOSSIBLE; /* Sigh, magic numeric return codes */ - if (keystore_read_data(block_offset(blockno), + if (keystore_read_data(ks_token_offset(blockno), block->bytes, KEYSTORE_PAGE_SIZE) != 1) return HAL_ERROR_KEYSTORE_ACCESS; - switch (block_get_type(block)) { + switch (hal_ks_block_get_type(block)) { case HAL_KS_BLOCK_TYPE_ERASED: case HAL_KS_BLOCK_TYPE_ZEROED: return HAL_OK; @@ -125,7 +122,7 @@ static hal_error_t ks_token_read(hal_k_t *ks, const unsigned blockno, ks_block_t return HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE; } - switch (block_get_status(block)) { + switch (hal_ks_block_get_status(block)) { case HAL_KS_BLOCK_STATUS_LIVE: case HAL_KS_BLOCK_STATUS_TOMBSTONE: break; @@ -134,12 +131,12 @@ static hal_error_t ks_token_read(hal_k_t *ks, const unsigned blockno, ks_block_t } /* Sigh, magic numeric return codes */ - if (keystore_read_data(block_offset(blockno) + KEYSTORE_PAGE_SIZE, + if (keystore_read_data(ks_token_offset(blockno) + KEYSTORE_PAGE_SIZE, block->bytes + KEYSTORE_PAGE_SIZE, sizeof(*block) - KEYSTORE_PAGE_SIZE) != 1) return HAL_ERROR_KEYSTORE_ACCESS; - if (calculate_block_crc(block) != block->header.crc) + if (hal_ks_block_calculate_crc(block) != block->header.crc) return HAL_ERROR_KEYSTORE_BAD_CRC; return HAL_OK; @@ -151,13 +148,13 @@ static hal_error_t ks_token_read(hal_k_t *ks, const unsigned blockno, ks_block_t * need to update the CRC for this, we just modify the first page. */ -static hal_error_t ks_token_deprecate(hal_k_t *ks, const unsigned blockno) +static hal_error_t ks_token_deprecate(hal_ks_t *ks, const unsigned blockno) { if (ks != hal_ks_token || blockno >= NUM_FLASH_BLOCKS) return HAL_ERROR_IMPOSSIBLE; uint8_t page[KEYSTORE_PAGE_SIZE]; - flash_block_header_t *header = (void *) page; + hal_ks_block_header_t *header = (void *) page; uint32_t offset = ks_token_offset(blockno); /* Sigh, magic numeric return codes */ @@ -177,7 +174,7 @@ static hal_error_t ks_token_deprecate(hal_k_t *ks, const unsigned blockno) * Zero (not erase) a flash block. Just need to zero the first page. */ -static hal_error_t ks_token_zero(hal_k_t *ks, const unsigned blockno) +static hal_error_t ks_token_zero(hal_ks_t *ks, const unsigned blockno) { if (ks != hal_ks_token || blockno >= NUM_FLASH_BLOCKS) return HAL_ERROR_IMPOSSIBLE; @@ -185,7 +182,7 @@ static hal_error_t ks_token_zero(hal_k_t *ks, const unsigned blockno) uint8_t page[KEYSTORE_PAGE_SIZE] = {0}; /* Sigh, magic numeric return codes */ - if (keystore_write_data(block_offset(blockno), page, sizeof(page)) != 1) + if (keystore_write_data(ks_token_offset(blockno), page, sizeof(page)) != 1) return HAL_ERROR_KEYSTORE_ACCESS; return HAL_OK; @@ -195,7 +192,7 @@ static hal_error_t ks_token_zero(hal_k_t *ks, const unsigned blockno) * Erase a flash block. Also see ks_token_erase_maybe(), below. */ -static hal_error_t ks_token_erase(hal_k_t *ks, const unsigned blockno) +static hal_error_t ks_token_erase(hal_ks_t *ks, const unsigned blockno) { if (ks != hal_ks_token || blockno >= NUM_FLASH_BLOCKS) return HAL_ERROR_IMPOSSIBLE; @@ -217,7 +214,7 @@ static hal_error_t ks_token_erase(hal_k_t *ks, const unsigned blockno) * leak information about, eg, key length, so we do constant time. */ -static hal_error_t ks_token_erase_maybe(hal_k_t *ks, const unsigned blockno) +static hal_error_t ks_token_erase_maybe(hal_ks_t *ks, const unsigned blockno) { if (ks != hal_ks_token || blockno >= NUM_FLASH_BLOCKS) return HAL_ERROR_IMPOSSIBLE; @@ -232,34 +229,34 @@ static hal_error_t ks_token_erase_maybe(hal_k_t *ks, const unsigned blockno) mask &= page[i]; } - return mask == 0xFF ? HAL_OK : ks_token_erase(blockno); + return mask == 0xFF ? HAL_OK : ks_token_erase(ks, blockno); } /* * Write a flash block, calculating CRC when appropriate. */ -static hal_error_t ks_token_write(hal_k_t *ks, const unsigned blockno, ks_block_t *block) +static hal_error_t ks_token_write(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block) { if (ks != hal_ks_token || block == NULL || blockno >= NUM_FLASH_BLOCKS || sizeof(*block) != KEYSTORE_SUBSECTOR_SIZE) return HAL_ERROR_IMPOSSIBLE; - hal_error_t err = ks_token_erase_maybe(blockno); + hal_error_t err = ks_token_erase_maybe(ks, blockno); if (err != HAL_OK) return err; - switch (block_get_type(block)) { + switch (hal_ks_block_get_type(block)) { case HAL_KS_BLOCK_TYPE_KEY: case HAL_KS_BLOCK_TYPE_PIN: - block->header.crc = calculate_block_crc(block); + block->header.crc = hal_ks_block_calculate_crc(block); break; default: break; } /* Sigh, magic numeric return codes */ - if (keystore_write_data(block_offset(blockno), block->bytes, sizeof(*block)) != 1) + if (keystore_write_data(ks_token_offset(blockno), block->bytes, sizeof(*block)) != 1) return HAL_ERROR_KEYSTORE_ACCESS; return HAL_OK; @@ -289,7 +286,7 @@ static hal_error_t ks_token_test_owner(hal_ks_t *ks, const * Forward reference. */ -static hal_error_t fetch_pin_block(unsigned *b, ks_block_t **block); +static hal_error_t fetch_pin_block(unsigned *b, hal_ks_block_t **block); /* * Initialize keystore. @@ -300,6 +297,7 @@ static hal_error_t ks_token_init(hal_ks_t *ks, const int alloc) if (ks != hal_ks_token) return HAL_ERROR_IMPOSSIBLE; + hal_ks_block_t *block = NULL; hal_error_t err = HAL_OK; hal_ks_lock(); @@ -349,14 +347,14 @@ static hal_error_t ks_token_init(hal_ks_t *ks, const int alloc) block->pin.so_pin = db->so_pin; block->pin.user_pin = db->user_pin; - if ((err = hal_ks_index_add(ks, &pin_uuid, &b, NULL)) != HAL_OK) + if ((err = hal_ks_index_add(ks, &hal_ks_pin_uuid, &b, NULL)) != HAL_OK) goto done; - cache_mark_used(block, b); + hal_ks_cache_mark_used(ks, block, b); - err = ks_token_write(b, block); + err = ks_token_write(ks, b, block); - cache_release(block); + hal_ks_cache_release(ks, block); if (err != HAL_OK) goto done; @@ -376,12 +374,12 @@ static hal_error_t ks_token_init(hal_ks_t *ks, const int alloc) static const hal_ks_driver_t ks_token_driver = { .init = ks_token_init, - .read = ks_token_read, + .read = ks_token_read, .write = ks_token_write, - .deprecate = ks_token_deprecate, + .deprecate = ks_token_deprecate, .zero = ks_token_zero, .erase = ks_token_erase, - .erase_maybe = ks_token_erase_maybe, + .erase_maybe = ks_token_erase_maybe, .set_owner = ks_token_set_owner, .test_owner = ks_token_test_owner }; @@ -408,19 +406,21 @@ hal_ks_t * const hal_ks_token = &_db.ks; void hal_ks_init_read_only_pins_only(void) { unsigned b, best_seen = ~0; - ks_block_t block[1]; + hal_ks_block_t block[1]; hal_ks_lock(); for (b = 0; b < NUM_FLASH_BLOCKS; b++) { - if (block_read(b, block) != HAL_OK || ks_token_get_type(block) != HAL_KS_BLOCK_TYPE_PIN) + if (hal_ks_block_read(hal_ks_token, b, block) != HAL_OK || + hal_ks_block_get_type(block) != HAL_KS_BLOCK_TYPE_PIN) continue; best_seen = b; - if (block_get_status(block) == HAL_KS_BLOCK_STATUS_LIVE) + if (hal_ks_block_get_status(block) == HAL_KS_BLOCK_STATUS_LIVE) break; } - if (b != best_seen && best_seen != ~0 && ks_token_read(best_seen, block) != HAL_OK) + if (b != best_seen && best_seen != ~0 && + hal_ks_block_read(hal_ks_token, best_seen, block) != HAL_OK) best_seen = ~0; if (best_seen == ~0) { @@ -466,7 +466,7 @@ hal_error_t hal_get_pin(const hal_user_t user, * should always sort to first slot in the index. */ -static hal_error_t fetch_pin_block(unsigned *b, ks_block_t **block) +static hal_error_t fetch_pin_block(unsigned *b, hal_ks_block_t **block) { if (block == NULL) return HAL_ERROR_IMPOSSIBLE; @@ -478,13 +478,13 @@ static hal_error_t fetch_pin_block(unsigned *b, ks_block_t **block) if (b == NULL) b = &b_; - if ((err = hal_ks_index_find(hal_ks_token, &pin_uuid, b, &hint)) != HAL_OK || - (err = ks_token_read_cached(*b, block)) != HAL_OK) + if ((err = hal_ks_index_find(hal_ks_token, &hal_ks_pin_uuid, b, &hint)) != HAL_OK || + (err = hal_ks_block_read_cached(hal_ks_token, *b, block)) != HAL_OK) return err; - cache_mark_used(*block, *b); + hal_ks_cache_mark_used(hal_ks_token, *block, *b); - if (block_get_type(*block) != HAL_KS_BLOCK_TYPE_PIN) + if (hal_ks_block_get_type(*block) != HAL_KS_BLOCK_TYPE_PIN) return HAL_ERROR_IMPOSSIBLE; return HAL_OK; @@ -498,17 +498,17 @@ static hal_error_t fetch_pin_block(unsigned *b, ks_block_t **block) */ static hal_error_t update_pin_block(const unsigned b, - ks_block_t *block, - const flash_pin_block_t * const new_data) + hal_ks_block_t *block, + const hal_ks_pin_block_t * const new_data) { - if (block == NULL || new_data == NULL || ks_token_get_type(block) != HAL_KS_BLOCK_TYPE_PIN) + if (block == NULL || new_data == NULL || hal_ks_block_get_type(block) != HAL_KS_BLOCK_TYPE_PIN) return HAL_ERROR_IMPOSSIBLE; int hint = 0; block->pin = *new_data; - return ks_token_update(b, block, &pin_uuid, &hint); + return hal_ks_block_update(hal_ks_token, b, block, &hal_ks_pin_uuid, &hint); } /* @@ -521,7 +521,7 @@ hal_error_t hal_set_pin(const hal_user_t user, if (pin == NULL) return HAL_ERROR_BAD_ARGUMENTS; - ks_block_t *block; + hal_ks_block_t *block; hal_error_t err; unsigned b; @@ -530,7 +530,7 @@ hal_error_t hal_set_pin(const hal_user_t user, if ((err = fetch_pin_block(&b, &block)) != HAL_OK) goto done; - flash_pin_block_t new_data = block->pin; + hal_ks_pin_block_t new_data = block->pin; hal_ks_pin_t *dp, *bp; switch (user) { @@ -572,7 +572,7 @@ hal_error_t hal_mkm_flash_read_no_lock(uint8_t *buf, const size_t len) if (buf != NULL && len != KEK_LENGTH) return HAL_ERROR_MASTERKEY_BAD_LENGTH; - ks_block_t *block; + hal_ks_block_t *block; hal_error_t err; unsigned b; @@ -604,7 +604,7 @@ hal_error_t hal_mkm_flash_write(const uint8_t * const buf, const size_t len) if (len != KEK_LENGTH) return HAL_ERROR_MASTERKEY_BAD_LENGTH; - ks_block_t *block; + hal_ks_block_t *block; hal_error_t err; unsigned b; @@ -613,7 +613,7 @@ hal_error_t hal_mkm_flash_write(const uint8_t * const buf, const size_t len) if ((err = fetch_pin_block(&b, &block)) != HAL_OK) goto done; - flash_pin_block_t new_data = block->pin; + hal_ks_pin_block_t new_data = block->pin; new_data.kek_set = FLASH_KEK_SET; memcpy(new_data.kek, buf, len); @@ -630,7 +630,7 @@ hal_error_t hal_mkm_flash_erase(const size_t len) if (len != KEK_LENGTH) return HAL_ERROR_MASTERKEY_BAD_LENGTH; - ks_block_t *block; + hal_ks_block_t *block; hal_error_t err; unsigned b; @@ -639,7 +639,7 @@ hal_error_t hal_mkm_flash_erase(const size_t len) if ((err = fetch_pin_block(&b, &block)) != HAL_OK) goto done; - flash_pin_block_t new_data = block->pin; + hal_ks_pin_block_t new_data = block->pin; new_data.kek_set = FLASH_KEK_SET; memset(new_data.kek, 0, len); @@ -653,7 +653,6 @@ hal_error_t hal_mkm_flash_erase(const size_t len) #endif /* HAL_MKM_FLASH_BACKUP_KLUDGE */ - /* * Local variables: * indent-tabs-mode: nil diff --git a/ks_volatile.c b/ks_volatile.c index 7e1a5f2..42d1ba1 100644 --- a/ks_volatile.c +++ b/ks_volatile.c @@ -58,7 +58,7 @@ typedef struct { hal_client_handle_t client; hal_session_handle_t session; - hal_ks_block_t block; + hal_ks_block_t block; } ks_volatile_key_t; typedef struct { @@ -77,7 +77,7 @@ typedef struct { * Read a block. CRC probably not necessary for RAM. */ -static hal_error_t ks_volatile_read(hal_k_t *ks, const unsigned blockno, ks_block_t *block) +static hal_error_t ks_volatile_read(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block) { if (ks != hal_ks_volatile || db->keys == NULL || block == NULL || blockno >= ks->size) return HAL_ERROR_IMPOSSIBLE; @@ -91,12 +91,12 @@ static hal_error_t ks_volatile_read(hal_k_t *ks, const unsigned blockno, ks_bloc * Convert a live block into a tombstone. */ -static hal_error_t ks_volatile_deprecate(hal_k_t *ks, const unsigned blockno) +static hal_error_t ks_volatile_deprecate(hal_ks_t *ks, const unsigned blockno) { if (ks != hal_ks_volatile || db->keys == NULL || blockno >= ks->size) return HAL_ERROR_IMPOSSIBLE; - db->keys[blockno].block.header->block_status = BLOCK_STATUS_TOMBSTONE; + db->keys[blockno].block.header.block_status = HAL_KS_BLOCK_STATUS_TOMBSTONE; return HAL_OK; } @@ -105,12 +105,12 @@ static hal_error_t ks_volatile_deprecate(hal_k_t *ks, const unsigned blockno) * Zero (not erase) a flash block. */ -static hal_error_t ks_volatile_zero(hal_k_t *ks, const unsigned blockno) +static hal_error_t ks_volatile_zero(hal_ks_t *ks, const unsigned blockno) { if (ks != hal_ks_volatile || db->keys == NULL || blockno >= ks->size) return HAL_ERROR_IMPOSSIBLE; - memset(db->keys[blockno].block, 0x00, sizeof(db->keys[blockno].block)); + memset(&db->keys[blockno].block, 0x00, sizeof(db->keys[blockno].block)); db->keys[blockno].client.handle = HAL_HANDLE_NONE; db->keys[blockno].session.handle = HAL_HANDLE_NONE; @@ -121,12 +121,12 @@ static hal_error_t ks_volatile_zero(hal_k_t *ks, const unsigned blockno) * Erase a flash block. */ -static hal_error_t ks_volatile_erase(hal_k_t *ks, const unsigned blockno) +static hal_error_t ks_volatile_erase(hal_ks_t *ks, const unsigned blockno) { if (ks != hal_ks_volatile || db->keys == NULL || blockno >= ks->size) return HAL_ERROR_IMPOSSIBLE; - memset(db->keys[blockno].block, 0xFF, sizeof(db->keys[blockno].block)); + memset(&db->keys[blockno].block, 0xFF, sizeof(db->keys[blockno].block)); db->keys[blockno].client.handle = HAL_HANDLE_NONE; db->keys[blockno].session.handle = HAL_HANDLE_NONE; @@ -137,7 +137,7 @@ static hal_error_t ks_volatile_erase(hal_k_t *ks, const unsigned blockno) * Write a flash block. CRC probably not necessary for RAM. */ -static hal_error_t ks_volatile_write(hal_k_t *ks, const unsigned blockno, ks_block_t *block) +static hal_error_t ks_volatile_write(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block) { if (ks != hal_ks_volatile || db->keys == NULL || block == NULL || blockno >= ks->size) return HAL_ERROR_IMPOSSIBLE; @@ -208,8 +208,8 @@ static hal_error_t ks_volatile_init(hal_ks_t *ks, const int alloc) if ((err = hal_ks_init_common(ks)) != HAL_OK) goto done; - for (unsigned b = 0; b < db->ks.size; i++) - if ((err = block_erase(ks, b)) != HAL_OK) + for (unsigned b = 0; b < db->ks.size; b++) + if ((err = hal_ks_block_erase(ks, b)) != HAL_OK) goto done; err = HAL_OK; @@ -224,14 +224,14 @@ static hal_error_t ks_volatile_init(hal_ks_t *ks, const int alloc) * the driver functions. */ -static const hal_ks_driver_t hal_ks_volatile_driver = { +static const hal_ks_driver_t ks_volatile_driver = { .init = ks_volatile_init, - .read = ks_volatile_read, + .read = ks_volatile_read, .write = ks_volatile_write, - .deprecate = ks_volatile_deprecate, + .deprecate = ks_volatile_deprecate, .zero = ks_volatile_zero, .erase = ks_volatile_erase, - .erase_maybe = ks_volatile_erase, /* sic */ + .erase_maybe = ks_volatile_erase, /* sic */ .set_owner = ks_volatile_set_owner, .test_owner = ks_volatile_test_owner }; diff --git a/rpc_pkey.c b/rpc_pkey.c index bdf8a7e..d280c54 100644 --- a/rpc_pkey.c +++ b/rpc_pkey.c @@ -270,52 +270,25 @@ static hal_error_t pkcs1_5_pad(const uint8_t * const data, const size_t data_len } /* - * Given key flags, open appropriate keystore driver. + * Given key flags, return appropriate keystore. */ -static inline hal_error_t ks_open_from_flags(hal_ks_t **ks, const hal_key_flags_t flags) +static inline hal_ks_t *ks_from_flags(const hal_key_flags_t flags) { - return hal_ks_open((flags & HAL_KEY_FLAG_TOKEN) == 0 - ? hal_ks_volatile_driver - : hal_ks_token_driver, - ks); + return (flags & HAL_KEY_FLAG_TOKEN) == 0 ? hal_ks_volatile : hal_ks_token; } /* - * Fetch a key from a driver. - */ - -static inline hal_error_t ks_fetch_from_driver(const hal_ks_driver_t * const driver, - hal_pkey_slot_t *slot, - uint8_t *der, size_t *der_len, const size_t der_max) -{ - hal_ks_t *ks = NULL; - hal_error_t err; - - if ((err = hal_ks_open(driver, &ks)) != HAL_OK) - return err; - - if ((err = hal_ks_fetch(ks, slot, der, der_len, der_max)) == HAL_OK) - err = hal_ks_close(ks); - else - (void) hal_ks_close(ks); - - return err; -} - -/* - * Same thing but from key flag in slot object rather than explict driver. + * Fetch a key from keystore indicated by key flag in slot object. */ static inline hal_error_t ks_fetch_from_flags(hal_pkey_slot_t *slot, uint8_t *der, size_t *der_len, const size_t der_max) { - assert(slot != NULL); + if (slot == NULL) + return HAL_ERROR_IMPOSSIBLE; - return ks_fetch_from_driver((slot->flags & HAL_KEY_FLAG_TOKEN) == 0 - ? hal_ks_volatile_driver - : hal_ks_token_driver, - slot, der, der_len, der_max); + return hal_ks_fetch(ks_from_flags(slot->flags), slot, der, der_len, der_max); } @@ -336,7 +309,6 @@ static hal_error_t pkey_local_load(const hal_client_handle_t client, hal_curve_name_t curve; hal_pkey_slot_t *slot; hal_key_type_t type; - hal_ks_t *ks = NULL; hal_error_t err; if ((err = check_writable(client, flags)) != HAL_OK) @@ -357,13 +329,7 @@ static hal_error_t pkey_local_load(const hal_client_handle_t client, slot->curve = curve; slot->flags = flags; - if ((err = ks_open_from_flags(&ks, flags)) == HAL_OK && - (err = hal_ks_store(ks, slot, der, der_len)) == HAL_OK) - err = hal_ks_close(ks); - else if (ks != NULL) - (void) hal_ks_close(ks); - - if (err != HAL_OK) { + if ((err = hal_ks_store(ks_from_flags(flags), slot, der, der_len)) != HAL_OK) { slot->type = HAL_KEY_TYPE_NONE; return err; } @@ -397,11 +363,11 @@ static hal_error_t pkey_local_open(const hal_client_handle_t client, slot->client_handle = client; slot->session_handle = session; - if ((err = ks_fetch_from_driver(hal_ks_token_driver, slot, NULL, NULL, 0)) == HAL_OK) + if ((err = hal_ks_fetch(hal_ks_token, slot, NULL, NULL, 0)) == HAL_OK) slot->pkey_handle.handle |= HAL_PKEY_HANDLE_TOKEN_FLAG; else if (err == HAL_ERROR_KEY_NOT_FOUND) - err = ks_fetch_from_driver(hal_ks_volatile_driver, slot, NULL, NULL, 0); + err = hal_ks_fetch(hal_ks_volatile, slot, NULL, NULL, 0); if (err != HAL_OK) goto fail; @@ -431,7 +397,6 @@ static hal_error_t pkey_local_generate_rsa(const hal_client_handle_t client, uint8_t keybuf[hal_rsa_key_t_size]; hal_rsa_key_t *key = NULL; hal_pkey_slot_t *slot; - hal_ks_t *ks = NULL; hal_error_t err; if ((err = check_writable(client, flags)) != HAL_OK) @@ -458,12 +423,8 @@ static hal_error_t pkey_local_generate_rsa(const hal_client_handle_t client, uint8_t der[hal_rsa_private_key_to_der_len(key)]; size_t der_len; - if ((err = hal_rsa_private_key_to_der(key, der, &der_len, sizeof(der))) == HAL_OK && - (err = ks_open_from_flags(&ks, flags)) == HAL_OK && - (err = hal_ks_store(ks, slot, der, der_len)) == HAL_OK) - err = hal_ks_close(ks); - else if (ks != NULL) - (void) hal_ks_close(ks); + if ((err = hal_rsa_private_key_to_der(key, der, &der_len, sizeof(der))) == HAL_OK) + err = hal_ks_store(ks_from_flags(flags), slot, der, der_len); memset(keybuf, 0, sizeof(keybuf)); memset(der, 0, sizeof(der)); @@ -495,7 +456,6 @@ static hal_error_t pkey_local_generate_ec(const hal_client_handle_t client, uint8_t keybuf[hal_ecdsa_key_t_size]; hal_ecdsa_key_t *key = NULL; hal_pkey_slot_t *slot; - hal_ks_t *ks = NULL; hal_error_t err; if ((err = check_writable(client, flags)) != HAL_OK) @@ -521,12 +481,8 @@ static hal_error_t pkey_local_generate_ec(const hal_client_handle_t client, uint8_t der[hal_ecdsa_private_key_to_der_len(key)]; size_t der_len; - if ((err = hal_ecdsa_private_key_to_der(key, der, &der_len, sizeof(der))) == HAL_OK && - (err = ks_open_from_flags(&ks, flags)) == HAL_OK && - (err = hal_ks_store(ks, slot, der, der_len)) == HAL_OK) - err = hal_ks_close(ks); - else if (ks != NULL) - (void) hal_ks_close(ks); + if ((err = hal_ecdsa_private_key_to_der(key, der, &der_len, sizeof(der))) == HAL_OK) + err = hal_ks_store(ks_from_flags(flags), slot, der, der_len); memset(keybuf, 0, sizeof(keybuf)); memset(der, 0, sizeof(der)); @@ -568,17 +524,12 @@ static hal_error_t pkey_local_delete(const hal_pkey_handle_t pkey) if (slot == NULL) return HAL_ERROR_KEY_NOT_FOUND; - hal_ks_t *ks = NULL; hal_error_t err; if ((err = check_writable(slot->client_handle, slot->flags)) != HAL_OK) return err; - if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK && - (err = hal_ks_delete(ks, slot)) == HAL_OK) - err = hal_ks_close(ks); - else if (ks != NULL) - (void) hal_ks_close(ks); + err = hal_ks_delete(ks_from_flags(slot->flags), slot); if (err == HAL_OK || err == HAL_ERROR_KEY_NOT_FOUND) clear_slot(slot); @@ -1018,7 +969,7 @@ static hal_error_t pkey_local_verify(const hal_pkey_handle_t pkey, return err; } -static inline hal_error_t match_one_keystore(const hal_ks_driver_t * const driver, +static inline hal_error_t match_one_keystore(hal_ks_t *ks, const hal_client_handle_t client, const hal_session_handle_t session, const hal_key_type_t type, @@ -1032,21 +983,12 @@ static inline hal_error_t match_one_keystore(const hal_ks_driver_t * const drive const unsigned result_max, const hal_uuid_t * const previous_uuid) { - hal_ks_t *ks = NULL; hal_error_t err; unsigned len; - if ((err = hal_ks_open(driver, &ks)) != HAL_OK) - return err; - if ((err = hal_ks_match(ks, client, session, type, curve, mask, flags, attributes, attributes_len, - *result, &len, result_max, previous_uuid)) != HAL_OK) { - (void) hal_ks_close(ks); - return err; - } - - if ((err = hal_ks_close(ks)) != HAL_OK) + *result, &len, result_max, previous_uuid)) != HAL_OK) return err; *result += len; @@ -1097,7 +1039,7 @@ static hal_error_t pkey_local_match(const hal_client_handle_t client, case MATCH_STATE_TOKEN: if (((mask & HAL_KEY_FLAG_TOKEN) == 0 || (mask & flags & HAL_KEY_FLAG_TOKEN) != 0) && - (err = match_one_keystore(hal_ks_token_driver, client, session, type, curve, + (err = match_one_keystore(hal_ks_token, client, session, type, curve, mask, flags, attributes, attributes_len, &result, result_len, result_max - *result_len, prev)) != HAL_OK) return err; @@ -1108,7 +1050,7 @@ static hal_error_t pkey_local_match(const hal_client_handle_t client, case MATCH_STATE_VOLATILE: if (((mask & HAL_KEY_FLAG_TOKEN) == 0 || (mask & flags & HAL_KEY_FLAG_TOKEN) == 0) && - (err = match_one_keystore(hal_ks_volatile_driver, client, session, type, curve, + (err = match_one_keystore(hal_ks_volatile, client, session, type, curve, mask, flags, attributes, attributes_len, &result, result_len, result_max - *result_len, prev)) != HAL_OK) return err; @@ -1133,19 +1075,12 @@ static hal_error_t pkey_local_set_attributes(const hal_pkey_handle_t pkey, if (slot == NULL) return HAL_ERROR_KEY_NOT_FOUND; - hal_ks_t *ks = NULL; hal_error_t err; if ((err = check_writable(slot->client_handle, slot->flags)) != HAL_OK) return err; - if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK && - (err = hal_ks_set_attributes(ks, slot, attributes, attributes_len)) == HAL_OK) - err = hal_ks_close(ks); - else if (ks != NULL) - (void) hal_ks_close(ks); - - return err; + return hal_ks_set_attributes(ks_from_flags(slot->flags), slot, attributes, attributes_len); } static hal_error_t pkey_local_get_attributes(const hal_pkey_handle_t pkey, @@ -1159,17 +1094,8 @@ static hal_error_t pkey_local_get_attributes(const hal_pkey_handle_t pkey, if (slot == NULL) return HAL_ERROR_KEY_NOT_FOUND; - hal_ks_t *ks = NULL; - hal_error_t err; - - if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK && - (err = hal_ks_get_attributes(ks, slot, attributes, attributes_len, - attributes_buffer, attributes_buffer_len)) == HAL_OK) - err = hal_ks_close(ks); - else if (ks != NULL) - (void) hal_ks_close(ks); - - return err; + return hal_ks_get_attributes(ks_from_flags(slot->flags), slot, attributes, attributes_len, + attributes_buffer, attributes_buffer_len); } static hal_error_t pkey_local_export(const hal_pkey_handle_t pkey_handle, diff --git a/rpc_server.c b/rpc_server.c index a01572e..f64d7d6 100644 --- a/rpc_server.c +++ b/rpc_server.c @@ -993,9 +993,9 @@ hal_error_t hal_rpc_server_init(void) { hal_error_t err; - if ((err = hal_ks_init(hal_ks_volatile_driver, 1)) != HAL_OK || - (err = hal_ks_init(hal_ks_token_driver, 1)) != HAL_OK || - (err = hal_rpc_server_transport_init()) != HAL_OK) + if ((err = hal_ks_init(hal_ks_volatile, 1)) != HAL_OK || + (err = hal_ks_init(hal_ks_token, 1)) != HAL_OK || + (err = hal_rpc_server_transport_init()) != HAL_OK) return err; return HAL_OK; @@ -1005,9 +1005,7 @@ hal_error_t hal_rpc_server_close(void) { hal_error_t err; - if ((err = hal_rpc_server_transport_close()) != HAL_OK || - (err = hal_ks_shutdown(hal_ks_token_driver)) != HAL_OK || - (err = hal_ks_shutdown(hal_ks_volatile_driver)) != HAL_OK) + if ((err = hal_rpc_server_transport_close()) != HAL_OK) return err; return HAL_OK; -- cgit v1.2.3 From 9e20f2fa42bcd493548bf7764958848ab72d1255 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Sun, 28 May 2017 18:50:23 -0400 Subject: Debug new keystore init code. --- ks_token.c | 5 +++++ ks_volatile.c | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ks_token.c b/ks_token.c index c2ebee2..6172f79 100644 --- a/ks_token.c +++ b/ks_token.c @@ -338,6 +338,11 @@ static hal_error_t ks_token_init(hal_ks_t *ks, const int alloc) unsigned b; + if ((block = hal_ks_cache_pick_lru(ks)) == NULL) { + err = HAL_ERROR_IMPOSSIBLE; + goto done; + } + memset(block, 0xFF, sizeof(*block)); block->header.block_type = HAL_KS_BLOCK_TYPE_PIN; diff --git a/ks_volatile.c b/ks_volatile.c index 42d1ba1..c1ea72d 100644 --- a/ks_volatile.c +++ b/ks_volatile.c @@ -205,13 +205,18 @@ static hal_error_t ks_volatile_init(hal_ks_t *ks, const int alloc) db->keys = mem; } - if ((err = hal_ks_init_common(ks)) != HAL_OK) + if (db->keys == NULL) { + err = HAL_ERROR_IMPOSSIBLE; goto done; + } for (unsigned b = 0; b < db->ks.size; b++) if ((err = hal_ks_block_erase(ks, b)) != HAL_OK) goto done; + if ((err = hal_ks_init_common(ks)) != HAL_OK) + goto done; + err = HAL_OK; done: -- cgit v1.2.3 From 5cee716555db92942c5b11c824839bb00aaf35b9 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Mon, 29 May 2017 00:44:18 -0400 Subject: Debug per-session keys. --- ks.c | 19 +++++++++---------- ks.h | 11 +++++++++++ ks_token.c | 24 ++++++++++++++++-------- ks_volatile.c | 25 ++++++++++++++++++++++--- unit-tests.py | 2 +- 5 files changed, 59 insertions(+), 22 deletions(-) diff --git a/ks.c b/ks.c index a0a4de7..2d1d8c4 100644 --- a/ks.c +++ b/ks.c @@ -182,6 +182,7 @@ hal_error_t hal_ks_block_update(hal_ks_t *ks, if ((err = hal_ks_block_deprecate(ks, b1)) != HAL_OK || (err = hal_ks_index_replace(ks, uuid, &b2, hint)) != HAL_OK || (err = hal_ks_block_write(ks, b2, block)) != HAL_OK || + (err = hal_ks_block_copy_owner(ks, b1, b2)) != HAL_OK || (err = hal_ks_block_zero(ks, b1)) != HAL_OK) return err; @@ -243,8 +244,8 @@ hal_error_t hal_ks_alloc_common(hal_ks_t *ks, if (mem == NULL) return HAL_ERROR_ALLOCATION_FAILURE; - memset(((uint8_t *) ks) + sizeof(hal_ks_driver_t), 0, - sizeof(hal_ks_t) - sizeof(hal_ks_driver_t)); + memset(((uint8_t *) ks) + sizeof(ks->driver), 0, + sizeof(hal_ks_t) - sizeof(ks->driver)); memset(mem, 0, len); ks->index = gnaw(&mem, &len, sizeof(*ks->index) * ks_blocks); @@ -498,23 +499,21 @@ static inline hal_error_t key_visible(hal_ks_t * const ks, const hal_session_handle_t session, const unsigned blockno) { + hal_error_t err; + if (ks == NULL) return HAL_ERROR_IMPOSSIBLE; if (!ks->per_session) return HAL_OK; - hal_error_t err; - - if ((err = hal_ks_block_test_owner(ks, blockno, client, session)) != HAL_OK) + if ((err = hal_ks_block_test_owner(ks, blockno, client, session)) != HAL_ERROR_KEY_NOT_FOUND) return err; - err = hal_rpc_is_logged_in(client, HAL_USER_WHEEL); - - if (err == HAL_ERROR_FORBIDDEN) - err = HAL_ERROR_KEY_NOT_FOUND; + if ((err = hal_rpc_is_logged_in(client, HAL_USER_WHEEL)) != HAL_ERROR_FORBIDDEN) + return err; - return err; + return HAL_ERROR_KEY_NOT_FOUND; } hal_error_t hal_ks_store(hal_ks_t *ks, diff --git a/ks.h b/ks.h index 6db0bd7..25f2acf 100644 --- a/ks.h +++ b/ks.h @@ -223,6 +223,7 @@ struct hal_ks_driver { const hal_client_handle_t client, const hal_session_handle_t session); hal_error_t (*test_owner) (hal_ks_t *ks, const unsigned blockno, const hal_client_handle_t client, const hal_session_handle_t session); + hal_error_t (*copy_owner) (hal_ks_t *ks, const unsigned source, const unsigned target); }; /* @@ -299,6 +300,16 @@ static inline hal_error_t hal_ks_block_test_owner(hal_ks_t *ks, const unsigned b ks->driver->test_owner(ks, blockno, client, session); } +static inline hal_error_t hal_ks_block_copy_owner(hal_ks_t *ks, + const unsigned source, + const unsigned target) +{ + return + ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : + ks->driver->copy_owner == NULL ? HAL_ERROR_NOT_IMPLEMENTED : + ks->driver->copy_owner(ks, source, target); +} + /* * Type safe casts. */ diff --git a/ks_token.c b/ks_token.c index 6172f79..e69eb02 100644 --- a/ks_token.c +++ b/ks_token.c @@ -267,17 +267,24 @@ static hal_error_t ks_token_write(hal_ks_t *ks, const unsigned blockno, hal_ks_b */ static hal_error_t ks_token_set_owner(hal_ks_t *ks, - const unsigned blockno, - const hal_client_handle_t client, - const hal_session_handle_t session) + const unsigned blockno, + const hal_client_handle_t client, + const hal_session_handle_t session) { return HAL_OK; } -static hal_error_t ks_token_test_owner(hal_ks_t *ks, const - unsigned blockno, - const hal_client_handle_t client, - const hal_session_handle_t session) +static hal_error_t ks_token_test_owner(hal_ks_t *ks, + const unsigned blockno, + const hal_client_handle_t client, + const hal_session_handle_t session) +{ + return HAL_OK; +} + +static hal_error_t ks_token_copy_owner(hal_ks_t *ks, + const unsigned source, + const unsigned target) { return HAL_OK; } @@ -386,7 +393,8 @@ static const hal_ks_driver_t ks_token_driver = { .erase = ks_token_erase, .erase_maybe = ks_token_erase_maybe, .set_owner = ks_token_set_owner, - .test_owner = ks_token_test_owner + .test_owner = ks_token_test_owner, + .copy_owner = ks_token_copy_owner }; static ks_token_db_t _db = { .ks.driver = &ks_token_driver }; diff --git a/ks_volatile.c b/ks_volatile.c index c1ea72d..0b39133 100644 --- a/ks_volatile.c +++ b/ks_volatile.c @@ -169,8 +169,8 @@ static hal_error_t ks_volatile_set_owner(hal_ks_t *ks, * Test key ownership. */ -static hal_error_t ks_volatile_test_owner(hal_ks_t *ks, const - unsigned blockno, +static hal_error_t ks_volatile_test_owner(hal_ks_t *ks, + const unsigned blockno, const hal_client_handle_t client, const hal_session_handle_t session) { @@ -184,6 +184,22 @@ static hal_error_t ks_volatile_test_owner(hal_ks_t *ks, const return HAL_ERROR_KEY_NOT_FOUND; } +/* + * Copy key ownership. + */ + +static hal_error_t ks_volatile_copy_owner(hal_ks_t *ks, + const unsigned source, + const unsigned target) +{ + if (ks != hal_ks_volatile || db->keys == NULL || source >= ks->size || target >= ks->size) + return HAL_ERROR_IMPOSSIBLE; + + db->keys[target].client = db->keys[source].client; + db->keys[target].session = db->keys[source].session; + return HAL_OK; +} + /* * Initialize keystore. */ @@ -217,6 +233,8 @@ static hal_error_t ks_volatile_init(hal_ks_t *ks, const int alloc) if ((err = hal_ks_init_common(ks)) != HAL_OK) goto done; + ks->per_session = 1; + err = HAL_OK; done: @@ -238,7 +256,8 @@ static const hal_ks_driver_t ks_volatile_driver = { .erase = ks_volatile_erase, .erase_maybe = ks_volatile_erase, /* sic */ .set_owner = ks_volatile_set_owner, - .test_owner = ks_volatile_test_owner + .test_owner = ks_volatile_test_owner, + .copy_owner = ks_volatile_copy_owner }; static ks_volatile_db_t _db = { .ks.driver = &ks_volatile_driver }; diff --git a/unit-tests.py b/unit-tests.py index 338af64..a304205 100644 --- a/unit-tests.py +++ b/unit-tests.py @@ -657,7 +657,7 @@ class TestPKeyAttribute(TestCaseLoggedIn): try: with hsm.pkey_open(uuid) as pkey: pkey.delete() - except: + except Exception as e: logger.debug("Problem deleting key %s: %s", uuid, e) def load_and_fill(self, flags, n_keys = 1, n_attrs = 2, n_fill = 0): -- cgit v1.2.3 From d2633bb4155c6798949e92d8113bc036b942a018 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Mon, 29 May 2017 12:44:12 -0400 Subject: Indentation. --- ks.c | 8 ++++---- ks.h | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ks.c b/ks.c index 2d1d8c4..2b33771 100644 --- a/ks.c +++ b/ks.c @@ -163,10 +163,10 @@ hal_error_t hal_ks_block_read_cached(hal_ks_t *ks, const unsigned blockno, hal_k */ hal_error_t hal_ks_block_update(hal_ks_t *ks, - const unsigned b1, - hal_ks_block_t *block, - const hal_uuid_t * const uuid, - int *hint) + const unsigned b1, + hal_ks_block_t *block, + const hal_uuid_t * const uuid, + int *hint) { if (block == NULL) return HAL_ERROR_IMPOSSIBLE; diff --git a/ks.h b/ks.h index 25f2acf..44581a2 100644 --- a/ks.h +++ b/ks.h @@ -111,7 +111,7 @@ typedef struct { uint8_t der[]; /* Must be last field -- C99 "flexible array member" */ } hal_ks_key_block_t; -#define SIZEOF_KS_KEY_BLOCK_DER \ +#define SIZEOF_KS_KEY_BLOCK_DER \ (HAL_KS_BLOCK_SIZE - offsetof(hal_ks_key_block_t, der)) /* @@ -390,14 +390,14 @@ extern hal_error_t hal_ks_attribute_insert(uint8_t *bytes, const size_t bytes_le extern hal_ks_block_t *hal_ks_cache_pick_lru(hal_ks_t *ks); extern hal_ks_block_t *hal_ks_cache_find_block(const hal_ks_t * const ks, - const unsigned blockno); + const unsigned blockno); extern void hal_ks_cache_mark_used(hal_ks_t *ks, - const hal_ks_block_t * const block, - const unsigned blockno); + const hal_ks_block_t * const block, + const unsigned blockno); extern void hal_ks_cache_release(hal_ks_t *ks, - const hal_ks_block_t * const block); + const hal_ks_block_t * const block); extern hal_error_t hal_ks_block_read_cached(hal_ks_t *ks, const unsigned blockno, -- cgit v1.2.3 From 776c4e8cfed92bc2d894f002cb7d222abc65bb50 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Mon, 29 May 2017 13:16:14 -0400 Subject: Simplify per-session keys. Cosmetic cleanup of pkey_slot along the way. --- hal_internal.h | 6 +++--- ks.c | 68 +++++++++++++--------------------------------------------- ks.h | 7 +++--- ks_volatile.c | 27 +++++++++++++++++------ rpc_pkey.c | 16 +++++++------- 5 files changed, 51 insertions(+), 73 deletions(-) diff --git a/hal_internal.h b/hal_internal.h index e998ae3..add7890 100644 --- a/hal_internal.h +++ b/hal_internal.h @@ -449,9 +449,9 @@ extern hal_error_t hal_mkm_flash_erase(const size_t len); */ typedef struct { - hal_client_handle_t client_handle; - hal_session_handle_t session_handle; - hal_pkey_handle_t pkey_handle; + hal_client_handle_t client; + hal_session_handle_t session; + hal_pkey_handle_t pkey; hal_key_type_t type; hal_curve_name_t curve; hal_key_flags_t flags; diff --git a/ks.c b/ks.c index 2b33771..e966b94 100644 --- a/ks.c +++ b/ks.c @@ -477,45 +477,6 @@ static inline int acceptable_key_type(const hal_key_type_t type) } } -/* - * Test 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. - * - * 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 inline hal_error_t key_visible(hal_ks_t * const ks, - const hal_client_handle_t client, - const hal_session_handle_t session, - const unsigned blockno) -{ - hal_error_t err; - - if (ks == NULL) - return HAL_ERROR_IMPOSSIBLE; - - if (!ks->per_session) - return HAL_OK; - - if ((err = hal_ks_block_test_owner(ks, blockno, client, session)) != HAL_ERROR_KEY_NOT_FOUND) - return err; - - if ((err = hal_rpc_is_logged_in(client, HAL_USER_WHEEL)) != HAL_ERROR_FORBIDDEN) - return err; - - return HAL_ERROR_KEY_NOT_FOUND; -} - hal_error_t hal_ks_store(hal_ks_t *ks, hal_pkey_slot_t *slot, const uint8_t * const der, const size_t der_len) @@ -571,7 +532,7 @@ hal_error_t hal_ks_store(hal_ks_t *ks, err = hal_ks_block_write(ks, b, block); if (err == HAL_OK) - err = hal_ks_block_set_owner(ks, b, slot->client_handle, slot->session_handle); + err = hal_ks_block_set_owner(ks, b, slot->client, slot->session); if (err == HAL_OK) goto done; @@ -598,9 +559,9 @@ hal_error_t hal_ks_fetch(hal_ks_t *ks, hal_ks_lock(); - if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || - (err = key_visible(ks, slot->client_handle, slot->session_handle, b)) != HAL_OK || - (err = hal_ks_block_read_cached(ks, b, &block)) != HAL_OK) + if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || + (err = hal_ks_block_test_owner(ks, b, slot->client, slot->session)) != HAL_OK || + (err = hal_ks_block_read_cached(ks, b, &block)) != HAL_OK) goto done; if (hal_ks_block_get_type(block) != HAL_KS_BLOCK_TYPE_KEY) { @@ -652,8 +613,8 @@ hal_error_t hal_ks_delete(hal_ks_t *ks, hal_ks_lock(); - if ((err = hal_ks_index_delete(ks, &slot->name, &b, &slot->hint)) != HAL_OK || - (err = key_visible(ks, slot->client_handle, slot->session_handle, b)) != HAL_OK) + if ((err = hal_ks_index_delete(ks, &slot->name, &b, &slot->hint)) != HAL_OK || + (err = hal_ks_block_test_owner(ks, b, slot->client, slot->session)) != HAL_OK) goto done; hal_ks_cache_release(ks, hal_ks_cache_find_block(ks, b)); @@ -725,9 +686,10 @@ hal_error_t hal_ks_match(hal_ks_t *ks, if ((err = hal_ks_block_read_cached(ks, b, &block)) != HAL_OK) goto done; - if ((err = key_visible(ks, client, session, b)) == HAL_ERROR_KEY_NOT_FOUND) + if ((err = hal_ks_block_test_owner(ks, b, client, session)) == HAL_ERROR_KEY_NOT_FOUND) continue; - else if (err != HAL_OK) + + if (err != HAL_OK) goto done; if ((type != HAL_KEY_TYPE_NONE && type != block->key.type) || @@ -799,9 +761,9 @@ hal_error_t hal_ks_set_attributes(hal_ks_t *ks, hal_ks_lock(); { - if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || - (err = key_visible(ks, slot->client_handle, slot->session_handle, b)) != HAL_OK || - (err = hal_ks_block_read_cached(ks, b, &block)) != HAL_OK) + if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || + (err = hal_ks_block_test_owner(ks, b, slot->client, slot->session)) != HAL_OK || + (err = hal_ks_block_read_cached(ks, b, &block)) != HAL_OK) goto done; hal_ks_cache_mark_used(ks, block, b); @@ -865,9 +827,9 @@ hal_error_t hal_ks_get_attributes(hal_ks_t *ks, hal_ks_lock(); { - if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || - (err = key_visible(ks, slot->client_handle, slot->session_handle, b)) != HAL_OK || - (err = hal_ks_block_read_cached(ks, b, &block)) != HAL_OK) + if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint)) != HAL_OK || + (err = hal_ks_block_test_owner(ks, b, slot->client, slot->session)) != HAL_OK || + (err = hal_ks_block_read_cached(ks, b, &block)) != HAL_OK) goto done; hal_ks_cache_mark_used(ks, block, b); diff --git a/ks.h b/ks.h index 44581a2..240d3e6 100644 --- a/ks.h +++ b/ks.h @@ -204,7 +204,6 @@ struct hal_ks { unsigned cache_lru; /* Cache LRU counter */ unsigned cache_size; /* Size (how many blocks) in cache */ hal_ks_cache_block_t *cache; /* Cache */ - int per_session; /* Whether objects have per-session semantics (PKCS #11, sigh) */ }; /* @@ -280,7 +279,8 @@ static inline hal_error_t hal_ks_block_erase_maybe(hal_ks_t *ks, const unsigned ks->driver->erase_maybe(ks, blockno); } -static inline hal_error_t hal_ks_block_set_owner(hal_ks_t *ks, const unsigned blockno, +static inline hal_error_t hal_ks_block_set_owner(hal_ks_t *ks, + const unsigned blockno, const hal_client_handle_t client, const hal_session_handle_t session) { @@ -290,7 +290,8 @@ static inline hal_error_t hal_ks_block_set_owner(hal_ks_t *ks, const unsigned bl ks->driver->set_owner(ks, blockno, client, session); } -static inline hal_error_t hal_ks_block_test_owner(hal_ks_t *ks, const unsigned blockno, +static inline hal_error_t hal_ks_block_test_owner(hal_ks_t *ks, + const unsigned blockno, const hal_client_handle_t client, const hal_session_handle_t session) { diff --git a/ks_volatile.c b/ks_volatile.c index 0b39133..02054ff 100644 --- a/ks_volatile.c +++ b/ks_volatile.c @@ -167,6 +167,20 @@ static hal_error_t ks_volatile_set_owner(hal_ks_t *ks, /* * Test key ownership. + * + * 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. + * + * 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 hal_error_t ks_volatile_test_owner(hal_ks_t *ks, @@ -177,11 +191,14 @@ static hal_error_t ks_volatile_test_owner(hal_ks_t *ks, if (ks != hal_ks_volatile || db->keys == NULL || blockno >= ks->size) return HAL_ERROR_IMPOSSIBLE; - if (db->keys[blockno].client.handle == client.handle && - db->keys[blockno].session.handle == session.handle) + if (db->keys[blockno].client.handle == HAL_HANDLE_NONE || + db->keys[blockno].client.handle == client.handle) + return HAL_OK; + + if (hal_rpc_is_logged_in(client, HAL_USER_WHEEL) == HAL_OK) return HAL_OK; - else - return HAL_ERROR_KEY_NOT_FOUND; + + return HAL_ERROR_KEY_NOT_FOUND; } /* @@ -233,8 +250,6 @@ static hal_error_t ks_volatile_init(hal_ks_t *ks, const int alloc) if ((err = hal_ks_init_common(ks)) != HAL_OK) goto done; - ks->per_session = 1; - err = HAL_OK; done: diff --git a/rpc_pkey.c b/rpc_pkey.c index d280c54..63bc8bd 100644 --- a/rpc_pkey.c +++ b/rpc_pkey.c @@ -79,10 +79,10 @@ static inline hal_pkey_slot_t *alloc_slot(const hal_key_flags_t flags) glop |= HAL_PKEY_HANDLE_TOKEN_FLAG; for (int i = 0; slot == NULL && i < sizeof(pkey_slot)/sizeof(*pkey_slot); i++) { - if (pkey_slot[i].pkey_handle.handle != HAL_HANDLE_NONE) + if (pkey_slot[i].pkey.handle != HAL_HANDLE_NONE) continue; memset(&pkey_slot[i], 0, sizeof(pkey_slot[i])); - pkey_slot[i].pkey_handle.handle = i | glop; + pkey_slot[i].pkey.handle = i | glop; pkey_slot[i].hint = -1; slot = &pkey_slot[i]; } @@ -120,7 +120,7 @@ static inline hal_pkey_slot_t *find_handle(const hal_pkey_handle_t handle) #if HAL_STATIC_PKEY_STATE_BLOCKS > 0 const int i = (int) (handle.handle & 0xFFFF); - if (i < sizeof(pkey_slot)/sizeof(*pkey_slot) && pkey_slot[i].pkey_handle.handle == handle.handle) + if (i < sizeof(pkey_slot)/sizeof(*pkey_slot) && pkey_slot[i].pkey.handle == handle.handle) slot = &pkey_slot[i]; #endif @@ -334,7 +334,7 @@ static hal_error_t pkey_local_load(const hal_client_handle_t client, return err; } - *pkey = slot->pkey_handle; + *pkey = slot->pkey; *name = slot->name; return HAL_OK; } @@ -364,7 +364,7 @@ static hal_error_t pkey_local_open(const hal_client_handle_t client, slot->session_handle = session; if ((err = hal_ks_fetch(hal_ks_token, slot, NULL, NULL, 0)) == HAL_OK) - slot->pkey_handle.handle |= HAL_PKEY_HANDLE_TOKEN_FLAG; + slot->pkey.handle |= HAL_PKEY_HANDLE_TOKEN_FLAG; else if (err == HAL_ERROR_KEY_NOT_FOUND) err = hal_ks_fetch(hal_ks_volatile, slot, NULL, NULL, 0); @@ -372,7 +372,7 @@ static hal_error_t pkey_local_open(const hal_client_handle_t client, if (err != HAL_OK) goto fail; - *pkey = slot->pkey_handle; + *pkey = slot->pkey; return HAL_OK; fail: @@ -434,7 +434,7 @@ static hal_error_t pkey_local_generate_rsa(const hal_client_handle_t client, return err; } - *pkey = slot->pkey_handle; + *pkey = slot->pkey; *name = slot->name; return HAL_OK; } @@ -492,7 +492,7 @@ static hal_error_t pkey_local_generate_ec(const hal_client_handle_t client, return err; } - *pkey = slot->pkey_handle; + *pkey = slot->pkey; *name = slot->name; return HAL_OK; } -- cgit v1.2.3 From 0c8117baa316d44af2d33775b3c126ac0c7fa0e0 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Mon, 29 May 2017 13:30:17 -0400 Subject: Missed a few references to old pkey_slot field names, oops. --- rpc_pkey.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/rpc_pkey.c b/rpc_pkey.c index 63bc8bd..ff61580 100644 --- a/rpc_pkey.c +++ b/rpc_pkey.c @@ -323,11 +323,11 @@ static hal_error_t pkey_local_load(const hal_client_handle_t client, if ((err = hal_uuid_gen(&slot->name)) != HAL_OK) return err; - slot->client_handle = client; - slot->session_handle = session; - slot->type = type; - slot->curve = curve; - slot->flags = flags; + slot->client = client; + slot->session = session; + slot->type = type; + slot->curve = curve; + slot->flags = flags; if ((err = hal_ks_store(ks_from_flags(flags), slot, der, der_len)) != HAL_OK) { slot->type = HAL_KEY_TYPE_NONE; @@ -359,9 +359,9 @@ static hal_error_t pkey_local_open(const hal_client_handle_t client, if ((slot = alloc_slot(0)) == NULL) return HAL_ERROR_NO_KEY_SLOTS_AVAILABLE; - slot->name = *name; - slot->client_handle = client; - slot->session_handle = session; + slot->name = *name; + slot->client = client; + slot->session = session; if ((err = hal_ks_fetch(hal_ks_token, slot, NULL, NULL, 0)) == HAL_OK) slot->pkey.handle |= HAL_PKEY_HANDLE_TOKEN_FLAG; @@ -408,11 +408,11 @@ static hal_error_t pkey_local_generate_rsa(const hal_client_handle_t client, if ((err = hal_uuid_gen(&slot->name)) != HAL_OK) return err; - slot->client_handle = client; - slot->session_handle = session; - slot->type = HAL_KEY_TYPE_RSA_PRIVATE; - slot->curve = HAL_CURVE_NONE; - slot->flags = flags; + slot->client = client; + slot->session = session; + slot->type = HAL_KEY_TYPE_RSA_PRIVATE; + slot->curve = HAL_CURVE_NONE; + slot->flags = flags; if ((err = hal_rsa_key_gen(NULL, &key, keybuf, sizeof(keybuf), key_length / 8, public_exponent, public_exponent_len)) != HAL_OK) { @@ -467,11 +467,11 @@ static hal_error_t pkey_local_generate_ec(const hal_client_handle_t client, if ((err = hal_uuid_gen(&slot->name)) != HAL_OK) return err; - slot->client_handle = client; - slot->session_handle = session; - slot->type = HAL_KEY_TYPE_EC_PRIVATE; - slot->curve = curve; - slot->flags = flags; + slot->client = client; + slot->session = session; + slot->type = HAL_KEY_TYPE_EC_PRIVATE; + slot->curve = curve; + slot->flags = flags; if ((err = hal_ecdsa_key_gen(NULL, &key, keybuf, sizeof(keybuf), curve)) != HAL_OK) { slot->type = HAL_KEY_TYPE_NONE; @@ -526,7 +526,7 @@ static hal_error_t pkey_local_delete(const hal_pkey_handle_t pkey) hal_error_t err; - if ((err = check_writable(slot->client_handle, slot->flags)) != HAL_OK) + if ((err = check_writable(slot->client, slot->flags)) != HAL_OK) return err; err = hal_ks_delete(ks_from_flags(slot->flags), slot); @@ -1077,7 +1077,7 @@ static hal_error_t pkey_local_set_attributes(const hal_pkey_handle_t pkey, hal_error_t err; - if ((err = check_writable(slot->client_handle, slot->flags)) != HAL_OK) + if ((err = check_writable(slot->client, slot->flags)) != HAL_OK) return err; return hal_ks_set_attributes(ks_from_flags(slot->flags), slot, attributes, attributes_len); -- cgit v1.2.3 From ade02ebabf88481bc763ea6a9b2f97e87f0a6210 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Mon, 29 May 2017 14:51:14 -0400 Subject: Adjust "bloat" unit tests for ks9 keystore parameters. --- unit-tests.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/unit-tests.py b/unit-tests.py index a304205..9ebf91e 100644 --- a/unit-tests.py +++ b/unit-tests.py @@ -671,17 +671,36 @@ class TestPKeyAttribute(TestCaseLoggedIn): for j in xrange(n_attrs))) pinwheel() + # These sizes work with a 4096-byte keystore block; if you tweak + # the undelrying block size, you may need to tweak these tests too. + + def test_attribute_svelt_volatile_many(self): + self.load_and_fill(0, n_attrs = 64) + def test_attribute_bloat_volatile_many(self): - self.load_and_fill(0, n_attrs = 128) # 192 + with self.assertRaises(HAL_ERROR_RESULT_TOO_LONG): + self.load_and_fill(0, n_attrs = 128) + + def test_attribute_svelt_volatile_big(self): + self.load_and_fill(0, n_attrs = 6, n_fill = 256) def test_attribute_bloat_volatile_big(self): - self.load_and_fill(0, n_attrs = 6, n_fill = 512) + with self.assertRaises(HAL_ERROR_RESULT_TOO_LONG): + self.load_and_fill(0, n_attrs = 6, n_fill = 512) + + def test_attribute_svelt_token_many(self): + self.load_and_fill(HAL_KEY_FLAG_TOKEN, n_attrs = 64) def test_attribute_bloat_token_many(self): - self.load_and_fill(HAL_KEY_FLAG_TOKEN, n_attrs = 128) + with self.assertRaises(HAL_ERROR_RESULT_TOO_LONG): + self.load_and_fill(HAL_KEY_FLAG_TOKEN, n_attrs = 128) + + def test_attribute_svelt_token_big(self): + self.load_and_fill(HAL_KEY_FLAG_TOKEN, n_attrs = 6, n_fill = 256) def test_attribute_bloat_token_big(self): - self.load_and_fill(HAL_KEY_FLAG_TOKEN, n_attrs = 4, n_fill = 512) # [16, 1024] + with self.assertRaises(HAL_ERROR_RESULT_TOO_LONG): + self.load_and_fill(HAL_KEY_FLAG_TOKEN, n_attrs = 6, n_fill = 512) @unittest.skipUnless(ecdsa_loaded, "Requires Python ECDSA package") -- cgit v1.2.3 From b9188794e2634aa4918ba46298b88f03f2454dd4 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Tue, 30 May 2017 19:26:50 -0400 Subject: Missed one instance of one API change in "logout" branch merge. --- ks_volatile.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ks_volatile.c b/ks_volatile.c index 57806e8..2b5bb61 100644 --- a/ks_volatile.c +++ b/ks_volatile.c @@ -235,8 +235,8 @@ static hal_error_t ks_volatile_logout(hal_ks_t *ks, if (db->keys[b].client.handle != client.handle) continue; - if ((err = hal_ks_index_delete(ks, &ks->names[b], 0, NULL, &hint)) != HAL_OK || - (err = hal_ks_block_zero(ks, b)) != HAL_OK) + if ((err = hal_ks_index_delete(ks, &ks->names[b], NULL, &hint)) != HAL_OK || + (err = hal_ks_block_zero(ks, b)) != HAL_OK) return err; i--; -- cgit v1.2.3 From 6b881dfa81a0d51d4897c62de5abdb94c1aba0b7 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Tue, 30 May 2017 19:52:32 -0400 Subject: Hold keystore lock before calling keystore driver methods. Most keystore methods already followed this rule, but hal_ks_*_init() and hal_ks_*_logout() were confused, in different ways. --- ks.c | 34 ++++++++++++++++++++++++++-------- ks_token.c | 26 ++++++-------------------- ks_volatile.c | 29 +++++++++++------------------ 3 files changed, 43 insertions(+), 46 deletions(-) diff --git a/ks.c b/ks.c index 92dc303..665a2fd 100644 --- a/ks.c +++ b/ks.c @@ -205,10 +205,19 @@ hal_error_t hal_ks_block_update(hal_ks_t *ks, hal_error_t hal_ks_init(hal_ks_t *ks, const int alloc) { - return - ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : - ks->driver->init == NULL ? HAL_ERROR_NOT_IMPLEMENTED : - ks->driver->init(ks, alloc); + if (ks == NULL || ks->driver == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (ks->driver->init == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + + hal_ks_lock(); + + const hal_error_t err = ks->driver->init(ks, alloc); + + hal_ks_unlock(); + + return err; } static inline void *gnaw(uint8_t **mem, size_t *len, const size_t size) @@ -466,10 +475,19 @@ hal_error_t hal_ks_init_common(hal_ks_t *ks) hal_error_t hal_ks_logout(hal_ks_t *ks, const hal_client_handle_t client) { - return - ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS : - ks->driver->logout == NULL ? HAL_ERROR_NOT_IMPLEMENTED : - ks->driver->logout(ks, client); + if (ks == NULL || ks->driver == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + if (ks->driver->logout == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + + hal_ks_lock(); + + const hal_error_t err = ks->driver->logout(ks, client); + + hal_ks_unlock(); + + return err; } /* diff --git a/ks_token.c b/ks_token.c index e29a90d..38ca5d8 100644 --- a/ks_token.c +++ b/ks_token.c @@ -313,13 +313,11 @@ static hal_error_t ks_token_init(hal_ks_t *ks, const int alloc) hal_ks_block_t *block = NULL; hal_error_t err = HAL_OK; - hal_ks_lock(); - if (alloc && (err = hal_ks_alloc_common(ks, NUM_FLASH_BLOCKS, KS_TOKEN_CACHE_SIZE, NULL, 0)) != HAL_OK) - goto done; + return err; if ((err = hal_ks_init_common(ks)) != HAL_OK) - goto done; + return err; /* * Fetch or create the PIN block. @@ -337,10 +335,7 @@ static hal_error_t ks_token_init(hal_ks_t *ks, const int alloc) db->user_pin = block->pin.user_pin; } - else if (err != HAL_ERROR_KEY_NOT_FOUND) - goto done; - - else { + else if (err == HAL_ERROR_KEY_NOT_FOUND) { /* * We found no PIN block, so create one, with the user and so PINs * cleared and the wheel PIN set to the last-gasp value. The @@ -351,10 +346,8 @@ static hal_error_t ks_token_init(hal_ks_t *ks, const int alloc) unsigned b; - if ((block = hal_ks_cache_pick_lru(ks)) == NULL) { - err = HAL_ERROR_IMPOSSIBLE; - goto done; - } + if ((block = hal_ks_cache_pick_lru(ks)) == NULL) + return HAL_ERROR_IMPOSSIBLE; memset(block, 0xFF, sizeof(*block)); @@ -366,22 +359,15 @@ static hal_error_t ks_token_init(hal_ks_t *ks, const int alloc) block->pin.user_pin = db->user_pin; if ((err = hal_ks_index_add(ks, &hal_ks_pin_uuid, &b, NULL)) != HAL_OK) - goto done; + return err; hal_ks_cache_mark_used(ks, block, b); err = ks_token_write(ks, b, block); hal_ks_cache_release(ks, block); - - if (err != HAL_OK) - goto done; } - err = HAL_OK; - - done: - hal_ks_unlock(); return err; } diff --git a/ks_volatile.c b/ks_volatile.c index 2b5bb61..1586f3d 100644 --- a/ks_volatile.c +++ b/ks_volatile.c @@ -254,35 +254,28 @@ static hal_error_t ks_volatile_init(hal_ks_t *ks, const int alloc) if (ks != hal_ks_volatile) return HAL_ERROR_IMPOSSIBLE; - hal_error_t err = HAL_OK; void *mem = NULL; + hal_error_t err; - hal_ks_lock(); + if (alloc && + (err = hal_ks_alloc_common(ks, STATIC_KS_VOLATILE_SLOTS, KS_VOLATILE_CACHE_SIZE, + &mem, sizeof(*db->keys) * STATIC_KS_VOLATILE_SLOTS)) != HAL_OK) + return err; - if (alloc) { - if ((err = hal_ks_alloc_common(ks, STATIC_KS_VOLATILE_SLOTS, KS_VOLATILE_CACHE_SIZE, - &mem, sizeof(*db->keys) * STATIC_KS_VOLATILE_SLOTS)) != HAL_OK) - goto done; + if (alloc) db->keys = mem; - } - if (db->keys == NULL) { - err = HAL_ERROR_IMPOSSIBLE; - goto done; - } + if (db->keys == NULL) + return HAL_ERROR_IMPOSSIBLE; for (unsigned b = 0; b < db->ks.size; b++) if ((err = hal_ks_block_erase(ks, b)) != HAL_OK) - goto done; + return err; if ((err = hal_ks_init_common(ks)) != HAL_OK) - goto done; - - err = HAL_OK; + return err; - done: - hal_ks_unlock(); - return err; + return HAL_OK; } /* -- cgit v1.2.3 From 35a88083a9936b2ed3d0091c0461530be81287c1 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Wed, 31 May 2017 01:41:38 -0400 Subject: Automatic logout when client disconnects or muxd restarts. The HSM itself should be detecting carrier drop on its RPC port, but I haven't figured out where the DCD bit is hiding in the STM32 UART API, and the muxd has to be involved in this in any case, since only the muxd knows when an individual client connection has dropped. So, for the moment, we handle all of this in the muxd. --- cryptech_muxd | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/cryptech_muxd b/cryptech_muxd index d5de227..d306eaf 100755 --- a/cryptech_muxd +++ b/cryptech_muxd @@ -58,6 +58,8 @@ import tornado.queues import tornado.locks import tornado.gen +from cryptech.libhal import HAL_OK, RPC_FUNC_GET_VERSION, RPC_FUNC_LOGOUT, RPC_FUNC_LOGOUT_ALL + logger = logging.getLogger("cryptech_muxd") @@ -89,6 +91,10 @@ def client_handle_set(msg, handle): return msg[:4] + struct.pack(">L", handle) + msg[8:] +logout_msg = struct.pack(">LL", RPC_FUNC_LOGOUT, 0) +logout_all_msg = struct.pack(">LL", RPC_FUNC_LOGOUT_ALL, 0) + + class SerialIOStream(tornado.iostream.BaseIOStream): """ Implementation of a Tornado IOStream over a PySerial device. @@ -157,10 +163,11 @@ class RPCIOStream(SerialIOStream): self.rpc_input_lock = tornado.locks.Lock() @tornado.gen.coroutine - def rpc_input(self, query, handle, queue): + def rpc_input(self, query, handle = 0, queue = None): "Send a query to the HSM." logger.debug("RPC send: %s", ":".join("{:02x}".format(ord(c)) for c in query)) - self.queues[handle] = queue + if queue is not None: + self.queues[handle] = queue with (yield self.rpc_input_lock.acquire()): yield self.write(query) logger.debug("RPC sent") @@ -182,13 +189,18 @@ class RPCIOStream(SerialIOStream): continue try: handle = client_handle_get(slip_decode(reply)) - queue = self.queues[handle] except: logger.debug("RPC skipping bad packet") continue - logger.debug("RPC queue put: handle 0x%x, qsize %s, maxsize %s", - handle, queue.qsize(), queue.maxsize) - queue.put_nowait(reply) + if handle not in self.queues: + logger.debug("RPC ignoring response: handle 0x%x", handle) + continue + logger.debug("RPC queue put: handle 0x%x, qsize %s", handle, self.queues[handle].qsize()) + self.queues[handle].put_nowait(reply) + + def logout_all(self): + "Execute an RPC LOGOUT_ALL operation." + return self.rpc_input(slip_encode(logout_all_msg)) class QueuedStreamClosedError(tornado.iostream.StreamClosedError): @@ -203,7 +215,7 @@ class RPCServer(PFUnixServer): @tornado.gen.coroutine def handle_stream(self, stream, address): "Handle one network connection." - handle = stream.socket.fileno() + handle = self.next_client_handle() queue = tornado.queues.Queue() logger.info("RPC connected %r, handle 0x%x", stream, handle) while True: @@ -223,8 +235,18 @@ class RPCServer(PFUnixServer): except tornado.iostream.StreamClosedError: logger.info("RPC closing %r, handle 0x%x", stream, handle) stream.close() + query = slip_encode(client_handle_set(logout_msg, handle)) + yield self.serial.rpc_input(query, handle) return + client_handle = int(time.time()) << 4 + + @classmethod + def next_client_handle(cls): + cls.client_handle += 1 + cls.client_handle &= 0xFFFFFFFF + return cls.client_handle + class CTYIOStream(SerialIOStream): """ @@ -331,8 +353,8 @@ class ProbeIOStream(SerialIOStream): @tornado.gen.coroutine def run_probe(self): - RPC_query = chr(0) * 8 # client_handle = 0, function code = RPC_FUNC_GET_VERSION - RPC_reply = chr(0) * 12 # opcode = RPC_FUNC_GET_VERSION, client_handle = 0, valret = HAL_OK + RPC_query = struct.pack(">LL", RPC_FUNC_GET_VERSION, 0) + RPC_reply = struct.pack(">LLL", RPC_FUNC_GET_VERSION, 0, HAL_OK) probe_string = SLIP_END + Control_U + SLIP_END + RPC_query + SLIP_END + Control_U + Control_M @@ -434,6 +456,7 @@ def main(): rpc_stream = RPCIOStream(device = args.rpc_device) rpc_server = RPCServer(rpc_stream, args.rpc_socket) futures.append(rpc_stream.rpc_output_loop()) + futures.append(rpc_stream.logout_all()) if args.cty_device is None: logger.warn("No CTY device found") -- cgit v1.2.3 From 6a47490407210471afdd80f009123bd72014db3a Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Fri, 2 Jun 2017 12:26:10 -0400 Subject: Fix AESKeywrapWithPadding handling of very long messages. We were XORing the low 32 bits of R[0] instead of the full 64 bits. Makes no difference for small values of n, so we never detected it. --- unit-tests.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/unit-tests.py b/unit-tests.py index 9ebf91e..8b86d44 100644 --- a/unit-tests.py +++ b/unit-tests.py @@ -1035,6 +1035,10 @@ class AESKeyWrapWithPadding(object): step = -1 if start > stop else 1 return xrange(start, stop + step, step) + @staticmethod + def _xor(R0, t): + return pack(">Q", unpack(">Q", R0)[0] ^ t) + def wrap(self, Q): "RFC 5649 section 4.1." m = len(Q) # Plaintext length @@ -1051,9 +1055,7 @@ class AESKeyWrapWithPadding(object): for j in self._start_stop(0, 5): for i in self._start_stop(1, n): R[0], R[i] = self._encrypt(R[0], R[i]) - W0, W1 = unpack(">LL", R[0]) - W1 ^= n * j + i - R[0] = pack(">LL", W0, W1) + R[0] = self._xor(R[0], n * j + i) assert len(R) == (n + 1) and all(len(r) == 8 for r in R) return "".join(R) @@ -1070,9 +1072,7 @@ class AESKeyWrapWithPadding(object): # RFC 3394 section 2.2.2 steps (1), (2), and part of (3) for j in self._start_stop(5, 0): for i in self._start_stop(n, 1): - W0, W1 = unpack(">LL", R[0]) - W1 ^= n * j + i - R[0] = pack(">LL", W0, W1) + R[0] = self._xor(R[0], n * j + i) R[0], R[i] = self._decrypt(R[0], R[i]) magic, m = unpack(">LL", R[0]) if magic != 0xa65959a6: -- cgit v1.2.3 From 61029eb57165c181497c09549cc2dd0fa9928f16 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Sat, 3 Jun 2017 10:56:47 -0400 Subject: Add --soft-backup option to cryptech_backup. cryptech_backup is designed to help the user transfer keys from one Cryptech HSM to another, but what is is a user who has no second HSM supposed to do for backup? The --soft-backup option enables a mode in which cryptech_backup generates its own KEKEK instead of getting one from the (nonexistent) target HSM. We make a best-effort attempt to keep this soft KEKEK secure, by wrapping it with a symmetric key derived from a passphrase, using AESKeyWrapWithPadding and PBKDF2, but there's a limit to what a software-only solution can do here. The --soft-backup code depends (heavily) on PyCrypto. --- cryptech_backup | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 183 insertions(+), 4 deletions(-) diff --git a/cryptech_backup b/cryptech_backup index f14f119..76cdfbb 100755 --- a/cryptech_backup +++ b/cryptech_backup @@ -17,6 +17,15 @@ sure only to export keys using a KEKEK known to have been generated by the target HSM. See the unit tests in the source repository for an example of how to fake this in a few lines of Python. +We also implement a software-based variant on this backup mechanism, +for cases where there is no second HSM. The protocol is much the +same, but the KEKEK is generated in software and encrypted using a +symmetric key derived from a passphrase using PBKDF2. This requires +the PyCrypto library, and is only as secure as memory on the machine +where you're running it (so it's theoretically vulnerable to root or +anybody with access to /dev/mem). Don't use this mode unless you +understand the risks, and see the "NOTE WELL" above. + YOU HAVE BEEN WARNED. Be careful out there. """ @@ -72,6 +81,11 @@ def main(): "-u", "--uuid", help = "UUID of existing KEKEK to use") + setup_mutex_group.add_argument( + "-s", "--soft-backup", + action = "store_true", + help = "software-based backup, see warnings") + setup_parser.add_argument( "-k", "--keylen", type = int, @@ -147,9 +161,11 @@ def cmd_setup(args, hsm): """ result = {} + uuids = [] - uuids = [] - if args.uuid: + if args.soft_backup: + SoftKEKEK.generate(args, result) + elif args.uuid: uuids.append(args.uuid) elif not args.new: uuids.extend(hsm.pkey_match( @@ -177,7 +193,11 @@ def cmd_setup(args, hsm): if not result: sys.exit("Could not find suitable KEKEK") - result.update(comment = "KEKEK public key") + if args.soft_backup: + result.update(comment = "KEKEK software keypair") + else: + result.update(comment = "KEKEK public key") + json.dump(result, args.output, indent = 4, sort_keys = True) args.output.write("\n") @@ -249,7 +269,14 @@ def cmd_import(args, hsm): """ db = json.load(args.input) - with hsm.pkey_open(uuid.UUID(db["kekek_uuid"]).bytes) as kekek: + + soft_key = SoftKEKEK.is_soft_key(db) + + with (hsm.pkey_load(SoftKEKEK.recover(db), HAL_KEY_FLAG_USAGE_KEYENCIPHERMENT) + if soft_key else + hsm.pkey_open(uuid.UUID(db["kekek_uuid"]).bytes) + ) as kekek: + for k in db["keys"]: pkcs8 = b64join(k.get("pkcs8", "")) spki = b64join(k.get("spki", "")) @@ -262,6 +289,158 @@ def cmd_import(args, hsm): with hsm.pkey_load(der = spki, flags = flags) as pkey: print "Loaded {} as {}".format(k["uuid"], pkey.uuid) + if soft_key: + kekek.delete() + + +class AESKeyWrapWithPadding(object): + """ + Implementation of AES Key Wrap With Padding from RFC 5649. + """ + + class UnwrapError(Exception): + "Something went wrong during unwrap." + + def __init__(self, key): + from Crypto.Cipher import AES + self.ctx = AES.new(key, AES.MODE_ECB) + + def _encrypt(self, b1, b2): + aes_block = self.ctx.encrypt(b1 + b2) + return aes_block[:8], aes_block[8:] + + def _decrypt(self, b1, b2): + aes_block = self.ctx.decrypt(b1 + b2) + return aes_block[:8], aes_block[8:] + + @staticmethod + def _start_stop(start, stop): # Syntactic sugar + step = -1 if start > stop else 1 + return xrange(start, stop + step, step) + + @staticmethod + def _xor(R0, t): + from struct import pack, unpack + return pack(">Q", unpack(">Q", R0)[0] ^ t) + + def wrap(self, Q): + "RFC 5649 section 4.1." + from struct import pack + m = len(Q) # Plaintext length + if m % 8 != 0: # Pad Q if needed + Q += "\x00" * (8 - (m % 8)) + R = [pack(">LL", 0xa65959a6, m)] # Magic MSB(32,A), build LSB(32,A) + R.extend(Q[i : i + 8] # Append Q + for i in xrange(0, len(Q), 8)) + n = len(R) - 1 + if n == 1: + R[0], R[1] = self._encrypt(R[0], R[1]) + else: + # RFC 3394 section 2.2.1 + for j in self._start_stop(0, 5): + for i in self._start_stop(1, n): + R[0], R[i] = self._encrypt(R[0], R[i]) + R[0] = self._xor(R[0], n * j + i) + assert len(R) == (n + 1) and all(len(r) == 8 for r in R) + return "".join(R) + + def unwrap(self, C): + "RFC 5649 section 4.2." + from struct import unpack + if len(C) % 8 != 0: + raise self.UnwrapError("Ciphertext length {} is not an integral number of blocks" + .format(len(C))) + n = (len(C) / 8) - 1 + R = [C[i : i + 8] for i in xrange(0, len(C), 8)] + if n == 1: + R[0], R[1] = self._decrypt(R[0], R[1]) + else: + # RFC 3394 section 2.2.2 steps (1), (2), and part of (3) + for j in self._start_stop(5, 0): + for i in self._start_stop(n, 1): + R[0] = self._xor(R[0], n * j + i) + R[0], R[i] = self._decrypt(R[0], R[i]) + magic, m = unpack(">LL", R[0]) + if magic != 0xa65959a6: + raise self.UnwrapError("Magic value in AIV should have been 0xa65959a6, was 0x{:02x}" + .format(magic)) + if m <= 8 * (n - 1) or m > 8 * n: + raise self.UnwrapError("Length encoded in AIV out of range: m {}, n {}".format(m, n)) + R = "".join(R[1:]) + assert len(R) == 8 * n + if any(r != "\x00" for r in R[m:]): + raise self.UnwrapError("Nonzero trailing bytes {}".format(R[m:].encode("hex"))) + return R[:m] + + +class SoftKEKEK(object): + """ + Wrapper around all the goo we need to implement soft backups. + Requires PyCrypto on about every other line. + """ + + oid_aesKeyWrap = "\x60\x86\x48\x01\x65\x03\x04\x01\x30" + + def parse_EncryptedPrivateKeyInfo(self, der): + from Crypto.Util.asn1 import DerObject, DerSequence, DerOctetString, DerObjectId + encryptedPrivateKeyInfo = DerSequence() + encryptedPrivateKeyInfo.decode(der) + encryptionAlgorithm = DerSequence() + algorithm = DerObjectId() + encryptedData = DerOctetString() + encryptionAlgorithm.decode(encryptedPrivateKeyInfo[0]) + DerObject.decode(algorithm, encryptionAlgorithm[0]) + DerObject.decode(encryptedData, encryptedPrivateKeyInfo[1]) + if algorithm.payload != self.oid_aesKeyWrap: + raise ValueError + return encryptedData.payload + + def encode_EncryptedPrivateKeyInfo(self, der): + from Crypto.Util.asn1 import DerSequence, DerOctetString + return DerSequence([ + DerSequence([ + chr(0x06) + chr(len(self.oid_aesKeyWrap)) + self.oid_aesKeyWrap + ]).encode(), + DerOctetString(der).encode() + ]).encode() + + def gen_salt(self, bytes = 16): + from Crypto import Random + return Random.new().read(bytes) + + def wrapper(self, salt, keylen = 256, iterations = 8000): + from Crypto.Protocol.KDF import PBKDF2 + from Crypto.Hash import SHA256, HMAC + return AESKeyWrapWithPadding(PBKDF2( + password = getpass.getpass("KEKEK Passphrase: "), + salt = salt, + dkLen = keylen/8, + count = iterations, + prf = lambda p, s: HMAC.new(p, s, SHA256).digest())) + + @classmethod + def is_soft_key(cls, db): + return all(k in db for k in ("kekek_pkcs8", "kekek_salt")) + + @classmethod + def generate(cls, args, result): + from Crypto.PublicKey import RSA + self = cls() + k = RSA.generate(args.keylen) + salt = self.gen_salt() + spki = k.publickey().exportKey(format = "DER") + pkcs8 = self.encode_EncryptedPrivateKeyInfo(self.wrapper(salt).wrap( + k.exportKey(format = "DER", pkcs = 8))) + result.update(kekek_salt = b64(salt), + kekek_pkcs8 = b64(pkcs8), + kekek_pubkey = b64(spki)) + + @classmethod + def recover(cls, db): + self = cls() + return self.wrapper(b64join(db["kekek_salt"])).unwrap( + self.parse_EncryptedPrivateKeyInfo(b64join(db["kekek_pkcs8"]))) + if __name__ == "__main__": main() -- cgit v1.2.3 From a83d9dfba5f882ca75eaab9a166e6ad9794f2f90 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Sun, 4 Jun 2017 12:21:45 -0400 Subject: Tweak CRC input to be backwards compatabile with ksng. Except for torture tests, we never really used the hideously complex multi-block capabilities of the ksng version of the flash keystore, among other reasons because the only keys large enough to trigger the multi-block code were slow enough to constitute torture on their own. So we can preserve backwards compatabliity simply by including the former *chunk fields (renamed legacy* here) in the CRC and checking for the expected single-block key values. We probably want to include everything in the CRC in any case except when there's an explicit reason omit something, so, this is cheap, just a bit obscure. At some point in the future we can phase out support for the backwards compatible values, but there's no particular hurry about it unless we want to reuse those fields for some other purpose. --- ks.c | 17 ++++++++++++++--- ks.h | 13 +++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/ks.c b/ks.c index 665a2fd..a4e7498 100644 --- a/ks.c +++ b/ks.c @@ -130,6 +130,12 @@ hal_crc32_t hal_ks_block_calculate_crc(const hal_ks_block_t * const block) crc = hal_crc32_update(crc, &block->header.block_type, sizeof(block->header.block_type)); + crc = hal_crc32_update(crc, &block->header.legacy_1, + sizeof(block->header.legacy_1)); + + crc = hal_crc32_update(crc, &block->header.legacy_2, + sizeof(block->header.legacy_2)); + crc = hal_crc32_update(crc, block->bytes + sizeof(hal_ks_block_header_t), sizeof(*block) - sizeof(hal_ks_block_header_t)); @@ -310,11 +316,16 @@ hal_error_t hal_ks_init_common(hal_ks_t *ks) if (err == HAL_ERROR_KEYSTORE_BAD_CRC || err == HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE) block_types[i] = HAL_KS_BLOCK_TYPE_UNKNOWN; - else if (err == HAL_OK) - block_types[i] = hal_ks_block_get_type(block); + else if (err != HAL_OK) + return err; + + else if ((block->header.legacy_1 != 0xFF || block->header.legacy_2 != 0xFF) && + (block->header.legacy_1 != 0x01 || block->header.legacy_2 != 0x00)) + block_types[i] = HAL_KS_BLOCK_TYPE_UNKNOWN; else - return err; + block_types[i] = hal_ks_block_get_type(block); + switch (block_types[i]) { case HAL_KS_BLOCK_TYPE_KEY: diff --git a/ks.h b/ks.h index 1c09b53..b95216d 100644 --- a/ks.h +++ b/ks.h @@ -86,13 +86,22 @@ typedef enum { } hal_ks_block_status_t; /* - * Common header for all keystore block types. - * A few of these fields are deliberately omitted from the CRC. + * Common header for all keystore block types. A few of these fields + * are deliberately omitted from the CRC. + * + * The legacy_1 and legacy_2 fields were used in the more complex + * "chunked" layout used in an earlier iteration of this keystore + * design, which proved more complex than it was worth. At the + * moment, the only thing we do with these fields is include them in + * the CRC and check them for allowed values, to avoid gratuitously + * breaking backwards compatability with the earlier design. */ typedef struct { uint8_t block_type; uint8_t block_status; + uint8_t legacy_1; + uint8_t legacy_2; hal_crc32_t crc; } hal_ks_block_header_t; -- cgit v1.2.3 From 62ee3290179c830cdc99474eded0f789ca14d940 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Sun, 4 Jun 2017 12:34:48 -0400 Subject: Include file dependencies were badly out of date. --- Makefile | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index f3a5979..866cb2b 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,6 @@ STATIC_HMAC_STATE_BLOCKS = 16 STATIC_PKEY_STATE_BLOCKS = 256 STATIC_KS_VOLATILE_SLOTS = 128 -INC = hal.h hal_internal.h LIB = libhal.a # Error checking on known control options, some of which allow the user entirely too much rope. @@ -259,16 +258,19 @@ daemon: mixed .PHONY: client mixed server serial daemon -${OBJ}: ${INC} - ${LIB}: ${OBJ} ${AR} rcs $@ $^ -asn1.o rsa.o ecdsa.o: asn1_internal.h -ecdsa.o: ecdsa_curves.h -novena-eim.o hal_io_eim.o: novena-eim.h -slip.o rpc_client_serial.o rpc_server_serial.o: slip_internal.h -ks_token.o: last_gasp_pin_internal.h +asn1.o rsa.o ecdsa.o: asn1_internal.h +ecdsa.o: ecdsa_curves.h +${OBJ}: hal.h +${OBJ}: hal_internal.h +ks.o ks_token.o ks_volatile.o ks_attribute.o ks_index.o: ks.h +ks_token.o: last_gasp_pin_internal.h +novena-eim.o hal_io_eim.o: novena-eim.h +slip.o rpc_client_serial.o rpc_server_serial.o: slip_internal.h +${OBJ}: verilog_constants.h +rpc_client.o rpc_server.o xdr.o: xdr_internal.h last_gasp_pin_internal.h: ./utils/last_gasp_default_pin >$@ -- cgit v1.2.3 From 6dcfc197e4d7fa3c74dc0adc06cd206051367862 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Thu, 8 Jun 2017 16:21:58 -0400 Subject: Document the keystore design. --- README.md | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/README.md b/README.md index 39de7b6..ba68f3c 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ math for ECDSA verification; ECDSA math for key generation and signing on the P-256 and P-384 curves is done in the ECDSA base point multiplier cores when those are available. + ## RSA ## The RSA implementation includes a compile-time option to bypass the @@ -72,6 +73,7 @@ a tad slow at the moment (others are hard at work fixing this). The RSA implementation includes optional blinding (enabled by default). + ## ECDSA ## The ECDSA implementation is specific to the NIST prime curves P-256, @@ -112,6 +114,92 @@ point arithmetic is performed in Jacobian projective coordinates, with the coordinates themselves in Montgomery form; final mapping back to affine coordinates also handles the final Montgomery reduction. + +## Keystore ## + +The keystore is basically a light-weight database intended to be run +directly over some kind of block-access device, with an internal +low-level driver interface so that we can use the same API for +multiple keystore devices (eg, flash for "token objects" and RAM for +"session objects", in the PKCS #11 senses of those terms). + +The available storage is divided up into "blocks" of a fixed size; for +simplicity, the block size is a multiple of the subsector size of the +flash chip on the Alpha platform, since that's the minimum erasable +unit. All state stored in the keystore itself follows the conventions +needed for flash devices, whether the device in question is flash or +not. The basic rule here is that one can only clear bits, never set +them: the only way to set a bit is to erase the whole block and start +over. So blocks progress from an initial state ("erased") where all +bits are set to one, through several states where the block contains +useful data, and ending in a state where all bits are set to zero +("zeroed"), because that's the way that flash hardware works. + +The keystore implementation also applies a light-weight form of wear +leveling to all keystore devices, whether they're flash devices or +not. The wear-leveling mechanism is not particularly sophisticated, +but should suffice. The wear-leveling code treats the entirety of a +particular keystore device as a ring buffer of blocks, and keeps track +of which blocks have been used recently by zeroing blocks upon freeing +them rather than erasing them immediately, while also always keeping +the block at the current head of the free list in the erased state. +Taken together, this is enough to recover location of the block at the +head of the free list after a reboot, which is sufficient for a +round-robin wear leveling strategy. + +The block format includes a field for a CRC-32 checksum, which covers +the entire block except for a few specific fields which need to be +left out. On reboot, blocks with bad CRC-32 values are considered +candidates for reuse, but are placed at the end of the free list, +preserve their contents for as long as possible in case the real +problem is a buggy firmware update. + +At the moment, the decision about whether to use the CRC-32 mechanism +is up to the individual driver: the flash driver uses it, the RAM +driver (which never stores anything across reboots anyway) does not. + +Since the flash-like semantics do not allow setting bits, updates to a +block always consist of allocating a new block and copying the +modified data. The keystore code uses a trivial lock-step protocol +for this: first: + +1. The old block is marked as a "tombstone"; +2. The new block (with modified data) is written; +3. The old block is erased. + +This protocol is deliberately as simple as possible, so that there is +always a simple recovery path on reboot. + +Active blocks within a keystore are named by UUIDs. With one +exception, these are always type-4 (random) UUIDs, generated directly +from output of the TRNG. The one exception is the current PIN block, +which always uses the reserved all-zeros UUID, which cannot possibly +conflict with a type-4 UUID (by definition). + +The core of the keystore mechanism is the `ks->index[]` array, which +contains nothing but a list of block numbers. This array is divided +into two parts: the first part is the index of active blocks, which is +kept sorted (by UUID); the second part is the round-robin free list. +Everything else in the keystore is indexed by these block numbers, +which means that the index array is the only data structure which the +keystore code needs to sort or rotate when adding, removing, or +updating a block. Because the block numbers themselves are small +integers, the index array itself is small enough that shuffling data +within it using `memmove()` is a relatively cheap operation, which in +turn avoids a lot of complexity that would be involved in managing +more sophisticated data structures. + +The keystore code includes both caching of recently used keystore +blocks (to avoid unnecessary flash reads) and caching of the location +of the block corresponding to a particular UUID (to avoid unnecessary +index searches). Aside from whatever direct performance benefits this +might bring, this also frees the pkey layer that sits directly on top +of the keystore code from needing to keep a lot of active state on +particular keystore objects, which is important given that this whole +thing sits under an RPC protocol driven by a client program which can +impose arbitrary delays between any two operations at the pkey layer. + + ## Key backup ## The key backup mechanism is a straightforward three-step process, @@ -134,6 +222,7 @@ Transfer of the wrapped keys between the two HSMs can be by any convenient mechanism; for simplicity, `cryptech_backup` script bundles everything up in a text file using JSON and Base64 encoding. + ## Multiplexer daemon ## While the C client library can be built to talk directly to the @@ -149,6 +238,7 @@ The multiplexer requires two external Python libraries, Tornado In the long run, the RPC mechanism will need to be wrapped in some kind of secure channel protocol, but we're not there yet. + ## API ## Yeah, we ought to document the API, Real Soon Now, perhaps using @@ -156,5 +246,6 @@ Yeah, we ought to document the API, Real Soon Now, perhaps using the Python definitions in cryptech.libhal, and and comments in the code. + [EFD]: http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html [Doxygen]: http://www.doxygen.org/ -- cgit v1.2.3