From c9515600fd22ae12b9bb42d562ba55d0195fae76 Mon Sep 17 00:00:00 2001 From: Ryan Everett Date: Thu, 14 Mar 2024 13:20:36 +0000 Subject: [PATCH 1/6] Fix state transition diagram This now represents the implemented model Signed-off-by: Ryan Everett --- .../key-slot-state-transitions.png | Bin 70492 -> 71568 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/architecture/psa-thread-safety/key-slot-state-transitions.png b/docs/architecture/psa-thread-safety/key-slot-state-transitions.png index 34cc79b3596d12a8e4e98ffbbb5620d0c7c8221c..1b50e7395d78051d20524568475128ea82228aa7 100644 GIT binary patch delta 48599 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 G^#1^VphQss literal 70492 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 Date: Thu, 14 Mar 2024 13:21:34 +0000 Subject: [PATCH 2/6] Rework and update psa-thread-safety.md I have restructured this file, and updated it to reflect changes in design/designs now being implemented. Signed-off-by: Ryan Everett --- .../psa-thread-safety/psa-thread-safety.md | 721 ++++++++---------- 1 file changed, 314 insertions(+), 407 deletions(-) diff --git a/docs/architecture/psa-thread-safety/psa-thread-safety.md b/docs/architecture/psa-thread-safety/psa-thread-safety.md index dc5d7e1894..06b317f490 100644 --- a/docs/architecture/psa-thread-safety/psa-thread-safety.md +++ b/docs/architecture/psa-thread-safety/psa-thread-safety.md @@ -1,450 +1,357 @@ -# Thread safety of the PSA subsystem +# Thread-safety of the PSA subsystem -Currently PSA Crypto API calls in Mbed TLS releases are not thread-safe. In Mbed TLS 3.6 we are planning to add a minimal support for thread-safety of the PSA Crypto API (see section [Strategy for 3.6](#strategy-for-36)). +Currently, PSA Crypto API calls in Mbed TLS releases are not thread-safe. -In the [Design analysis](#design-analysis) section we analyse design choices. This discussion is not constrained to what is planned for 3.6 and considers future developments. It also leaves some questions open and discusses options that have been (or probably will be) rejected. +As of Mbed TLS 3.6, an MVP for making the [PSA Crypto key management API](https://arm-software.github.io/psa-api/crypto/1.1/api/keys/management.html) and [`psa_crypto_init`](https://arm-software.github.io/psa-api/crypto/1.1/api/library/library.html#c.psa_crypto_init) thread-safe has been implemented. Implementations which only ever call PSA functions from a single thread are not affected by this new feature. -## Design analysis +Summary of recent work: -This section explores possible designs and does not reflect what is currently implemented. +- Key Store: + - Slot states are described in the [Key slot states](#key-slot-states) section. They guarantee safe concurrent access to slot contents. + - Key slots are protected by a global mutex, as described in [Key store consistency and abstraction function](#key-store-consistency-and-abstraction-function). + - Key destruction strategy abiding by [Key destruction guarantees](#key-destruction-guarantees), with an implementation discussed in [Key destruction implementation](#key-destruction-implementation). +- The main `global_data` (the one in `psa_crypto.c`) is protected by its own mutex as described in the [Global data](#global-data) section. +- The testing system has now been made thread-safe. Tests can now spin up multiple threads, see [Thread-safe testing](#thread-safe-testing) for details. +- Some multithreaded testing of the key management API has been added, this is outlined in [Testing-and-analysis](#testing-and-analysis). +- The solution uses the pre-existing `MBEDTLS_THREADING_C` threading abstraction. +- The core makes no additional guarantees for drivers. See [Driver policy](#driver-policy) for details. -### Requirements +The usage of keys within other PSA Crypto APIs is planned to be made thread-safe in future, but currently we are not testing this. -#### Backward compatibility requirement +## Overview of the document -Code that is currently working must keep working. There can be an exception for code that uses features that are advertised as experimental; for example, it would be annoying but ok to add extra requirements for drivers. +* The [Guarantees](#guarantees) section describes the properties that are followed when PSA functions are invoked by multiple threads. +* The [Usage guide](#usage-guide) section gives guidance on initializing, using and freeing PSA when using multiple threads. +* The [Current strategy](#current-strategy) section describes how thread-safety of key management and `global_data` is achieved. +* The [Testing and analysis](#testing-and-analysis) section discusses the state of our testing, as well as how this testing will be extended in future. +* The [Future work](#future-work) section outlines our long-term goals for thread-safety; it also analyses how we might go about achieving these goals. -(In this section, “currently” means Mbed TLS releases without proper concurrency management: 3.0.0, 3.1.0, and any other subsequent 3.x version.) +## Definitions -In particular, if you either protect all PSA calls with a mutex, or only ever call PSA functions from a single thread, your application currently works and must keep working. If your application currently builds and works with `MBEDTLS_PSA_CRYPTO_C` and `MBEDTLS_THREADING_C` enabled, it must keep building and working. +*Concurrent calls* -As a consequence, we must not add a new platform requirement beyond mutexes for the base case. It would be ok to add new platform requirements if they're only needed for PSA drivers, or if they're only performance improvements. +The PSA specification defines concurrent calls as: "In some environments, an application can make calls to the Crypto API in separate threads. In such an environment, concurrent calls are two or more calls to the API whose execution can overlap in time." (https://arm-software.github.io/psa-api/crypto/1.1/overview/conventions.html#concurrent-calls). -Tempting platform requirements that we cannot add to the default `MBEDTLS_THREADING_C` include: +*Thread-safety* -* Releasing a mutex from a different thread than the one that acquired it. This isn't even guaranteed to work with pthreads. -* New primitives such as semaphores or condition variables. +In general, a system is thread-safe if any valid set of concurrent calls is handled as if the effect and return code of every call is equivalent to some sequential ordering. We implement a weaker notion of thread-safety, we only guarantee thread-safety in the circumstances described in the [PSA Concurrent calling conventions](#psa-concurrent-calling-conventions) section. -#### Correctness out of the box +## Guarantees -If you build with `MBEDTLS_PSA_CRYPTO_C` and `MBEDTLS_THREADING_C`, the code must be functionally correct: no race conditions, deadlocks or livelocks. +### Correctness out of the box -The [PSA Crypto API specification](https://armmbed.github.io/mbed-crypto/html/overview/conventions.html#concurrent-calls) defines minimum expectations for concurrent calls. They must work as if they had been executed one at a time (excluding resource-management errors), except that the following cases have undefined behavior: +Building with `MBEDTLS_PSA_CRYPTO_C` and `MBEDTLS_THREADING_C` gives code which is correct; there are no race-conditions, deadlocks or livelocks when concurrently calling any set of PSA key management functions once `psa_crypto_init` has been called (see the [Initialization](#initialization) section for details on how to correctly initialize the PSA subsystem when using multiple threads). -* Destroying a key while it's in use. -* Concurrent calls using the same operation object. (An operation object may not be used by more than one thread at a time. But it can move from one thread to another between calls.) -* Overlap of an output buffer with an input or output of a concurrent call. -* Modification of an input buffer during a call. +We do not test or support calling other PSA API functions concurrently. -Note that while the specification does not define the behavior in such cases, Mbed TLS can be used as a crypto service. It's acceptable if an application can mess itself up, but it is not acceptable if an application can mess up the crypto service. As a consequence, destroying a key while it's in use may violate the security property that all key material is erased as soon as `psa_destroy_key` returns, but it may not cause data corruption or read-after-free inside the key store. +There is no busy-waiting in our implementation, every API call completes in a finite number of steps regardless of the locking policy of the underlying mutexes. -#### No spinning +When only considering key management functions: Mbed TLS 3.6 abides by the minimum expectation for concurrent calls set by the PSA specification (see [PSA Concurrent calling conventions](#psa-concurrent-calling-conventions)). -The code must not spin on a potentially non-blocking task. For example, this is proscribed: -``` -lock(m); -while (!its_my_turn) { - unlock(m); - lock(m); -} -``` +#### PSA Concurrent calling conventions -Rationale: this can cause battery drain, and can even be a livelock (spinning forever), e.g. if the thread that might unblock this one has a lower priority. +These are the conventions which are planned to be added to the PSA 1.2 specification, Mbed TLS 3.6 abides by these when only considering [key management functions](https://arm-software.github.io/psa-api/crypto/1.1/api/keys/management.html): -#### Driver requirements +> The result of two or more concurrent calls must be consistent with the same set of calls being executed sequentially in some order, provided that the calls obey the following constraints: +> +> * There is no overlap between an output parameter of one call and an input or output parameter of another call. Overlap between input parameters is permitted. +> +> * A call to :code:`psa_destroy_key()` must not overlap with a concurrent call to any of the following functions: +> - Any call where the same key identifier is a parameter to the call. +> - Any call in a multi-part operation, where the same key identifier was used as a parameter to a previous step in the multi-part operation. +> +> * Concurrent calls must not use the same operation object. +> +> If any of these constraints are violated, the behaviour is undefined. +> +> The consistency requirement does not apply to errors that arise from resource failures or limitations. For example, errors resulting from resource exhaustion can arise in concurrent execution that do not arise in sequential execution. +> +> As an example of this rule: suppose two calls are executed concurrently which both attempt to create a new key with the same key identifier that is not already in the key store. Then: +> * If one call returns :code:`PSA_ERROR_ALREADY_EXISTS`, then the other call must succeed. +> * If one of the calls succeeds, then the other must fail: either with :code:`PSA_ERROR_ALREADY_EXISTS` or some other error status. +> * Both calls can fail with error codes that are not :code:`PSA_ERROR_ALREADY_EXISTS`. +> +> If the application concurrently modifies an input parameter while a function call is in progress, the behaviour is undefined. -At the time of writing, the driver interface specification does not consider multithreaded environments. +### Backwards compatibility -We need to define clear policies so that driver implementers know what to expect. Here are two possible policies at two ends of the spectrum; what is desirable is probably somewhere in between. +Code which was working prior to Mbed TLS 3.6 will still work. Implementations which only ever call PSA functions from a single thread, or which protect all PSA calls using a mutex, are not affected by this new feature. If an application previously worked with a 3.X version, it will still work on version 3.6. -* **Policy 1:** Driver entry points may be called concurrently from multiple threads, even if they're using the same key, and even including destroying a key while an operation is in progress on it. -* **Policy 2:** At most one driver entry point is active at any given time. +### Supported threading implementations -Combining the two we arrive at **Policy 3**: +Currently, the only threading library with support shipped in the code base is pthread (enabled by `MBEDTLS_THREADING_PTHREAD`). The only concurrency primitives we use are mutexes, see [Condition variables](#condition-variables) for discussion about implementing new primitives in future major releases. + +Users can add support to any platform which has mutexes using the Mbed TLS platform abstraction layer (see `include/mbedtls/threading.h` for details). + +We intend to ship support for other platforms including Windows in future releases. + +### Key destruction guarantees + +Much like all other API calls, `psa_destroy_key` does not block indefinitely, and when `psa_destroy_key` returns: + +1. The key identifier does not exist. This is a functional requirement for persistent keys: any thread can immediately create a new key with the same identifier. +2. The resources from the key have been freed. This allows threads to create similar keys immediately after destruction, regardless of resources. + +When `psa_destroy_key` is called on a key that is in use, guarantee 2 may be violated. This is consistent with the PSA specification requirements, as destruction of a key in use is undefined. + +In future versions we aim to enforce stronger requirements for key destruction, see [Long term key destruction requirements](#long-term-key-destruction-requirements) for details. + +### Driver policy + +The core makes no additional guarantees for drivers. Driver entry points may be called concurrently from multiple threads. Threads can concurrently call entry points using the same key, there is also no protection from destroying a key which is in use. + +### Random number generators + +The PSA RNG can be accessed both from various PSA functions, and from application code via `mbedtls_psa_get_random`. + +When using the built-in RNG implementations, i.e. when `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` is disabled, querying the RNG is thread-safe (`init` and `seed` are only thread-safe when called as part of `psa_crypto_init`, but not when called directly. `free` is not thread-safe). + +When `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` is enabled, thread-safety depends on the implementation. + +## Usage guide + +### Initialization + +The PSA subsystem is initialized via a call to [`psa_crypto_init`](https://arm-software.github.io/psa-api/crypto/1.1/api/library/library.html#c.psa_crypto_init). This is a thread-safe function, and multiple calls to `psa_crypto_init` are explicitly allowed. It is valid to have multiple threads each calling `psa_crypto_init` followed by a call to any PSA key management function (if the init succeeds). + +### General usage + +Once initialized, threads can use any PSA function if there is no overlap between their calls. All threads share the same set of keys, as soon as one thread returns from creating/loading a key via a key management API call the key can be used by any thread. If multiple threads attempt to load the same persistent key, with the same key identifier, only one thread can succeed - the others will return `PSA_ERROR_ALREADY_EXISTS`. + +Applications may need careful handling of resource management errors. As explained in ([PSA Concurrent calling conventions](#psa-concurrent-calling-conventions)) operations in progress can have memory related side effects, it is possible for a lack of resources to cause errors which do not arise in sequential execution. For example, multiple threads attempting to load the same persistent key can lead to some threads returning `PSA_ERROR_INSUFFICIENT_MEMORY` if the key is not currently in the key store - while trying to load a persistent key into the key store a thread temporarily reserves a free key slot. + +If a mutex operation fails, which only happens if the mutex implementation fails, the error code `PSA_ERROR_SERVICE_FAILURE` will be returned. If this code is returned, execution of the PSA subsystem must be stopped. All functions which have internal mutex locks and unlocks (except for when the lock/unlock occurs in a function that has no return value) will return with this error code in this situation. + +### Freeing + +There is no thread-safe way to free all PSA resources. This is because any such operation would need to wait for all other threads to complete their tasks before wiping resources. + +`mbedtls_psa_crypto_free` must only be called by a single thread once all threads have completed their operations. + +## Current strategy + +This section describes how we have implemented thread-safety. There is discussion of: techniques, internal properties for enforcing thread-safe access, how the system stays consistent and our abstraction model. + +### Protected resources + +#### Global data + +We have added a mutex `mbedtls_threading_psa_globaldata_mutex` defined in `include/mbedtls/threading.h`, which is used to make `psa_crypto_init` thread-safe. + +There are two `psa_global_data_t` structs, each with a single instance `global_data`: + +* The struct in `library/psa_crypto.c` is protected by `mbedtls_threading_psa_globaldata_mutex`. The RNG fields within this struct are not protected by this mutex, and are not always thread-safe (see [Random number generators](#random-number-generators)). +* The struct in `library/psa_crypto_slot_management.c` has two fields: `key_slots` is protected as described in [Key slots](#key-slots), `key_slots_initialized` is protected by the global data mutex. + +#### Key slots + +Keys are stored internally in a global array of key slots known as the "key store", defined in `library/psa_slot_management.c`. + +##### Key slot states + +Each key slot has a state variable and a `registered_readers` counter. These two variables dictate whether an operation can access a slot, and in what way the slot can be used. + +There are four possible states for a key slot: + +* `PSA_SLOT_EMPTY`: no thread is currently accessing the slot, and no information is stored in the slot. Any thread is able to change the slot's state to `PSA_SLOT_FILLING` and begin to load data into the slot. +* `PSA_SLOT_FILLING`: one thread is currently loading or creating material to fill the slot, this thread is responsible for the next state transition. Other threads cannot read the contents of a slot which is in this state. +* `PSA_SLOT_FULL`: the slot contains a key, and any thread is able to use the key after registering as a reader, increasing `registered_readers` by 1. +* `PSA_SLOT_PENDING_DELETION`: the key within the slot has been destroyed or marked for destruction, but at least one thread is still registered as a reader (`registered_readers > 0`). No thread can register to read this slot. The slot must not be wiped until the last reader unregisters. It is during the last unregister that the contents of the slot are wiped, and the slot's state is set to `PSA_SLOT_EMPTY`. + +###### Key slot state transition diagram +![](key-slot-state-transitions.png) + +In the state transition diagram above, an arrow between two states `q1` and `q2` with label `f` indicates that if the state of a slot is `q1` immediately before `f`'s linearization point, it may be `q2` immediately after `f`'s linearization point. The `PSA_SLOT_PENDING_DELETION -> PSA_SLOT_EMPTY` transition can be done by any function which calls `psa_unregister_read`. + +The state transition diagram can be generated in https://app.diagrams.net/ via this [url](https://viewer.diagrams.net/?tags=%7B%7D&highlight=0000ff&edit=_blank&layers=1&nav=1#R3VxLd5s4FP4ts%2FAyOUjiuazrJJPW7aQn7UzTjQ8F2VaLgQP4lV8%2FwjyMBMaCgEOaRQ8S0gV0rz599%2BGO0PvV7i4w%2FeUnz8bOCEr2boQmIwh1JNF%2F44590iFrKOlYBMROusCx45E847QznbdYExuHzMDI85yI%2BGyn5bkutiKmzwwCb8sOm3sO%2B1TfXOBSx6NlOuXe%2F4gdLdPPgtqx%2F29MFsvsyUA1kjsrMxucfkm4NG1vW%2BhCNyP0PvC8KLla7d5jJ167bF2Sebcn7uYvFmA3EpnwAzxP5%2F%2Fef9h%2BMVx3PH765f%2F6eJUqY2M66%2FSDR1B1qLyxTTb0chFf3n6bTrNuKr9wp2Lw0gxnATZtHITZ3Z8BP56XclifaJ8tOn1xql%2FaGG%2BXJMKPvmnFd7bUwuInRCuHtgC9NEM%2FUfqc7LCdiyouSrpOGxxEeFfoShfpDnsrHAV7OiS9K%2BupwlKLVaW0vS3oP%2B1aFlSf9ZmpxS1yyUel0ItULw10BECFkvgVc%2B13sbXTluWYYUgsdqECb%2B3a8QpNJNqiixPsv6e3Do2nYmOyK46b7LPWjkT5JHr9VOg%2FTokb2YyT2gi9dWDh83YZmcECRzXj5GQctpldXNZtQXdKhe6yvgA7ZkQ27N6vUmj6hAeP0C87aTqKytlE8t3prOJe5QVpxrVR%2BNMZsTLixCbLVBJ7sLZ8EV5ggLBkgH5oztZugBckjHBw2PBlm6RKmZo%2F6XnA7lmHLNzYTqlR4IB2xFuTUMR9l95YEduOZYwDHJJn8%2BdBXmxOfvyBh09WxiNlUmlg9TuIB4H83EifwkBzFThI11DSDEYbV%2FBltpOJAdwUbz4PcT%2F6FAH9m08PX5%2FeJoIPDsCNzgE8hWKpAMWgFopzyJeKkC%2Bdgfz2AA4FARwMC8BZyykBrTB%2BnzkHegbsjHJygD03iTP7jfczi%2BJ1RDx3yJCd7ZkhQnYqGTJC%2Bfn94Xf5OL69n07vP98NGoaR%2BsowLIsce418nb%2F%2BOGfn1ZWkiCjp4ebz5GDugnqa3Exvvt7%2F8%2Flt6mhwfEbri86Ie5btPNj2dCYLPZ2jM3BQdAZK9PDTZagB6lHKisqegoqsXmtA0aCuKbSBOPGiXAeoHGmSL0t2dBHEiOkP5SY42ODZPMD4QINCx4uEMSSWENJPiTgG1RZRBsO0st08RKKVi5Evxa1AFUvoHtyulTbeWjLrVf21YQXcYoCTpAzgVIpFLBSBOIyWIRzSJa0dxMkSB3HU1hFnkn3H4IRhbk5cEi4rUerNYFK%2BC18MSlfUQGRDY1EJdWN8eqXUC2BUFUnmdGmt6VFn55zoctkB8ZBUe7ASzQ6gQYGVrCmMwahay%2BiSonKCeKZ%2FAouoDZj7wrB0e55%2BYYOFPU2S6t8LcuPBmfGyXiefXiRv3HbzTMayBcid5TvPeH1PHpV3DzsxBzOhi0xyowFNpGs%2FM1175njW75xozr43kmE5XnhgqY1m%2BWuq%2BOKsN8xLc5Tr4gwASGdtrZsIIMt2oXKpEwBV5e07YalF%2BBZ0wQu0tN%2BcgiwI%2B8PKKUANMlaiKC1hH5yKA12IgqIq16gShmwcRoG3fzl8dQOVg4W4fBt34HvLuso6P9k2eKH5XrFcw%2BgB4irP6e6DjJW09hxaNcfF%2FvFtWEFGxLE%2BpW3SFGmcly1Ia7uyuLKHXRM1HAKm1O6bTliTwYWMO4IUkPn3fbrOlatzkUoM4egei0Q9RvdEHWZl0MiigrbIwtVjqP0V0FVaXfbeHLQwNGmwgNJZIUYMKNnUjjkKG9%2FroxCjWq8Xqsw9Dw1tYKg9nCiCcDIwR%2BxEeKoxnECOqKj9lXdVmx2sMLtK16hUotvArdoSv5wmfRvxpPrN2kWiE%2BisW9QRkAH0SkBWlV3iNNl5RqF1oGgoeMKFx1vH85GsXUNdV2QoG7IBkY54uUySEwqBTdNgP%2F2a4lOAwb6EJqFrDUr5H6r%2FImDUCgOgiTCl%2FZv1k0OoKOge7A8i6vd7B1goKTpL6jridH2UfDyQ9W46hzNdX3zZ%2FLj9aLmuX%2FlziG5IHFO90ernVb1RONGCtmHVeyCuEEPhQ0TCkKucCVp1R%2BEqbU6YwZGV7yU1Zw1TkC4OzKhFMN3zm8ftY5Zo44BsDtUmM28d%2Bevo7aUha9GhA6RUZFDpqL44nMYIvVipHHz9VHpzu%2FrDfmYlmjIQrYQ5ppOy3NSFamMMNiJcKiIWLtXjBIkmSY%2BCOt04H57k79uncPzNDvFXcm9f3c30HghHZqc5XWhiqWz6XqyGnjOkfjkIGhLl0Fp7eRzl0KCYZTZ15PiouSZ3W1VVadJlPlOL3q912guf4qxBn97FNQgKDLWb4x1xOTjO%2F2%2BDU7R5%2FI9HkuHH%2F70F3fwP), which encodes the graph. +##### Key slot access primitives + +The state of a key slot is updated via the internal function `psa_key_slot_state_transition`. To change the state of `slot` from `expected_state` to `new_state`, when `new_state` is not `PSA_SLOT_EMPTY`, one must call `psa_key_slot_state_transition(slot, expected_state, new_state)`; if the state was not `expected_state` then `PSA_ERROR_CORRUPTION_DETECTED` is returned, this must not be a possibility in our code. The sole reason for having an expected state parameter here is to guarantee that our functions work as expected. + +Changing a slot's state to `PSA_SLOT_EMPTY` is done via `psa_wipe_key_slot`, this function wipes the entirety of the key slot. + +The reader count of a slot is incremented via `psa_register_read`, and decremented via `psa_unregister_read`. Library functions register to read a slot via the `psa_get_and_lock_key_slot_X` functions, read from the slot, then call `psa_unregister_read` to make known that they have finished reading the slot's contents. + +##### Key store consistency and abstraction function + +The key store is protected by a single global mutex `mbedtls_threading_key_slot_mutex`. + +We maintain the consistency of the key store by ensuring that all reads and writes to `slot->state` and `slot->registered_readers` are performed under `mbedtls_threading_key_slot_mutex`. All the access primitives described above must be called while the mutex is held; there is a convenience function `psa_unregister_read_under_mutex` which wraps a call to `psa_unregister_read` in a mutex lock/unlock pair. + +A thread can only traverse the key store while holding `mbedtls_threading_key_slot_mutex`, the set of keys within the key store which the thread holding the mutex can access is equivalent to the set: + + {mbedtls_svc_key_id_t k : (\exists slot := &global_data.key_slots[i]) [ + (slot->state == PSA_SLOT_FULL) && + (slot->attr.id == k)]} + +The union of this set and the set of persistent keys not currently loaded into slots is our abstraction function for the key store, any key not in this union does not currently exist (even if the key is in a slot which has a `PSA_SLOT_FILLING` or `PSA_SLOT_PENDING_DELETION` state). Attempting to start using any key which is not a member of the union will result in a `PSA_ERROR_INVALID_HANDLE` error code. + +##### Locking and unlocking the mutex + +If a lock or unlock operation fails and this is the first failure within a function, the function will return `PSA_ERROR_SERVICE_FAILURE`. If a lock or unlock operation fails after a different failure has been identified, the status code is not overwritten. + +We have defined a set of macros in `library/psa_crypto_core.h` to capture the common pattern of (un)locking the mutex and returning or jumping to an exit label upon failure. + +##### Key creation and loading + +To load a new key into a slot, the following internal utility functions are used: + +* `psa_reserve_free_key_slot` - This function, which must be called under `mbedtls_threading_key_slot_mutex`, iterates through the key store to find a slot whose state is `PSA_SLOT_EMPTY`. If found, it reserves the slot by setting its state to `PSA_SLOT_FILLING`. If not found, it will see if there are any persistent keys loaded which do not have any readers, if there are it will kick one such key out of the key store. +* `psa_start_key_creation` - This function wraps around `psa_reserve_free_key_slot`, if a slot has been found then the slot id is set. This second step is not done under the mutex, at this point the calling thread has exclusive access to the slot. +* `psa_finish_key_creation` - After the contents of the key have been loaded (again this loading is not done under the mutex), the thread calls `psa_finish_key_creation`. This function takes the mutex, checks that the key does not exist in the key store (this check cannot be done before this stage), sets the slot's state to `PSA_SLOT_FULL` and releases the mutex. Upon success, any thread is immediately able to use the new key. +* `psa_fail_key_creation` - If there is a failure at any point in the key creation stage, this clean-up function takes the mutex, wipes the slot, and releases the mutex. Immediately after this unlock, any thread can start to use the slot for another key load. + +##### Re-loading persistent keys + +As described above, persistent keys can be kicked out of the key slot array provided they are not currently being used (`registered_readers == 0`). When attempting to use a persistent key that has been kicked out of a slot, the call to `psa_get_and_lock_key_slot` will see that the key is not in a slot, call `psa_reserve_free_key_slot` and load the key back into the reserved slot. This entire sequence is done during a single mutex lock, which is necessary for thread-safety (see documentation of `psa_get_and_lock_key_slot`). + +If `psa_reserve_free_key_slot` cannot find a suitable slot, the key cannot be loaded back in. This will lead to a `PSA_ERROR_INSUFFICIENT_MEMORY` error. + +##### Using existing keys + +One-shot operations follow a standard pattern when using an existing key: + +* They call some `psa_get_and_lock_key_slot_X` function, which finds the key and registers the thread as a reader. +* They operate on the key slot, usually copying the key into a separate buffer to be used by the operation. This step is not done under the mutex. +* Once finished, they call `psa_unregister_read_under_mutex`. + +Multi-part and restartable operations each have a "setup" function where the key is inputted. This function follows the above pattern. The key is copied into the `operation` object, and the thread unregisters. They do not access the key slots again. The copy of the key will not be destroyed during a call to `psa_destroy_key`, the thread running the operation is responsible for deleting this copy in the clean-up. This may need to change to enforce the long term key requirements ([Long term key destruction requirements](#long-term-key-destruction-requirements)). + +##### Key destruction implementation + +The locking strategy here is explained in `library/psa_crypto.c`. The destroying thread (the thread calling `psa_destroy_key`) does not always wipe the key slot. The destroying thread registers to read the key, sets the slot's state to `PSA_SLOT_PENDING_DELETION`, wipes the slot from memory if the key is persistent, and then unregisters from reading the slot. + +`psa_unregister_read` internally calls `psa_wipe_key_slot` iff the slot's state is `PSA_SLOT_PENDING_DELETION` and the slot's registered reader counter is equal to 1. This implements a "last one out closes the door" approach, where the final thread to unregister from reading a destroyed key will automatically wipe the contents of the slot; this ensure that there is no corruption. + +### Linearisability of the system + +To satisfy the requirements in [Correctness out of the box](#correctness-out-of-the-box), we require our functions to be "linearisable" (under certain constraints). This means that any (constraint satisfying) set of concurrent calls are performed as if they were executed in some sequential order. + +The standard way of reasoning that this is the case is to identify a "linearization point" for each call, this is a single execution step where the function takes effect (this is usually a step in which the effects of the call become visible to other threads). If every call has a linearization point, the set of calls is equivalent to sequentially performing the calls in order of when their linearization point occurred. + +We only require linearisability to hold in the case where a resource-management error is not returned. In a set of concurrent calls, it is permitted for a call c to fail with a `PSA_ERROR_INSUFFICIENT_MEMORY` return code even if there does not exist a sequential ordering of the calls in which c returns this error. Even if such an error occurs, all calls are still required to be functionally correct. + +To help justify that our system is linearisable, here are the linearization points/planned linearization points of each PSA call : + +* Key creation functions (including `psa_copy_key`) - The linearization point for a successful call is the mutex unlock within `psa_finish_key_creation`; it is at this point that the key becomes visible to other threads. The linearization point for a failed call is the closest mutex unlock after the failure is first identified. +* `psa_destroy_key` - The linearization point for a successful destruction is the mutex unlock, the slot is now in the state `PSA_SLOT_PENDING_DELETION` meaning that the key has been destroyed. For failures, the linearization point is the same. +* `psa_purge_key`, `psa_close_key` - The linearization point is the mutex unlock after wiping the slot for a success, or unregistering for a failure. +* One shot operations - The linearization point is the final unlock of the mutex within `psa_get_and_lock_key_slot`, as that is the point in which it is decided whether the key exists. +* Multi-part operations - The linearization point of the key input function is the final unlock of the mutex within `psa_get_and_lock_key_slot`. All other steps have no non resource-related side effects (except for key derivation, covered in the key creation functions). + +Please note that one shot operations and multi-part operations are not yet considered thread-safe, as we do not test whether they rely on unprotected global resources. + +## Testing and analysis + +### Thread-safe testing + +It is now possible for individual tests to spin up multiple threads. This work has made the global variables used in tests thread-safe. If multiple threads fail a test assert, the first failure will be reported with correct line numbers. + +The `step` feature used in some tests is not thread-safe, it cannot be made thread-safe unless we introduce thread-local variables. + +### Current state of testing + +Our testing is a work in progress. It is not feasible to run our traditional, single-threaded, tests in such a way that tests concurrency. Therefore, we must write a new suite of testing. + +Our tests currently only run on pthread. + +We run tests using [ThreadSanitizer](https://clang.llvm.org/docs/ThreadSanitizer.html) to detect data races. We test the key store, and test that our key slot state system is enforced. + +Currently, not every API call is tested, we also cannot feasibly test every combination of concurrent API calls. API calls can in general be split into a few categories, each category calling the same internal functions in the same order - it is the internal functions that are in charge of locking mutexes and interacting with the key store; we have tests which cover every category. + +Since we do not run every cryptographic operation concurrently, we do not test that operations are free of unexpected global variables, cryptographic operations are not considered thread-safe. + +### Expanding testing + +Through future work on testing, it would be good to: + +* For every API call, have a test which runs multiple copies of the call simultaneously. +* After implementing other threading platforms, expand the tests to these platforms. +* Have increased testing for kicking persistent keys out of slots. +* Explicitly test that there all global variables are protected, for this we need to cover every operation in a concurrent scenario while running ThreadSanitizer. + +### Performance + +Key loading does somewhat run in parallel, deriving the key and copying it key into the slot is not done under any mutex. + +Key destruction is entirely sequential, this is required for persistent keys to stop issues with re-loading keys which cannot otherwise be avoided without changing our approach to thread-safety. + + +## Future work + +### Long term requirements + +As explained previously, we eventually aim to make the entirety of the PSA API thread-safe. This will build on the work that we have already completed. This requires a full suite of testing, see [Expanding testing](#expanding-testing) for details. + +### Long term performance requirements + +Our plan for cryptographic operations is that they are not performed under any global mutex. One-shot operations and multi-part operations will each only hold the global mutex for finding the relevant key in the key slot, and unregistering as a reader after the operation. + +### Long term key destruction requirements + +The [PSA Crypto Key destruction specification](https://arm-software.github.io/psa-api/crypto/1.1/api/keys/management.html#key-destruction) mandates that implementations make a best effort to ensure that the key material cannot be recovered. In the long term, it would be good to guarantee that `psa_destroy_key` wipes all copies of the key material. + +Here are our long term key destruction goals: + +`psa_destroy_key` does not block indefinitely, and when `psa_destroy_key` returns: + +1. The key identifier does not exist. This is a functional requirement for persistent keys: any thread can immediately create a new key with the same identifier. +2. The resources from the key have been freed. This allows threads to create similar keys immediately after destruction, regardless of resources. +4. No copy of the key material exists. Rationale: this is a security requirement. We do not have this requirement yet, but we need to document this as a security weakness, and we would like to satisfy this security requirement in the future. + +#### Condition variables + +It would be ideal to add these to a future major version; we cannot add these as requirements to the default `MBEDTLS_THREADING_C` for backwards compatibility reasons. + +Condition variables would enable us to fulfil the final requirement in [Long term key destruction requirements](#long-term-key-destruction-requirements). Destruction would then work as follows: + + * When a thread calls `psa_destroy_key`, they continue as normal until the `psa_unregister_read` call. + * Instead of calling `psa_unregister_read`, the thread waits until the condition `slot->registered_readers == 1` is true (the destroying thread is the final reader). + * At this point, the destroying thread directly calls `psa_wipe_key_slot`. + +A few changes are needed for this to follow our destruction requirements: + + * Multi-part operations will need to remain registered as readers of their key slot until their copy of the key is destroyed, i.e. at the end of the finish/abort call. + * The functionality where `psa_unregister_read` can wipe the key slot will need to be removed, slot wiping is now only done by the destroying/wiping thread. + +### Protecting operation contexts + +Currently, we rely on the crypto service to ensure that the same operation is not invoked concurrently. This abides by the PSA Crypto API Specification ([PSA Concurrent calling conventions](#psa-concurrent-calling-conventions)). + +Concurrent access to the same operation object can compromise the crypto service. For example, if the operation context has a pointer (depending on the compiler and the platform, the pointer assignment may or may not be atomic). This violates the functional correctness requirement of the crypto service. + +If, in future, we want to protect against this within the library then operations will require a status field protected by a global mutex. On entry, API calls would check the state and return an error if the state is ACTIVE. If the state is INACTIVE, then the call will set the state to ACTIVE, do the operation section and then restore the state to INACTIVE before returning. + +### Future driver work + +A future policy we may wish to enforce for drivers is: * By default, each driver only has at most one entry point active at any given time. In other words, each driver has its own exclusive lock. * Drivers have an optional `"thread_safe"` boolean property. If true, it allows concurrent calls to this driver. * Even with a thread-safe driver, the core never starts the destruction of a key while there are operations in progress on it, and never performs concurrent calls on the same multipart operation. -#### Long-term performance requirements - -In the short term, correctness is the important thing. We can start with a global lock. - -In the medium to long term, performing a slow or blocking operation (for example, a driver call, or an RSA decryption) should not block other threads, even if they're calling the same driver or using the same key object. - -We may want to go directly to a more sophisticated approach because when a system works with a global lock, it's typically hard to get rid of it to get more fine-grained concurrency. - -#### Key destruction short-term requirements - -##### Summary of guarantees in the short term - -When `psa_destroy_key` returns: - -1. The key identifier doesn't exist. Rationale: this is a functional requirement for persistent keys: the caller can immediately create a new key with the same identifier. -2. The resources from the key have been freed. Rationale: in a low-resource condition, this may be necessary for the caller to re-create a similar key, which should be possible. -3. The call must not block indefinitely, and in particular cannot wait for an event that is triggered by application code such as calling an abort function. Rationale: this may not strictly be a functional requirement, but it is an expectation `psa_destroy_key` does not block forever due to another thread, which could potentially be another process on a multi-process system. In particular, it is only acceptable for `psa_destroy_key` to block, when waiting for another thread to complete a PSA Cryptography API call that it had already started. - -When `psa_destroy_key` is called on a key that is in use, guarantee 2. might be violated. (This is consistent with the requirement [“Correctness out of the box”](#correctness-out-of-the-box), as destroying a key while it's in use is undefined behavior.) - -#### Key destruction long-term requirements - -The [PSA Crypto API specification](https://armmbed.github.io/mbed-crypto/html/api/keys/management.html#key-destruction) mandates that implementations make a best effort to ensure that the key material cannot be recovered. In the long term, it would be good to guarantee that `psa_destroy_key` wipes all copies of the key material. - -##### Summary of guarantees in the long term - -When `psa_destroy_key` returns: - -1. The key identifier doesn't exist. Rationale: this is a functional requirement for persistent keys: the caller can immediately create a new key with the same identifier. -2. The resources from the key have been freed. Rationale: in a low-resource condition, this may be necessary for the caller to re-create a similar key, which should be possible. -3. The call must not block indefinitely, and in particular cannot wait for an event that is triggered by application code such as calling an abort function. Rationale: this may not strictly be a functional requirement, but it is an expectation `psa_destroy_key` does not block forever due to another thread, which could potentially be another process on a multi-process system. In particular, it is only acceptable for `psa_destroy_key` to block, when waiting for another thread to complete a PSA Cryptography API call that it had already started. -4. No copy of the key material exists. Rationale: this is a security requirement. We do not have this requirement yet, but we need to document this as a security weakness, and we would like to satisfy this security requirement in the future. - -As opposed to the short term requirements, all the above guarantees hold even if `psa_destroy_key` is called on a key that is in use. - -### Resources to protect - -Analysis of the behavior of the PSA key store as of Mbed TLS 9202ba37b19d3ea25c8451fd8597fce69eaa6867. - -#### Global variables - -* `psa_crypto_slot_management::global_data.key_slots[i]`: see [“Key slots”](#key-slots). - -* `psa_crypto_slot_management::global_data.key_slots_initialized`: - * `psa_initialize_key_slots`: modification. - * `psa_wipe_all_key_slots`: modification. - * `psa_get_empty_key_slot`: read. - * `psa_get_and_lock_key_slot`: read. - -* `psa_crypto::global_data.rng`: depends on the RNG implementation. See [“Random generator”](#random-generator). - * `psa_generate_random`: query. - * `mbedtls_psa_crypto_configure_entropy_sources` (only if `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` is enabled): setup. Only called from `psa_crypto_init` via `mbedtls_psa_random_init`, or from test code. - * `mbedtls_psa_crypto_free`: deinit. - * `psa_crypto_init`: seed (via `mbedtls_psa_random_seed`); setup via `mbedtls_psa_crypto_configure_entropy_sources. - -* `psa_crypto::global_data.{initialized,rng_state}`: these are bit-fields and cannot be modified independently so they must be protected by the same mutex. The following functions access these fields: - * `mbedtls_psa_crypto_configure_entropy_sources` [`rng_state`] (only if `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` is enabled): read. Only called from `psa_crypto_init` via `mbedtls_psa_random_init`, or from test code. - * `mbedtls_psa_crypto_free`: modification. - * `psa_crypto_init`: modification. - * Many functions via `GUARD_MODULE_INITIALIZED`: read. - -#### Key slots - -##### Key slot array traversal - -“Occupied key slot” is determined by `psa_is_key_slot_occupied` based on `slot->attr.type`. - -The following functions traverse the key slot array: - -* `psa_get_and_lock_key_slot_in_memory`: reads `slot->attr.id`. -* `psa_get_and_lock_key_slot_in_memory`: calls `psa_lock_key_slot` on one occupied slot. -* `psa_get_empty_key_slot`: calls `psa_is_key_slot_occupied`. -* `psa_get_empty_key_slot`: calls `psa_wipe_key_slot` and more modifications on one occupied slot with no active user. -* `psa_get_empty_key_slot`: calls `psa_lock_key_slot` and more modification on one unoccupied slot. -* `psa_wipe_all_key_slots`: writes to all slots. -* `mbedtls_psa_get_stats`: reads from all slots. - -##### Key slot state - -The following functions modify a slot's usage state: - -* `psa_lock_key_slot`: writes to `slot->lock_count`. -* `psa_unlock_key_slot`: writes to `slot->lock_count`. -* `psa_wipe_key_slot`: writes to `slot->lock_count`. -* `psa_destroy_key`: reads `slot->lock_count`, calls `psa_lock_key_slot`. -* `psa_wipe_all_key_slots`: writes to all slots. -* `psa_get_empty_key_slot`: writes to `slot->lock_count` and calls `psa_wipe_key_slot` and `psa_lock_key_slot` on one occupied slot with no active user; calls `psa_lock_key_slot` on one unoccupied slot. -* `psa_close_key`: reads `slot->lock_count`; calls `psa_get_and_lock_key_slot_in_memory`, `psa_wipe_key_slot` and `psa_unlock_key_slot`. -* `psa_purge_key`: reads `slot->lock_count`; calls `psa_get_and_lock_key_slot_in_memory`, `psa_wipe_key_slot` and `psa_unlock_key_slot`. - -**slot->attr access:** -`psa_crypto_core.h`: -* `psa_key_slot_set_flags` - writes to attr.flags -* `psa_key_slot_set_bits_in_flags` - writes to attr.flags -* `psa_key_slot_clear_bits` - writes to attr.flags -* `psa_is_key_slot_occupied` - reads attr.type (but see “[Determining whether a key slot is occupied](#determining-whether-a-key-slot-is-occupied)”) -* `psa_key_slot_get_flags` - reads attr.flags - -`psa_crypto_slot_management.c`: -* `psa_get_and_lock_key_slot_in_memory` - reads attr.id -* `psa_get_empty_key_slot` - reads attr.lifetime -* `psa_load_persistent_key_into_slot` - passes attr pointer to psa_load_persistent_key -* `psa_load_persistent_key` - reads attr.id and passes pointer to psa_parse_key_data_from_storage -* `psa_parse_key_data_from_storage` - writes to many attributes -* `psa_get_and_lock_key_slot` - writes to attr.id, attr.lifetime, and attr.policy.usage -* `psa_purge_key` - reads attr.lifetime, calls psa_wipe_key_slot -* `mbedtls_psa_get_stats` - reads attr.lifetime, attr.id - -`psa_crypto.c`: -* `psa_get_and_lock_key_slot_with_policy` - reads attr.type, attr.policy. -* `psa_get_and_lock_transparent_key_slot_with_policy` - reads attr.lifetime -* `psa_destroy_key` - reads attr.lifetime, attr.id -* `psa_get_key_attributes` - copies all publicly available attributes of a key -* `psa_export_key` - copies attributes -* `psa_export_public_key` - reads attr.type, copies attributes -* `psa_start_key_creation` - writes to the whole attr structure -* `psa_validate_optional_attributes` - reads attr.type, attr.bits -* `psa_import_key` - reads attr.bits -* `psa_copy_key` - reads attr.bits, attr.type, attr.lifetime, attr.policy -* `psa_mac_setup` - copies whole attr structure -* `psa_mac_compute_internal` - copies whole attr structure -* `psa_verify_internal` - copies whole attr structure -* `psa_sign_internal` - copies whole attr structure, reads attr.type -* `psa_assymmetric_encrypt` - reads attr.type -* `psa_assymetric_decrypt` - reads attr.type -* `psa_cipher_setup` - copies whole attr structure, reads attr.type -* `psa_cipher_encrypt` - copies whole attr structure, reads attr.type -* `psa_cipher_decrypt` - copies whole attr structure, reads attr.type -* `psa_aead_encrypt` - copies whole attr structure -* `psa_aead_decrypt` - copies whole attr structure -* `psa_aead_setup` - copies whole attr structure -* `psa_generate_derived_key_internal` - reads attr.type, writes to and reads from attr.bits, copies whole attr structure -* `psa_key_derivation_input_key` - reads attr.type -* `psa_key_agreement_raw_internal` - reads attr.type and attr.bits - -##### Determining whether a key slot is occupied - -`psa_is_key_slot_occupied` currently uses the `attr.type` field to determine whether a key slot is occupied. This works because we maintain the invariant that an occupied slot contains key material. With concurrency, it is desirable to allow a key slot to be reserved, but not yet contain key material or even metadata. When creating a key, determining the key type can be costly, for example when loading a persistent key from storage or (not yet implemented) when importing or unwrapping a key using an interface that determines the key type from the data that it parses. So we should not need to hold the global key store lock while the key type is undetermined. - -Instead, `psa_is_key_slot_occupied` should use the key identifier to decide whether a slot is occupied. The key identifier is always readily available: when allocating a slot for a persistent key, it's an input of the function that allocates the key slot; when allocating a slot for a volatile key, the identifier is calculated from the choice of slot. - -Alternatively, we could use a dedicated indicator that the slot is occupied. The advantage of this is that no field of the `attr` structure would be needed to determine the slot state. This would be a clean separation between key attributes and slot state and `attr` could be treated exactly like key slot content. This would save code size and maintenance effort. The cost of it would be that each slot would need an extra field to indicate whether it is occupied. - -##### Key slot content - -Other than what is used to determine the [“key slot state”](#key-slot-state), the contents of a key slot are only accessed as follows: - -* Modification during key creation (between `psa_start_key_creation` and `psa_finish_key_creation` or `psa_fail_key_creation`). -* Destruction in `psa_wipe_key_slot`. -* Read in many functions, between calls to `psa_lock_key_slot` and `psa_unlock_key_slot`. - -**slot->key access:** -* `psa_allocate_buffer_to_slot` - allocates key.data, sets key.bytes; -* `psa_copy_key_material_into_slot` - writes to key.data -* `psa_remove_key_data_from_memory` - writes and reads to/from key data -* `psa_get_key_attributes` - reads from key data -* `psa_export_key` - passes key data to psa_driver_wrapper_export_key -* `psa_export_public_key` - passes key data to psa_driver_wrapper_export_public_key -* `psa_finish_key_creation` - passes key data to psa_save_persistent_key -* `psa_validate_optional_attributes` - passes key data and bytes to mbedtls_psa_rsa_load_representation -* `psa_import_key` - passes key data to psa_driver_wrapper_import_key -* `psa_copy_key` - passes key data to psa_driver_wrapper_copy_key, psa_copy_key_material_into_slot -* `psa_mac_setup` - passes key data to psa_driver_wrapper_mac_sign_setup, psa_driver_wrapper_mac_verify_setup -* `psa_mac_compute_internal` - passes key data to psa_driver_wrapper_mac_compute -* `psa_sign_internal` - passes key data to psa_driver_wrapper_sign_message, psa_driver_wrapper_sign_hash -* `psa_verify_internal` - passes key data to psa_driver_wrapper_verify_message, psa_driver_wrapper_verify_hash -* `psa_asymmetric_encrypt` - passes key data to mbedtls_psa_rsa_load_representation -* `psa_asymmetric_decrypt` - passes key data to mbedtls_psa_rsa_load_representation -* `psa_cipher_setup ` - passes key data to psa_driver_wrapper_cipher_encrypt_setup and psa_driver_wrapper_cipher_decrypt_setup -* `psa_cipher_encrypt` - passes key data to psa_driver_wrapper_cipher_encrypt -* `psa_cipher_decrypt` - passes key data to psa_driver_wrapper_cipher_decrypt -* `psa_aead_encrypt` - passes key data to psa_driver_wrapper_aead_encrypt -* `psa_aead_decrypt` - passes key data to psa_driver_wrapper_aead_decrypt -* `psa_aead_setup` - passes key data to psa_driver_wrapper_aead_encrypt_setup and psa_driver_wrapper_aead_decrypt_setup -* `psa_generate_derived_key_internal` - passes key data to psa_driver_wrapper_import_key -* `psa_key_derivation_input_key` - passes key data to psa_key_derivation_input_internal -* `psa_key_agreement_raw_internal` - passes key data to mbedtls_psa_ecp_load_representation -* `psa_generate_key` - passes key data to psa_driver_wrapper_generate_key - -#### Random generator - -The PSA RNG can be accessed both from various PSA functions, and from application code via `mbedtls_psa_get_random`. - -With the built-in RNG implementations using `mbedtls_ctr_drbg_context` or `mbedtls_hmac_drbg_context`, querying the RNG with `mbedtls_xxx_drbg_random()` is thread-safe (protected by a mutex inside the RNG implementation), but other operations (init, free, seed) are not. - -When `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` is enabled, thread safety depends on the implementation. - -#### Driver resources - -Depends on the driver. The PSA driver interface specification does not discuss whether drivers must support concurrent calls. - -### Simple global lock strategy - -Have a single mutex protecting all accesses to the key store and other global variables. In practice, this means every PSA API function needs to take the lock on entry and release on exit, except for: - -* Hash function. -* Accessors for key attributes and other local structures. - -Note that operation functions do need to take the lock, since they need to prevent the destruction of the key. - -Note that this does not protect access to the RNG via `mbedtls_psa_get_random`, which is guaranteed to be thread-safe when `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` is disabled. - -This approach is conceptually simple, but requires extra instrumentation to every function and has bad performance in a multithreaded environment since a slow operation in one thread blocks unrelated operations on other threads. - -### Global lock excluding slot content - -Have a single mutex protecting all accesses to the key store and other global variables, except that it's ok to access the content of a key slot without taking the lock if one of the following conditions holds: - -* The key slot is in a state that guarantees that the thread has exclusive access. -* The key slot is in a state that guarantees that no other thread can modify the slot content, and the accessing thread is only reading the slot. - -Note that a thread must hold the global mutex when it reads or changes a slot's state. - -#### Slot states - -For concurrency purposes, a slot can be in one of four states: - -* EMPTY: no thread is currently accessing the slot, and no information is stored in the slot. Any thread is able to change the slot's state to FILLING and begin loading data. -* FILLING: one thread is currently loading or creating material to fill the slot, this thread is responsible for the next state transition. Other threads cannot read the contents of a slot which is in FILLING. -* FULL: the slot contains a key, and any thread is able to use the key after registering as a reader. -* PENDING_DELETION: the key within the slot has been destroyed or marked for destruction, but at least one thread is still registered as a reader. No thread can register to read this slot. The slot must not be wiped until the last reader de-registers, wiping the slot by calling `psa_wipe_key_slot`. - -To change `slot` to state `new_state`, a function must call `psa_slot_state_transition(slot, new_state)`. - -A counter field within each slot keeps track of how many readers have registered. Library functions must call `psa_register_read` before reading the key data within a slot, and `psa_unregister_read` after they have finished operating. - -Any call to `psa_slot_state_transition`, `psa_register_read` or `psa_unregister_read` must be performed by a thread which holds the global mutex. - -##### Linearizability of the system - -To satisfy the requirements in [Correctness out of the box](#correctness-out-of-the-box), we require our functions to be "linearizable" (under certain constraints). This means that any (constraint satisfying) set of concurrent calls are performed as if they were executed in some sequential order. - -The standard way of reasoning that this is the case is to identify a "linearization point" for each call, this is a single execution step where the function takes effect (this is usually a step in which the effects of the call become visible to other threads). If every call has a linearization point, the set of calls is equivalent to sequentially performing the calls in order of when their linearization point occurred. - -We only require linearizability to hold in the case where a resource-management error is not returned. In a set of concurrent calls, it is permitted for a call c to fail with a PSA_ERROR_INSUFFICIENT_MEMORY return code even if there does not exist a sequential ordering of the calls in which c returns this error. Even if such an error occurs, all calls are still required to be functionally correct. - -We only access and modify a slot's state and reader count while we hold the global lock. This ensures the memory in which these fields are stored is correctly synchronized. It also ensures that the key data within the slot is synchronised where needed (the writer unlocks the mutex after filling the data, and any reader must lock the mutex before reading the data). - -To help justify that our system is linearizable, here is a list of key slot state changing functions and their linearization points (for the sake of brevity not all failure cases are covered, but those cases are not complex): -* `psa_wipe_key_slot, psa_register_read, psa_unregister_read, psa_slot_state_transition,` - These functions are all always performed under the global mutex, so they have no effects visible to other threads (this implies that they are linearizable). -* `psa_get_empty_key_slot, psa_get_and_lock_key_slot_in_memory, psa_load_X_key_into_slot, psa_fail_key_creation` - These functions hold the mutex for all non-setup/finalizing code, their linearization points are the release of the mutex. -* `psa_get_and_lock_key_slot` - If the key is already in a slot, the linearization point is the linearization point of the call to `psa_get_and_lock_key_slot_in_memory`. If the key is not in a slot and is loaded into one, the linearization point is the linearization point of the call to `psa_load_X_key_into_slot`. -* `psa_start_key_creation` - From the perspective of other threads, the only effect of a successful call to this function is that the amount of usable resources decreases (a key slot which was usable is now unusable). Since we do not consider resource management as linearizable behaviour, when arguing for linearizability of the system we consider this function to have no visible effect to other threads. -* `psa_finish_key_creation` - On a successful load, we lock the mutex and set the state of the slot to FULL, the linearization point is then the following unlock. On an unsuccessful load, the linearization point is when we return - no action we have performed has been made visible to another thread as the slot is still in a FILLING state. -* `psa_destroy_key, psa_close_key, psa_purge_key` - As per the requirements, we need only argue for the case where the key is not in use here. The linearization point is the unlock after wiping the data and setting the slot state to EMPTY. -* `psa_import_key, psa_copy_key, psa_generate_key, mbedtls_psa_register_se_key` - These functions call both `psa_start_key_creation` and `psa_finish_key_creation`, the linearization point of a successful call is the linearization point of the call to `psa_finish_key_creation`. The linearization point of an unsuccessful call is the linearization point of the call to `psa_fail_key_creation`. -* `psa_key_derivation_output_key` - Same as above. If the operation object is in use by multiple threads, the behaviour need not be linearizable. - -Library functions which operate on a slot will return `PSA_ERROR_BAD_STATE` if the slot is in an inappropriate state for the function at the linearization point. - -##### Key slot state transition diagram - -![](key-slot-state-transitions.png) - -In the state transition diagram above, an arrow between two states `q1` and `q2` with label `f` indicates that if the state of a slot is `q1` immediately before `f`'s linearization point, it may be `q2` immediately after `f`'s linearization point. - -##### Generating the key slot state transition diagram from source - -To generate the state transition diagram in https://app.diagrams.net/, open the following url: - -https://viewer.diagrams.net/?tags=%7B%7D&highlight=FFFFFF&edit=_blank&layers=1&nav=1&title=key-slot-state-transitions#R5Vxbd5s4EP4t%2B%2BDH5iAJcXms4ySbrdtNT7qX9MWHgGyrxcABHNv59SsM2EhgDBhs3PVL0CANoBl9fDMaMkC3i%2FWDb3jzz65F7AGUrPUAjQYQAqBh9ieSbGKJIqFYMPOplXTaC57pO0mEUiJdUosEXMfQde2QerzQdB2HmCEnM3zfXfHdpq7NX9UzZiQneDYNOy%2F9h1rhPJZqUN3Lfyd0Nk%2BvDBQ9PrMw0s7JkwRzw3JXGRG6G6Bb33XD%2BGixviV2NHnpvMTj7g%2Bc3d2YT5ywyoDv4H08%2Ffvxj9VX3XGGw5cf3o9PHxJjvBn2MnngAVRspm9o0Td2OIsO7%2F8aj1Mx0585U9B5bgQTnxgW8YP07Ksv9he1bOcn3KSTzm6c2Zc1hqs5DcmzZ5jRmRVzsegK4cJmLcAOjcCLjT6la2LtVGUnJZmnN%2BKHZJ0RJZP0QNwFCf0N65KclbXEYDuPTdqrjP0T0Txj%2BlRmJB4322neG4UdJHapYSMACowkzphjfYy8nbVM2wgCavIT5btLx4pmaCSxFpscf%2FNvcmrbeMk2Rutsv9Emba1puBvEjl8y8v2QqJGOOGiNwF36Jjnul6Hhz0hY0k%2BO%2BxGLW8V522Zshwtsl8p8YhshfePXfpFBkys8uZQ92UHXwYrgE%2FFzJ6Oya1VUpOo3euancWplJKiNpymnduttu0k4wQFhzgGXjk9mNAiJv13seX9kBhkbr%2BxlwK9Xm86cyEeZQxCfCaJlSRnafkxOLKhlRTqGPgnou%2FG61Re5khc93PZx8XCAR4XOVb56RADYvTOSq3CwXAQM0g2UVJ2zxAd4mt%2BkaoAwxJ1OA9KNLasA%2Ft3np28v14nevQNvvXXwTmBYysAwKIXhHdxLWbiXjsB9c%2FCGFcEb9Au8ec%2FJgWxl7D7yDugYrFO6mXE4LzAmU4Pak59kMzEZXofUdfoM2ema6SNkJ5ohp1Qc3x1%2B51%2FF94%2Fj8eOXh17DMFIuDMNyldderTjnt18u0Lm4kXAVIz3dfRlt3b2inUZ347tvj39%2BuU4b9Y7PqF3RmepRZbPotTmdSdNOx%2BgM7BWdgRJ7%2BWkyVAGLJmWs8G9BLCs3KsAq1FTMGkhQX5XrAEUgTfJ5yY5WyHXYFSdk4YWbLeEJbDfsMdlJF1Qfuc5OjXwuegOKXtTt48sNbhIwxaMuGjL1K98VYYwkpRijMDjg0QBEWawUZJAmqc1QRpYElGG%2BjgSX7DoFVow0U%2BrQYH41cVW6uE7Gmg%2FM7rKu8mCDWvEpRSvUegboKaKfgi3Npf%2B2RZaYbZwv51492dMcg6rm3FGvMEhWMecwitowb4MVQZHIoQ9ADPMBY5PplizPwzes82imSlL5fUGhPzjSX9bK9LOD%2BI6bLp7RUDYBfTA9%2B50sH%2Bkz%2Fvi0rha6CVsGFQO4lNEZjjWxXfNnhtTV0GDabkCiobVGeUtm8uyo%2BtFjf9A%2FtVEb6A%2BQxntZO1k1nr5CfC7sR0X74K3QzixwVwxrMzyz2zy9XBHw%2B5WnhyrkvATjhoAPDuVWzsQpUVGsUwhDFglC392cDl%2FtQGVvIW63jFsIpmVN4aOZdBmc6L47HN5wkNc9xsmX4LfHwKs%2BTB6Eu57AE6N3mcwa0gBnbaSCorO1uaqsZpJ7CtDrXKQjHouQVn7P4l2iIzwWl%2BrvhsfmyyOup9JFbo3gsegeC47bEvh1kUgsNGT7%2BxSXxrfW6BzsFV4iIbzFTesukCpkCSvG72153HXtRZQumlYiRF3YcmqLPqVZzC4ThIWzc5ZKrspbEzwMdbg1UTUtiHsNKwpoCitCPZfSXfFtMSMprufiQsLeAkprhVwRoECekbQVj%2FG7GF0UchXb9UxV%2FcehoQkMNYcTXBFO%2BhXVwQNJ%2BNpwAgWWonRXHlrsdrDA7XJpoFzQUyN9tKIeyeXoryNvXr5Q26jQ2H0P1y6IAXQhEMuT3pwlz55TOohNfcESIXHSeMcSbbNAGpahrMs6RBoS9XLVGbAS0NRNA7GnyV4F6PxNqBK6UaG0%2B6HyJwJ6qTIA6ijDze%2Bso%2BxSPoToZXqpfK3%2Fz9JLT3S5Hk%2FhRNNmX9%2B%2B338yHccr%2FIyqHfLGlZw1%2BiSzM%2BpWtRC2X0VqSKgew2JeqDLc4iOZqvaoW6HPVWJuEQOzXcOaeMQPIlxxwi0ZY%2Ffk1q%2Ba2Gp6XVI7pM4JakrLN66DGpaiQAuIiGVQGIie6Pxnq6CAl6wAqu9Cv9gXl1VT%2F1VL9%2Fa74OmW%2Brk2T%2Fnkbu57gsolw4KiqrUde0WnLBnW3P9fj7j7%2Fr%2BjoLv%2FAA%3D%3D - -#### Destruction of a key in use - -Problem: In [Key destruction long-term requirements](#key-destruction-long-term-requirements) we require that the key slot is destroyed (by `psa_wipe_key_slot`) even while it's in use (FILLING or with at least one reader). - -How do we ensure that? This needs something more sophisticated than mutexes (concurrency number >2)! Even a per-slot mutex isn't enough (we'd need a reader-writer lock). - -Solution: after some team discussion, we've decided to rely on a new threading abstraction which mimics C11 (i.e. `mbedtls_fff` where `fff` is the C11 function name, having the same parameters and return type, with default implementations for C11, pthreads and Windows). We'll likely use condition variables in addition to mutexes. - -##### Mutex only - -When calling `psa_wipe_key_slot` it is the callers responsibility to set the slot state to PENDING_DELETION first. For most functions this is a clean {FULL, !has_readers} -> PENDING_DELETION transition: psa_get_empty_key_slot, psa_get_and_lock_key_slot, psa_close_key, psa_purge_key. - -`psa_wipe_all_key_slots` is only called from `mbedtls_psa_crypto_free`, here we will need to return an error as we won't be able to free the key store if a key is in use without compromising the state of the secure side. This is acceptable as an untrusted application cannot call `mbedtls_psa_crypto_free` in a crypto service. In a service integration, `mbedtls_psa_crypto_free` on the client cuts the communication with the crypto service. Also, this is the current behaviour. - -`psa_destroy_key` registers as a reader, marks the slot as deleted, deletes persistent keys and opaque keys and unregisters before returning. This will free the key ID, but the slot might be still in use. This only works if drivers are protected by a mutex (and the persistent storage as well if needed). `psa_destroy_key` transfers to PENDING_DELETION as an intermediate state. The last reading operation will wipe the key slot upon unregistering. In case of volatile keys freeing up the ID while the slot is still in use does not provide any benefit and we don't need to do it. - -These are serious limitations, but this can be implemented with mutexes only and arguably satisfies the [Key destruction short-term requirements](#key-destruction-short-term-requirements). - -Variations: - -1. As a first step the multipart operations would lock the keys for reading on setup and release on free -2. In a later stage this would be improved by locking the keys on entry into multi-part API calls and released before exiting. - -The second variant can't be implemented as a backward compatible improvement on the first as multipart operations that were successfully completed in the first case, would fail in the second. If we want to implement these incrementally, multipart operations in a multithreaded environment must be left unsupported in the first variant. This makes the first variant impractical (multipart operations returning an error in builds with multithreading enabled is not a behaviour that would be very useful to release). - -We can't reuse the `lock_count` field to mark key slots deleted, as we still need to keep track the lock count while the slot is marked for deletion. This means that we will need to add a new field to key slots. This new field can be reused to indicate whether the slot is occupied (see section [Determining whether a key slot is occupied](#determining-whether-a-key-slot-is-occupied)). (There would be three states: deleted, occupied, empty.) - -#### Condition variables - -Clean UNUSED -> PENDING_DELETION transition works as before. - -`psa_wipe_all_key_slots` and `psa_destroy_key` mark the slot as deleted and go to sleep until the slot has no registered readers. When waking up, they wipe the slot, and return. - -If the slot is already marked as deleted the threads calling `psa_wipe_all_key_slots` and `psa_destroy_key` go to sleep until the deletion completes. To satisfy [Key destruction long-term requirements](#key-destruction-long-term-requirements) none of the threads may return from the call until the slot is deleted completely. This can be achieved by signalling them when the slot has already been wiped and ready for use, that is not marked for deletion anymore. To handle spurious wake-ups, these threads need to be able to tell whether the slot was already deleted. This is not trivial, because by the time the thread wakes up, theoretically the slot might be in any state. It might have been reused and maybe even marked for deletion again. - -To resolve this, we can either: - -1. Depend on the deletion marker. If the slot has been reused and is marked for deletion again, the threads keep waiting until the second deletion completes. -2. Introduce a uuid (eg a global counter plus a slot ID), which is recorded by the thread waiting for deletion and checks whether it matches. If it doesn't, the function can return as the slot was already reallocated. If it does match, it can check whether it is still marked for deletion, if it is, the thread goes back to sleep, if it isn't, the function can return. - -##### Platform abstraction - -Introducing condition variables to the platform abstraction layer would be best done in a major version. If we can't wait until that, we will need to introduce a new compile time flag. Considering that this only will be needed on the PSA Crypto side and the upcoming split, it makes sense to make this flag responsible for the entire PSA Crypto threading support. Therefore if we want to keep the option open for implementing this in a backward compatible manner, we need to introduce and use this new flag already when implementing [Mutex only](#mutex-only). (If we keep the abstraction layer for mutexes the same, this shouldn't mean increase in code size and would mean only minimal effort on the porting side.) - -#### Operation contexts - -Concurrent access to the same operation context can compromise the crypto service for example if the operation context has a pointer (depending on the compiler and the platform, the pointer assignment may or may not be atomic). This violates the functional correctness requirement of the crypto service. (Concurrent calls to operations is undefined behaviour, but still should not compromise the CIA of the crypto service.) - -If we want to protect against this in the library, operations will need a status field protected by a global mutex similarly to key slots. On entry, API calls would check the state and return an error if it is already ACTIVE. Otherwise they set it to ACTIVE and restore it to INACTIVE before returning. - -Alternatively, protecting operation contexts can be left as the responsibility of the crypto service. The [PSA Crypto API Specification](https://arm-software.github.io/psa-api/crypto/1.1/overview/conventions.html#concurrent-calls) does not require the library to provide any protection in this case. A crypto service can easily add its own mutex in its operation structure wrapper (the same structure where it keeps track of which client connection owns that operation object). - -#### Drivers - -Each driver that hasn’t got the "thread_safe” property set has a dedicated mutex. - -Implementing "thread_safe” drivers depends on the condition variable protection in the key store, as we must guarantee that the core never starts the destruction of a key while there are operations in progress on it. - -Start with implementing threading for drivers without the "thread_safe” property (all drivers behave like the property wasn't set). Add "thread_safe" drivers at some point after the [Condition variables](#condition-variables) approach is implemented in the core. - -##### Reentrancy - -It is natural sometimes to want to perform cryptographic operations from a driver, for example calculating a hash as part of various other crypto primitives, or using a block cipher in a driver for a mode, etc. Also encrypting/authenticating communication with a secure element. - -**Non-thread-safe drivers:** - -A driver is non-thread-safe if the `thread-safe` property (see [Driver requirements](#driver-requirements)) is set to false. - In the non-thread-safe case we have these natural assumptions/requirements: -1. Drivers don't call the core for any operation for which they provide an entry point -2. The core doesn't hold the driver mutex between calls to entry points -With these, the only way of a deadlock is when we have several drivers and they have circular dependencies. That is, Driver A makes a call that is despatched to Driver B and upon executing that Driver B makes a call that is despatched to Driver A. For example Driver A does CCM calls Driver B to do CBC-MAC, which in turn calls Driver A to do AES. This example is pretty contrived and it is hard to find a more practical example. +1. Drivers don't call the core for any operation for which they provide an entry point. +2. The core doesn't hold the driver mutex between calls to entry points. + +With these, the only way of a deadlock is when there are several drivers with circular dependencies. That is, Driver A makes a call that is dispatched to Driver B; upon executing this call Driver B makes a call that is dispatched to Driver A. For example Driver A does CCM, which calls driver B to do CBC-MAC, which in turn calls Driver A to perform AES. Potential ways for resolving this: -1. Non-thread-safe drivers must not call the core -2. Provide a new public API that drivers can safely call -3. Make the dispatch layer public for drivers to call + +1. Non-thread-safe drivers must not call the core. +2. Provide a new public API that drivers can safely call. +3. Make the dispatch layer public for drivers to call. 4. There is a whitelist of core APIs that drivers can call. Drivers providing entry points to these must not make a call to the core when handling these calls. (Drivers are still allowed to call any core API that can't have a driver entry point.) The first is too restrictive, the second and the third would require making it a stable API, and would likely increase the code size for a relatively rare feature. We are choosing the fourth as that is the most viable option. **Thread-safe drivers:** -A driver is non-thread-safe if the `thread-safe` property (see [Driver requirements](#driver-requirements)) is set to true. +A driver would be non-thread-safe if the `thread-safe` property is set to true. -To make reentrancy in non-thread-safe drivers work, thread-safe drivers must not make a call to the core when handling a call that is on the non-thread-safe driver core API whitelist. +To make re-entrancy in non-thread-safe drivers work, thread-safe drivers must not make a call to the core when handling a call that is on the non-thread-safe driver core API whitelist. -Thread-safe drivers have less guarantees from the core and need to implement more complex logic and we can reasonably expect them to be more flexible in terms of reentrancy as well. At this point it is hard to see what further guarantees would be useful and feasible. Therefore, we don't provide any further guarantees for now. +Thread-safe drivers have fewer guarantees from the core and need to implement more complex logic. We can reasonably expect them to be more flexible in terms of re-entrancy as well. At this point it is hard to see what further guarantees would be useful and feasible. Therefore, we don't provide any further guarantees for now. -Thread-safe drivers must not make any assumption about the operation of the core beyond what is discussed in the [Reentrancy](#reentrancy) and [Driver requirements](#driver-requirements) sections. - -#### Global data - -PSA Crypto makes use of a `global_data` variable that will be accessible from multiple threads and needs to be protected. Any function accessing this variable (or its members) must take the corresponding lock first. Since `global_data` holds the RNG state, these will involve relatively expensive operations and therefore ideally `global_data` should be protected by its own, dedicated lock (different from the one protecting the key store). - -Note that this does not protect access to the RNG via `mbedtls_psa_get_random`, which is guaranteed to be thread-safe when `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` is disabled. Still, doing so is conceptually simpler and we probably will want to remove the lower level mutex in the long run, since the corresponding interface will be removed from the public API. The two mutexes are different and are always taken in the same order, there is no risk of deadlock. - -The purpose of `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` is very similar to the driver interface (and might even be added to it in the long run), therefore it makes sense to handle it the same way. In particular, we can use the `global_data` mutex to protect it as a default and when we implement the "thread_safe” property for drivers, we implement it for `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` as well. - -#### Implementation notes - -Since we only have simple mutexes, locking the same mutex from the same thread is a deadlock. Therefore functions taking the global mutex must not be called while holding the same mutex. Functions taking the mutex will document this fact and the implications. - -Releasing the mutex before a function call might introduce race conditions. Therefore might not be practical to take the mutex in low level access functions. If functions like that don't take the mutex, they need to rely on the caller to take it for them. These functions will document that the caller is required to hold the mutex. - -To avoid performance degradation, functions must hold mutexes for as short time as possible. In particular, they must not start expensive operations (eg. doing cryptography) while holding the mutex. - -## Strategy for 3.6 - -The goal is to provide viable threading support without extending the platform abstraction. (Condition variables should be added in 4.0.) This means that we will be relying on mutexes only. - -- Key Store - - Slot states are described in the [Slot states](#slot-states) section. They guarantee safe concurrent access to slot contents. - - Slot states will be protected by a global mutex as described in the introduction of the [Global lock excluding slot content](#global-lock-excluding-slot-content) section. - - Simple key destruction strategy as described in the [Mutex only](#mutex-only) section (variant 2). - - The slot state and key attributes will be separated as described in the last paragraph of the [Determining whether a key slot is occupied](#determining-whether-a-key-slot-is-occupied) section. -- The main `global_data` (the one in `psa_crypto.c`) shall be protected by its own mutex as described in the [Global data](#global-data) section. -- The solution shall use the pre-existing `MBEDTLS_THREADING_C` threading abstraction. That is, the flag proposed in the [Platform abstraction](#platform-abstraction) section won't be implemented. -- The core makes no additional guarantees for drivers. That is, Policy 1 in section [Driver requirements](#driver-requirements) applies. +Thread-safe drivers must not make any assumption about the operation of the core beyond what is discussed here. From f6f973c235dd179fa418324578e736496fe8b47f Mon Sep 17 00:00:00 2001 From: Ryan Everett Date: Thu, 14 Mar 2024 15:54:07 +0000 Subject: [PATCH 3/6] Document security weakness in concurrent execution of psa_destroy_key Signed-off-by: Ryan Everett --- include/psa/crypto.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/psa/crypto.h b/include/psa/crypto.h index 73889e0ddc..7083bd911b 100644 --- a/include/psa/crypto.h +++ b/include/psa/crypto.h @@ -527,6 +527,11 @@ psa_status_t psa_copy_key(mbedtls_svc_key_id_t source_key, * If a key is currently in use in a multipart operation, then destroying the * key will cause the multipart operation to fail. * + * \warning We can only guarantee that the the key material will + * eventually be wiped from memory. With threading enabled + * and during concurrent execution, copies of the key material may + * still exist until all threads have finished using the key. + * * \param key Identifier of the key to erase. If this is \c 0, do nothing and * return #PSA_SUCCESS. * From c408ef463ca37f3cc0c9af7b79142997841ef683 Mon Sep 17 00:00:00 2001 From: Ryan Everett Date: Fri, 15 Mar 2024 17:29:46 +0000 Subject: [PATCH 4/6] Update slot transition diagram Adds missing transition and italicises internal functions Signed-off-by: Ryan Everett --- .../key-slot-state-transitions.png | Bin 71568 -> 50367 bytes .../psa-thread-safety/psa-thread-safety.md | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/architecture/psa-thread-safety/key-slot-state-transitions.png b/docs/architecture/psa-thread-safety/key-slot-state-transitions.png index 1b50e7395d78051d20524568475128ea82228aa7..08e4cc080e8e14e4f27d661f47ca368d4eea6664 100644 GIT binary patch literal 50367 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 literal 71568 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 Km8ulY0{;) PSA_SLOT_EMPTY` transition can be done by any function which calls `psa_unregister_read`. +In the state transition diagram above, an arrow between two states `q1` and `q2` with label `f` indicates that if the state of a slot is `q1` immediately before `f`'s linearization point, it may be `q2` immediately after `f`'s linearization point. Internal functions have italicized labels. The `PSA_SLOT_PENDING_DELETION -> PSA_SLOT_EMPTY` transition can be done by any function which calls `psa_unregister_read`. -The state transition diagram can be generated in https://app.diagrams.net/ via this [url](https://viewer.diagrams.net/?tags=%7B%7D&highlight=0000ff&edit=_blank&layers=1&nav=1#R3VxLd5s4FP4ts%2FAyOUjiuazrJJPW7aQn7UzTjQ8F2VaLgQP4lV8%2FwjyMBMaCgEOaRQ8S0gV0rz599%2BGO0PvV7i4w%2FeUnz8bOCEr2boQmIwh1JNF%2F44590iFrKOlYBMROusCx45E847QznbdYExuHzMDI85yI%2BGyn5bkutiKmzwwCb8sOm3sO%2B1TfXOBSx6NlOuXe%2F4gdLdPPgtqx%2F29MFsvsyUA1kjsrMxucfkm4NG1vW%2BhCNyP0PvC8KLla7d5jJ167bF2Sebcn7uYvFmA3EpnwAzxP5%2F%2Fef9h%2BMVx3PH765f%2F6eJUqY2M66%2FSDR1B1qLyxTTb0chFf3n6bTrNuKr9wp2Lw0gxnATZtHITZ3Z8BP56XclifaJ8tOn1xql%2FaGG%2BXJMKPvmnFd7bUwuInRCuHtgC9NEM%2FUfqc7LCdiyouSrpOGxxEeFfoShfpDnsrHAV7OiS9K%2BupwlKLVaW0vS3oP%2B1aFlSf9ZmpxS1yyUel0ItULw10BECFkvgVc%2B13sbXTluWYYUgsdqECb%2B3a8QpNJNqiixPsv6e3Do2nYmOyK46b7LPWjkT5JHr9VOg%2FTokb2YyT2gi9dWDh83YZmcECRzXj5GQctpldXNZtQXdKhe6yvgA7ZkQ27N6vUmj6hAeP0C87aTqKytlE8t3prOJe5QVpxrVR%2BNMZsTLixCbLVBJ7sLZ8EV5ggLBkgH5oztZugBckjHBw2PBlm6RKmZo%2F6XnA7lmHLNzYTqlR4IB2xFuTUMR9l95YEduOZYwDHJJn8%2BdBXmxOfvyBh09WxiNlUmlg9TuIB4H83EifwkBzFThI11DSDEYbV%2FBltpOJAdwUbz4PcT%2F6FAH9m08PX5%2FeJoIPDsCNzgE8hWKpAMWgFopzyJeKkC%2Bdgfz2AA4FARwMC8BZyykBrTB%2BnzkHegbsjHJygD03iTP7jfczi%2BJ1RDx3yJCd7ZkhQnYqGTJC%2Bfn94Xf5OL69n07vP98NGoaR%2BsowLIsce418nb%2F%2BOGfn1ZWkiCjp4ebz5GDugnqa3Exvvt7%2F8%2Flt6mhwfEbri86Ie5btPNj2dCYLPZ2jM3BQdAZK9PDTZagB6lHKisqegoqsXmtA0aCuKbSBOPGiXAeoHGmSL0t2dBHEiOkP5SY42ODZPMD4QINCx4uEMSSWENJPiTgG1RZRBsO0st08RKKVi5Evxa1AFUvoHtyulTbeWjLrVf21YQXcYoCTpAzgVIpFLBSBOIyWIRzSJa0dxMkSB3HU1hFnkn3H4IRhbk5cEi4rUerNYFK%2BC18MSlfUQGRDY1EJdWN8eqXUC2BUFUnmdGmt6VFn55zoctkB8ZBUe7ASzQ6gQYGVrCmMwahay%2BiSonKCeKZ%2FAouoDZj7wrB0e55%2BYYOFPU2S6t8LcuPBmfGyXiefXiRv3HbzTMayBcid5TvPeH1PHpV3DzsxBzOhi0xyowFNpGs%2FM1175njW75xozr43kmE5XnhgqY1m%2BWuq%2BOKsN8xLc5Tr4gwASGdtrZsIIMt2oXKpEwBV5e07YalF%2BBZ0wQu0tN%2BcgiwI%2B8PKKUANMlaiKC1hH5yKA12IgqIq16gShmwcRoG3fzl8dQOVg4W4fBt34HvLuso6P9k2eKH5XrFcw%2BgB4irP6e6DjJW09hxaNcfF%2FvFtWEFGxLE%2BpW3SFGmcly1Ia7uyuLKHXRM1HAKm1O6bTliTwYWMO4IUkPn3fbrOlatzkUoM4egei0Q9RvdEHWZl0MiigrbIwtVjqP0V0FVaXfbeHLQwNGmwgNJZIUYMKNnUjjkKG9%2FroxCjWq8Xqsw9Dw1tYKg9nCiCcDIwR%2BxEeKoxnECOqKj9lXdVmx2sMLtK16hUotvArdoSv5wmfRvxpPrN2kWiE%2BisW9QRkAH0SkBWlV3iNNl5RqF1oGgoeMKFx1vH85GsXUNdV2QoG7IBkY54uUySEwqBTdNgP%2F2a4lOAwb6EJqFrDUr5H6r%2FImDUCgOgiTCl%2FZv1k0OoKOge7A8i6vd7B1goKTpL6jridH2UfDyQ9W46hzNdX3zZ%2FLj9aLmuX%2FlziG5IHFO90ernVb1RONGCtmHVeyCuEEPhQ0TCkKucCVp1R%2BEqbU6YwZGV7yU1Zw1TkC4OzKhFMN3zm8ftY5Zo44BsDtUmM28d%2Bevo7aUha9GhA6RUZFDpqL44nMYIvVipHHz9VHpzu%2FrDfmYlmjIQrYQ5ppOy3NSFamMMNiJcKiIWLtXjBIkmSY%2BCOt04H57k79uncPzNDvFXcm9f3c30HghHZqc5XWhiqWz6XqyGnjOkfjkIGhLl0Fp7eRzl0KCYZTZ15PiouSZ3W1VVadJlPlOL3q912guf4qxBn97FNQgKDLWb4x1xOTjO%2F2%2BDU7R5%2FI9HkuHH%2F70F3fwP), which encodes the graph. +The state transition diagram can be generated in https://app.diagrams.net/ via this [url](https://viewer.diagrams.net/?tags=%7B%7D&highlight=0000ff&edit=_blank&layers=1&nav=1#R3Vxbd5s4EP4t%2B%2BDH5CBxf6zrJJvW7aYn7W7dFx9qZFstBg7gW379CnMxkoUtY%2BGQ%2BiVISCPQjD59mhnSU98vNg%2BRE84%2FBS7yelBxNz110IMQAEsnf9KabVZjmHnFLMJu3mhf8YxfUF6p5LVL7KKYapgEgZfgkK6cBL6PJglV50RRsKabTQOPHjV0Zuig4nnieIe1%2F2E3mWe1FjT39X8jPJsXIwPDzu4snKJx%2Fibx3HGDdaVKveup76MgSLKrxeY98tLJK%2BYl63dfc7d8sAj5iUiHH%2BBlOP338cP6i%2B37%2Ff7oV%2Fjr442aSVk53jJ%2F4R40PCKv7%2BIVuZyll%2FffhsOimsiv3OE0njvxOEKOi6K4uPszYtuzUnbzk2yLSScPTvRLCv31HCfoOXQm6Z01MbF0hGThkRIgl04cZkqf4g1yS1HVScnnaYWiBG0qVfkkPaBggZJoS5rkdzUrV1hhsUpeXlf0n1fNK6ov6pzc4mal5L1SyEWulzN0BABHSeyM%2Be671NpJaeI5cYwn9ERFwdJ30xkaKKREJifafs9v7QqjamGwqbYbbIvSBidlJ3I9qtTvu6SFoketNuJgGU3QabtMnGiGkiPttKwdcqlVfKjbiu50ju6Kugh5ToJX9NrnKTQf4SnA5M1qTUc3GJvI3jvvVV2rrCDTvrUrP4sSq6mM2GyaDsTurK2chAsMENaiBC7WcBg746UfoRmOExTtEKCy2HH9UieaGzo%2Fya5BL2wPz%2FzUmInloIhUpOsXE1h%2Bl99YYNdNZfQjFOMX5%2BdOXmpzYToLu3nR%2Bz19wLXC48uMRYpyc8lHofCbhyDKLVRMm1LZDbzMwAoxgOkSTKcxakfpIjvD3aenr6O3CfOdQ3lbOsrneK1U8BocxetyXygLo2qhZl9ojvJQEOVBt1CetpwDNBYG%2BRObRcuoXvDSU6g%2BdbA3%2Fo224wkB9QQH%2FlvD9WJhdRHXc8mQEsr2bw%2FkDzf2%2B8fh8PHzQ6exWjVeGas1kb3xrFPTX3%2FcsenVlaSLKOnp7vNgZ%2B6CehrcDe%2B%2BPv7z%2BW3qqHOkx2yL84ifUZudhZtznsKJdYrzwE5xHqiQzc%2FSoAnI2VTTDXoX1DXj1gS6CS1TJwWVES9KiIDBMCvtuozIEkEMLkciZAVFKzSeRgjtuFLsBQmfJwkCDXeYmExAwuViXBw6OWpnOVuBC12kbKUY7VosDfD4hnyYvNWbHA6zXq96POyWEzCFSkUpoNIgqEaDGkhdewVWqpZiNgNLTWHAkti6yphk237B5oA5xT6O5wLHyjcGXOVSvRi5bogVabZJQ5cqx0ItrtQrABmPkzO6nCzJRuqWFOx6YQ1xN1lzRBMNa6idQjStiNmWMdyGHi%2FdYASxB4sawCI24GwrzfLlWf%2FANo2NpqIcfy7ItAcn2mvWMfnkInvipotn0NcmAD9MQu8FLR%2Fxs%2F7uaSN2nq1hpyejMpew0pqwTzNKKjYkMZKx47tjL5j8Lvn2%2BPtFA6VyJ14Q7wj8Wb3CJbHaaq%2BDwf8wel7iuIxdDqgWvZou5Oe5ZJr0Q%2F1ae5zKS6mQQtarG5SgT6PCztuN5GiCG1u3IjnQhJSV6HrDjQ3UOdauxMRV3gmRi1UuipMo2F6OcXLwtLMQVy5jCS4IzTLoM2CxDC403xuaTdktQByXicj32nKJ%2Bym0Oh8X28e3bnltVYbX6k1D1arJOBsEibssi6t3NDR1w3YBeI4uLinUymYc9ZJwBxRujjY9CNzZuUqSjLAnlIarFj2hon4DvdPwY4Cm8MOkyhjtJUByra547orZHXCpzgKKtPSXFFCKrpKJDO3mbCP9ha%2FXK2VWn4aGJjDUHE50QTjp2Gmtxkt3NpxAhs0Y7WXe8c0O1tKZhr42eZ61NQ4PqdPbdV8dX%2FYywsvlF05yIRGorwSJPKrNaFJ6iKaxX6oryMTEGxoHSFTNvIWWpWtQszUbqpbKyqVCy1AIts6NnpC3qY4CbPohTEW9NaFS%2FtTjbwTso8IAOEeY3vzJ2gnKcLP23%2FKnMcdBQQJgKrpFc0hJFLKNbJwnvNwMp3BsWbMvqx%2F3Hye%2BH3I%2FjJHDGanEmkZf47XGGEWzFruViqMyOTI667YSxmX9hCNNHmPk2pwQYUxxBi%2FCIEsRPMtPP0M%2BipykgYM%2FCM%2BPJaT00kURXu3yfsbBMgmX1DOfn1X9GlB5FB0kIKWuAe65%2BGLvHSX0almMsLMJDCeyCeScfv6wT%2FdEAyKimUz7YFkRebtSbpNNu7IPcs6F8zEZQaIh4L0gqUvww0j7vh7F%2FW9ujL7iR%2FfmYWy1QF0KOy2JxzmWSicnvP4nF93KumPJi9n4UMmQFxOKWea550bW3W9qcrPiuCZdz4yaJ4x1gVwcXb8SyAWwDTlsQmUijIxPogmYkeL%2B3%2BJkzff%2FXEi9%2Bx8%3D). ##### Key slot access primitives The state of a key slot is updated via the internal function `psa_key_slot_state_transition`. To change the state of `slot` from `expected_state` to `new_state`, when `new_state` is not `PSA_SLOT_EMPTY`, one must call `psa_key_slot_state_transition(slot, expected_state, new_state)`; if the state was not `expected_state` then `PSA_ERROR_CORRUPTION_DETECTED` is returned, this must not be a possibility in our code. The sole reason for having an expected state parameter here is to guarantee that our functions work as expected. From f266b51e3feebd8d88584eab54b82ebc7df5d1f3 Mon Sep 17 00:00:00 2001 From: Ryan Everett Date: Fri, 15 Mar 2024 17:30:31 +0000 Subject: [PATCH 5/6] Respond to feedback on psa-thread-safety.md A few typo fixes, extrapolations and extra details. Signed-off-by: Ryan Everett --- .../psa-thread-safety/psa-thread-safety.md | 68 +++++++++++-------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/docs/architecture/psa-thread-safety/psa-thread-safety.md b/docs/architecture/psa-thread-safety/psa-thread-safety.md index 58051fda12..d33e0a6c47 100644 --- a/docs/architecture/psa-thread-safety/psa-thread-safety.md +++ b/docs/architecture/psa-thread-safety/psa-thread-safety.md @@ -10,13 +10,13 @@ Summary of recent work: - Slot states are described in the [Key slot states](#key-slot-states) section. They guarantee safe concurrent access to slot contents. - Key slots are protected by a global mutex, as described in [Key store consistency and abstraction function](#key-store-consistency-and-abstraction-function). - Key destruction strategy abiding by [Key destruction guarantees](#key-destruction-guarantees), with an implementation discussed in [Key destruction implementation](#key-destruction-implementation). -- The main `global_data` (the one in `psa_crypto.c`) is protected by its own mutex as described in the [Global data](#global-data) section. +- `global_data` variables in `psa_crypto.c` and `psa_crypto_slot_management.c` are now protected by mutexes, as described in the [Global data](#global-data) section. - The testing system has now been made thread-safe. Tests can now spin up multiple threads, see [Thread-safe testing](#thread-safe-testing) for details. - Some multithreaded testing of the key management API has been added, this is outlined in [Testing-and-analysis](#testing-and-analysis). - The solution uses the pre-existing `MBEDTLS_THREADING_C` threading abstraction. - The core makes no additional guarantees for drivers. See [Driver policy](#driver-policy) for details. -The usage of keys within other PSA Crypto APIs is planned to be made thread-safe in future, but currently we are not testing this. +The other functions in the PSA Crypto API are planned to be made thread-safe in future, but currently we are not testing this. ## Overview of the document @@ -30,7 +30,7 @@ The usage of keys within other PSA Crypto APIs is planned to be made thread-safe *Concurrent calls* -The PSA specification defines concurrent calls as: "In some environments, an application can make calls to the Crypto API in separate threads. In such an environment, concurrent calls are two or more calls to the API whose execution can overlap in time." (https://arm-software.github.io/psa-api/crypto/1.1/overview/conventions.html#concurrent-calls). +The PSA specification defines concurrent calls as: "In some environments, an application can make calls to the Crypto API in separate threads. In such an environment, concurrent calls are two or more calls to the API whose execution can overlap in time." (See PSA documentation [here](https://arm-software.github.io/psa-api/crypto/1.1/overview/conventions.html#concurrent-calls).) *Thread-safety* @@ -56,7 +56,7 @@ These are the conventions which are planned to be added to the PSA 1.2 specifica > > * There is no overlap between an output parameter of one call and an input or output parameter of another call. Overlap between input parameters is permitted. > -> * A call to :code:`psa_destroy_key()` must not overlap with a concurrent call to any of the following functions: +> * A call to `psa_destroy_key()` must not overlap with a concurrent call to any of the following functions: > - Any call where the same key identifier is a parameter to the call. > - Any call in a multi-part operation, where the same key identifier was used as a parameter to a previous step in the multi-part operation. > @@ -67,9 +67,9 @@ These are the conventions which are planned to be added to the PSA 1.2 specifica > The consistency requirement does not apply to errors that arise from resource failures or limitations. For example, errors resulting from resource exhaustion can arise in concurrent execution that do not arise in sequential execution. > > As an example of this rule: suppose two calls are executed concurrently which both attempt to create a new key with the same key identifier that is not already in the key store. Then: -> * If one call returns :code:`PSA_ERROR_ALREADY_EXISTS`, then the other call must succeed. -> * If one of the calls succeeds, then the other must fail: either with :code:`PSA_ERROR_ALREADY_EXISTS` or some other error status. -> * Both calls can fail with error codes that are not :code:`PSA_ERROR_ALREADY_EXISTS`. +> * If one call returns `PSA_ERROR_ALREADY_EXISTS`, then the other call must succeed. +> * If one of the calls succeeds, then the other must fail: either with `PSA_ERROR_ALREADY_EXISTS` or some other error status. +> * Both calls can fail with error codes that are not `PSA_ERROR_ALREADY_EXISTS`. > > If the application concurrently modifies an input parameter while a function call is in progress, the behaviour is undefined. @@ -104,9 +104,9 @@ The core makes no additional guarantees for drivers. Driver entry points may be The PSA RNG can be accessed both from various PSA functions, and from application code via `mbedtls_psa_get_random`. -When using the built-in RNG implementations, i.e. when `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` is disabled, querying the RNG is thread-safe (`init` and `seed` are only thread-safe when called as part of `psa_crypto_init`, but not when called directly. `free` is not thread-safe). +When using the built-in RNG implementations, i.e. when `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` is disabled, querying the RNG is thread-safe (`mbedtls_psa_random_init` and `mbedtls_psa_random_seed` are only thread-safe when called while holding `mbedtls_threading_psa_rngdata_mutex`. `mbedtls_psa_random_free` is not thread-safe). -When `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` is enabled, thread-safety depends on the implementation. +When `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` is enabled, it is down to the external implementation to ensure thread-safety, should threading be enabled. ## Usage guide @@ -118,7 +118,7 @@ The PSA subsystem is initialized via a call to [`psa_crypto_init`](https://arm-s Once initialized, threads can use any PSA function if there is no overlap between their calls. All threads share the same set of keys, as soon as one thread returns from creating/loading a key via a key management API call the key can be used by any thread. If multiple threads attempt to load the same persistent key, with the same key identifier, only one thread can succeed - the others will return `PSA_ERROR_ALREADY_EXISTS`. -Applications may need careful handling of resource management errors. As explained in ([PSA Concurrent calling conventions](#psa-concurrent-calling-conventions)) operations in progress can have memory related side effects, it is possible for a lack of resources to cause errors which do not arise in sequential execution. For example, multiple threads attempting to load the same persistent key can lead to some threads returning `PSA_ERROR_INSUFFICIENT_MEMORY` if the key is not currently in the key store - while trying to load a persistent key into the key store a thread temporarily reserves a free key slot. +Applications may need careful handling of resource management errors. As explained in ([PSA Concurrent calling conventions](#psa-concurrent-calling-conventions)), operations in progress can have memory related side effects. It is possible for a lack of resources to cause errors which do not arise in sequential execution. For example, multiple threads attempting to load the same persistent key can lead to some threads returning `PSA_ERROR_INSUFFICIENT_MEMORY` if the key is not currently in the key store - while trying to load a persistent key into the key store a thread temporarily reserves a free key slot. If a mutex operation fails, which only happens if the mutex implementation fails, the error code `PSA_ERROR_SERVICE_FAILURE` will be returned. If this code is returned, execution of the PSA subsystem must be stopped. All functions which have internal mutex locks and unlocks (except for when the lock/unlock occurs in a function that has no return value) will return with this error code in this situation. @@ -143,8 +143,13 @@ There are two `psa_global_data_t` structs, each with a single instance `global_d * The struct in `library/psa_crypto.c` is protected by `mbedtls_threading_psa_globaldata_mutex`. The RNG fields within this struct are not protected by this mutex, and are not always thread-safe (see [Random number generators](#random-number-generators)). * The struct in `library/psa_crypto_slot_management.c` has two fields: `key_slots` is protected as described in [Key slots](#key-slots), `key_slots_initialized` is protected by the global data mutex. +#### Mutex usage + +A deadlock would occur if a thread attempts to lock a mutex while already holding it. Functions which need to be called while holding the global mutex have documentation to say this. + #### Key slots + Keys are stored internally in a global array of key slots known as the "key store", defined in `library/psa_slot_management.c`. ##### Key slot states @@ -166,7 +171,7 @@ In the state transition diagram above, an arrow between two states `q1` and `q2` The state transition diagram can be generated in https://app.diagrams.net/ via this [url](https://viewer.diagrams.net/?tags=%7B%7D&highlight=0000ff&edit=_blank&layers=1&nav=1#R3Vxbd5s4EP4t%2B%2BDH5CBxf6zrJJvW7aYn7W7dFx9qZFstBg7gW379CnMxkoUtY%2BGQ%2BiVISCPQjD59mhnSU98vNg%2BRE84%2FBS7yelBxNz110IMQAEsnf9KabVZjmHnFLMJu3mhf8YxfUF6p5LVL7KKYapgEgZfgkK6cBL6PJglV50RRsKabTQOPHjV0Zuig4nnieIe1%2F2E3mWe1FjT39X8jPJsXIwPDzu4snKJx%2Fibx3HGDdaVKveup76MgSLKrxeY98tLJK%2BYl63dfc7d8sAj5iUiHH%2BBlOP338cP6i%2B37%2Ff7oV%2Fjr442aSVk53jJ%2F4R40PCKv7%2BIVuZyll%2FffhsOimsiv3OE0njvxOEKOi6K4uPszYtuzUnbzk2yLSScPTvRLCv31HCfoOXQm6Z01MbF0hGThkRIgl04cZkqf4g1yS1HVScnnaYWiBG0qVfkkPaBggZJoS5rkdzUrV1hhsUpeXlf0n1fNK6ov6pzc4mal5L1SyEWulzN0BABHSeyM%2Be671NpJaeI5cYwn9ERFwdJ30xkaKKREJifafs9v7QqjamGwqbYbbIvSBidlJ3I9qtTvu6SFoketNuJgGU3QabtMnGiGkiPttKwdcqlVfKjbiu50ju6Kugh5ToJX9NrnKTQf4SnA5M1qTUc3GJvI3jvvVV2rrCDTvrUrP4sSq6mM2GyaDsTurK2chAsMENaiBC7WcBg746UfoRmOExTtEKCy2HH9UieaGzo%2Fya5BL2wPz%2FzUmInloIhUpOsXE1h%2Bl99YYNdNZfQjFOMX5%2BdOXmpzYToLu3nR%2Bz19wLXC48uMRYpyc8lHofCbhyDKLVRMm1LZDbzMwAoxgOkSTKcxakfpIjvD3aenr6O3CfOdQ3lbOsrneK1U8BocxetyXygLo2qhZl9ojvJQEOVBt1CetpwDNBYG%2BRObRcuoXvDSU6g%2BdbA3%2Fo224wkB9QQH%2FlvD9WJhdRHXc8mQEsr2bw%2FkDzf2%2B8fh8PHzQ6exWjVeGas1kb3xrFPTX3%2FcsenVlaSLKOnp7vNgZ%2B6CehrcDe%2B%2BPv7z%2BW3qqHOkx2yL84ifUZudhZtznsKJdYrzwE5xHqiQzc%2FSoAnI2VTTDXoX1DXj1gS6CS1TJwWVES9KiIDBMCvtuozIEkEMLkciZAVFKzSeRgjtuFLsBQmfJwkCDXeYmExAwuViXBw6OWpnOVuBC12kbKUY7VosDfD4hnyYvNWbHA6zXq96POyWEzCFSkUpoNIgqEaDGkhdewVWqpZiNgNLTWHAkti6yphk237B5oA5xT6O5wLHyjcGXOVSvRi5bogVabZJQ5cqx0ItrtQrABmPkzO6nCzJRuqWFOx6YQ1xN1lzRBMNa6idQjStiNmWMdyGHi%2FdYASxB4sawCI24GwrzfLlWf%2FANo2NpqIcfy7ItAcn2mvWMfnkInvipotn0NcmAD9MQu8FLR%2Fxs%2F7uaSN2nq1hpyejMpew0pqwTzNKKjYkMZKx47tjL5j8Lvn2%2BPtFA6VyJ14Q7wj8Wb3CJbHaaq%2BDwf8wel7iuIxdDqgWvZou5Oe5ZJr0Q%2F1ae5zKS6mQQtarG5SgT6PCztuN5GiCG1u3IjnQhJSV6HrDjQ3UOdauxMRV3gmRi1UuipMo2F6OcXLwtLMQVy5jCS4IzTLoM2CxDC403xuaTdktQByXicj32nKJ%2Bym0Oh8X28e3bnltVYbX6k1D1arJOBsEibssi6t3NDR1w3YBeI4uLinUymYc9ZJwBxRujjY9CNzZuUqSjLAnlIarFj2hon4DvdPwY4Cm8MOkyhjtJUByra547orZHXCpzgKKtPSXFFCKrpKJDO3mbCP9ha%2FXK2VWn4aGJjDUHE50QTjp2Gmtxkt3NpxAhs0Y7WXe8c0O1tKZhr42eZ61NQ4PqdPbdV8dX%2FYywsvlF05yIRGorwSJPKrNaFJ6iKaxX6oryMTEGxoHSFTNvIWWpWtQszUbqpbKyqVCy1AIts6NnpC3qY4CbPohTEW9NaFS%2FtTjbwTso8IAOEeY3vzJ2gnKcLP23%2FKnMcdBQQJgKrpFc0hJFLKNbJwnvNwMp3BsWbMvqx%2F3Hye%2BH3I%2FjJHDGanEmkZf47XGGEWzFruViqMyOTI667YSxmX9hCNNHmPk2pwQYUxxBi%2FCIEsRPMtPP0M%2BipykgYM%2FCM%2BPJaT00kURXu3yfsbBMgmX1DOfn1X9GlB5FB0kIKWuAe65%2BGLvHSX0almMsLMJDCeyCeScfv6wT%2FdEAyKimUz7YFkRebtSbpNNu7IPcs6F8zEZQaIh4L0gqUvww0j7vh7F%2FW9ujL7iR%2FfmYWy1QF0KOy2JxzmWSicnvP4nF93KumPJi9n4UMmQFxOKWea550bW3W9qcrPiuCZdz4yaJ4x1gVwcXb8SyAWwDTlsQmUijIxPogmYkeL%2B3%2BJkzff%2FXEi9%2Bx8%3D). ##### Key slot access primitives -The state of a key slot is updated via the internal function `psa_key_slot_state_transition`. To change the state of `slot` from `expected_state` to `new_state`, when `new_state` is not `PSA_SLOT_EMPTY`, one must call `psa_key_slot_state_transition(slot, expected_state, new_state)`; if the state was not `expected_state` then `PSA_ERROR_CORRUPTION_DETECTED` is returned, this must not be a possibility in our code. The sole reason for having an expected state parameter here is to guarantee that our functions work as expected. +The state of a key slot is updated via the internal function `psa_key_slot_state_transition`. To change the state of `slot` from `expected_state` to `new_state`, when `new_state` is not `PSA_SLOT_EMPTY`, one must call `psa_key_slot_state_transition(slot, expected_state, new_state)`; if the state was not `expected_state` then `PSA_ERROR_CORRUPTION_DETECTED` is returned. The sole reason for having an expected state parameter here is to help guarantee that our functions work as expected, this error code cannot occur without an internal coding error. Changing a slot's state to `PSA_SLOT_EMPTY` is done via `psa_wipe_key_slot`, this function wipes the entirety of the key slot. @@ -184,7 +189,7 @@ A thread can only traverse the key store while holding `mbedtls_threading_key_sl (slot->state == PSA_SLOT_FULL) && (slot->attr.id == k)]} -The union of this set and the set of persistent keys not currently loaded into slots is our abstraction function for the key store, any key not in this union does not currently exist (even if the key is in a slot which has a `PSA_SLOT_FILLING` or `PSA_SLOT_PENDING_DELETION` state). Attempting to start using any key which is not a member of the union will result in a `PSA_ERROR_INVALID_HANDLE` error code. +The union of this set and the set of persistent keys not currently loaded into slots is our abstraction function for the key store, any key not in this union does not currently exist as far as the code is concerned (even if the key is in a slot which has a `PSA_SLOT_FILLING` or `PSA_SLOT_PENDING_DELETION` state). Attempting to start using any key which is not a member of the union will result in a `PSA_ERROR_INVALID_HANDLE` error code. ##### Locking and unlocking the mutex @@ -211,35 +216,35 @@ If `psa_reserve_free_key_slot` cannot find a suitable slot, the key cannot be lo One-shot operations follow a standard pattern when using an existing key: -* They call some `psa_get_and_lock_key_slot_X` function, which finds the key and registers the thread as a reader. -* They operate on the key slot, usually copying the key into a separate buffer to be used by the operation. This step is not done under the mutex. +* They call one of the `psa_get_and_lock_key_slot_X` functions, which then finds the key and registers the thread as a reader. +* They operate on the key slot, usually copying the key into a separate buffer to be used by the operation. This step is not performed under the key slot mutex. * Once finished, they call `psa_unregister_read_under_mutex`. -Multi-part and restartable operations each have a "setup" function where the key is inputted. This function follows the above pattern. The key is copied into the `operation` object, and the thread unregisters. They do not access the key slots again. The copy of the key will not be destroyed during a call to `psa_destroy_key`, the thread running the operation is responsible for deleting this copy in the clean-up. This may need to change to enforce the long term key requirements ([Long term key destruction requirements](#long-term-key-destruction-requirements)). +Multi-part and restartable operations each have a "setup" function where the key is passed in, these functions follow the above pattern. The key is copied into the `operation` object, and the thread unregisters from reading the key (the operations do not access the key slots again). The copy of the key will not be destroyed during a call to `psa_destroy_key`, the thread running the operation is responsible for deleting its copy in the clean-up. This may need to change to enforce the long term key requirements ([Long term key destruction requirements](#long-term-key-destruction-requirements)). ##### Key destruction implementation The locking strategy here is explained in `library/psa_crypto.c`. The destroying thread (the thread calling `psa_destroy_key`) does not always wipe the key slot. The destroying thread registers to read the key, sets the slot's state to `PSA_SLOT_PENDING_DELETION`, wipes the slot from memory if the key is persistent, and then unregisters from reading the slot. -`psa_unregister_read` internally calls `psa_wipe_key_slot` iff the slot's state is `PSA_SLOT_PENDING_DELETION` and the slot's registered reader counter is equal to 1. This implements a "last one out closes the door" approach, where the final thread to unregister from reading a destroyed key will automatically wipe the contents of the slot; this ensure that there is no corruption. +`psa_unregister_read` internally calls `psa_wipe_key_slot` if and only if the slot's state is `PSA_SLOT_PENDING_DELETION` and the slot's registered reader counter is equal to 1. This implements a "last one out closes the door" approach. The final thread to unregister from reading a destroyed key will automatically wipe the contents of the slot; no readers remain to reference the slot post deletion, so there cannot be corruption. -### Linearisability of the system +### linearizability of the system -To satisfy the requirements in [Correctness out of the box](#correctness-out-of-the-box), we require our functions to be "linearisable" (under certain constraints). This means that any (constraint satisfying) set of concurrent calls are performed as if they were executed in some sequential order. +To satisfy the requirements in [Correctness out of the box](#correctness-out-of-the-box), we require our functions to be "linearizable" (under certain constraints). This means that any (constraint satisfying) set of concurrent calls are performed as if they were executed in some sequential order. The standard way of reasoning that this is the case is to identify a "linearization point" for each call, this is a single execution step where the function takes effect (this is usually a step in which the effects of the call become visible to other threads). If every call has a linearization point, the set of calls is equivalent to sequentially performing the calls in order of when their linearization point occurred. -We only require linearisability to hold in the case where a resource-management error is not returned. In a set of concurrent calls, it is permitted for a call c to fail with a `PSA_ERROR_INSUFFICIENT_MEMORY` return code even if there does not exist a sequential ordering of the calls in which c returns this error. Even if such an error occurs, all calls are still required to be functionally correct. +We only require linearizability to hold in the case where a resource-management error is not returned. In a set of concurrent calls, it is permitted for a call c to fail with a `PSA_ERROR_INSUFFICIENT_MEMORY` return code even if there does not exist a sequential ordering of the calls in which c returns this error. Even if such an error occurs, all calls are still required to be functionally correct. -To help justify that our system is linearisable, here are the linearization points/planned linearization points of each PSA call : +To help justify that our system is linearizable, here are the linearization points/planned linearization points of each PSA call : * Key creation functions (including `psa_copy_key`) - The linearization point for a successful call is the mutex unlock within `psa_finish_key_creation`; it is at this point that the key becomes visible to other threads. The linearization point for a failed call is the closest mutex unlock after the failure is first identified. * `psa_destroy_key` - The linearization point for a successful destruction is the mutex unlock, the slot is now in the state `PSA_SLOT_PENDING_DELETION` meaning that the key has been destroyed. For failures, the linearization point is the same. * `psa_purge_key`, `psa_close_key` - The linearization point is the mutex unlock after wiping the slot for a success, or unregistering for a failure. -* One shot operations - The linearization point is the final unlock of the mutex within `psa_get_and_lock_key_slot`, as that is the point in which it is decided whether the key exists. +* One shot operations - The linearization point is the final unlock of the mutex within `psa_get_and_lock_key_slot`, as that is the point in which it is decided whether or not the key exists. * Multi-part operations - The linearization point of the key input function is the final unlock of the mutex within `psa_get_and_lock_key_slot`. All other steps have no non resource-related side effects (except for key derivation, covered in the key creation functions). -Please note that one shot operations and multi-part operations are not yet considered thread-safe, as we do not test whether they rely on unprotected global resources. +Please note that one shot operations and multi-part operations are not yet considered thread-safe, as we have not yet tested whether they rely on unprotected global resources. The key slot access in these operations is thread-safe. ## Testing and analysis @@ -247,19 +252,19 @@ Please note that one shot operations and multi-part operations are not yet consi It is now possible for individual tests to spin up multiple threads. This work has made the global variables used in tests thread-safe. If multiple threads fail a test assert, the first failure will be reported with correct line numbers. -The `step` feature used in some tests is not thread-safe, it cannot be made thread-safe unless we introduce thread-local variables. +Although the `step` feature used in some tests is thread-safe, it may produce unexpected results for multi-threaded tests. `mbedtls_test_set_step` or `mbedtls_test_increment_step` calls within threads can happen in any order, thus may not produce the desired result when precise ordering is required. ### Current state of testing -Our testing is a work in progress. It is not feasible to run our traditional, single-threaded, tests in such a way that tests concurrency. Therefore, we must write a new suite of testing. +Our testing is a work in progress. It is not feasible to run our traditional, single-threaded, tests in such a way that tests concurrency. We need to write new test suites for concurrency testing. -Our tests currently only run on pthread. +Our tests currently only run on pthread, we hope to expand this in the future (our API already allows this). -We run tests using [ThreadSanitizer](https://clang.llvm.org/docs/ThreadSanitizer.html) to detect data races. We test the key store, and test that our key slot state system is enforced. +We run tests using [ThreadSanitizer](https://clang.llvm.org/docs/ThreadSanitizer.html) to detect data races. We test the key store, and test that our key slot state system is enforced. We also test the thread-safety of `psa_crypto_init`. -Currently, not every API call is tested, we also cannot feasibly test every combination of concurrent API calls. API calls can in general be split into a few categories, each category calling the same internal functions in the same order - it is the internal functions that are in charge of locking mutexes and interacting with the key store; we have tests which cover every category. +Currently, not every API call is tested, we also cannot feasibly test every combination of concurrent API calls. API calls can in general be split into a few categories, each category calling the same internal key management functions in the same order - it is the internal functions that are in charge of locking mutexes and interacting with the key store; we test the thread-safety of these functions. -Since we do not run every cryptographic operation concurrently, we do not test that operations are free of unexpected global variables, cryptographic operations are not considered thread-safe. +Since we do not run every cryptographic operation concurrently, we do not test that operations are free of unexpected global variables. ### Expanding testing @@ -268,7 +273,8 @@ Through future work on testing, it would be good to: * For every API call, have a test which runs multiple copies of the call simultaneously. * After implementing other threading platforms, expand the tests to these platforms. * Have increased testing for kicking persistent keys out of slots. -* Explicitly test that there all global variables are protected, for this we need to cover every operation in a concurrent scenario while running ThreadSanitizer. +* Explicitly test that all global variables are protected, for this we would need to cover every operation in a concurrent scenario while running ThreadSanitizer. +* Run tests on more threading implementations, once these implementations are supported. ### Performance @@ -285,7 +291,9 @@ As explained previously, we eventually aim to make the entirety of the PSA API t ### Long term performance requirements -Our plan for cryptographic operations is that they are not performed under any global mutex. One-shot operations and multi-part operations will each only hold the global mutex for finding the relevant key in the key slot, and unregistering as a reader after the operation. +Our plan for cryptographic operations is that they are not performed under any global mutex. One-shot operations and multi-part operations will each only hold the global mutex for finding the relevant key in the key slot, and unregistering as a reader after the operation, using their own operation-specific mutexes to guard any shared data that they use. + +We aim to eventually replace some/all of the mutexes with RWLocks, if possible. ### Long term key destruction requirements From 765b75f2f8622a7f33021d3696d31104e4387333 Mon Sep 17 00:00:00 2001 From: Ryan Everett Date: Mon, 18 Mar 2024 10:20:43 +0000 Subject: [PATCH 6/6] Update docs/architecture/psa-thread-safety/psa-thread-safety.md Co-authored-by: Paul Elliott <62069445+paul-elliott-arm@users.noreply.github.com> Signed-off-by: Ryan Everett --- docs/architecture/psa-thread-safety/psa-thread-safety.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/architecture/psa-thread-safety/psa-thread-safety.md b/docs/architecture/psa-thread-safety/psa-thread-safety.md index d33e0a6c47..edb94c56ba 100644 --- a/docs/architecture/psa-thread-safety/psa-thread-safety.md +++ b/docs/architecture/psa-thread-safety/psa-thread-safety.md @@ -147,6 +147,8 @@ There are two `psa_global_data_t` structs, each with a single instance `global_d A deadlock would occur if a thread attempts to lock a mutex while already holding it. Functions which need to be called while holding the global mutex have documentation to say this. +To avoid performance degradation, functions must hold mutexes for as short a time as possible. In particular, they must not start expensive operations (eg. doing cryptography) while holding the mutex. + #### Key slots