Merge pull request #9464 from gilles-peskine-arm/psa-keystore-dynamic-development
dynamically sized key store
diff --git a/ChangeLog.d/dynamic-keystore.txt b/ChangeLog.d/dynamic-keystore.txt
index d576dcd..c6aac3c 100644
--- a/ChangeLog.d/dynamic-keystore.txt
+++ b/ChangeLog.d/dynamic-keystore.txt
@@ -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
diff --git a/include/mbedtls/mbedtls_config.h b/include/mbedtls/mbedtls_config.h
index e3589ea..4a5f21a 100644
--- a/include/mbedtls/mbedtls_config.h
+++ b/include/mbedtls/mbedtls_config.h
@@ -1233,6 +1233,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.
* Compared to the default implementation:
@@ -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.
diff --git a/tests/scripts/components-configuration-crypto.sh b/tests/scripts/components-configuration-crypto.sh
index f4744a8..37b610f 100644
--- a/tests/scripts/components-configuration-crypto.sh
+++ b/tests/scripts/components-configuration-crypto.sh
@@ -2069,6 +2069,40 @@
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 @@
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
diff --git a/tf-psa-crypto/core/psa_crypto.c b/tf-psa-crypto/core/psa_crypto.c
index fd05128..d1c93fd 100644
--- a/tf-psa-crypto/core/psa_crypto.c
+++ b/tf-psa-crypto/core/psa_crypto.c
@@ -1210,15 +1210,15 @@
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 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
* 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
diff --git a/tf-psa-crypto/core/psa_crypto_core.h b/tf-psa-crypto/core/psa_crypto_core.h
index e7ff151..21e7559 100644
--- a/tf-psa-crypto/core/psa_crypto_core.h
+++ b/tf-psa-crypto/core/psa_crypto_core.h
@@ -59,6 +59,8 @@
* 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 @@
* 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 @@
*/
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)
diff --git a/tf-psa-crypto/core/psa_crypto_slot_management.c b/tf-psa-crypto/core/psa_crypto_slot_management.c
index 9b297c9..9850d8c 100644
--- a/tf-psa-crypto/core/psa_crypto_slot_management.c
+++ b/tf-psa-crypto/core/psa_crypto_slot_management.c
@@ -58,17 +58,140 @@
+#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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
error:
*p_slot = NULL;
- *volatile_key_id = 0;
return status;
}
@@ -430,9 +832,8 @@
/* 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 @@
/* 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 @@
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 @@
}
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 @@
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;
+ }
}
}
}
diff --git a/tf-psa-crypto/core/psa_crypto_slot_management.h b/tf-psa-crypto/core/psa_crypto_slot_management.h
index 88b7c83..af1208e 100644
--- a/tf-psa-crypto/core/psa_crypto_slot_management.h
+++ b/tf-psa-crypto/core/psa_crypto_slot_management.h
@@ -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_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 @@
* 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 @@
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_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;
}
diff --git a/tf-psa-crypto/tests/suites/test_suite_psa_crypto_slot_management.data b/tf-psa-crypto/tests/suites/test_suite_psa_crypto_slot_management.data
index af3b946..f379dba 100644
--- a/tf-psa-crypto/tests/suites/test_suite_psa_crypto_slot_management.data
+++ b/tf-psa-crypto/tests/suites/test_suite_psa_crypto_slot_management.data
@@ -129,9 +129,9 @@
# 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 @@
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
diff --git a/tf-psa-crypto/tests/suites/test_suite_psa_crypto_slot_management.function b/tf-psa-crypto/tests/suites/test_suite_psa_crypto_slot_management.function
index f679f2e..604c4bd 100644
--- a/tf-psa-crypto/tests/suites/test_suite_psa_crypto_slot_management.function
+++ b/tf-psa-crypto/tests/suites/test_suite_psa_crypto_slot_management.function
@@ -98,10 +98,30 @@
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 @@
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 @@
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 @@
}
/* 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 @@
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++) {