mirror of
https://github.com/Mbed-TLS/mbedtls.git
synced 2025-03-01 04:13:29 +00:00
Merge pull request #9464 from gilles-peskine-arm/psa-keystore-dynamic-development
dynamically sized key store
This commit is contained in:
commit
ab0af45d11
@ -1,3 +1,9 @@
|
||||
Features
|
||||
* When the new compilation option MBEDTLS_PSA_KEY_STORE_DYNAMIC is enabled,
|
||||
the number of volatile PSA keys is virtually unlimited, at the expense
|
||||
of increased code size. This option is off by default, but enabled in
|
||||
the default mbedtls_config.h. Fixes #9216.
|
||||
|
||||
Bugfix
|
||||
* Fix interference between PSA volatile keys and built-in keys
|
||||
when MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS is enabled and
|
||||
|
@ -1232,6 +1232,23 @@
|
||||
*/
|
||||
//#define MBEDTLS_PSA_CRYPTO_SPM
|
||||
|
||||
/**
|
||||
* \def MBEDTLS_PSA_KEY_STORE_DYNAMIC
|
||||
*
|
||||
* Dynamically resize the PSA key store to accommodate any number of
|
||||
* volatile keys (until the heap memory is exhausted).
|
||||
*
|
||||
* If this option is disabled, the key store has a fixed size
|
||||
* #MBEDTLS_PSA_KEY_SLOT_COUNT for volatile keys and loaded persistent keys
|
||||
* together.
|
||||
*
|
||||
* This option has no effect when #MBEDTLS_PSA_CRYPTO_C is disabled.
|
||||
*
|
||||
* Module: library/psa_crypto.c
|
||||
* Requires: MBEDTLS_PSA_CRYPTO_C
|
||||
*/
|
||||
#define MBEDTLS_PSA_KEY_STORE_DYNAMIC
|
||||
|
||||
/**
|
||||
* Uncomment to enable p256-m. This is an alternative implementation of
|
||||
* key generation, ECDH and (randomized) ECDSA on the curve SECP256R1.
|
||||
@ -3825,9 +3842,13 @@
|
||||
|
||||
/** \def MBEDTLS_PSA_KEY_SLOT_COUNT
|
||||
*
|
||||
* The maximum amount of PSA keys simultaneously in memory. This counts all
|
||||
* When #MBEDTLS_PSA_KEY_STORE_DYNAMIC is disabled,
|
||||
* the maximum amount of PSA keys simultaneously in memory. This counts all
|
||||
* volatile keys, plus loaded persistent keys.
|
||||
*
|
||||
* When #MBEDTLS_PSA_KEY_STORE_DYNAMIC is enabled,
|
||||
* the maximum number of loaded persistent keys.
|
||||
*
|
||||
* Currently, persistent keys do not need to be loaded all the time while
|
||||
* a multipart operation is in progress, only while the operation is being
|
||||
* set up. This may change in future versions of the library.
|
||||
|
@ -2069,6 +2069,40 @@ common_block_cipher_dispatch () {
|
||||
scripts/config.py set MBEDTLS_DEPRECATED_REMOVED
|
||||
}
|
||||
|
||||
component_test_full_block_cipher_psa_dispatch_static_keystore () {
|
||||
msg "build: full + PSA dispatch in block_cipher with static keystore"
|
||||
# Check that the static key store works well when CTR_DRBG uses a
|
||||
# PSA key for AES.
|
||||
scripts/config.py unset MBEDTLS_PSA_KEY_STORE_DYNAMIC
|
||||
|
||||
loc_accel_list="ALG_ECB_NO_PADDING \
|
||||
KEY_TYPE_AES KEY_TYPE_ARIA KEY_TYPE_CAMELLIA"
|
||||
|
||||
# Configure
|
||||
# ---------
|
||||
|
||||
common_block_cipher_dispatch 1
|
||||
|
||||
# Build
|
||||
# -----
|
||||
|
||||
helper_libtestdriver1_make_drivers "$loc_accel_list"
|
||||
|
||||
helper_libtestdriver1_make_main "$loc_accel_list"
|
||||
|
||||
# Make sure disabled components were not re-enabled by accident (additive
|
||||
# config)
|
||||
not grep mbedtls_aes_ library/aes.o
|
||||
not grep mbedtls_aria_ library/aria.o
|
||||
not grep mbedtls_camellia_ library/camellia.o
|
||||
|
||||
# Run the tests
|
||||
# -------------
|
||||
|
||||
msg "test: full + PSA dispatch in block_cipher with static keystore"
|
||||
make test
|
||||
}
|
||||
|
||||
component_test_full_block_cipher_psa_dispatch () {
|
||||
msg "build: full + PSA dispatch in block_cipher"
|
||||
|
||||
@ -2593,6 +2627,16 @@ component_test_se_default () {
|
||||
make test
|
||||
}
|
||||
|
||||
component_test_full_static_keystore () {
|
||||
msg "build: full config - MBEDTLS_PSA_KEY_STORE_DYNAMIC"
|
||||
scripts/config.py full
|
||||
scripts/config.py unset MBEDTLS_PSA_KEY_STORE_DYNAMIC
|
||||
make CC=clang CFLAGS="$ASAN_CFLAGS -Os" LDFLAGS="$ASAN_CFLAGS"
|
||||
|
||||
msg "test: full config - MBEDTLS_PSA_KEY_STORE_DYNAMIC"
|
||||
make test
|
||||
}
|
||||
|
||||
component_test_psa_crypto_drivers () {
|
||||
msg "build: full + test drivers dispatching to builtins"
|
||||
scripts/config.py full
|
||||
|
@ -1210,15 +1210,15 @@ psa_status_t psa_wipe_key_slot(psa_key_slot_t *slot)
|
||||
case PSA_SLOT_PENDING_DELETION:
|
||||
/* In this state psa_wipe_key_slot() must only be called if the
|
||||
* caller is the last reader. */
|
||||
if (slot->registered_readers != 1) {
|
||||
MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->registered_readers == 1);
|
||||
if (slot->var.occupied.registered_readers != 1) {
|
||||
MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->var.occupied.registered_readers == 1);
|
||||
status = PSA_ERROR_CORRUPTION_DETECTED;
|
||||
}
|
||||
break;
|
||||
case PSA_SLOT_FILLING:
|
||||
/* In this state registered_readers must be 0. */
|
||||
if (slot->registered_readers != 0) {
|
||||
MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->registered_readers == 0);
|
||||
if (slot->var.occupied.registered_readers != 0) {
|
||||
MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->var.occupied.registered_readers == 0);
|
||||
status = PSA_ERROR_CORRUPTION_DETECTED;
|
||||
}
|
||||
break;
|
||||
@ -1232,6 +1232,11 @@ psa_status_t psa_wipe_key_slot(psa_key_slot_t *slot)
|
||||
status = PSA_ERROR_CORRUPTION_DETECTED;
|
||||
}
|
||||
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
size_t slice_index = slot->slice_index;
|
||||
#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
|
||||
|
||||
/* Multipart operations may still be using the key. This is safe
|
||||
* because all multipart operation objects are independent from
|
||||
* the key slot: if they need to access the key after the setup
|
||||
@ -1242,6 +1247,17 @@ psa_status_t psa_wipe_key_slot(psa_key_slot_t *slot)
|
||||
* zeroize because the metadata is not particularly sensitive.
|
||||
* This memset also sets the slot's state to PSA_SLOT_EMPTY. */
|
||||
memset(slot, 0, sizeof(*slot));
|
||||
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
/* If the slot is already corrupted, something went deeply wrong,
|
||||
* like a thread still using the slot or a stray pointer leading
|
||||
* to the slot's memory being used for another object. Let the slot
|
||||
* leak rather than make the corruption worse. */
|
||||
if (status == PSA_SUCCESS) {
|
||||
status = psa_free_key_slot(slice_index, slot);
|
||||
}
|
||||
#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -1753,8 +1769,6 @@ static psa_status_t psa_start_key_creation(
|
||||
psa_se_drv_table_entry_t **p_drv)
|
||||
{
|
||||
psa_status_t status;
|
||||
psa_key_id_t volatile_key_id;
|
||||
psa_key_slot_t *slot;
|
||||
|
||||
(void) method;
|
||||
*p_drv = NULL;
|
||||
@ -1764,11 +1778,16 @@ static psa_status_t psa_start_key_creation(
|
||||
return status;
|
||||
}
|
||||
|
||||
int key_is_volatile = PSA_KEY_LIFETIME_IS_VOLATILE(attributes->lifetime);
|
||||
psa_key_id_t volatile_key_id;
|
||||
|
||||
#if defined(MBEDTLS_THREADING_C)
|
||||
PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
|
||||
&mbedtls_threading_key_slot_mutex));
|
||||
#endif
|
||||
status = psa_reserve_free_key_slot(&volatile_key_id, p_slot);
|
||||
status = psa_reserve_free_key_slot(
|
||||
key_is_volatile ? &volatile_key_id : NULL,
|
||||
p_slot);
|
||||
#if defined(MBEDTLS_THREADING_C)
|
||||
PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
|
||||
&mbedtls_threading_key_slot_mutex));
|
||||
@ -1776,7 +1795,7 @@ static psa_status_t psa_start_key_creation(
|
||||
if (status != PSA_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
slot = *p_slot;
|
||||
psa_key_slot_t *slot = *p_slot;
|
||||
|
||||
/* We're storing the declared bit-size of the key. It's up to each
|
||||
* creation mechanism to verify that this information is correct.
|
||||
@ -1787,7 +1806,7 @@ static psa_status_t psa_start_key_creation(
|
||||
* definition. */
|
||||
|
||||
slot->attr = *attributes;
|
||||
if (PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
|
||||
if (key_is_volatile) {
|
||||
#if !defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER)
|
||||
slot->attr.id = volatile_key_id;
|
||||
#else
|
||||
|
@ -59,6 +59,8 @@ typedef enum {
|
||||
* and metadata for one key.
|
||||
*/
|
||||
typedef struct {
|
||||
/* This field is accessed in a lot of places. Putting it first
|
||||
* reduces the code size. */
|
||||
psa_key_attributes_t attr;
|
||||
|
||||
/*
|
||||
@ -78,35 +80,77 @@ typedef struct {
|
||||
* slots that are in a suitable state for the function.
|
||||
* For example, psa_get_and_lock_key_slot_in_memory, which finds a slot
|
||||
* containing a given key ID, will only check slots whose state variable is
|
||||
* PSA_SLOT_FULL. */
|
||||
* PSA_SLOT_FULL.
|
||||
*/
|
||||
psa_key_slot_state_t state;
|
||||
|
||||
/*
|
||||
* Number of functions registered as reading the material in the key slot.
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
/* The index of the slice containing this slot.
|
||||
* This field must be filled if the slot contains a key
|
||||
* (including keys being created or destroyed), and can be either
|
||||
* filled or 0 when the slot is free.
|
||||
*
|
||||
* Library functions must not write directly to registered_readers
|
||||
*
|
||||
* A function must call psa_register_read(slot) before reading the current
|
||||
* contents of the slot for an operation.
|
||||
* They then must call psa_unregister_read(slot) once they have finished
|
||||
* reading the current contents of the slot. If the key slot mutex is not
|
||||
* held (when mutexes are enabled), this call must be done via a call to
|
||||
* psa_unregister_read_under_mutex(slot).
|
||||
* A function must call psa_key_slot_has_readers(slot) to check if
|
||||
* the slot is in use for reading.
|
||||
*
|
||||
* This counter is used to prevent resetting the key slot while the library
|
||||
* may access it. For example, such control is needed in the following
|
||||
* scenarios:
|
||||
* . In case of key slot starvation, all key slots contain the description
|
||||
* of a key, and the library asks for the description of a persistent
|
||||
* key not present in the key slots, the key slots currently accessed by
|
||||
* the library cannot be reclaimed to free a key slot to load the
|
||||
* persistent key.
|
||||
* . In case of a multi-threaded application where one thread asks to close
|
||||
* or purge or destroy a key while it is in use by the library through
|
||||
* another thread. */
|
||||
size_t registered_readers;
|
||||
* In most cases, the slice index can be deduced from the key identifer.
|
||||
* We keep it in a separate field for robustness (it reduces the chance
|
||||
* that a coding mistake in the key store will result in accessing the
|
||||
* wrong slice), and also so that it's available even on code paths
|
||||
* during creation or destruction where the key identifier might not be
|
||||
* filled in.
|
||||
* */
|
||||
uint8_t slice_index;
|
||||
#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
|
||||
union {
|
||||
struct {
|
||||
/* The index of the next slot in the free list for this
|
||||
* slice, relative * to the next array element.
|
||||
*
|
||||
* That is, 0 means the next slot, 1 means the next slot
|
||||
* but one, etc. -1 would mean the slot itself. -2 means
|
||||
* the previous slot, etc.
|
||||
*
|
||||
* If this is beyond the array length, the free list ends with the
|
||||
* current element.
|
||||
*
|
||||
* The reason for this strange encoding is that 0 means the next
|
||||
* element. This way, when we allocate a slice and initialize it
|
||||
* to all-zero, the slice is ready for use, with a free list that
|
||||
* consists of all the slots in order.
|
||||
*/
|
||||
int32_t next_free_relative_to_next;
|
||||
} free;
|
||||
|
||||
struct {
|
||||
/*
|
||||
* Number of functions registered as reading the material in the key slot.
|
||||
*
|
||||
* Library functions must not write directly to registered_readers
|
||||
*
|
||||
* A function must call psa_register_read(slot) before reading
|
||||
* the current contents of the slot for an operation.
|
||||
* They then must call psa_unregister_read(slot) once they have
|
||||
* finished reading the current contents of the slot. If the key
|
||||
* slot mutex is not held (when mutexes are enabled), this call
|
||||
* must be done via a call to
|
||||
* psa_unregister_read_under_mutex(slot).
|
||||
* A function must call psa_key_slot_has_readers(slot) to check if
|
||||
* the slot is in use for reading.
|
||||
*
|
||||
* This counter is used to prevent resetting the key slot while
|
||||
* the library may access it. For example, such control is needed
|
||||
* in the following scenarios:
|
||||
* . In case of key slot starvation, all key slots contain the
|
||||
* description of a key, and the library asks for the
|
||||
* description of a persistent key not present in the
|
||||
* key slots, the key slots currently accessed by the
|
||||
* library cannot be reclaimed to free a key slot to load
|
||||
* the persistent key.
|
||||
* . In case of a multi-threaded application where one thread
|
||||
* asks to close or purge or destroy a key while it is in use
|
||||
* by the library through another thread. */
|
||||
size_t registered_readers;
|
||||
} occupied;
|
||||
} var;
|
||||
|
||||
/* Dynamically allocated key data buffer.
|
||||
* Format as specified in psa_export_key(). */
|
||||
@ -169,7 +213,7 @@ typedef struct {
|
||||
*/
|
||||
static inline int psa_key_slot_has_readers(const psa_key_slot_t *slot)
|
||||
{
|
||||
return slot->registered_readers > 0;
|
||||
return slot->var.occupied.registered_readers > 0;
|
||||
}
|
||||
|
||||
#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
|
||||
|
@ -58,17 +58,140 @@ MBEDTLS_STATIC_ASSERT(PSA_KEY_ID_VOLATILE_MAX < MBEDTLS_PSA_KEY_ID_BUILTIN_MIN |
|
||||
|
||||
|
||||
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
|
||||
/* Dynamic key store.
|
||||
*
|
||||
* The key store consists of multiple slices.
|
||||
*
|
||||
* The volatile keys are stored in variable-sized tables called slices.
|
||||
* Slices are allocated on demand and deallocated when possible.
|
||||
* The size of slices increases exponentially, so the average overhead
|
||||
* (number of slots that are allocated but not used) is roughly
|
||||
* proportional to the number of keys (with a factor that grows
|
||||
* when the key store is fragmented).
|
||||
*
|
||||
* One slice is dedicated to the cache of persistent and built-in keys.
|
||||
* For simplicity, they are separated from volatile keys. This cache
|
||||
* slice has a fixed size and has the slice index KEY_SLOT_CACHE_SLICE_INDEX,
|
||||
* located after the slices for volatile keys.
|
||||
*/
|
||||
|
||||
/* Size of the last slice containing the cache of persistent and built-in keys. */
|
||||
#define PERSISTENT_KEY_CACHE_COUNT MBEDTLS_PSA_KEY_SLOT_COUNT
|
||||
|
||||
/* Volatile keys are stored in slices 0 through
|
||||
* (KEY_SLOT_VOLATILE_SLICE_COUNT - 1) inclusive.
|
||||
* Each slice is twice the size of the previous slice.
|
||||
* Volatile key identifiers encode the slice number as follows:
|
||||
* bits 30..31: 0b10 (mandated by the PSA Crypto specification).
|
||||
* bits 25..29: slice index (0...KEY_SLOT_VOLATILE_SLICE_COUNT-1)
|
||||
* bits 0..24: slot index in slice
|
||||
*/
|
||||
#define KEY_ID_SLOT_INDEX_WIDTH 25u
|
||||
#define KEY_ID_SLICE_INDEX_WIDTH 5u
|
||||
|
||||
#define KEY_SLOT_VOLATILE_SLICE_BASE_LENGTH 16u
|
||||
#define KEY_SLOT_VOLATILE_SLICE_COUNT 22u
|
||||
#define KEY_SLICE_COUNT (KEY_SLOT_VOLATILE_SLICE_COUNT + 1u)
|
||||
#define KEY_SLOT_CACHE_SLICE_INDEX KEY_SLOT_VOLATILE_SLICE_COUNT
|
||||
|
||||
|
||||
/* Check that the length of the largest slice (calculated as
|
||||
* KEY_SLICE_LENGTH_MAX below) does not overflow size_t. We use
|
||||
* an indirect method in case the calculation of KEY_SLICE_LENGTH_MAX
|
||||
* itself overflows uintmax_t: if (BASE_LENGTH << c)
|
||||
* overflows size_t then BASE_LENGTH > SIZE_MAX >> c.
|
||||
*/
|
||||
#if (KEY_SLOT_VOLATILE_SLICE_BASE_LENGTH > \
|
||||
SIZE_MAX >> (KEY_SLOT_VOLATILE_SLICE_COUNT - 1))
|
||||
#error "Maximum slice length overflows size_t"
|
||||
#endif
|
||||
|
||||
#if KEY_ID_SLICE_INDEX_WIDTH + KEY_ID_SLOT_INDEX_WIDTH > 30
|
||||
#error "Not enough room in volatile key IDs for slice index and slot index"
|
||||
#endif
|
||||
#if KEY_SLOT_VOLATILE_SLICE_COUNT > (1 << KEY_ID_SLICE_INDEX_WIDTH)
|
||||
#error "Too many slices to fit the slice index in a volatile key ID"
|
||||
#endif
|
||||
#define KEY_SLICE_LENGTH_MAX \
|
||||
(KEY_SLOT_VOLATILE_SLICE_BASE_LENGTH << (KEY_SLOT_VOLATILE_SLICE_COUNT - 1))
|
||||
#if KEY_SLICE_LENGTH_MAX > 1 << KEY_ID_SLOT_INDEX_WIDTH
|
||||
#error "Not enough room in volatile key IDs for a slot index in the largest slice"
|
||||
#endif
|
||||
#if KEY_ID_SLICE_INDEX_WIDTH > 8
|
||||
#error "Slice index does not fit in uint8_t for psa_key_slot_t::slice_index"
|
||||
#endif
|
||||
|
||||
|
||||
/* Calculate the volatile key id to use for a given slot.
|
||||
* This function assumes valid parameter values. */
|
||||
static psa_key_id_t volatile_key_id_of_index(size_t slice_idx,
|
||||
size_t slot_idx)
|
||||
{
|
||||
/* We assert above that the slice and slot indexes fit in separate
|
||||
* bit-fields inside psa_key_id_t, which is a 32-bit type per the
|
||||
* PSA Cryptography specification. */
|
||||
return (psa_key_id_t) (0x40000000u |
|
||||
(slice_idx << KEY_ID_SLOT_INDEX_WIDTH) |
|
||||
slot_idx);
|
||||
}
|
||||
|
||||
/* Calculate the slice containing the given volatile key.
|
||||
* This function assumes valid parameter values. */
|
||||
static size_t slice_index_of_volatile_key_id(psa_key_id_t key_id)
|
||||
{
|
||||
size_t mask = (1LU << KEY_ID_SLICE_INDEX_WIDTH) - 1;
|
||||
return (key_id >> KEY_ID_SLOT_INDEX_WIDTH) & mask;
|
||||
}
|
||||
|
||||
/* Calculate the index of the slot containing the given volatile key.
|
||||
* This function assumes valid parameter values. */
|
||||
static size_t slot_index_of_volatile_key_id(psa_key_id_t key_id)
|
||||
{
|
||||
return key_id & ((1LU << KEY_ID_SLOT_INDEX_WIDTH) - 1);
|
||||
}
|
||||
|
||||
/* In global_data.first_free_slot_index, use this special value to
|
||||
* indicate that the slice is full. */
|
||||
#define FREE_SLOT_INDEX_NONE ((size_t) -1)
|
||||
|
||||
#if defined(MBEDTLS_TEST_HOOKS)
|
||||
size_t psa_key_slot_volatile_slice_count(void)
|
||||
{
|
||||
return KEY_SLOT_VOLATILE_SLICE_COUNT;
|
||||
}
|
||||
#endif
|
||||
|
||||
#else /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
|
||||
/* Static key store.
|
||||
*
|
||||
* All the keys (volatile or persistent) are in a single slice.
|
||||
* We only use slices as a concept to allow some differences between
|
||||
* static and dynamic key store management to be buried in auxiliary
|
||||
* functions.
|
||||
*/
|
||||
|
||||
#define PERSISTENT_KEY_CACHE_COUNT MBEDTLS_PSA_KEY_SLOT_COUNT
|
||||
#define KEY_SLICE_COUNT 1u
|
||||
#define KEY_SLOT_CACHE_SLICE_INDEX 0
|
||||
|
||||
#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
|
||||
|
||||
typedef struct {
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
psa_key_slot_t *key_slices[KEY_SLICE_COUNT];
|
||||
size_t first_free_slot_index[KEY_SLOT_VOLATILE_SLICE_COUNT];
|
||||
#else /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
psa_key_slot_t key_slots[MBEDTLS_PSA_KEY_SLOT_COUNT];
|
||||
#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
uint8_t key_slots_initialized;
|
||||
} psa_global_data_t;
|
||||
|
||||
static psa_global_data_t global_data;
|
||||
|
||||
MBEDTLS_STATIC_ASSERT(ARRAY_LENGTH(global_data.key_slots) <=
|
||||
PSA_KEY_ID_VOLATILE_MAX - PSA_KEY_ID_VOLATILE_MIN + 1,
|
||||
"The key slot array is larger than the volatile key ID range");
|
||||
|
||||
static uint8_t psa_get_key_slots_initialized(void)
|
||||
{
|
||||
uint8_t initialized;
|
||||
@ -86,6 +209,125 @@ static uint8_t psa_get_key_slots_initialized(void)
|
||||
return initialized;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** The length of the given slice in the key slot table.
|
||||
*
|
||||
* \param slice_idx The slice number. It must satisfy
|
||||
* 0 <= slice_idx < KEY_SLICE_COUNT.
|
||||
*
|
||||
* \return The number of elements in the given slice.
|
||||
*/
|
||||
static inline size_t key_slice_length(size_t slice_idx);
|
||||
|
||||
/** Get a pointer to the slot where the given volatile key is located.
|
||||
*
|
||||
* \param key_id The key identifier. It must be a valid volatile key
|
||||
* identifier.
|
||||
* \return A pointer to the only slot that the given key
|
||||
* can be in. Note that the slot may be empty or
|
||||
* contain a different key.
|
||||
*/
|
||||
static inline psa_key_slot_t *get_volatile_key_slot(psa_key_id_t key_id);
|
||||
|
||||
/** Get a pointer to an entry in the persistent key cache.
|
||||
*
|
||||
* \param slot_idx The index in the table. It must satisfy
|
||||
* 0 <= slot_idx < PERSISTENT_KEY_CACHE_COUNT.
|
||||
* \return A pointer to the slot containing the given
|
||||
* persistent key cache entry.
|
||||
*/
|
||||
static inline psa_key_slot_t *get_persistent_key_slot(size_t slot_idx);
|
||||
|
||||
/** Get a pointer to a slot given by slice and index.
|
||||
*
|
||||
* \param slice_idx The slice number. It must satisfy
|
||||
* 0 <= slice_idx < KEY_SLICE_COUNT.
|
||||
* \param slot_idx An index in the given slice. It must satisfy
|
||||
* 0 <= slot_idx < key_slice_length(slice_idx).
|
||||
*
|
||||
* \return A pointer to the given slot.
|
||||
*/
|
||||
static inline psa_key_slot_t *get_key_slot(size_t slice_idx, size_t slot_idx);
|
||||
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
|
||||
#if defined(MBEDTLS_TEST_HOOKS)
|
||||
size_t (*mbedtls_test_hook_psa_volatile_key_slice_length)(size_t slice_idx) = NULL;
|
||||
#endif
|
||||
|
||||
static inline size_t key_slice_length(size_t slice_idx)
|
||||
{
|
||||
if (slice_idx == KEY_SLOT_CACHE_SLICE_INDEX) {
|
||||
return PERSISTENT_KEY_CACHE_COUNT;
|
||||
} else {
|
||||
#if defined(MBEDTLS_TEST_HOOKS)
|
||||
if (mbedtls_test_hook_psa_volatile_key_slice_length != NULL) {
|
||||
return mbedtls_test_hook_psa_volatile_key_slice_length(slice_idx);
|
||||
}
|
||||
#endif
|
||||
return KEY_SLOT_VOLATILE_SLICE_BASE_LENGTH << slice_idx;
|
||||
}
|
||||
}
|
||||
|
||||
static inline psa_key_slot_t *get_volatile_key_slot(psa_key_id_t key_id)
|
||||
{
|
||||
size_t slice_idx = slice_index_of_volatile_key_id(key_id);
|
||||
if (slice_idx >= KEY_SLOT_VOLATILE_SLICE_COUNT) {
|
||||
return NULL;
|
||||
}
|
||||
size_t slot_idx = slot_index_of_volatile_key_id(key_id);
|
||||
if (slot_idx >= key_slice_length(slice_idx)) {
|
||||
return NULL;
|
||||
}
|
||||
psa_key_slot_t *slice = global_data.key_slices[slice_idx];
|
||||
if (slice == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return &slice[slot_idx];
|
||||
}
|
||||
|
||||
static inline psa_key_slot_t *get_persistent_key_slot(size_t slot_idx)
|
||||
{
|
||||
return &global_data.key_slices[KEY_SLOT_CACHE_SLICE_INDEX][slot_idx];
|
||||
}
|
||||
|
||||
static inline psa_key_slot_t *get_key_slot(size_t slice_idx, size_t slot_idx)
|
||||
{
|
||||
return &global_data.key_slices[slice_idx][slot_idx];
|
||||
}
|
||||
|
||||
#else /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
|
||||
static inline size_t key_slice_length(size_t slice_idx)
|
||||
{
|
||||
(void) slice_idx;
|
||||
return ARRAY_LENGTH(global_data.key_slots);
|
||||
}
|
||||
|
||||
static inline psa_key_slot_t *get_volatile_key_slot(psa_key_id_t key_id)
|
||||
{
|
||||
MBEDTLS_STATIC_ASSERT(ARRAY_LENGTH(global_data.key_slots) <=
|
||||
PSA_KEY_ID_VOLATILE_MAX - PSA_KEY_ID_VOLATILE_MIN + 1,
|
||||
"The key slot array is larger than the volatile key ID range");
|
||||
return &global_data.key_slots[key_id - PSA_KEY_ID_VOLATILE_MIN];
|
||||
}
|
||||
|
||||
static inline psa_key_slot_t *get_persistent_key_slot(size_t slot_idx)
|
||||
{
|
||||
return &global_data.key_slots[slot_idx];
|
||||
}
|
||||
|
||||
static inline psa_key_slot_t *get_key_slot(size_t slice_idx, size_t slot_idx)
|
||||
{
|
||||
(void) slice_idx;
|
||||
return &global_data.key_slots[slot_idx];
|
||||
}
|
||||
|
||||
#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
|
||||
|
||||
|
||||
int psa_is_valid_key_id(mbedtls_svc_key_id_t key, int vendor_ok)
|
||||
{
|
||||
psa_key_id_t key_id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(key);
|
||||
@ -147,12 +389,13 @@ static psa_status_t psa_get_and_lock_key_slot_in_memory(
|
||||
psa_key_slot_t *slot = NULL;
|
||||
|
||||
if (psa_key_id_is_volatile(key_id)) {
|
||||
slot = &global_data.key_slots[key_id - PSA_KEY_ID_VOLATILE_MIN];
|
||||
slot = get_volatile_key_slot(key_id);
|
||||
|
||||
/* Check if both the PSA key identifier key_id and the owner
|
||||
* identifier of key match those of the key slot. */
|
||||
if ((slot->state == PSA_SLOT_FULL) &&
|
||||
(mbedtls_svc_key_id_equal(key, slot->attr.id))) {
|
||||
if (slot != NULL &&
|
||||
slot->state == PSA_SLOT_FULL &&
|
||||
mbedtls_svc_key_id_equal(key, slot->attr.id)) {
|
||||
status = PSA_SUCCESS;
|
||||
} else {
|
||||
status = PSA_ERROR_DOES_NOT_EXIST;
|
||||
@ -162,8 +405,8 @@ static psa_status_t psa_get_and_lock_key_slot_in_memory(
|
||||
return PSA_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
for (slot_idx = 0; slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT; slot_idx++) {
|
||||
slot = &global_data.key_slots[slot_idx];
|
||||
for (slot_idx = 0; slot_idx < PERSISTENT_KEY_CACHE_COUNT; slot_idx++) {
|
||||
slot = get_persistent_key_slot(slot_idx);
|
||||
/* Only consider slots which are in a full state. */
|
||||
if ((slot->state == PSA_SLOT_FULL) &&
|
||||
(mbedtls_svc_key_id_equal(key, slot->attr.id))) {
|
||||
@ -186,29 +429,169 @@ static psa_status_t psa_get_and_lock_key_slot_in_memory(
|
||||
|
||||
psa_status_t psa_initialize_key_slots(void)
|
||||
{
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
global_data.key_slices[KEY_SLOT_CACHE_SLICE_INDEX] =
|
||||
mbedtls_calloc(PERSISTENT_KEY_CACHE_COUNT,
|
||||
sizeof(*global_data.key_slices[KEY_SLOT_CACHE_SLICE_INDEX]));
|
||||
if (global_data.key_slices[KEY_SLOT_CACHE_SLICE_INDEX] == NULL) {
|
||||
return PSA_ERROR_INSUFFICIENT_MEMORY;
|
||||
}
|
||||
#else /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
/* Nothing to do: program startup and psa_wipe_all_key_slots() both
|
||||
* guarantee that the key slots are initialized to all-zero, which
|
||||
* means that all the key slots are in a valid, empty state. The global
|
||||
* data mutex is already held when calling this function, so no need to
|
||||
* lock it here, to set the flag. */
|
||||
#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
|
||||
global_data.key_slots_initialized = 1;
|
||||
return PSA_SUCCESS;
|
||||
}
|
||||
|
||||
void psa_wipe_all_key_slots(void)
|
||||
{
|
||||
size_t slot_idx;
|
||||
|
||||
for (slot_idx = 0; slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT; slot_idx++) {
|
||||
psa_key_slot_t *slot = &global_data.key_slots[slot_idx];
|
||||
slot->registered_readers = 1;
|
||||
slot->state = PSA_SLOT_PENDING_DELETION;
|
||||
(void) psa_wipe_key_slot(slot);
|
||||
for (size_t slice_idx = 0; slice_idx < KEY_SLICE_COUNT; slice_idx++) {
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
if (global_data.key_slices[slice_idx] == NULL) {
|
||||
continue;
|
||||
}
|
||||
#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
for (size_t slot_idx = 0; slot_idx < key_slice_length(slice_idx); slot_idx++) {
|
||||
psa_key_slot_t *slot = get_key_slot(slice_idx, slot_idx);
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
/* When MBEDTLS_PSA_KEY_STORE_DYNAMIC is disabled, calling
|
||||
* psa_wipe_key_slot() on an unused slot is useless, but it
|
||||
* happens to work (because we flip the state to PENDING_DELETION).
|
||||
*
|
||||
* When MBEDTLS_PSA_KEY_STORE_DYNAMIC is enabled,
|
||||
* psa_wipe_key_slot() needs to have a valid slice_index
|
||||
* field, but that value might not be correct in a
|
||||
* free slot, so we must not call it.
|
||||
*
|
||||
* Bypass the call to psa_wipe_key_slot() if the slot is empty,
|
||||
* but only if MBEDTLS_PSA_KEY_STORE_DYNAMIC is enabled, to save
|
||||
* a few bytes of code size otherwise.
|
||||
*/
|
||||
if (slot->state == PSA_SLOT_EMPTY) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
slot->var.occupied.registered_readers = 1;
|
||||
slot->state = PSA_SLOT_PENDING_DELETION;
|
||||
(void) psa_wipe_key_slot(slot);
|
||||
}
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
mbedtls_free(global_data.key_slices[slice_idx]);
|
||||
global_data.key_slices[slice_idx] = NULL;
|
||||
#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
}
|
||||
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
for (size_t slice_idx = 0; slice_idx < KEY_SLOT_VOLATILE_SLICE_COUNT; slice_idx++) {
|
||||
global_data.first_free_slot_index[slice_idx] = 0;
|
||||
}
|
||||
#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
|
||||
/* The global data mutex is already held when calling this function. */
|
||||
global_data.key_slots_initialized = 0;
|
||||
}
|
||||
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
|
||||
static psa_status_t psa_allocate_volatile_key_slot(psa_key_id_t *key_id,
|
||||
psa_key_slot_t **p_slot)
|
||||
{
|
||||
size_t slice_idx;
|
||||
for (slice_idx = 0; slice_idx < KEY_SLOT_VOLATILE_SLICE_COUNT; slice_idx++) {
|
||||
if (global_data.first_free_slot_index[slice_idx] != FREE_SLOT_INDEX_NONE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (slice_idx == KEY_SLOT_VOLATILE_SLICE_COUNT) {
|
||||
return PSA_ERROR_INSUFFICIENT_MEMORY;
|
||||
}
|
||||
|
||||
if (global_data.key_slices[slice_idx] == NULL) {
|
||||
global_data.key_slices[slice_idx] =
|
||||
mbedtls_calloc(key_slice_length(slice_idx),
|
||||
sizeof(psa_key_slot_t));
|
||||
if (global_data.key_slices[slice_idx] == NULL) {
|
||||
return PSA_ERROR_INSUFFICIENT_MEMORY;
|
||||
}
|
||||
}
|
||||
psa_key_slot_t *slice = global_data.key_slices[slice_idx];
|
||||
|
||||
size_t slot_idx = global_data.first_free_slot_index[slice_idx];
|
||||
*key_id = volatile_key_id_of_index(slice_idx, slot_idx);
|
||||
|
||||
psa_key_slot_t *slot = &slice[slot_idx];
|
||||
size_t next_free = slot_idx + 1 + slot->var.free.next_free_relative_to_next;
|
||||
if (next_free >= key_slice_length(slice_idx)) {
|
||||
next_free = FREE_SLOT_INDEX_NONE;
|
||||
}
|
||||
global_data.first_free_slot_index[slice_idx] = next_free;
|
||||
/* The .next_free field is not meaningful when the slot is not free,
|
||||
* so give it the same content as freshly initialized memory. */
|
||||
slot->var.free.next_free_relative_to_next = 0;
|
||||
|
||||
psa_status_t status = psa_key_slot_state_transition(slot,
|
||||
PSA_SLOT_EMPTY,
|
||||
PSA_SLOT_FILLING);
|
||||
if (status != PSA_SUCCESS) {
|
||||
/* The only reason for failure is if the slot state was not empty.
|
||||
* This indicates that something has gone horribly wrong.
|
||||
* In this case, we leave the slot out of the free list, and stop
|
||||
* modifying it. This minimizes any further corruption. The slot
|
||||
* is a memory leak, but that's a lesser evil. */
|
||||
return status;
|
||||
}
|
||||
|
||||
*p_slot = slot;
|
||||
/* We assert at compile time that the slice index fits in uint8_t. */
|
||||
slot->slice_index = (uint8_t) slice_idx;
|
||||
return PSA_SUCCESS;
|
||||
}
|
||||
|
||||
psa_status_t psa_free_key_slot(size_t slice_idx,
|
||||
psa_key_slot_t *slot)
|
||||
{
|
||||
|
||||
if (slice_idx == KEY_SLOT_CACHE_SLICE_INDEX) {
|
||||
/* This is a cache entry. We don't maintain a free list, so
|
||||
* there's nothing to do. */
|
||||
return PSA_SUCCESS;
|
||||
}
|
||||
if (slice_idx >= KEY_SLOT_VOLATILE_SLICE_COUNT) {
|
||||
return PSA_ERROR_CORRUPTION_DETECTED;
|
||||
}
|
||||
|
||||
psa_key_slot_t *slice = global_data.key_slices[slice_idx];
|
||||
psa_key_slot_t *slice_end = slice + key_slice_length(slice_idx);
|
||||
if (slot < slice || slot >= slice_end) {
|
||||
/* The slot isn't actually in the slice! We can't detect that
|
||||
* condition for sure, because the pointer comparison itself is
|
||||
* undefined behavior in that case. That same condition makes the
|
||||
* subtraction to calculate the slot index also UB.
|
||||
* Give up now to avoid causing further corruption.
|
||||
*/
|
||||
return PSA_ERROR_CORRUPTION_DETECTED;
|
||||
}
|
||||
size_t slot_idx = slot - slice;
|
||||
|
||||
size_t next_free = global_data.first_free_slot_index[slice_idx];
|
||||
if (next_free >= key_slice_length(slice_idx)) {
|
||||
/* The slot was full. The newly freed slot thus becomes the
|
||||
* end of the free list. */
|
||||
next_free = key_slice_length(slice_idx);
|
||||
}
|
||||
global_data.first_free_slot_index[slice_idx] = slot_idx;
|
||||
slot->var.free.next_free_relative_to_next =
|
||||
(int32_t) next_free - (int32_t) slot_idx - 1;
|
||||
|
||||
return PSA_SUCCESS;
|
||||
}
|
||||
#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
|
||||
psa_status_t psa_reserve_free_key_slot(psa_key_id_t *volatile_key_id,
|
||||
psa_key_slot_t **p_slot)
|
||||
{
|
||||
@ -221,9 +604,19 @@ psa_status_t psa_reserve_free_key_slot(psa_key_id_t *volatile_key_id,
|
||||
goto error;
|
||||
}
|
||||
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
if (volatile_key_id != NULL) {
|
||||
return psa_allocate_volatile_key_slot(volatile_key_id, p_slot);
|
||||
}
|
||||
#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
|
||||
/* With a dynamic key store, allocate an entry in the cache slice,
|
||||
* applicable only to non-volatile keys that get cached in RAM.
|
||||
* With a static key store, allocate an entry in the sole slice,
|
||||
* applicable to all keys. */
|
||||
selected_slot = unused_persistent_key_slot = NULL;
|
||||
for (slot_idx = 0; slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT; slot_idx++) {
|
||||
psa_key_slot_t *slot = &global_data.key_slots[slot_idx];
|
||||
for (slot_idx = 0; slot_idx < PERSISTENT_KEY_CACHE_COUNT; slot_idx++) {
|
||||
psa_key_slot_t *slot = get_key_slot(KEY_SLOT_CACHE_SLICE_INDEX, slot_idx);
|
||||
if (slot->state == PSA_SLOT_EMPTY) {
|
||||
selected_slot = slot;
|
||||
break;
|
||||
@ -261,8 +654,18 @@ psa_status_t psa_reserve_free_key_slot(psa_key_id_t *volatile_key_id,
|
||||
goto error;
|
||||
}
|
||||
|
||||
*volatile_key_id = PSA_KEY_ID_VOLATILE_MIN +
|
||||
((psa_key_id_t) (selected_slot - global_data.key_slots));
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
selected_slot->slice_index = KEY_SLOT_CACHE_SLICE_INDEX;
|
||||
#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
|
||||
#if !defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
if (volatile_key_id != NULL) {
|
||||
/* Refresh slot_idx, for when the slot is not the original
|
||||
* selected_slot but rather unused_persistent_key_slot. */
|
||||
slot_idx = selected_slot - global_data.key_slots;
|
||||
*volatile_key_id = PSA_KEY_ID_VOLATILE_MIN + slot_idx;
|
||||
}
|
||||
#endif
|
||||
*p_slot = selected_slot;
|
||||
|
||||
return PSA_SUCCESS;
|
||||
@ -271,7 +674,6 @@ psa_status_t psa_reserve_free_key_slot(psa_key_id_t *volatile_key_id,
|
||||
|
||||
error:
|
||||
*p_slot = NULL;
|
||||
*volatile_key_id = 0;
|
||||
|
||||
return status;
|
||||
}
|
||||
@ -430,9 +832,8 @@ psa_status_t psa_get_and_lock_key_slot(mbedtls_svc_key_id_t key,
|
||||
/* Loading keys from storage requires support for such a mechanism */
|
||||
#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) || \
|
||||
defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS)
|
||||
psa_key_id_t volatile_key_id;
|
||||
|
||||
status = psa_reserve_free_key_slot(&volatile_key_id, p_slot);
|
||||
status = psa_reserve_free_key_slot(NULL, p_slot);
|
||||
if (status != PSA_SUCCESS) {
|
||||
#if defined(MBEDTLS_THREADING_C)
|
||||
PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
|
||||
@ -500,12 +901,12 @@ psa_status_t psa_unregister_read(psa_key_slot_t *slot)
|
||||
/* If we are the last reader and the slot is marked for deletion,
|
||||
* we must wipe the slot here. */
|
||||
if ((slot->state == PSA_SLOT_PENDING_DELETION) &&
|
||||
(slot->registered_readers == 1)) {
|
||||
(slot->var.occupied.registered_readers == 1)) {
|
||||
return psa_wipe_key_slot(slot);
|
||||
}
|
||||
|
||||
if (psa_key_slot_has_readers(slot)) {
|
||||
slot->registered_readers--;
|
||||
slot->var.occupied.registered_readers--;
|
||||
return PSA_SUCCESS;
|
||||
}
|
||||
|
||||
@ -639,7 +1040,7 @@ psa_status_t psa_close_key(psa_key_handle_t handle)
|
||||
return status;
|
||||
}
|
||||
|
||||
if (slot->registered_readers == 1) {
|
||||
if (slot->var.occupied.registered_readers == 1) {
|
||||
status = psa_wipe_key_slot(slot);
|
||||
} else {
|
||||
status = psa_unregister_read(slot);
|
||||
@ -674,7 +1075,7 @@ psa_status_t psa_purge_key(mbedtls_svc_key_id_t key)
|
||||
}
|
||||
|
||||
if ((!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) &&
|
||||
(slot->registered_readers == 1)) {
|
||||
(slot->var.occupied.registered_readers == 1)) {
|
||||
status = psa_wipe_key_slot(slot);
|
||||
} else {
|
||||
status = psa_unregister_read(slot);
|
||||
@ -689,34 +1090,39 @@ psa_status_t psa_purge_key(mbedtls_svc_key_id_t key)
|
||||
|
||||
void mbedtls_psa_get_stats(mbedtls_psa_stats_t *stats)
|
||||
{
|
||||
size_t slot_idx;
|
||||
|
||||
memset(stats, 0, sizeof(*stats));
|
||||
|
||||
for (slot_idx = 0; slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT; slot_idx++) {
|
||||
const psa_key_slot_t *slot = &global_data.key_slots[slot_idx];
|
||||
if (psa_key_slot_has_readers(slot)) {
|
||||
++stats->locked_slots;
|
||||
}
|
||||
if (slot->state == PSA_SLOT_EMPTY) {
|
||||
++stats->empty_slots;
|
||||
for (size_t slice_idx = 0; slice_idx < KEY_SLICE_COUNT; slice_idx++) {
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
if (global_data.key_slices[slice_idx] == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
|
||||
++stats->volatile_slots;
|
||||
} else {
|
||||
psa_key_id_t id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(slot->attr.id);
|
||||
++stats->persistent_slots;
|
||||
if (id > stats->max_open_internal_key_id) {
|
||||
stats->max_open_internal_key_id = id;
|
||||
#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
for (size_t slot_idx = 0; slot_idx < key_slice_length(slice_idx); slot_idx++) {
|
||||
const psa_key_slot_t *slot = get_key_slot(slice_idx, slot_idx);
|
||||
if (slot->state == PSA_SLOT_EMPTY) {
|
||||
++stats->empty_slots;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (PSA_KEY_LIFETIME_GET_LOCATION(slot->attr.lifetime) !=
|
||||
PSA_KEY_LOCATION_LOCAL_STORAGE) {
|
||||
psa_key_id_t id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(slot->attr.id);
|
||||
++stats->external_slots;
|
||||
if (id > stats->max_open_external_key_id) {
|
||||
stats->max_open_external_key_id = id;
|
||||
if (psa_key_slot_has_readers(slot)) {
|
||||
++stats->locked_slots;
|
||||
}
|
||||
if (PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
|
||||
++stats->volatile_slots;
|
||||
} else {
|
||||
psa_key_id_t id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(slot->attr.id);
|
||||
++stats->persistent_slots;
|
||||
if (id > stats->max_open_internal_key_id) {
|
||||
stats->max_open_internal_key_id = id;
|
||||
}
|
||||
}
|
||||
if (PSA_KEY_LIFETIME_GET_LOCATION(slot->attr.lifetime) !=
|
||||
PSA_KEY_LOCATION_LOCAL_STORAGE) {
|
||||
psa_key_id_t id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(slot->attr.id);
|
||||
++stats->external_slots;
|
||||
if (id > stats->max_open_external_key_id) {
|
||||
stats->max_open_external_key_id = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,10 @@
|
||||
*
|
||||
* The first #MBEDTLS_PSA_KEY_SLOT_COUNT identifiers of the implementation
|
||||
* range of key identifiers are reserved for volatile key identifiers.
|
||||
* A volatile key identifier is equal to #PSA_KEY_ID_VOLATILE_MIN plus the
|
||||
* index of the key slot containing the volatile key definition.
|
||||
*
|
||||
* If \c id is a a volatile key identifier, #PSA_KEY_ID_VOLATILE_MIN - \c id
|
||||
* indicates the key slot containing the volatile key definition. See
|
||||
* psa_crypto_slot_management.c for details.
|
||||
*/
|
||||
|
||||
/** The minimum value for a volatile key identifier.
|
||||
@ -27,8 +29,12 @@
|
||||
|
||||
/** The maximum value for a volatile key identifier.
|
||||
*/
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
#define PSA_KEY_ID_VOLATILE_MAX (MBEDTLS_PSA_KEY_ID_BUILTIN_MIN - 1)
|
||||
#else /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
#define PSA_KEY_ID_VOLATILE_MAX \
|
||||
(PSA_KEY_ID_VOLATILE_MIN + MBEDTLS_PSA_KEY_SLOT_COUNT - 1)
|
||||
#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
|
||||
/** Test whether a key identifier is a volatile key identifier.
|
||||
*
|
||||
@ -94,6 +100,24 @@ psa_status_t psa_get_and_lock_key_slot(mbedtls_svc_key_id_t key,
|
||||
*/
|
||||
psa_status_t psa_initialize_key_slots(void);
|
||||
|
||||
#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
/* Allow test code to customize the key slice length. We use this in tests
|
||||
* that exhaust the key store to reach a full key store in reasonable time
|
||||
* and memory.
|
||||
*
|
||||
* The length of each slice must be between 1 and
|
||||
* (1 << KEY_ID_SLOT_INDEX_WIDTH) inclusive.
|
||||
*
|
||||
* The length for a given slice index must not change while
|
||||
* the key store is initialized.
|
||||
*/
|
||||
extern size_t (*mbedtls_test_hook_psa_volatile_key_slice_length)(
|
||||
size_t slice_idx);
|
||||
|
||||
/* The number of volatile key slices. */
|
||||
size_t psa_key_slot_volatile_slice_count(void);
|
||||
#endif
|
||||
|
||||
/** Delete all data from key slots in memory.
|
||||
* This function is not thread safe, it wipes every key slot regardless of
|
||||
* state and reader count. It should only be called when no slot is in use.
|
||||
@ -113,13 +137,22 @@ void psa_wipe_all_key_slots(void);
|
||||
* If multi-threading is enabled, the caller must hold the
|
||||
* global key slot mutex.
|
||||
*
|
||||
* \param[out] volatile_key_id On success, volatile key identifier
|
||||
* associated to the returned slot.
|
||||
* \param[out] volatile_key_id - If null, reserve a cache slot for
|
||||
* a persistent or built-in key.
|
||||
* - If non-null, allocate a slot for
|
||||
* a volatile key. On success,
|
||||
* \p *volatile_key_id is the
|
||||
* identifier corresponding to the
|
||||
* returned slot. It is the caller's
|
||||
* responsibility to set this key identifier
|
||||
* in the attributes.
|
||||
* \param[out] p_slot On success, a pointer to the slot.
|
||||
*
|
||||
* \retval #PSA_SUCCESS \emptydescription
|
||||
* \retval #PSA_ERROR_INSUFFICIENT_MEMORY
|
||||
* There were no free key slots.
|
||||
* When #MBEDTLS_PSA_KEY_STORE_DYNAMIC is enabled, there was not
|
||||
* enough memory to allocate more slots.
|
||||
* \retval #PSA_ERROR_BAD_STATE \emptydescription
|
||||
* \retval #PSA_ERROR_CORRUPTION_DETECTED
|
||||
* This function attempted to operate on a key slot which was in an
|
||||
@ -128,6 +161,29 @@ void psa_wipe_all_key_slots(void);
|
||||
psa_status_t psa_reserve_free_key_slot(psa_key_id_t *volatile_key_id,
|
||||
psa_key_slot_t **p_slot);
|
||||
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
/** Return a key slot to the free list.
|
||||
*
|
||||
* Call this function when a slot obtained from psa_reserve_free_key_slot()
|
||||
* is no longer in use.
|
||||
*
|
||||
* If multi-threading is enabled, the caller must hold the
|
||||
* global key slot mutex.
|
||||
*
|
||||
* \param slice_idx The slice containing the slot.
|
||||
* This is `slot->slice_index` when the slot
|
||||
* is obtained from psa_reserve_free_key_slot().
|
||||
* \param slot The key slot.
|
||||
*
|
||||
* \retval #PSA_SUCCESS \emptydescription
|
||||
* \retval #PSA_ERROR_CORRUPTION_DETECTED
|
||||
* This function attempted to operate on a key slot which was in an
|
||||
* unexpected state.
|
||||
*/
|
||||
psa_status_t psa_free_key_slot(size_t slice_idx,
|
||||
psa_key_slot_t *slot);
|
||||
#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
|
||||
/** Change the state of a key slot.
|
||||
*
|
||||
* This function changes the state of the key slot from expected_state to
|
||||
@ -174,10 +230,10 @@ static inline psa_status_t psa_key_slot_state_transition(
|
||||
static inline psa_status_t psa_register_read(psa_key_slot_t *slot)
|
||||
{
|
||||
if ((slot->state != PSA_SLOT_FULL) ||
|
||||
(slot->registered_readers >= SIZE_MAX)) {
|
||||
(slot->var.occupied.registered_readers >= SIZE_MAX)) {
|
||||
return PSA_ERROR_CORRUPTION_DETECTED;
|
||||
}
|
||||
slot->registered_readers++;
|
||||
slot->var.occupied.registered_readers++;
|
||||
|
||||
return PSA_SUCCESS;
|
||||
}
|
||||
|
@ -129,9 +129,9 @@ depends_on:MBEDTLS_PSA_CRYPTO_STORAGE_C
|
||||
# writing, this happens in builds where AES uses a PSA driver and the
|
||||
# PSA RNG uses AES-CTR_DRBG through the PSA AES.
|
||||
# Pick a key id that's in the middle of the volatile key ID range.
|
||||
# That works out both when MBEDTLS_PSA_KEY_SLOT_DYNAMIC is enabled and
|
||||
# That works out both when MBEDTLS_PSA_KEY_STORE_DYNAMIC is enabled and
|
||||
# volatile key IDs are assigned starting with the lowest value, and when
|
||||
# MBEDTLS_PSA_KEY_SLOT_DYNAMIC is disabled and volatile key IDs are assigned
|
||||
# MBEDTLS_PSA_KEY_STORE_DYNAMIC is disabled and volatile key IDs are assigned
|
||||
# starting with the highest values.
|
||||
open_fail:(PSA_KEY_ID_VOLATILE_MIN + PSA_KEY_ID_VOLATILE_MAX) / 2:PSA_ERROR_DOES_NOT_EXIST
|
||||
|
||||
@ -228,6 +228,11 @@ invalid_handle:INVALID_HANDLE_HUGE:PSA_ERROR_INVALID_HANDLE
|
||||
Key slot count: maximum
|
||||
many_transient_keys:MBEDTLS_PSA_KEY_SLOT_COUNT - MBEDTLS_TEST_PSA_INTERNAL_KEYS
|
||||
|
||||
Key slot count: dynamic: more than MBEDTLS_PSA_KEY_SLOT_COUNT
|
||||
depends_on:MBEDTLS_PSA_KEY_STORE_DYNAMIC
|
||||
# Check that MBEDTLS_PSA_KEY_SLOT_COUNT doesn't apply to volatile keys.
|
||||
many_transient_keys:MBEDTLS_PSA_KEY_SLOT_COUNT + 1
|
||||
|
||||
Key slot count: try to overfill, destroy first
|
||||
fill_key_store:0
|
||||
|
||||
|
@ -98,10 +98,30 @@ exit:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Currently, there is always a maximum number of volatile keys that can
|
||||
* realistically be reached in tests. When we add configurations where this
|
||||
* is not true, undefine the macro in such configurations. */
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
|
||||
#if defined(MBEDTLS_TEST_HOOKS)
|
||||
/* Artificially restrictable dynamic key store */
|
||||
#define KEY_SLICE_1_LENGTH 4
|
||||
#define KEY_SLICE_2_LENGTH 10
|
||||
static size_t tiny_key_slice_length(size_t slice_idx)
|
||||
{
|
||||
switch (slice_idx) {
|
||||
case 1: return KEY_SLICE_1_LENGTH;
|
||||
case 2: return KEY_SLICE_2_LENGTH;
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
#define MAX_VOLATILE_KEYS \
|
||||
(KEY_SLICE_1_LENGTH + KEY_SLICE_2_LENGTH + \
|
||||
psa_key_slot_volatile_slice_count() - 2)
|
||||
|
||||
#else /* Effectively unbounded dynamic key store */
|
||||
#undef MAX_VOLATILE_KEYS
|
||||
#endif
|
||||
|
||||
#else /* Static key store */
|
||||
#define MAX_VOLATILE_KEYS MBEDTLS_PSA_KEY_SLOT_COUNT
|
||||
#endif
|
||||
|
||||
/* END_HEADER */
|
||||
|
||||
@ -867,6 +887,10 @@ void fill_key_store(int key_to_destroy_arg)
|
||||
uint8_t exported[sizeof(size_t)];
|
||||
size_t exported_length;
|
||||
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC) && defined(MBEDTLS_TEST_HOOKS)
|
||||
mbedtls_test_hook_psa_volatile_key_slice_length = &tiny_key_slice_length;
|
||||
#endif
|
||||
|
||||
PSA_ASSERT(psa_crypto_init());
|
||||
|
||||
mbedtls_psa_stats_t stats;
|
||||
@ -949,6 +973,9 @@ void fill_key_store(int key_to_destroy_arg)
|
||||
exit:
|
||||
PSA_DONE();
|
||||
mbedtls_free(keys);
|
||||
#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC) && defined(MBEDTLS_TEST_HOOKS)
|
||||
mbedtls_test_hook_psa_volatile_key_slice_length = NULL;
|
||||
#endif
|
||||
}
|
||||
/* END_CASE */
|
||||
|
||||
@ -1028,7 +1055,7 @@ exit:
|
||||
}
|
||||
/* END_CASE */
|
||||
|
||||
/* BEGIN_CASE depends_on:MBEDTLS_PSA_CRYPTO_STORAGE_C */
|
||||
/* BEGIN_CASE depends_on:MBEDTLS_PSA_CRYPTO_STORAGE_C:!MBEDTLS_PSA_KEY_STORE_DYNAMIC */
|
||||
void non_reusable_key_slots_integrity_in_case_of_key_slot_starvation()
|
||||
{
|
||||
psa_status_t status;
|
||||
@ -1068,7 +1095,14 @@ void non_reusable_key_slots_integrity_in_case_of_key_slot_starvation()
|
||||
TEST_ASSERT(mbedtls_svc_key_id_equal(returned_key_id, persistent_key));
|
||||
|
||||
/*
|
||||
* Create the maximum available number of volatile keys
|
||||
* Create the maximum available number of keys that are locked in
|
||||
* memory. This can be:
|
||||
* - volatile keys, when MBEDTLS_PSA_KEY_STORE_DYNAMIC is disabled;
|
||||
* - opened persistent keys (could work, but not currently implemented
|
||||
* in this test function);
|
||||
* - keys in use by another thread (we don't do this because it would
|
||||
* be hard to arrange and we can't control how long the keys are
|
||||
* locked anyway).
|
||||
*/
|
||||
psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE);
|
||||
for (i = 0; i < available_key_slots; i++) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user