From 1f78f1bad3ab08706df3030936275b6114f31e24 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Thu, 10 Nov 2016 09:48:45 -0500 Subject: First cut at ks_flash support for attribute get/set/delete API. Passes minimal unit-testing and the same minimal tests report that it does deliver the desired performance speed-up. More testing and much cleanup still needed. Attribute API not quite stable yet, we're probably going to want to remove all the singleton attribute operations from the RPC protocol, and it turns out that ks_delete_attributes() has enough code in common with ks_set_attributes() that it makes more sense to handle the former as a special case of the latter. --- hal.h | 1 + hal_internal.h | 77 ++++++++-- ks_attribute.c | 19 +-- ks_flash.c | 459 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- libhal.py | 1 + unit-tests.py | 40 ++++- 6 files changed, 563 insertions(+), 34 deletions(-) diff --git a/hal.h b/hal.h index 74e0a51..59018ea 100644 --- a/hal.h +++ b/hal.h @@ -156,6 +156,7 @@ 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") \ END_OF_HAL_ERROR_LIST /* Marker to forestall silly line continuation errors */ diff --git a/hal_internal.h b/hal_internal.h index 6b82b9c..554ca62 100644 --- a/hal_internal.h +++ b/hal_internal.h @@ -575,34 +575,46 @@ extern const hal_ks_driver_t static inline hal_error_t hal_ks_init(const hal_ks_driver_t * const driver) { - if (driver == NULL || driver->init == NULL) + if (driver == NULL) return HAL_ERROR_BAD_ARGUMENTS; + if (driver->init == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + return driver->init(driver); } static inline hal_error_t hal_ks_shutdown(const hal_ks_driver_t * const driver) { - if (driver == NULL || driver->shutdown == NULL) + 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 || driver->open == NULL || ks == NULL) + 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 || ks->driver->close == NULL) + 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); } @@ -610,9 +622,12 @@ 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 || ks->driver->store == NULL || slot == NULL || der == NULL) + 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); } @@ -620,18 +635,24 @@ 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 || ks->driver->fetch == NULL || slot == NULL) + 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 || ks->driver->delete == NULL || slot == NULL) + 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); } @@ -642,9 +663,12 @@ static inline hal_error_t hal_ks_list(hal_ks_t *ks, unsigned *result_len, const unsigned result_max) { - if (ks == NULL || ks->driver == NULL || ks->driver->list == NULL) + if (ks == NULL || ks->driver == NULL) return HAL_ERROR_BAD_ARGUMENTS; + if (ks->driver->list == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + return ks->driver->list(ks, client, session, result, result_len, result_max); } @@ -661,9 +685,12 @@ static inline hal_error_t hal_ks_match(hal_ks_t *ks, const unsigned result_max, const hal_uuid_t * const previous_uuid) { - if (ks == NULL || ks->driver == NULL || ks->driver->match == NULL) + 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, flags, attributes, attributes_len, result, result_len, result_max, previous_uuid); } @@ -674,9 +701,12 @@ static inline hal_error_t hal_ks_set_attribute(hal_ks_t *ks, const uint8_t * const value, const size_t value_len) { - if (ks == NULL || ks->driver == NULL || ks->driver->set_attribute == NULL || slot == NULL) + if (ks == NULL || ks->driver == NULL || slot == NULL) return HAL_ERROR_BAD_ARGUMENTS; + if (ks->driver->set_attribute == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + return ks->driver->set_attribute(ks, slot, type, value, value_len); } @@ -687,9 +717,12 @@ static inline hal_error_t hal_ks_get_attribute(hal_ks_t *ks, size_t *value_len, const size_t value_max) { - if (ks == NULL || ks->driver == NULL || ks->driver->get_attribute == NULL || slot == NULL) + if (ks == NULL || ks->driver == NULL || slot == NULL) return HAL_ERROR_BAD_ARGUMENTS; + if (ks->driver->get_attribute == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + return ks->driver->get_attribute(ks, slot, type, value, value_len, value_max); } @@ -697,9 +730,12 @@ static inline hal_error_t hal_ks_delete_attribute(hal_ks_t *ks, hal_pkey_slot_t *slot, const uint32_t type) { - if (ks == NULL || ks->driver == NULL || ks->driver->delete_attribute == NULL || slot == NULL) + if (ks == NULL || ks->driver == NULL || slot == NULL) return HAL_ERROR_BAD_ARGUMENTS; + if (ks->driver->delete_attribute == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + return ks->driver->delete_attribute(ks, slot, type); } @@ -708,10 +744,13 @@ static inline hal_error_t hal_ks_set_attributes(hal_ks_t *ks, const hal_rpc_pkey_attribute_t *attributes, const unsigned attributes_len) { - if (ks == NULL || ks->driver == NULL || ks->driver->set_attributes == NULL || slot == NULL || + 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; + for (int i = 0; i < attributes_len; i++) if (attributes[i].length == 0 || attributes[i].value == NULL) return HAL_ERROR_BAD_ARGUMENTS; @@ -726,11 +765,14 @@ static inline hal_error_t hal_ks_get_attributes(hal_ks_t *ks, uint8_t *attributes_buffer, const size_t attributes_buffer_len) { - if (ks == NULL || ks->driver == NULL || ks->driver->get_attributes == NULL || slot == NULL || + if (ks == NULL || ks->driver == NULL || slot == NULL || attributes == NULL || attributes_len == 0 || attributes_buffer == NULL || attributes_buffer_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); } @@ -740,10 +782,13 @@ static inline hal_error_t hal_ks_delete_attributes(hal_ks_t *ks, const uint32_t *types, const unsigned types_len) { - if (ks == NULL || ks->driver == NULL || ks->driver->delete_attributes == NULL || slot == NULL || + if (ks == NULL || ks->driver == NULL || slot == NULL || types == NULL || types_len == 0) return HAL_ERROR_BAD_ARGUMENTS; + if (ks->driver->delete_attributes == NULL) + return HAL_ERROR_NOT_IMPLEMENTED; + return ks->driver->delete_attributes(ks, slot, types, types_len); } @@ -881,6 +926,8 @@ extern hal_error_t hal_ks_index_fsck(hal_ks_index_t *ksi); * Keystore attribute utilities, for use by keystore drivers. */ +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_rpc_pkey_attribute_t *attributes, diff --git a/ks_attribute.c b/ks_attribute.c index 0c71345..53cd6bf 100644 --- a/ks_attribute.c +++ b/ks_attribute.c @@ -44,12 +44,13 @@ * issues, and doing it this way just isn't expensive enough to worry about. */ -#define HEADER_LEN (4 + 2) +const size_t hal_ks_attribute_header_size = 2 * sizeof(uint32_t); static inline hal_error_t read_header(const uint8_t * const bytes, const size_t bytes_len, uint32_t *attribute_type, size_t *attribute_len) { - if (bytes == NULL || bytes_len < HEADER_LEN || attribute_type == NULL || attribute_len == NULL) + if (bytes == NULL || bytes_len < hal_ks_attribute_header_size || + attribute_type == NULL || attribute_len == NULL) return HAL_ERROR_BAD_ARGUMENTS; *attribute_type = ((bytes[0] << 24) | @@ -65,7 +66,7 @@ static inline hal_error_t read_header(const uint8_t * const bytes, const size_t static inline hal_error_t write_header(uint8_t *bytes, const size_t bytes_len, const uint32_t attribute_type, const size_t attribute_len) { - if (bytes == NULL || bytes_len < HEADER_LEN) + if (bytes == NULL || bytes_len < hal_ks_attribute_header_size) return HAL_ERROR_BAD_ARGUMENTS; bytes[0] = (attribute_type >> 24) & 0xFF; @@ -94,7 +95,7 @@ hal_error_t hal_ks_attribute_scan(const uint8_t * const bytes, const size_t byte hal_error_t err = read_header(b, end - b, &type, &length); if (err != HAL_OK) return err; - b += HEADER_LEN; + b += hal_ks_attribute_header_size; if (attributes != NULL) { attributes[i].type = type; attributes[i].length = length; @@ -127,8 +128,8 @@ hal_error_t hal_ks_attribute_delete(uint8_t *bytes, const size_t bytes_len, if (i == *attributes_len) return HAL_OK; - const size_t delete_length = HEADER_LEN + attributes[i].length; - const size_t delete_offset = attributes[i].value - HEADER_LEN - bytes; + const size_t delete_length = hal_ks_attribute_header_size + attributes[i].length; + const size_t delete_offset = attributes[i].value - hal_ks_attribute_header_size - bytes; if (delete_offset + delete_length > *total_len) return HAL_ERROR_IMPOSSIBLE; @@ -163,7 +164,7 @@ hal_error_t hal_ks_attribute_insert(uint8_t *bytes, const size_t bytes_len, if (err != HAL_OK) return err; - if (*total_len + HEADER_LEN + value_len > bytes_len) + if (*total_len + hal_ks_attribute_header_size + value_len > bytes_len) return HAL_ERROR_RESULT_TOO_LONG; uint8_t *b = bytes + *total_len; @@ -171,11 +172,11 @@ hal_error_t hal_ks_attribute_insert(uint8_t *bytes, const size_t bytes_len, if ((err = write_header(b, bytes_len - *total_len, type, value_len)) != HAL_OK) return err; - b += HEADER_LEN; + b += hal_ks_attribute_header_size; memcpy(b, value, value_len); - *total_len += HEADER_LEN + value_len; + *total_len += hal_ks_attribute_header_size + value_len; attributes[*attributes_len].type = type; attributes[*attributes_len].length = value_len; diff --git a/ks_flash.c b/ks_flash.c index c96efd5..0d8542e 100644 --- a/ks_flash.c +++ b/ks_flash.c @@ -315,7 +315,7 @@ static hal_crc32_t calculate_block_crc(const flash_block_t * const block) * Calculate block offset. */ -static uint32_t block_offset(const unsigned blockno) +static inline uint32_t block_offset(const unsigned blockno) { return blockno * KEYSTORE_SUBSECTOR_SIZE; } @@ -1544,6 +1544,458 @@ static hal_error_t ks_delete_attribute(hal_ks_t *ks, return HAL_OK; } +static hal_error_t ks_set_attributes(hal_ks_t *ks, + hal_pkey_slot_t *slot, + const hal_rpc_pkey_attribute_t *attributes, + const unsigned attributes_len) +{ +#warning This function is much too long + // Probably needs to be broken up into multiple sub-functions, or at least + // into discrete blocks to scope most of the variables, but let's finish + // writing the icky version before polishing the chrome. + + if (ks != &db.ks || slot == NULL || attributes == NULL || attributes_len == 0) + return HAL_ERROR_BAD_ARGUMENTS; + + /* + * Try to add new attribute as a single-block update. + */ + + unsigned updated_attributes_len = attributes_len; + flash_block_t *block; + unsigned chunk = 0; + hal_error_t err; + unsigned b; + + 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) + return err; + + if (block->header.this_chunk != chunk) + return HAL_ERROR_IMPOSSIBLE; + + 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) + return err; + + updated_attributes_len += *attrs_len; + + hal_rpc_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) + return err; + + for (int i = 0; err == HAL_OK && i < attributes_len; i++) { + if (attributes[i].length == 0) + 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) + return err; + + return block_update(b, block, &slot->name, chunk, &hint); + + } while (++chunk < block->header.total_chunks); + + /* + * If we get here, we have to add a new block, which requires + * rewriting all the others to bump the total_blocks count. 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 for the entire new object. + * + * 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_rpc_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; + + 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) + return err; + + if (block->header.this_chunk != chunk) + return HAL_ERROR_IMPOSSIBLE; + + 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) + return err; + + hal_rpc_pkey_attribute_t attrs[*attrs_len]; + size_t total; + + if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, &total)) != HAL_OK) + return err; + + 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)) + return HAL_ERROR_IMPOSSIBLE; + 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++; + } + } + + 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 = 0; + + if (updated_attributes_len >= sizeof(updated_attributes)/sizeof(*updated_attributes)) + return HAL_ERROR_IMPOSSIBLE; + 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++; + } + + chunk = 0; + + for (int i = 0; i < updated_attributes_len; i++) { + + if (updated_attributes[i].length == 0) { + memmove(&updated_attributes[i], &updated_attributes[i + 1], updated_attributes_len - i - 1); + updated_attributes_len--; + i--; + 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) + return HAL_ERROR_RESULT_TOO_LONG; + + bytes_available -= needed; + } + + const unsigned total_chunks_new = chunk + 1; + + if (db.ksi.used + total_chunks_new > db.ksi.size) + return HAL_ERROR_NO_KEY_INDEX_SLOTS; + + /* + * 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) + return err; + old_blocks[chunk] = b; + } + + /* + * Phase 2: Write new chunks, copying attributes from old chunks and attributes[] + * as needed. This is tedious. + */ + + { + hal_rpc_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) + return HAL_ERROR_IMPOSSIBLE; + + if (updated_attributes[updated_attributes_i].value != NULL) { + 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; + } + + else { + + if (old_block == NULL) { + + if ((err = block_read_cached(old_blocks[old_chunk], &old_block)) != HAL_OK) + return err; + + if (old_block->header.this_chunk != old_chunk) + return HAL_ERROR_IMPOSSIBLE; + + 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) + return err; + + old_attrs_i = 0; + } + + while (old_attrs_i < *old_attrs_len && + old_attrs[old_attrs_i].type != updated_attributes[updated_attributes_i].type) + old_attrs_i++; + + 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_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) + return err; + if (tmp_block->header.this_chunk != 0) + return HAL_ERROR_IMPOSSIBLE; + 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) + return err; + + new_total = 0; + } + + 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); + + 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) + break; + default: + return err; + } + + 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) + return err; + + cache_mark_used(new_block, b); + + new_block = NULL; + new_chunk++; + } + } + + /* + * 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) + return err; + + return HAL_OK; +} + +static hal_error_t ks_get_attributes(hal_ks_t *ks, + hal_pkey_slot_t *slot, + hal_rpc_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 || attributes_buffer_len == 0) + 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; + unsigned chunk = 0; + unsigned found = 0; + hal_error_t err; + unsigned b; + + 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) + return err; + + if (block->header.this_chunk != chunk) + return HAL_ERROR_IMPOSSIBLE; + + if (chunk == 0) + slot->hint = hint; + + 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) + return err; + + if (*attrs_len == 0) + continue; + + hal_rpc_pkey_attribute_t attrs[*attrs_len]; + + if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, NULL)) != HAL_OK) + return err; + + for (int i = 0; i < attributes_len; i++) { + + if (attributes[i].value != NULL) + continue; + + int j = 0; + while (j < *attrs_len && attrs[j].type != attributes[i].type) + j++; + if (j >= *attrs_len) + continue; + + if (attrs[j].length > attributes_buffer + attributes_buffer_len - abuf) + return HAL_ERROR_RESULT_TOO_LONG; + + memcpy(abuf, attrs[j].value, attrs[j].length); + attributes[i].value = abuf; + attributes[i].length = attrs[j].length; + abuf += attrs[j].length; + found++; + } + + } while (found < attributes_len && ++chunk < block->header.total_chunks); + + if (found < attributes_len) + return HAL_ERROR_ATTRIBUTE_NOT_FOUND; + + return HAL_OK; +} + +static hal_error_t ks_delete_attributes(hal_ks_t *ks, + hal_pkey_slot_t *slot, + const uint32_t *types, + const unsigned types_len) +{ + // Most likely we will remove this function from the API entirely, + // but for now implement it in terms of ks_set_attributes() to keep + // all the horrid update logic in one place. + + if (ks != &db.ks || slot == NULL || types == NULL || types_len == 0) + return HAL_ERROR_BAD_ARGUMENTS; + + hal_rpc_pkey_attribute_t attributes[types_len]; + + for (int i = 0; i < types_len; i++) { + attributes[i].type = types[i]; + attributes[i].length = 0; + attributes[i].value = NULL; + } + + return ks_set_attributes(ks, slot, attributes, types_len); +} + const hal_ks_driver_t hal_ks_token_driver[1] = {{ ks_init, ks_shutdown, @@ -1556,7 +2008,10 @@ const hal_ks_driver_t hal_ks_token_driver[1] = {{ ks_match, ks_set_attribute, ks_get_attribute, - ks_delete_attribute + ks_delete_attribute, + ks_set_attributes, + ks_get_attributes, + ks_delete_attributes }}; /* diff --git a/libhal.py b/libhal.py index 4023edc..b243a26 100644 --- a/libhal.py +++ b/libhal.py @@ -113,6 +113,7 @@ 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_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") class Enum(int): diff --git a/unit-tests.py b/unit-tests.py index 43b56ba..3d79da9 100644 --- a/unit-tests.py +++ b/unit-tests.py @@ -701,18 +701,30 @@ class TestPKeyAttributeReadSpeedToken(TestCaseLoggedIn): der = PreloadedKey.db[HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256].der self.k = hsm.pkey_load(HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256, der, HAL_KEY_FLAG_TOKEN) self.addCleanup(self.k.delete) - for i in xrange(12): - self.k.set_attribute(i, "Attribute {}".format(i)) + if False: + for i in xrange(12): + self.k.set_attribute(i, "Attribute {}".format(i)) + else: + self.k.set_attributes(dict((i, "Attribute {}".format(i)) + for i in xrange(12))) super(TestPKeyAttributeReadSpeedToken, self).setUp() + def verify_attributes(self, n_attrs, attributes): + expected = dict((i, "Attribute {}".format(i)) + for i in xrange(n_attrs)) + self.assertEqual(attributes, expected) + def get_attributes_single(self, n_attrs): pinwheel = Pinwheel() + attributes = {} for i in xrange(n_attrs): pinwheel() - self.k.get_attribute(i) + attributes[i] = self.k.get_attribute(i) + self.verify_attributes(n_attrs, attributes) def get_attributes_bulk(self, n_attrs): - self.k.get_attributes(range(n_attrs)) + attributes = self.k.get_attributes(range(n_attrs)) + self.verify_attributes(n_attrs, attributes) def test_get_1_attribute_single(self): self.get_attributes_single(1) @@ -741,18 +753,30 @@ class TestPKeyAttributeReadSpeedVolatile(TestCaseLoggedIn): der = PreloadedKey.db[HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256].der self.k = hsm.pkey_load(HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256, der, 0) self.addCleanup(self.k.delete) - for i in xrange(12): - self.k.set_attribute(i, "Attribute {}".format(i)) + if False: + for i in xrange(12): + self.k.set_attribute(i, "Attribute {}".format(i)) + else: + self.k.set_attributes(dict((i, "Attribute {}".format(i)) + for i in xrange(12))) super(TestPKeyAttributeReadSpeedVolatile, self).setUp() + def verify_attributes(self, n_attrs, attributes): + expected = dict((i, "Attribute {}".format(i)) + for i in xrange(n_attrs)) + self.assertEqual(attributes, expected) + def get_attributes_single(self, n_attrs): pinwheel = Pinwheel() + attributes = {} for i in xrange(n_attrs): pinwheel() - self.k.get_attribute(i) + attributes[i] = self.k.get_attribute(i) + self.verify_attributes(n_attrs, attributes) def get_attributes_bulk(self, n_attrs): - self.k.get_attributes(range(n_attrs)) + attributes = self.k.get_attributes(range(n_attrs)) + self.verify_attributes(n_attrs, attributes) def test_get_1_attribute_single(self): self.get_attributes_single(1) -- cgit v1.2.3