Add mbedtls_psa_crypto_init_subsystem()

Internal only for now, but can be made external with some more
work. Break up psa_crypto_init into chunks to prevent deadlocks when
initialising RNG, likewise break up mbedtls_crypto_free() to stop having
to hold more than one mutex at a time.

Signed-off-by: Paul Elliott <paul.elliott@arm.com>
This commit is contained in:
Paul Elliott 2024-03-08 21:38:02 +00:00
parent 358165246b
commit 47cee8e2ee

View File

@ -93,10 +93,25 @@ static int key_type_is_raw_bytes(psa_key_type_t type)
#define RNG_INITIALIZED 1 #define RNG_INITIALIZED 1
#define RNG_SEEDED 2 #define RNG_SEEDED 2
typedef enum {
PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS = 0,
PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS,
PSA_CRYPTO_SUBSYSTEM_RNG,
PSA_CRYPTO_SUBSYSTEM_TRANSACTION,
} mbedtls_psa_crypto_subsystem;
#define PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED 0x01
#define PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED 0x02
#define PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED 0x04
#define PSA_CRYPTO_SUBSYSTEM_ALL_INITIALISED ( \
PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED | \
PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED | \
PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED)
typedef struct { typedef struct {
uint8_t initialized; uint8_t initialized;
uint8_t rng_state; uint8_t rng_state;
uint8_t drivers_initialized;
mbedtls_psa_random_context_t rng; mbedtls_psa_random_context_t rng;
} psa_global_data_t; } psa_global_data_t;
@ -106,11 +121,22 @@ static uint8_t psa_get_initialized(void)
{ {
uint8_t initialized; uint8_t initialized;
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex);
#endif /* defined(MBEDTLS_THREADING_C) */
initialized = global_data.rng_state == RNG_SEEDED;
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_unlock(&mbedtls_threading_psa_rngdata_mutex);
#endif /* defined(MBEDTLS_THREADING_C) */
#if defined(MBEDTLS_THREADING_C) #if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex); mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
#endif /* defined(MBEDTLS_THREADING_C) */ #endif /* defined(MBEDTLS_THREADING_C) */
initialized = global_data.initialized; initialized =
(initialized && (global_data.initialized == PSA_CRYPTO_SUBSYSTEM_ALL_INITIALISED));
#if defined(MBEDTLS_THREADING_C) #if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex); mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
@ -127,7 +153,7 @@ static uint8_t psa_get_drivers_initialized(void)
mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex); mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
#endif /* defined(MBEDTLS_THREADING_C) */ #endif /* defined(MBEDTLS_THREADING_C) */
initialized = global_data.drivers_initialized; initialized = (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED) != 0;
#if defined(MBEDTLS_THREADING_C) #if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex); mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
@ -7490,29 +7516,53 @@ psa_status_t mbedtls_psa_crypto_configure_entropy_sources(
void mbedtls_psa_crypto_free(void) void mbedtls_psa_crypto_free(void)
{ {
/* Need to hold the mutex here to prevent this going ahead before
* psa_crypto_init() has completed, and to ensure integrity of
* global_data. */
#if defined(MBEDTLS_THREADING_C) #if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex); mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
#endif /* defined(MBEDTLS_THREADING_C) */ #endif /* defined(MBEDTLS_THREADING_C) */
psa_wipe_all_key_slots(); /* Nothing to do to free transaction. */
if (global_data.rng_state != RNG_NOT_INITIALIZED) { if (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED) {
mbedtls_psa_random_free(&global_data.rng); global_data.initialized &= ~PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED;
} }
/* Wipe all remaining data, including configuration. if (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED) {
* In particular, this sets all state indicator to the value psa_wipe_all_key_slots();
* indicating "uninitialized". */ global_data.initialized &= ~PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED;
mbedtls_platform_zeroize(&global_data, sizeof(global_data)); }
#if defined(MBEDTLS_THREADING_C) #if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex); mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
#endif /* defined(MBEDTLS_THREADING_C) */ #endif /* defined(MBEDTLS_THREADING_C) */
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex);
#endif /* defined(MBEDTLS_THREADING_C) */
if (global_data.rng_state != RNG_NOT_INITIALIZED) {
mbedtls_psa_random_free(&global_data.rng);
}
global_data.rng_state = RNG_NOT_INITIALIZED;
mbedtls_platform_zeroize(&global_data.rng, sizeof(global_data.rng));
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_unlock(&mbedtls_threading_psa_rngdata_mutex);
#endif /* defined(MBEDTLS_THREADING_C) */
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
#endif /* defined(MBEDTLS_THREADING_C) */
/* Terminate drivers */ /* Terminate drivers */
psa_driver_wrapper_free(); if (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED) {
psa_driver_wrapper_free();
global_data.initialized &= ~PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED;
}
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
#endif /* defined(MBEDTLS_THREADING_C) */
} }
#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS) #if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS)
@ -7540,74 +7590,172 @@ static psa_status_t psa_crypto_recover_transaction(
} }
#endif /* PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS */ #endif /* PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS */
static psa_status_t mbedtls_psa_crypto_init_subsystem(mbedtls_psa_crypto_subsystem subsystem)
{
psa_status_t status = PSA_SUCCESS;
uint8_t driver_wrappers_initialized = 0;
switch (subsystem) {
case PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS:
#if defined(MBEDTLS_THREADING_C)
PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex));
#endif /* defined(MBEDTLS_THREADING_C) */
if (!(global_data.initialized & PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED)) {
/* Init drivers */
status = psa_driver_wrapper_init();
/* Drivers need shutdown regardless of startup errors. */
global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED;
}
#if defined(MBEDTLS_THREADING_C)
PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
&mbedtls_threading_psa_globaldata_mutex));
#endif /* defined(MBEDTLS_THREADING_C) */
break;
case PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS:
#if defined(MBEDTLS_THREADING_C)
PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex));
#endif /* defined(MBEDTLS_THREADING_C) */
if (!(global_data.initialized & PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED)) {
status = psa_initialize_key_slots();
/* Need to wipe keys even if initialization fails. */
global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED;
}
#if defined(MBEDTLS_THREADING_C)
PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
&mbedtls_threading_psa_globaldata_mutex));
#endif /* defined(MBEDTLS_THREADING_C) */
break;
case PSA_CRYPTO_SUBSYSTEM_RNG:
#if defined(MBEDTLS_THREADING_C)
PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex));
#endif /* defined(MBEDTLS_THREADING_C) */
driver_wrappers_initialized =
(global_data.initialized & PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED);
#if defined(MBEDTLS_THREADING_C)
PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
&mbedtls_threading_psa_globaldata_mutex));
#endif /* defined(MBEDTLS_THREADING_C) */
/* Need to use separate mutex here, as initialisation can require
* testing of init flags, which requires locking the global data
* mutex. */
#if defined(MBEDTLS_THREADING_C)
PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex));
#endif /* defined(MBEDTLS_THREADING_C) */
/* Initialize and seed the random generator. */
if (global_data.rng_state == RNG_NOT_INITIALIZED && driver_wrappers_initialized) {
mbedtls_psa_random_init(&global_data.rng);
global_data.rng_state = RNG_INITIALIZED;
status = mbedtls_psa_random_seed(&global_data.rng);
if (status == PSA_SUCCESS) {
global_data.rng_state = RNG_SEEDED;
}
}
#if defined(MBEDTLS_THREADING_C)
PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
&mbedtls_threading_psa_rngdata_mutex));
#endif /* defined(MBEDTLS_THREADING_C) */
break;
case PSA_CRYPTO_SUBSYSTEM_TRANSACTION:
#if defined(MBEDTLS_THREADING_C)
PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex));
#endif /* defined(MBEDTLS_THREADING_C) */
if (!(global_data.initialized & PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED)) {
#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS)
status = psa_crypto_load_transaction();
if (status == PSA_SUCCESS) {
status = psa_crypto_recover_transaction(&psa_crypto_transaction);
if (status == PSA_SUCCESS) {
global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED;
}
status = psa_crypto_stop_transaction();
} else if (status == PSA_ERROR_DOES_NOT_EXIST) {
/* There's no transaction to complete. It's all good. */
global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED;
status = PSA_SUCCESS;
}
#else /* defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS) */
global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED;
status = PSA_SUCCESS;
#endif /* defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS) */
}
#if defined(MBEDTLS_THREADING_C)
PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
&mbedtls_threading_psa_globaldata_mutex));
#endif /* defined(MBEDTLS_THREADING_C) */
break;
default:
status = PSA_ERROR_CORRUPTION_DETECTED;
}
/* Exit label only required when using threading macros. */
#if defined(MBEDTLS_THREADING_C)
exit:
#endif /* defined(MBEDTLS_THREADING_C) */
return status;
}
psa_status_t psa_crypto_init(void) psa_status_t psa_crypto_init(void)
{ {
psa_status_t status; psa_status_t status;
/* Need to hold the mutex for the entire function to prevent incomplete /* Double initialization is explicitly allowed. Early out if everything is
* initialisation before someone calls psa_crypto_free() or calls this * done. */
* function again before we set global_data.initialised to 1. */ if (psa_get_initialized()) {
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
#endif /* defined(MBEDTLS_THREADING_C) */
/* We cannot use psa_get_initialized() here as we already hold the mutex. */
if (global_data.initialized == 1) {
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
#endif /* defined(MBEDTLS_THREADING_C) */
/* Double initialization is explicitly allowed. */
return PSA_SUCCESS; return PSA_SUCCESS;
} }
/* Init drivers */ status = mbedtls_psa_crypto_init_subsystem(PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS);
status = psa_driver_wrapper_init();
if (status != PSA_SUCCESS) {
goto exit;
}
global_data.drivers_initialized = 1;
status = psa_initialize_key_slots();
if (status != PSA_SUCCESS) { if (status != PSA_SUCCESS) {
goto exit; goto exit;
} }
/* Initialize and seed the random generator. */ status = mbedtls_psa_crypto_init_subsystem(PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS);
mbedtls_psa_random_init(&global_data.rng);
global_data.rng_state = RNG_INITIALIZED;
status = mbedtls_psa_random_seed(&global_data.rng);
if (status != PSA_SUCCESS) { if (status != PSA_SUCCESS) {
goto exit; goto exit;
} }
global_data.rng_state = RNG_SEEDED;
#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS) status = mbedtls_psa_crypto_init_subsystem(PSA_CRYPTO_SUBSYSTEM_RNG);
status = psa_crypto_load_transaction(); if (status != PSA_SUCCESS) {
if (status == PSA_SUCCESS) { goto exit;
status = psa_crypto_recover_transaction(&psa_crypto_transaction);
if (status != PSA_SUCCESS) {
goto exit;
}
status = psa_crypto_stop_transaction();
} else if (status == PSA_ERROR_DOES_NOT_EXIST) {
/* There's no transaction to complete. It's all good. */
status = PSA_SUCCESS;
} }
#endif /* PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS */
/* All done. */ status = mbedtls_psa_crypto_init_subsystem(PSA_CRYPTO_SUBSYSTEM_TRANSACTION);
global_data.initialized = 1;
exit: exit:
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
#endif /* defined(MBEDTLS_THREADING_C) */
if (status != PSA_SUCCESS) { if (status != PSA_SUCCESS) {
mbedtls_psa_crypto_free(); mbedtls_psa_crypto_free();
} }
return status; return status;
} }