core/keystore/key_cache_flash.c (440 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. #include <stddef.h> #include <stdlib.h> #include <string.h> #include "platform_api.h" #include "common/type_cast.h" #include "common/unused.h" #include "keystore/key_cache_flash.h" #include "keystore/keystore_logging.h" /** * Increment Index by one and if the index reaches to length value rollover to zero value. * * @param cache_flash The key cache flash instance. * @param index Index that needs to be updated * @param length Maximum length up to that index can be updated * * @return Return updated index Value */ static size_t key_cache_flash_increment_queue_index (const struct key_cache_flash *cache_flash, size_t index, size_t length) { size_t new_index = 0; if (index < length) { new_index = ((index + 1) % length); } else { /* Queue index must not be greater than queue length */ debug_log_create_entry (DEBUG_LOG_SEVERITY_ERROR, DEBUG_LOG_COMPONENT_KEYSTORE, KEYSTORE_LOGGING_CACHE_INVALID_QUEUE_INDEX, index, length); /* Reinitialized state and log an error. */ cache_flash->state->is_cache_initialized = false; } return new_index; } /** * Decrement Index by one and if the index reaches to length value rollover to zero value. * * @param cache_flash The key cache flash instance. * @param index Index that needs to be updated * @param length Maximum length up to that index can be updated * * @return Return updated index Value */ static size_t key_cache_flash_decrement_queue_index (const struct key_cache_flash *cache_flash, size_t index, size_t length) { size_t new_index = 0; if (index == 0) { new_index = length - 1; } else if (index < length) { new_index = (index - 1); } else { /* Queue index must not be greater than queue length */ debug_log_create_entry (DEBUG_LOG_SEVERITY_ERROR, DEBUG_LOG_COMPONENT_KEYSTORE, KEYSTORE_LOGGING_CACHE_INVALID_QUEUE_INDEX, index, length); /* Reinitialized cache */ cache_flash->state->is_cache_initialized = false; } return new_index; } /** * Try to erase the flash sector and validate whether that flash sector is accessible or not. * * @param cache_flash The key cache flash instance. * @param flash_id The flash sector ID to erase. * * @return enum type key_cache_flash_sector_status. */ static enum key_cache_flash_sector_status key_cache_flash_try_erase ( const struct key_cache_flash *cache_flash, uint32_t flash_id) { int status; /* Clean the Flash section after reading Key from the memory */ status = cache_flash->store->erase (cache_flash->store, flash_id); if (status != 0) { debug_log_create_entry (DEBUG_LOG_SEVERITY_ERROR, DEBUG_LOG_COMPONENT_KEYSTORE, KEYSTORE_LOGGING_CACHE_BLOCK_CORRUPTED, flash_id, status); return KEY_CACHE_FLASH_SECTOR_STATUS_CORRUPTED; } return KEY_CACHE_FLASH_SECTOR_STATUS_WITH_NO_KEY; } /** * Read the flash sector and verify whether the key is stored on the flash or not. The flash sector * doesn't have key data then it will try to clean the sector and validate whether that flash sector * is accessible or not. * * @param cache_flash The key cache flash instance. * @param flash_id The flash sector ID to read. * * @return enum type key_cache_flash_sector_status. */ static enum key_cache_flash_sector_status key_cache_flash_read_key_and_validate_flash_sector ( const struct key_cache_flash *cache_flash, uint32_t flash_id) { uint8_t *key; int key_length; int read_status; enum key_cache_flash_sector_status status = KEY_CACHE_FLASH_SECTOR_STATUS_WITH_VALID_KEY; /* Read the key from the flash to check for corruption in the data. The actual key data itself * is not interesting at this point. */ key_length = cache_flash->store->get_data_length (cache_flash->store, flash_id); if (ROT_IS_ERROR (key_length)) { /* Flash has no data or the flash sector is corrupted. * Make sure that the flash sector is clear and can be used for storing the keys. */ if (key_length != FLASH_STORE_NO_DATA) { debug_log_create_entry (DEBUG_LOG_SEVERITY_ERROR, DEBUG_LOG_COMPONENT_KEYSTORE, KEYSTORE_LOGGING_CACHE_READ_AND_VALIDATE_FAIL, flash_id, key_length); } return key_cache_flash_try_erase (cache_flash, flash_id); } key = platform_malloc (key_length); if (key == NULL) { debug_log_create_entry (DEBUG_LOG_SEVERITY_ERROR, DEBUG_LOG_COMPONENT_KEYSTORE, KEYSTORE_LOGGING_CACHE_READ_AND_VALIDATE_FAIL, flash_id, KEY_CACHE_NO_MEMORY); /* If allocation has failed, assume the key is no good and erase it. */ return key_cache_flash_try_erase (cache_flash, flash_id); } read_status = cache_flash->store->read (cache_flash->store, flash_id, key, key_length); platform_free (key); if (ROT_IS_ERROR (read_status)) { debug_log_create_entry (DEBUG_LOG_SEVERITY_ERROR, DEBUG_LOG_COMPONENT_KEYSTORE, KEYSTORE_LOGGING_CACHE_READ_AND_VALIDATE_FAIL, flash_id, read_status); /* Make sure that the flash sector is clear and can be used for storing the keys */ return key_cache_flash_try_erase (cache_flash, flash_id); } return status; } /** * Update the initialized key information with a new physical ID for the affected key info. * * @param cache_flash The key cache flash instance. * @param logical_id Logical index to update the key info */ static void key_cache_flash_update_key_info_in_flash_error ( const struct key_cache_flash *cache_flash, size_t logical_id) { uint32_t corrupted_physical_id; uint32_t new_physical_id; size_t new_sector_index = cache_flash->state->free_index_dec; /* First validate that a valid flash sector is available for use */ /* Cannot find new good flash sector */ if (new_sector_index <= cache_flash->num_keys) { debug_log_create_entry (DEBUG_LOG_SEVERITY_ERROR, DEBUG_LOG_COMPONENT_KEYSTORE, KEYSTORE_LOGGING_CACHE_UNAVAILABLE_STORAGE, new_sector_index, cache_flash->num_keys); /* Reinitialized cache */ cache_flash->state->is_cache_initialized = false; return; } corrupted_physical_id = cache_flash->key_info[logical_id].physical_id; new_physical_id = cache_flash->key_info[new_sector_index].physical_id; /* Assign the new physical ID key info */ cache_flash->key_info[logical_id].physical_id = new_physical_id; /* Assign the corrupted physical ID to the current free index */ cache_flash->key_info[new_sector_index].physical_id = corrupted_physical_id; /* Update the lkg_flash_sector_id */ cache_flash->state->free_index_dec--; } /** * Update the add index and requestor credit after consuming a key. * * @param cache_flash The key cache flash instance. * @param requestor_id The requestor ID for the key. */ static void key_cache_flash_update_add_index (const struct key_cache_flash *cache_flash, uint16_t requestor_id) { size_t remove_index = cache_flash->state->remove_index; size_t previous_remove_index; /* Update the credit value in case of pass or fail */ if (cache_flash->requestor_credit[requestor_id] > 0) { cache_flash->requestor_credit[requestor_id]--; } /* Get the previous element index to update the requestor data as we have lock lockless queue * with one element extra */ previous_remove_index = key_cache_flash_decrement_queue_index (cache_flash, remove_index, cache_flash->num_keys); /* Update the Key information */ cache_flash->key_info[previous_remove_index].requestor_id = requestor_id; cache_flash->key_info[remove_index].requestor_id = KEY_CACHE_FLASH_UNASSIGNED_REQUESTOR_ID; cache_flash->key_info[remove_index].valid = KEY_CACHE_FLASH_INVALID; /* Move the add index */ cache_flash->state->remove_index = key_cache_flash_increment_queue_index (cache_flash, remove_index, cache_flash->num_keys); } /** * Arrange the key_info elements so that sectors with valid keys come first, followed by sectors * with no keys, and finally corrupted sectors * * @param cache_flash The key cache flash instance. * @param num_flash_block The number of flash blocks available for storing keys. * * @return 0 in case of success or an appropriate error code. */ static int key_cache_flash_arrange_key_info (const struct key_cache_flash *cache_flash) { size_t low_index = 0; size_t mid_index = 0; size_t high_index = 0; struct key_cache_flash_key_info key_info_temp; if ((cache_flash == NULL) || (cache_flash->num_flash_sectors == 0)) { return KEY_CACHE_INVALID_ARGUMENT; } high_index = cache_flash->num_flash_sectors - 1; while (mid_index < high_index) { switch (cache_flash->key_info[mid_index].valid) { case KEY_CACHE_FLASH_VALID: if (cache_flash->key_info[low_index].valid != cache_flash->key_info[mid_index].valid) { key_info_temp = cache_flash->key_info[low_index]; cache_flash->key_info[low_index] = cache_flash->key_info[mid_index]; cache_flash->key_info[mid_index] = key_info_temp; } low_index++; mid_index++; break; case KEY_CACHE_FLASH_INVALID: mid_index++; break; case KEY_CACHE_FLASH_CORRUPTED: if (cache_flash->key_info[mid_index].valid != cache_flash->key_info[high_index].valid) { key_info_temp = cache_flash->key_info[mid_index]; cache_flash->key_info[mid_index] = cache_flash->key_info[high_index]; cache_flash->key_info[high_index] = key_info_temp; } if (high_index > 0) { high_index--; } break; default: return KEY_CACHE_INVALID_ARGUMENT; } } /* Setup consumer index at zero as all the valid ley are kept on top of the key_info or * in case of no valid key it will point to the empty key_info element */ cache_flash->state->remove_index = 0; /* Save index will point to the first empty location to save the new key on that key_info * key_info_index currently pointing to the first empty element */ if (low_index >= cache_flash->num_keys) { /* More valid key than expected set add index on the last index of queue */ cache_flash->state->add_index = cache_flash->num_keys - 1; } else { cache_flash->state->add_index = low_index; } /* Validate that the total valid flash sectors are more than the num_keys*/ if ((high_index + 1) < cache_flash->num_keys) { /* Not enough storage available to store the keys */ cache_flash->state->is_error_state = true; debug_log_create_entry (DEBUG_LOG_SEVERITY_ERROR, DEBUG_LOG_COMPONENT_KEYSTORE, KEYSTORE_LOGGING_CACHE_UNAVAILABLE_STORAGE, high_index + 1, cache_flash->num_keys); } /* Assign the next free index to be used for bad sector replacement as the max flash sector * index */ cache_flash->state->free_index_dec = high_index; return 0; } bool key_cache_flash_is_initialized (const struct key_cache *cache) { const struct key_cache_flash *cache_flash = TO_DERIVED_TYPE (cache, const struct key_cache_flash, base); if (cache_flash == NULL) { return false; } return cache_flash->state->is_cache_initialized; } bool key_cache_flash_is_error_state (const struct key_cache *cache) { const struct key_cache_flash *cache_flash = TO_DERIVED_TYPE (cache, const struct key_cache_flash, base); if (cache_flash == NULL) { return true; } return cache_flash->state->is_error_state; } bool key_cache_flash_is_full (const struct key_cache *cache) { const struct key_cache_flash *cache_flash = TO_DERIVED_TYPE (cache, const struct key_cache_flash, base); size_t new_save_index; if (cache_flash == NULL) { return false; } new_save_index = key_cache_flash_increment_queue_index (cache_flash, cache_flash->state->add_index, cache_flash->num_keys); return (new_save_index == cache_flash->state->remove_index); } bool key_cache_flash_is_empty (const struct key_cache *cache) { const struct key_cache_flash *cache_flash = TO_DERIVED_TYPE (cache, const struct key_cache_flash, base); if (cache_flash == NULL) { return true; } return (cache_flash->state->add_index == cache_flash->state->remove_index); } int key_cache_flash_initialize_cache (const struct key_cache *cache) { const struct key_cache_flash *cache_flash = TO_DERIVED_TYPE (cache, const struct key_cache_flash, base); uint16_t requestor_id; size_t i; int status; if ((cache_flash == NULL) || (cache_flash->state == NULL) || (cache_flash->store == NULL)) { return KEY_CACHE_INVALID_ARGUMENT; } /* Read all the flash blocks and update the status for the keys */ for (i = 0; i < cache_flash->num_flash_sectors; i++) { cache_flash->key_info[i].valid = key_cache_flash_read_key_and_validate_flash_sector (cache_flash, i); cache_flash->key_info[i].physical_id = i; } /* * Reorder the key_info elements so that sectors with valid keys come first, followed by * sectors with no keys, and finally corrupted sectors */ status = key_cache_flash_arrange_key_info (cache_flash); if (status != 0) { return status; } requestor_id = 0; /* Updated requestors' credit for all the valid & invalid keys for the num_keys managed */ for (i = 0; i < cache_flash->num_keys; i++) { cache_flash->key_info[i].requestor_id = requestor_id; if (cache_flash->key_info[i].valid == KEY_CACHE_FLASH_VALID) { if (cache_flash->requestor_credit[requestor_id] < cache_flash->max_credit) { cache_flash->requestor_credit[requestor_id]++; cache_flash->key_info[i].requestor_id = KEY_CACHE_FLASH_UNASSIGNED_REQUESTOR_ID; } } requestor_id = key_cache_flash_increment_queue_index (cache_flash, requestor_id, cache_flash->max_requestors); } cache_flash->state->is_cache_initialized = true; return status; } int key_cache_flash_add (const struct key_cache *cache, const uint8_t *key, size_t length) { const struct key_cache_flash *cache_flash = TO_DERIVED_TYPE (cache, const struct key_cache_flash, base); uint32_t physical_id; size_t add_index; uint16_t requestor_id; int i; int status; if ((cache == NULL) || (key == NULL) || (length == 0)) { return KEY_CACHE_INVALID_ARGUMENT; } status = platform_mutex_lock (&cache_flash->state->lock); if (status != 0) { return status; } do { if (cache_flash->state->is_cache_initialized == false) { status = KEY_CACHE_NOT_INITIALIZED; break; } /* Check if the cache is in error state */ if (cache_flash->state->is_error_state == true) { status = KEY_CACHE_UNAVAILABLE_STORAGE; break; } add_index = cache_flash->state->add_index; if (add_index >= cache_flash->num_keys) { /* Key info is corrupted and the database cannot be trusted anymore */ cache_flash->state->is_cache_initialized = false; status = KEY_CACHE_MEMORY_CORRUPTED; break; } requestor_id = cache_flash->key_info[add_index].requestor_id; if (requestor_id >= cache_flash->max_requestors) { /* Key info is corrupted and the database cannot be trusted anymore */ cache_flash->state->is_cache_initialized = false; status = KEY_CACHE_MEMORY_CORRUPTED; break; } physical_id = cache_flash->key_info[add_index].physical_id; if (physical_id >= cache_flash->num_flash_sectors) { /* Key info is corrupted and the database cannot be trusted anymore */ cache_flash->state->is_cache_initialized = false; status = KEY_CACHE_MEMORY_CORRUPTED; break; } if (cache_flash->base.is_full (&cache_flash->base)) { status = KEY_CACHE_QUEUE_IS_FULL; break; } /* Retry the Flash write operation if it fails as key generation is a time-consuming * operation */ for (i = 0; i < KEY_CACHE_FLASH_MAX_ADD_RETRY; i++) { status = cache_flash->store->write (cache_flash->store, physical_id, key, length); if (status == 0) { break; } } if (status == 0) { /* Increment the credit */ if (cache_flash->requestor_credit[requestor_id] < cache_flash->max_credit) { cache_flash->requestor_credit[requestor_id]++; } /* Update the key information */ cache_flash->key_info[add_index].requestor_id = KEY_CACHE_FLASH_UNASSIGNED_REQUESTOR_ID; cache_flash->key_info[add_index].valid = 1; /* Update the save index */ add_index = key_cache_flash_increment_queue_index (cache_flash, add_index, cache_flash->num_keys); cache_flash->state->add_index = add_index; } else { /* Corrupted flash sector detected */ key_cache_flash_update_key_info_in_flash_error (cache_flash, add_index); break; } } while (0); platform_mutex_unlock (&cache_flash->state->lock); return status; } int key_cache_flash_remove (const struct key_cache *cache, uint16_t requestor_id, uint8_t *key, size_t input_buffer_length, size_t *length) { const struct key_cache_flash *cache_flash = TO_DERIVED_TYPE (cache, const struct key_cache_flash, base); size_t remove_index; uint32_t physical_id; int status; if ((cache == NULL) || (key == NULL) || (length == NULL)) { return KEY_CACHE_INVALID_ARGUMENT; } status = platform_mutex_lock (&cache_flash->state->lock); if (status != 0) { return status; } do { /* Critical Error encountered during the adding of a key, indicating a severe issue */ if (cache_flash->state->is_cache_initialized == false) { status = KEY_CACHE_NOT_INITIALIZED; break; } /* Check if the cache is in error state */ if (cache_flash->state->is_error_state == true) { status = KEY_CACHE_UNAVAILABLE_STORAGE; break; } /* Verify requestor ID */ if (requestor_id >= cache_flash->max_requestors) { status = KEY_CACHE_INVALID_REQUESTOR_ID; break; } remove_index = cache_flash->state->remove_index; if (remove_index > cache_flash->num_keys) { // Key info is corrupted and the database cannot be trusted anymore cache_flash->state->is_cache_initialized = false; status = KEY_CACHE_INVALID_REMOVE_INDEX; break; } /* Validate the queue is not empty and make sure the add index is valid */ if (cache_flash->base.is_empty (&cache_flash->base)) { status = KEY_CACHE_QUEUE_IS_EMPTY; break; } if (cache_flash->key_info[remove_index].valid != KEY_CACHE_FLASH_VALID) { key_cache_flash_update_add_index (cache_flash, requestor_id); status = KEY_CACHE_KEY_NOT_FOUND_AT_INDEX; break; } physical_id = cache_flash->key_info[remove_index].physical_id; if (physical_id >= cache_flash->num_flash_sectors) { // Key info is corrupted and the database cannot be trusted anymore cache_flash->state->is_cache_initialized = false; status = KEY_CACHE_MEMORY_CORRUPTED; break; } /* verify the credit */ if (cache_flash->requestor_credit[requestor_id] == 0) { status = KEY_CACHE_CREDIT_NOT_AVAILABLE; break; } /* Read the key data from the flash */ status = cache_flash->store->read (cache_flash->store, physical_id, key, input_buffer_length); if (!(ROT_IS_ERROR (status))) { *length = status; /* Update status to success in case no error */ status = 0; } /* Clean the Flash section after reading Key from the memory */ if (key_cache_flash_try_erase (cache_flash, physical_id) != KEY_CACHE_FLASH_SECTOR_STATUS_WITH_NO_KEY) { /* Corrupted flash sector detected */ key_cache_flash_update_key_info_in_flash_error (cache_flash, remove_index); } /* Update the key info and add index for the add request */ key_cache_flash_update_add_index (cache_flash, requestor_id); } while (0); platform_mutex_unlock (&cache_flash->state->lock); return status; } /** * Initialize a key cache implemented using a flash store. The total number of keys managed by this * key cache is determined by multiplying max_requestors and max_credit. * * @param cache_flash The key cache to initialize. * @param state Variable context for the key cache. This must be uninitialized. * @param store The flash store to use for storing the keys. * @param key_info An array of key metadata structures used for cache management. The length of * this array is determined by the number of flash blocks being used by the key cache. * @param flash_blocks The number of key info structures in the list. This represents the total * number of flash blocks available to use in the flash store. * @param requestor_credit An array for tracking available credits for different requestors. The * length of this array must be equal to the number of supported requestors. * @param max_requestors The number of requestors supported by the cache. Requestor IDs will be * assigned sequentially from 0 to max_requestors - 1. * @param max_credit The maximum credit value per requestor. * * @return 0 if the key cache was successfully initialized or an error code. */ int key_cache_flash_init (struct key_cache_flash *cache_flash, struct key_cache_flash_state *state, const struct flash_store *store, struct key_cache_flash_key_info *key_info, size_t flash_blocks, uint8_t *requestor_credit, size_t max_requestors, uint8_t max_credit) { if (cache_flash == NULL) { return KEY_CACHE_INVALID_ARGUMENT; } memset (cache_flash, 0, sizeof (struct key_cache_flash)); cache_flash->base.is_initialized = key_cache_flash_is_initialized; cache_flash->base.is_error_state = key_cache_flash_is_error_state; cache_flash->base.is_full = key_cache_flash_is_full; cache_flash->base.is_empty = key_cache_flash_is_empty; cache_flash->base.initialize_cache = key_cache_flash_initialize_cache; cache_flash->base.add = key_cache_flash_add; cache_flash->base.remove = key_cache_flash_remove; cache_flash->state = state; cache_flash->store = store; cache_flash->key_info = key_info; cache_flash->num_flash_sectors = flash_blocks; cache_flash->requestor_credit = requestor_credit; cache_flash->max_requestors = max_requestors; cache_flash->max_credit = max_credit; /* The total number of keys managed by the key cache flash is calculated as (max requestors * * max credits per requestor) + 1 */ cache_flash->num_keys = (cache_flash->max_requestors * cache_flash->max_credit) + 1; return key_cache_flash_init_state (cache_flash); } /** * Initialize only the variable state for a key cache in flash. The rest of the key cache is * assumed to have already been initialized. * * This would generally be used with a statically initialized instance. * * @param cache_flash The key cache that contains the state to initialize. * * @return 0 if the state was successfully initialized or an error code. */ int key_cache_flash_init_state (const struct key_cache_flash *cache_flash) { int num_flash_sectors; if ((cache_flash == NULL) || (cache_flash->state == NULL) || (cache_flash->store == NULL) || (cache_flash->key_info == NULL) || (cache_flash->requestor_credit == NULL) || (cache_flash->max_requestors == 0) || (cache_flash->max_credit == 0)) { return KEY_CACHE_INVALID_ARGUMENT; } if (cache_flash->num_flash_sectors < cache_flash->num_keys) { return KEY_CACHE_INSUFFICIENT_STORAGE; } memset (cache_flash->state, 0, sizeof (struct key_cache_flash_state)); memset (cache_flash->key_info, 0, sizeof (*cache_flash->key_info) * cache_flash->num_flash_sectors); memset (cache_flash->requestor_credit, 0, sizeof (*cache_flash->requestor_credit) * cache_flash->max_requestors); /* Confirm the flash store has the expected number of blocks available. */ num_flash_sectors = cache_flash->store->get_num_blocks (cache_flash->store); if (ROT_IS_ERROR (num_flash_sectors)) { return num_flash_sectors; } if ((size_t) num_flash_sectors < cache_flash->num_flash_sectors) { return KEY_CACHE_STORAGE_MISMATCH; } return platform_mutex_init (&cache_flash->state->lock); } /** * Release the resources used by the key cache. * * @param cache The key cache to release. */ void key_cache_flash_release (const struct key_cache_flash *cache_flash) { if ((cache_flash == NULL) || (cache_flash->state == NULL)) { return; } platform_mutex_free (&cache_flash->state->lock); memset (cache_flash->state, 0, sizeof (struct key_cache_flash_state)); }