diff options
-rw-r--r-- | hal.h | 1 | ||||
-rw-r--r-- | hal_internal.h | 77 | ||||
-rw-r--r-- | ks_attribute.c | 19 | ||||
-rw-r--r-- | ks_flash.c | 459 | ||||
-rw-r--r-- | libhal.py | 1 | ||||
-rw-r--r-- | unit-tests.py | 40 |
6 files changed, 563 insertions, 34 deletions
@@ -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; @@ -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 }}; /* @@ -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) |