Merge pull request #8705 from daverodgman/ctr-perf
Ctr perf
diff --git a/docs/redirects.yaml b/docs/redirects.yaml
index 7ea1d95..969ffe4 100644
--- a/docs/redirects.yaml
+++ b/docs/redirects.yaml
@@ -7,5 +7,5 @@
# expose it.
- type: exact
- from_url: /projects/api/en/latest/$rest
- to_url: /projects/api/en/development/
+ from_url: /projects/api/en/latest/*
+ to_url: /projects/api/en/development/:splat
diff --git a/library/psa_crypto.c b/library/psa_crypto.c
index a8baa6b..f17e14f 100644
--- a/library/psa_crypto.c
+++ b/library/psa_crypto.c
@@ -890,8 +890,9 @@
* In case of a persistent key, the function loads the description of the key
* into a key slot if not already done.
*
- * On success, the returned key slot is locked. It is the responsibility of
- * the caller to unlock the key slot when it does not access it anymore.
+ * On success, the returned key slot has been registered for reading.
+ * It is the responsibility of the caller to call psa_unregister_read(slot)
+ * when they have finished reading the contents of the slot.
*/
static psa_status_t psa_get_and_lock_key_slot_with_policy(
mbedtls_svc_key_id_t key,
@@ -935,7 +936,7 @@
error:
*p_slot = NULL;
- psa_unlock_key_slot(slot);
+ psa_unregister_read(slot);
return status;
}
@@ -950,8 +951,9 @@
* psa_get_and_lock_key_slot_with_policy() when there is no opaque key support
* for a cryptographic operation.
*
- * On success, the returned key slot is locked. It is the responsibility of the
- * caller to unlock the key slot when it does not access it anymore.
+ * On success, the returned key slot has been registered for reading.
+ * It is the responsibility of the caller to call psa_unregister_read(slot)
+ * when they have finished reading the contents of the slot.
*/
static psa_status_t psa_get_and_lock_transparent_key_slot_with_policy(
mbedtls_svc_key_id_t key,
@@ -966,7 +968,7 @@
}
if (psa_key_lifetime_is_external((*p_slot)->attr.lifetime)) {
- psa_unlock_key_slot(*p_slot);
+ psa_unregister_read(*p_slot);
*p_slot = NULL;
return PSA_ERROR_NOT_SUPPORTED;
}
@@ -994,15 +996,41 @@
/*
* As the return error code may not be handled in case of multiple errors,
- * do our best to report an unexpected lock counter. Assert with
- * MBEDTLS_TEST_HOOK_TEST_ASSERT that the lock counter is equal to one:
+ * do our best to report an unexpected amount of registered readers or
+ * an unexpected state.
+ * Assert with MBEDTLS_TEST_HOOK_TEST_ASSERT that the slot is valid for
+ * wiping.
* if the MBEDTLS_TEST_HOOKS configuration option is enabled and the
* function is called as part of the execution of a test suite, the
* execution of the test suite is stopped in error if the assertion fails.
*/
- if (slot->lock_count != 1) {
- MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->lock_count == 1);
- status = PSA_ERROR_CORRUPTION_DETECTED;
+ switch (slot->state) {
+ case PSA_SLOT_FULL:
+ /* In this state psa_wipe_key_slot() must only be called if the
+ * caller is the last reader. */
+ 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);
+ 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);
+ status = PSA_ERROR_CORRUPTION_DETECTED;
+ }
+ break;
+ case PSA_SLOT_EMPTY:
+ /* The slot is already empty, it cannot be wiped. */
+ MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->state != PSA_SLOT_EMPTY);
+ status = PSA_ERROR_CORRUPTION_DETECTED;
+ break;
+ default:
+ /* The slot's state is invalid. */
+ status = PSA_ERROR_CORRUPTION_DETECTED;
}
/* Multipart operations may still be using the key. This is safe
@@ -1012,7 +1040,8 @@
* key material can linger until all operations are completed. */
/* At this point, key material and other type-specific content has
* been wiped. Clear remaining metadata. We can call memset and not
- * zeroize because the metadata is not particularly sensitive. */
+ * 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));
return status;
}
@@ -1031,28 +1060,26 @@
}
/*
- * Get the description of the key in a key slot. In case of a persistent
- * key, this will load the key description from persistent memory if not
- * done yet. We cannot avoid this loading as without it we don't know if
+ * Get the description of the key in a key slot, and register to read it.
+ * In the case of a persistent key, this will load the key description
+ * from persistent memory if not done yet.
+ * We cannot avoid this loading as without it we don't know if
* the key is operated by an SE or not and this information is needed by
- * the current implementation.
- */
+ * the current implementation. */
status = psa_get_and_lock_key_slot(key, &slot);
if (status != PSA_SUCCESS) {
return status;
}
- /*
- * If the key slot containing the key description is under access by the
- * library (apart from the present access), the key cannot be destroyed
- * yet. For the time being, just return in error. Eventually (to be
- * implemented), the key should be destroyed when all accesses have
- * stopped.
- */
- if (slot->lock_count > 1) {
- psa_unlock_key_slot(slot);
- return PSA_ERROR_GENERIC_ERROR;
- }
+ /* Set the key slot containing the key description's state to
+ * PENDING_DELETION. This stops new operations from registering
+ * to read the slot. Current readers can safely continue to access
+ * the key within the slot; the last registered reader will
+ * automatically wipe the slot when they call psa_unregister_read().
+ * If the key is persistent, we can now delete the copy of the key
+ * from memory. If the key is opaque, we require the driver to
+ * deal with the deletion. */
+ slot->state = PSA_SLOT_PENDING_DELETION;
if (PSA_KEY_LIFETIME_IS_READ_ONLY(slot->attr.lifetime)) {
/* Refuse the destruction of a read-only key (which may or may not work
@@ -1100,6 +1127,9 @@
#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C)
if (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
+ /* Destroy the copy of the persistent key from storage.
+ * The slot will still hold a copy of the key until the last reader
+ * unregisters. */
status = psa_destroy_persistent_key(slot->attr.id);
if (overall_status == PSA_SUCCESS) {
overall_status = status;
@@ -1126,8 +1156,11 @@
#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
exit:
- status = psa_wipe_key_slot(slot);
- /* Prioritize CORRUPTION_DETECTED from wiping over a storage error */
+ /* Unregister from reading the slot. If we are the last active reader
+ * then this will wipe the slot. */
+ status = psa_unregister_read(slot);
+ /* Prioritize CORRUPTION_DETECTED from unregistering over
+ * a storage error. */
if (status != PSA_SUCCESS) {
overall_status = status;
}
@@ -1252,7 +1285,7 @@
psa_reset_key_attributes(attributes);
}
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
return (status == PSA_SUCCESS) ? unlock_status : status;
}
@@ -1348,7 +1381,7 @@
slot->key.data, slot->key.bytes,
data, data_size, data_length);
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
return (status == PSA_SUCCESS) ? unlock_status : status;
}
@@ -1462,7 +1495,7 @@
data, data_size, data_length);
exit:
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
return (status == PSA_SUCCESS) ? unlock_status : status;
}
@@ -1579,8 +1612,9 @@
* In case of failure at any step, stop the sequence and call
* psa_fail_key_creation().
*
- * On success, the key slot is locked. It is the responsibility of the caller
- * to unlock the key slot when it does not access it anymore.
+ * On success, the key slot's state is PSA_SLOT_FILLING.
+ * It is the responsibility of the caller to change the slot's state to
+ * PSA_SLOT_EMPTY/FULL once key creation has finished.
*
* \param method An identification of the calling function.
* \param[in] attributes Key attributes for the new key.
@@ -1611,7 +1645,7 @@
return status;
}
- status = psa_get_empty_key_slot(&volatile_key_id, p_slot);
+ status = psa_reserve_free_key_slot(&volatile_key_id, p_slot);
if (status != PSA_SUCCESS) {
return status;
}
@@ -1637,7 +1671,7 @@
/* Erase external-only flags from the internal copy. To access
* external-only flags, query `attributes`. Thanks to the check
* in psa_validate_key_attributes(), this leaves the dual-use
- * flags and any internal flag that psa_get_empty_key_slot()
+ * flags and any internal flag that psa_reserve_free_key_slot()
* may have set. */
slot->attr.flags &= ~MBEDTLS_PSA_KA_MASK_EXTERNAL_ONLY;
@@ -1689,8 +1723,6 @@
}
#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
- slot->status = PSA_SLOT_OCCUPIED;
-
return PSA_SUCCESS;
}
@@ -1702,9 +1734,9 @@
* See the documentation of psa_start_key_creation() for the intended use
* of this function.
*
- * If the finalization succeeds, the function unlocks the key slot (it was
- * locked by psa_start_key_creation()) and the key slot cannot be accessed
- * anymore as part of the key creation process.
+ * If the finalization succeeds, the function sets the key slot's state to
+ * PSA_SLOT_FULL, and the key slot can no longer be accessed as part of the
+ * key creation process.
*
* \param[in,out] slot Pointer to the slot with key material.
* \param[in] driver The secure element driver for the key,
@@ -1780,7 +1812,8 @@
if (status == PSA_SUCCESS) {
*key = slot->attr.id;
- status = psa_unlock_key_slot(slot);
+ status = psa_key_slot_state_transition(slot, PSA_SLOT_FILLING,
+ PSA_SLOT_FULL);
if (status != PSA_SUCCESS) {
*key = MBEDTLS_SVC_KEY_ID_INIT;
}
@@ -1795,7 +1828,7 @@
* or after psa_finish_key_creation() fails. In other circumstances, this
* function may not clean up persistent storage.
* See the documentation of psa_start_key_creation() for the intended use
- * of this function.
+ * of this function. Sets the slot's state to PSA_SLOT_EMPTY.
*
* \param[in,out] slot Pointer to the slot with key material.
* \param[in] driver The secure element driver for the key,
@@ -2134,7 +2167,7 @@
psa_fail_key_creation(target_slot, driver);
}
- unlock_status = psa_unlock_key_slot(source_slot);
+ unlock_status = psa_unregister_read(source_slot);
return (status == PSA_SUCCESS) ? unlock_status : status;
}
@@ -2455,7 +2488,7 @@
psa_mac_abort(operation);
}
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
return (status == PSA_SUCCESS) ? unlock_status : status;
}
@@ -2641,7 +2674,7 @@
psa_wipe_tag_output_buffer(mac, status, mac_size, *mac_length);
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
return (status == PSA_SUCCESS) ? unlock_status : status;
}
@@ -2785,7 +2818,7 @@
psa_wipe_tag_output_buffer(signature, status, signature_size,
*signature_length);
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
return (status == PSA_SUCCESS) ? unlock_status : status;
}
@@ -2833,7 +2866,7 @@
signature, signature_length);
}
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
return (status == PSA_SUCCESS) ? unlock_status : status;
@@ -3100,7 +3133,7 @@
alg, input, input_length, salt, salt_length,
output, output_size, output_length);
exit:
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
return (status == PSA_SUCCESS) ? unlock_status : status;
}
@@ -3152,7 +3185,7 @@
output, output_size, output_length);
exit:
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
return (status == PSA_SUCCESS) ? unlock_status : status;
}
@@ -3261,7 +3294,7 @@
psa_sign_hash_abort_internal(operation);
}
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
if (unlock_status != PSA_SUCCESS) {
operation->error_occurred = 1;
@@ -3406,7 +3439,7 @@
psa_verify_hash_abort_internal(operation);
}
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
if (unlock_status != PSA_SUCCESS) {
operation->error_occurred = 1;
@@ -3978,7 +4011,7 @@
psa_cipher_abort(operation);
}
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
return (status == PSA_SUCCESS) ? unlock_status : status;
}
@@ -4223,7 +4256,7 @@
output_size - default_iv_length, output_length);
exit:
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
if (status == PSA_SUCCESS) {
status = unlock_status;
}
@@ -4284,7 +4317,7 @@
output, output_size, output_length);
exit:
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
if (status == PSA_SUCCESS) {
status = unlock_status;
}
@@ -4410,7 +4443,7 @@
}
exit:
- psa_unlock_key_slot(slot);
+ psa_unregister_read(slot);
return status;
}
@@ -4465,7 +4498,7 @@
}
exit:
- psa_unlock_key_slot(slot);
+ psa_unregister_read(slot);
return status;
}
@@ -4577,7 +4610,7 @@
operation->key_type = psa_get_key_type(&attributes);
exit:
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
if (status == PSA_SUCCESS) {
status = unlock_status;
@@ -6900,7 +6933,7 @@
slot->key.data,
slot->key.bytes);
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
return (status == PSA_SUCCESS) ? unlock_status : status;
}
@@ -7057,7 +7090,7 @@
}
}
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
return (status == PSA_SUCCESS) ? unlock_status : status;
}
@@ -7118,7 +7151,7 @@
*output_length = output_size;
}
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
return (status == PSA_SUCCESS) ? unlock_status : status;
}
@@ -7792,7 +7825,7 @@
if (status != PSA_SUCCESS) {
psa_pake_abort(operation);
}
- unlock_status = psa_unlock_key_slot(slot);
+ unlock_status = psa_unregister_read(slot);
return (status == PSA_SUCCESS) ? unlock_status : status;
}
diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h
index ff01add..1edd63e 100644
--- a/library/psa_crypto_core.h
+++ b/library/psa_crypto_core.h
@@ -47,8 +47,10 @@
typedef enum {
PSA_SLOT_EMPTY = 0,
- PSA_SLOT_OCCUPIED,
-} psa_key_slot_status_t;
+ PSA_SLOT_FILLING,
+ PSA_SLOT_FULL,
+ PSA_SLOT_PENDING_DELETION,
+} psa_key_slot_state_t;
/** The data structure representing a key slot, containing key material
* and metadata for one key.
@@ -56,18 +58,37 @@
typedef struct {
psa_core_key_attributes_t attr;
- psa_key_slot_status_t status;
+ /*
+ * The current state of the key slot, as described in
+ * docs/architecture/psa-thread-safety/psa-thread-safety.md.
+ *
+ * Library functions can modify the state of a key slot by calling
+ * psa_key_slot_state_transition.
+ *
+ * The state variable is used to help determine whether library functions
+ * which operate on the slot succeed. For example, psa_finish_key_creation,
+ * which transfers the state of a slot from PSA_SLOT_FILLING to
+ * PSA_SLOT_FULL, must fail with error code PSA_ERROR_CORRUPTION_DETECTED
+ * if the state of the slot is not PSA_SLOT_FILLING.
+ *
+ * Library functions which traverse the array of key slots only consider
+ * 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_key_slot_state_t state;
/*
- * Number of locks on the key slot held by the library.
+ * Number of functions registered as reading the material in the key slot.
*
- * This counter is incremented by one each time a library function
- * retrieves through one of the dedicated internal API a pointer to the
- * key slot.
+ * Library functions must not write directly to registered_readers
*
- * This counter is decremented by one each time a library function stops
- * accessing the key slot and states it by calling the
- * psa_unlock_key_slot() API.
+ * 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.
+ * 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
@@ -78,10 +99,9 @@
* 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 used by the library through
- * another thread.
- */
- size_t lock_count;
+ * or purge or destroy a key while it is in use by the library through
+ * another thread. */
+ size_t registered_readers;
/* Dynamically allocated key data buffer.
* Format as specified in psa_export_key(). */
@@ -96,31 +116,15 @@
#define PSA_KA_MASK_INTERNAL_ONLY ( \
0)
-/** Test whether a key slot is occupied.
- *
- * A key slot is occupied iff the key type is nonzero. This works because
- * no valid key can have 0 as its key type.
+/** Test whether a key slot has any registered readers.
*
* \param[in] slot The key slot to test.
*
- * \return 1 if the slot is occupied, 0 otherwise.
+ * \return 1 if the slot has any registered readers, 0 otherwise.
*/
-static inline int psa_is_key_slot_occupied(const psa_key_slot_t *slot)
+static inline int psa_key_slot_has_readers(const psa_key_slot_t *slot)
{
- return slot->status == PSA_SLOT_OCCUPIED;
-}
-
-/** Test whether a key slot is locked.
- *
- * A key slot is locked iff its lock counter is strictly greater than 0.
- *
- * \param[in] slot The key slot to test.
- *
- * \return 1 if the slot is locked, 0 otherwise.
- */
-static inline int psa_is_key_slot_locked(const psa_key_slot_t *slot)
-{
- return slot->lock_count > 0;
+ return slot->registered_readers > 0;
}
/** Retrieve flags from psa_key_slot_t::attr::core::flags.
@@ -190,13 +194,18 @@
/** Completely wipe a slot in memory, including its policy.
*
* Persistent storage is not affected.
+ * Sets the slot's state to PSA_SLOT_EMPTY.
*
* \param[in,out] slot The key slot to wipe.
*
* \retval #PSA_SUCCESS
- * Success. This includes the case of a key slot that was
- * already fully wiped.
- * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription
+ * The slot has been successfully wiped.
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED
+ * The slot's state was PSA_SLOT_FULL or PSA_SLOT_PENDING_DELETION, and
+ * the amount of registered readers was not equal to 1. Or,
+ * the slot's state was PSA_SLOT_EMPTY. Or,
+ * the slot's state was PSA_SLOT_FILLING, and the amount
+ * of registered readers was not equal to 0.
*/
psa_status_t psa_wipe_key_slot(psa_key_slot_t *slot);
diff --git a/library/psa_crypto_slot_management.c b/library/psa_crypto_slot_management.c
index 5ecc3a7..8d7ff90 100644
--- a/library/psa_crypto_slot_management.c
+++ b/library/psa_crypto_slot_management.c
@@ -108,7 +108,9 @@
for (slot_idx = 0; slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT; slot_idx++) {
slot = &global_data.key_slots[slot_idx];
- if (mbedtls_svc_key_id_equal(key, slot->attr.id)) {
+ /* Only consider slots which are in a full state. */
+ if ((slot->state == PSA_SLOT_FULL) &&
+ (mbedtls_svc_key_id_equal(key, slot->attr.id))) {
break;
}
}
@@ -117,7 +119,7 @@
}
if (status == PSA_SUCCESS) {
- status = psa_lock_key_slot(slot);
+ status = psa_register_read(slot);
if (status == PSA_SUCCESS) {
*p_slot = slot;
}
@@ -141,36 +143,38 @@
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->lock_count = 1;
+ slot->registered_readers = 1;
+ slot->state = PSA_SLOT_PENDING_DELETION;
(void) psa_wipe_key_slot(slot);
}
global_data.key_slots_initialized = 0;
}
-psa_status_t psa_get_empty_key_slot(psa_key_id_t *volatile_key_id,
- psa_key_slot_t **p_slot)
+psa_status_t psa_reserve_free_key_slot(psa_key_id_t *volatile_key_id,
+ psa_key_slot_t **p_slot)
{
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
size_t slot_idx;
- psa_key_slot_t *selected_slot, *unlocked_persistent_key_slot;
+ psa_key_slot_t *selected_slot, *unused_persistent_key_slot;
if (!global_data.key_slots_initialized) {
status = PSA_ERROR_BAD_STATE;
goto error;
}
- selected_slot = unlocked_persistent_key_slot = NULL;
+ 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];
- if (!psa_is_key_slot_occupied(slot)) {
+ if (slot->state == PSA_SLOT_EMPTY) {
selected_slot = slot;
break;
}
- if ((unlocked_persistent_key_slot == NULL) &&
- (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) &&
- (!psa_is_key_slot_locked(slot))) {
- unlocked_persistent_key_slot = slot;
+ if ((unused_persistent_key_slot == NULL) &&
+ (slot->state == PSA_SLOT_FULL) &&
+ (!psa_key_slot_has_readers(slot)) &&
+ (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime))) {
+ unused_persistent_key_slot = slot;
}
}
@@ -182,14 +186,18 @@
* storage.
*/
if ((selected_slot == NULL) &&
- (unlocked_persistent_key_slot != NULL)) {
- selected_slot = unlocked_persistent_key_slot;
- selected_slot->lock_count = 1;
- psa_wipe_key_slot(selected_slot);
+ (unused_persistent_key_slot != NULL)) {
+ selected_slot = unused_persistent_key_slot;
+ psa_register_read(selected_slot);
+ status = psa_wipe_key_slot(selected_slot);
+ if (status != PSA_SUCCESS) {
+ goto error;
+ }
}
if (selected_slot != NULL) {
- status = psa_lock_key_slot(selected_slot);
+ status = psa_key_slot_state_transition(selected_slot, PSA_SLOT_EMPTY,
+ PSA_SLOT_FILLING);
if (status != PSA_SUCCESS) {
goto error;
}
@@ -239,7 +247,8 @@
slot, data->slot_number, sizeof(data->slot_number));
if (status == PSA_SUCCESS) {
- slot->status = PSA_SLOT_OCCUPIED;
+ status = psa_key_slot_state_transition(slot, PSA_SLOT_FILLING,
+ PSA_SLOT_FULL);
}
goto exit;
}
@@ -250,7 +259,8 @@
goto exit;
}
- slot->status = PSA_SLOT_OCCUPIED;
+ status = psa_key_slot_state_transition(slot, PSA_SLOT_FILLING,
+ PSA_SLOT_FULL);
exit:
psa_free_persistent_key_data(key_data, key_data_length);
@@ -324,8 +334,9 @@
/* Copy actual key length and core attributes into the slot on success */
slot->key.bytes = key_buffer_length;
slot->attr = attributes.core;
- slot->status = PSA_SLOT_OCCUPIED;
+ status = psa_key_slot_state_transition(slot, PSA_SLOT_FILLING,
+ PSA_SLOT_FULL);
exit:
if (status != PSA_SUCCESS) {
psa_remove_key_data_from_memory(slot);
@@ -358,7 +369,7 @@
defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS)
psa_key_id_t volatile_key_id;
- status = psa_get_empty_key_slot(&volatile_key_id, p_slot);
+ status = psa_reserve_free_key_slot(&volatile_key_id, p_slot);
if (status != PSA_SUCCESS) {
return status;
}
@@ -380,12 +391,17 @@
if (status != PSA_SUCCESS) {
psa_wipe_key_slot(*p_slot);
+
if (status == PSA_ERROR_DOES_NOT_EXIST) {
status = PSA_ERROR_INVALID_HANDLE;
}
} else {
/* Add implicit usage flags. */
psa_extend_key_usage_flags(&(*p_slot)->attr.policy.usage);
+
+ psa_key_slot_state_transition((*p_slot), PSA_SLOT_FILLING,
+ PSA_SLOT_FULL);
+ status = psa_register_read(*p_slot);
}
return status;
@@ -394,26 +410,37 @@
#endif /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
}
-psa_status_t psa_unlock_key_slot(psa_key_slot_t *slot)
+psa_status_t psa_unregister_read(psa_key_slot_t *slot)
{
if (slot == NULL) {
return PSA_SUCCESS;
}
+ if ((slot->state != PSA_SLOT_FULL) &&
+ (slot->state != PSA_SLOT_PENDING_DELETION)) {
+ return PSA_ERROR_CORRUPTION_DETECTED;
+ }
- if (slot->lock_count > 0) {
- slot->lock_count--;
+ /* 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)) {
+ return psa_wipe_key_slot(slot);
+ }
+
+ if (psa_key_slot_has_readers(slot)) {
+ slot->registered_readers--;
return PSA_SUCCESS;
}
/*
* As the return error code may not be handled in case of multiple errors,
- * do our best to report if the lock counter is equal to zero. Assert with
- * MBEDTLS_TEST_HOOK_TEST_ASSERT that the lock counter is strictly greater
- * than zero: if the MBEDTLS_TEST_HOOKS configuration option is enabled and
+ * do our best to report if there are no registered readers. Assert with
+ * MBEDTLS_TEST_HOOK_TEST_ASSERT that there are registered readers:
+ * if the MBEDTLS_TEST_HOOKS configuration option is enabled and
* the function is called as part of the execution of a test suite, the
* execution of the test suite is stopped in error if the assertion fails.
*/
- MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->lock_count > 0);
+ MBEDTLS_TEST_HOOK_TEST_ASSERT(psa_key_slot_has_readers(slot));
return PSA_ERROR_CORRUPTION_DETECTED;
}
@@ -480,7 +507,7 @@
*handle = key;
- return psa_unlock_key_slot(slot);
+ return psa_unregister_read(slot);
#else /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
(void) key;
@@ -506,10 +533,10 @@
return status;
}
- if (slot->lock_count <= 1) {
+ if (slot->registered_readers == 1) {
return psa_wipe_key_slot(slot);
} else {
- return psa_unlock_key_slot(slot);
+ return psa_unregister_read(slot);
}
}
@@ -524,10 +551,10 @@
}
if ((!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) &&
- (slot->lock_count <= 1)) {
+ (slot->registered_readers == 1)) {
return psa_wipe_key_slot(slot);
} else {
- return psa_unlock_key_slot(slot);
+ return psa_unregister_read(slot);
}
}
@@ -539,10 +566,10 @@
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_is_key_slot_locked(slot)) {
+ if (psa_key_slot_has_readers(slot)) {
++stats->locked_slots;
}
- if (!psa_is_key_slot_occupied(slot)) {
+ if (slot->state == PSA_SLOT_EMPTY) {
++stats->empty_slots;
continue;
}
diff --git a/library/psa_crypto_slot_management.h b/library/psa_crypto_slot_management.h
index 6041a35..0b0d7b3 100644
--- a/library/psa_crypto_slot_management.h
+++ b/library/psa_crypto_slot_management.h
@@ -54,8 +54,9 @@
* In case of a persistent key, the function loads the description of the key
* into a key slot if not already done.
*
- * On success, the returned key slot is locked. It is the responsibility of
- * the caller to unlock the key slot when it does not access it anymore.
+ * On success, the returned key slot has been registered for reading.
+ * It is the responsibility of the caller to call psa_unregister_read(slot)
+ * when they have finished reading the contents of the slot.
*
* \param key Key identifier to query.
* \param[out] p_slot On success, `*p_slot` contains a pointer to the
@@ -95,50 +96,85 @@
* This does not affect persistent storage. */
void psa_wipe_all_key_slots(void);
-/** Find a free key slot.
+/** Find a free key slot and reserve it to be filled with a key.
*
- * This function returns a key slot that is available for use and is in its
- * ground state (all-bits-zero). On success, the key slot is locked. It is
- * the responsibility of the caller to unlock the key slot when it does not
- * access it anymore.
+ * This function finds a key slot that is free,
+ * sets its state to PSA_SLOT_FILLING and then returns the slot.
+ *
+ * On success, the key slot's state is PSA_SLOT_FILLING.
+ * It is the responsibility of the caller to change the slot's state to
+ * PSA_SLOT_EMPTY/FULL once key creation has finished.
*
* \param[out] volatile_key_id On success, volatile key identifier
* associated to the returned slot.
* \param[out] p_slot On success, a pointer to the slot.
*
* \retval #PSA_SUCCESS \emptydescription
- * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
+ * There were no free key 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
+ * unexpected state.
*/
-psa_status_t psa_get_empty_key_slot(psa_key_id_t *volatile_key_id,
- psa_key_slot_t **p_slot);
+psa_status_t psa_reserve_free_key_slot(psa_key_id_t *volatile_key_id,
+ psa_key_slot_t **p_slot);
-/** Lock a key slot.
+/** Change the state of a key slot.
*
- * This function increments the key slot lock counter by one.
+ * This function changes the state of the key slot from expected_state to
+ * new state. If the state of the slot was not expected_state, the state is
+ * unchanged.
+ *
+ * \param[in] slot The key slot.
+ * \param[in] expected_state The current state of the slot.
+ * \param[in] new_state The new state of the slot.
+ *
+ * \retval #PSA_SUCCESS
+ The key slot's state variable is new_state.
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED
+ * The slot's state was not expected_state.
+ */
+static inline psa_status_t psa_key_slot_state_transition(
+ psa_key_slot_t *slot, psa_key_slot_state_t expected_state,
+ psa_key_slot_state_t new_state)
+{
+ if (slot->state != expected_state) {
+ return PSA_ERROR_CORRUPTION_DETECTED;
+ }
+ slot->state = new_state;
+ return PSA_SUCCESS;
+}
+
+/** Register as a reader of a key slot.
+ *
+ * This function increments the key slot registered reader counter by one.
*
* \param[in] slot The key slot.
*
* \retval #PSA_SUCCESS
- The key slot lock counter was incremented.
+ The key slot registered reader counter was incremented.
* \retval #PSA_ERROR_CORRUPTION_DETECTED
- * The lock counter already reached its maximum value and was not
- * increased.
+ * The reader counter already reached its maximum value and was not
+ * increased, or the slot's state was not PSA_SLOT_FULL.
*/
-static inline psa_status_t psa_lock_key_slot(psa_key_slot_t *slot)
+static inline psa_status_t psa_register_read(psa_key_slot_t *slot)
{
- if (slot->lock_count >= SIZE_MAX) {
+ if ((slot->state != PSA_SLOT_FULL) ||
+ (slot->registered_readers >= SIZE_MAX)) {
return PSA_ERROR_CORRUPTION_DETECTED;
}
-
- slot->lock_count++;
+ slot->registered_readers++;
return PSA_SUCCESS;
}
-/** Unlock a key slot.
+/** Unregister from reading a key slot.
*
- * This function decrements the key slot lock counter by one.
+ * This function decrements the key slot registered reader counter by one.
+ * If the state of the slot is PSA_SLOT_PENDING_DELETION,
+ * and there is only one registered reader (the caller),
+ * this function will call psa_wipe_key_slot().
*
* \note To ease the handling of errors in retrieving a key slot
* a NULL input pointer is valid, and the function returns
@@ -146,13 +182,16 @@
*
* \param[in] slot The key slot.
* \retval #PSA_SUCCESS
- * \p slot is NULL or the key slot lock counter has been
- * decremented successfully.
+ * \p slot is NULL or the key slot reader counter has been
+ * decremented (and potentially wiped) successfully.
* \retval #PSA_ERROR_CORRUPTION_DETECTED
- * The lock counter was equal to 0.
- *
+ * The slot's state was neither PSA_SLOT_FULL nor
+ * PSA_SLOT_PENDING_DELETION.
+ * Or a wipe was attempted and the slot's state was not
+ * PSA_SLOT_PENDING_DELETION.
+ * Or registered_readers was equal to 0.
*/
-psa_status_t psa_unlock_key_slot(psa_key_slot_t *slot);
+psa_status_t psa_unregister_read(psa_key_slot_t *slot);
/** Test whether a lifetime designates a key in an external cryptoprocessor.
*