Squashed 'lib/mbedtls/external/mbedtls/' content from commit 2ca6c285a0dd

git-subtree-dir: lib/mbedtls/external/mbedtls
git-subtree-split: 2ca6c285a0dd3f33982dd57299012dacab1ff206
diff --git a/docs/architecture/Makefile b/docs/architecture/Makefile
new file mode 100644
index 0000000..5bee504
--- /dev/null
+++ b/docs/architecture/Makefile
@@ -0,0 +1,21 @@
+PANDOC = pandoc
+
+default: all
+
+all_markdown = $(wildcard *.md */*.md)
+
+html: $(all_markdown:.md=.html)
+pdf: $(all_markdown:.md=.pdf)
+all: html pdf
+
+.SUFFIXES:
+.SUFFIXES: .md .html .pdf
+
+.md.html:
+	$(PANDOC) -o $@ $<
+.md.pdf:
+	$(PANDOC) -o $@ $<
+
+clean:
+	rm -f *.html *.pdf
+	rm -f testing/*.html testing/*.pdf
diff --git a/docs/architecture/alternative-implementations.md b/docs/architecture/alternative-implementations.md
new file mode 100644
index 0000000..eacdea7
--- /dev/null
+++ b/docs/architecture/alternative-implementations.md
@@ -0,0 +1,91 @@
+Alternative implementations of Mbed TLS functionality
+=====================================================
+
+This document describes how parts of the Mbed TLS functionality can be replaced at compile time to integrate the library on a platform.
+
+This document is an overview. It is not exhaustive. Please consult the documentation of individual modules and read the library header files for more details.
+
+## Platform integration
+
+Mbed TLS works out of the box on Unix/Linux/POSIX-like systems and on Windows. On embedded platforms, you may need to customize some aspects of how Mbed TLS interacts with the underlying platform. This section discusses the main areas that can be configured.
+
+The platform module (`include/mbedtls/platform.h`) controls how Mbed TLS accesses standard library features such as memory management (`calloc`, `free`), `printf`, `exit`. You can define custom functions instead of the ones from the C standard library through `MBEDTLS_PLATFORM_XXX` options in the configuration file. Many options have two mechanisms: either define `MBEDTLS_PLATFORM_XXX_MACRO` to the name of a function to call instead of the standard function `xxx`, or define `MBEDTLS_PLATFORM_XXX_ALT` and [register an alternative implementation during the platform setup](#alternative-implementations-of-platform-functions).
+
+The storage of the non-volatile seed for random generation, enabled with `MBEDTLS_ENTROPY_NV_SEED`, is also controlled via the platform module.
+
+For timing functions, you can [declare an alternative implementation of the timing module](#module-alternative-implementations).
+
+On multithreaded platforms, [declare an alternative implementation of the threading module](#module-alternative-implementations).
+
+To configure entropy sources (hardware random generators), see the `MBEDTLS_ENTROPY_XXX` options in the configuration file.
+
+For networking, the `net_sockets` module does not currently support alternative implementations. If this module does not work on your platform, disable `MBEDTLS_NET_C` and use custom functions for TLS.
+
+If your platform has a cryptographic accelerator, you can use it via a [PSA driver](#psa-cryptography-drivers) or declare an [alternative implementation of the corresponding module(s)](#module-alternative-implementations) or [of specific functions](#function-alternative-implementations). PSA drivers will ultimately replace the alternative implementation mechanism, but alternative implementation will remain supported in at least all Mbed TLS versions of the form 3.x. The interface of PSA drivers is currently still experimental and subject to change.
+
+## PSA cryptography drivers
+
+On platforms where a hardware cryptographic engine is present, you can implement a driver for this engine in the PSA interface. Drivers are supported for cryptographic operations with transparent keys (keys available in cleartext), for cryptographic operations with opaque keys (keys that are only available inside the cryptographic engine), and for random generation. Calls to `psa_xxx` functions that perform cryptographic operations are directed to drivers instead of the built-in code as applicable. See the [PSA cryptography driver interface specification](docs/proposed/psa-driver-interface.md), the [Mbed TLS PSA driver developer guide](docs/proposed/psa-driver-developer-guide.md) and the [Mbed TLS PSA driver integration guide](docs/proposed/psa-driver-integration-guide.md) for more information.
+
+As of Mbed TLS 3.0, this interface is still experimental and subject to change, and not all operations support drivers yet. The configuration option `MBEDTLS_USE_PSA_CRYPTO` causes parts of the `mbedtls_xxx` API to use PSA crypto and therefore to support drivers, however it is not yet compatible with all drivers.
+
+## Module alternative implementations
+
+You can replace the code of some modules of Mbed TLS at compile time by a custom implementation. This is possible for low-level cryptography modules (symmetric algorithms, DHM, RSA, ECP, ECJPAKE) and for some platform-related modules (threading, timing). Such custom implementations are called “alternative implementations”, or “ALT implementations” for short.
+
+The general principle of an alternative implementation is:
+* Enable `MBEDTLS_XXX_ALT` in the compile-time configuration where XXX is the module name. For example, `MBEDTLS_AES_ALT` for an implementation of the AES module. This is in addition to enabling `MBEDTLS_XXX_C`.
+* Create a header file `xxx_alt.h` that defines the context type(s) used by the module. For example, `mbedtls_aes_context` for AES.
+* Implement all the functions from the module, i.e. the functions declared in `include/mbedtls/xxx.h`.
+
+See https://mbed-tls.readthedocs.io/en/latest/kb/development/hw_acc_guidelines for a more detailed guide.
+
+### Constraints on context types
+
+Generally, alternative implementations can define their context types to any C type except incomplete and array types (although they would normally be `struct` types). This section lists some known limitations where the context type needs to be a structure with certain fields.
+
+Where a context type needs to have a certain field, the field must have the same type and semantics as in the built-in implementation, but does not need to be at the same position in the structure. Furthermore, unless otherwise indicated, only read access is necessary: the field can be `const`, and modifications to it do not need to be supported. For example, if an alternative implementation of asymmetric cryptography uses a different representation of large integers, it is sufficient to provide a read-only copy of the fields listed here of type `mbedtls_mpi`.
+
+* AES: if `MBEDTLS_AESNI_C` or `MBEDTLS_PADLOCK_C` is enabled, `mbedtls_aes_context` must have the fields `nr` and `rk`.
+* DHM: if `MBEDTLS_DEBUG_C` is enabled, `mbedtls_dhm_context` must have the fields `P`, `Q`, `G`, `GX`, `GY` and `K`.
+* ECP: `mbedtls_ecp_group` must have the fields `id`, `P`, `A`, `B`, `G`, `N`, `pbits` and `nbits`.
+    * If `MBEDTLS_PK_PARSE_EC_EXTENDED` is enabled, those fields must be writable, and `mbedtls_ecp_point_read_binary()` must support a group structure where only `P`, `pbits`, `A` and `B` are set.
+
+It must be possible to move a context object in memory (except during the execution of a library function that takes this context as an argument). (This is necessary, for example, to support applications that populate a context on the stack of an inner function and then copy the context upwards through the call chain, or applications written in a language with automatic memory management that can move objects on the heap.) That is, call sequences like the following must work:
+```
+mbedtls_xxx_context ctx1, ctx2;
+mbedtls_xxx_init(&ctx1);
+mbedtls_xxx_setup(&ctx1, …);
+ctx2 = ctx1;
+memset(&ctx1, 0, sizeof(ctx1));
+mbedtls_xxx_do_stuff(&ctx2, …);
+mbedtls_xxx_free(&ctx2);
+```
+In practice, this means that a pointer to a context or to a part of a context does not remain valid across function calls. Alternative implementations do not need to support copying of contexts: contexts can only be cloned through explicit `clone()` functions.
+
+## Function alternative implementations
+
+In some cases, it is possible to replace a single function or a small set of functions instead of [providing an alternative implementation of the whole module](#module-alternative-implementations).
+
+### Alternative implementations of cryptographic functions
+
+Options to replace individual functions of cryptographic modules generally have a name obtained by upper-casing the function name and appending `_ALT`. If the function name contains `_internal`, `_ext` or `_ret`, this is removed in the `_ALT` symbol. When the corresponding option is enabled, the built-in implementation of the function will not be compiled, and you must provide an alternative implementation at link time.
+
+For example, enable `MBEDTLS_AES_ENCRYPT_ALT` at compile time and provide your own implementation of `mbedtls_aes_encrypt()` to provide an accelerated implementation of AES encryption that is compatible with the built-in key schedule. If you wish to implement key schedule differently, you can also enable `MBEDTLS_AES_SETKEY_ENC_ALT` and implement `mbedtls_aes_setkey_enc()`.
+
+Another example: enable `MBEDTLS_SHA256_PROCESS_ALT` and implement `mbedtls_internal_sha256_process()` to provide an accelerated implementation of SHA-256 and SHA-224.
+
+Note that since alternative implementations of individual functions cooperate with the built-in implementation of other functions, you must use the same layout for context objects as the built-in implementation. If you want to use different context types, you need to [provide an alternative implementation of the whole module](#module-alternative-implementations).
+
+### Alternative implementations of platform functions
+
+Several platform functions can be reconfigured dynamically by following the process described here. To reconfigure how Mbed TLS calls the standard library function `xxx()`:
+
+* Define the symbol `MBEDTLS_PLATFORM_XXX_ALT` at compile time.
+* During the initialization of your application, set the global variable `mbedtls_xxx` to an alternative implementation of `xxx()`.
+
+For example, to provide a custom `printf` function at run time, enable `MBEDTLS_PLATFORM_PRINTF_ALT` at compile time and assign to `mbedtls_printf` during the initialization of your application.
+
+Merely enabling `MBEDTLS_PLATFORM_XXX_ALT` does not change the behavior: by default, `mbedtls_xxx` points to the standard function `xxx`.
+
+Note that there are variations on the naming pattern. For example, some configurable functions are activated in pairs, such as `mbedtls_calloc` and `mbedtls_free` via `MBEDTLS_PLATFORM_MEMORY`. Consult the documentation of individual configuration options and of the platform module for details.
diff --git a/docs/architecture/mbed-crypto-storage-specification.md b/docs/architecture/mbed-crypto-storage-specification.md
new file mode 100644
index 0000000..3c8370d
--- /dev/null
+++ b/docs/architecture/mbed-crypto-storage-specification.md
@@ -0,0 +1,467 @@
+Mbed TLS storage specification
+=================================
+
+This document specifies how Mbed TLS uses storage.
+Key storage was originally introduced in a product called Mbed Crypto, which was re-distributed via Mbed TLS and has since been merged into Mbed TLS.
+This document contains historical information both from before and after this merge.
+
+Mbed Crypto may be upgraded on an existing device with the storage preserved. Therefore:
+
+1. Any change may break existing installations and may require an upgrade path.
+1. This document retains historical information about all past released versions. Do not remove information from this document unless it has always been incorrect or it is about a version that you are sure was never released.
+
+Mbed Crypto 0.1.0
+-----------------
+
+Tags: mbedcrypto-0.1.0b, mbedcrypto-0.1.0b2
+
+Released in November 2018. <br>
+Integrated in Mbed OS 5.11.
+
+Supported backends:
+
+* [PSA ITS](#file-namespace-on-its-for-0.1.0)
+* [C stdio](#file-namespace-on-stdio-for-0.1.0)
+
+Supported features:
+
+* [Persistent transparent keys](#key-file-format-for-0.1.0) designated by a [slot number](#key-names-for-0.1.0).
+* [Nonvolatile random seed](#nonvolatile-random-seed-file-format-for-0.1.0) on ITS only.
+
+This is a beta release, and we do not promise backward compatibility, with one exception:
+
+> On Mbed OS, if a device has a nonvolatile random seed file produced with Mbed OS 5.11.x and is upgraded to a later version of Mbed OS, the nonvolatile random seed file is preserved or upgraded.
+
+We do not make any promises regarding key storage, or regarding the nonvolatile random seed file on other platforms.
+
+### Key names for 0.1.0
+
+Information about each key is stored in a dedicated file whose name is constructed from the key identifier. The way in which the file name is constructed depends on the storage backend. The content of the file is described [below](#key-file-format-for-0.1.0).
+
+The valid values for a key identifier are the range from 1 to 0xfffeffff. This limitation on the range is not documented in user-facing documentation: according to the user-facing documentation, arbitrary 32-bit values are valid.
+
+The code uses the following constant in an internal header (note that despite the name, this value is actually one plus the maximum permitted value):
+
+    #define PSA_MAX_PERSISTENT_KEY_IDENTIFIER 0xffff0000
+
+There is a shared namespace for all callers.
+
+### Key file format for 0.1.0
+
+All integers are encoded in little-endian order in 8-bit bytes.
+
+The layout of a key file is:
+
+* magic (8 bytes): `"PSA\0KEY\0"`
+* version (4 bytes): 0
+* type (4 bytes): `psa_key_type_t` value
+* policy usage flags (4 bytes): `psa_key_usage_t` value
+* policy usage algorithm (4 bytes): `psa_algorithm_t` value
+* key material length (4 bytes)
+* key material: output of `psa_export_key`
+* Any trailing data is rejected on load.
+
+### Nonvolatile random seed file format for 0.1.0
+
+The nonvolatile random seed file contains a seed for the random generator. If present, it is rewritten at each boot as part of the random generator initialization.
+
+The file format is just the seed as a byte string with no metadata or encoding of any kind.
+
+### File namespace on ITS for 0.1.0
+
+Assumption: ITS provides a 32-bit file identifier namespace. The Crypto service can use arbitrary file identifiers and no other part of the system accesses the same file identifier namespace.
+
+* File 0: unused.
+* Files 1 through 0xfffeffff: [content](#key-file-format-for-0.1.0) of the [key whose identifier is the file identifier](#key-names-for-0.1.0).
+* File 0xffffff52 (`PSA_CRYPTO_ITS_RANDOM_SEED_UID`): [nonvolatile random seed](#nonvolatile-random-seed-file-format-for-0.1.0).
+* Files 0xffff0000 through 0xffffff51, 0xffffff53 through 0xffffffff: unused.
+
+### File namespace on stdio for 0.1.0
+
+Assumption: C stdio, allowing names containing lowercase letters, digits and underscores, of length up to 23.
+
+An undocumented build-time configuration value `CRYPTO_STORAGE_FILE_LOCATION` allows storing the key files in a directory other than the current directory. This value is simply prepended to the file name (so it must end with a directory separator to put the keys in a different directory).
+
+* `CRYPTO_STORAGE_FILE_LOCATION "psa_key_slot_0"`: used as a temporary file. Must be writable. May be overwritten or deleted if present.
+* `sprintf(CRYPTO_STORAGE_FILE_LOCATION "psa_key_slot_%lu", key_id)` [content](#key-file-format-for-0.1.0) of the [key whose identifier](#key-names-for-0.1.0) is `key_id`.
+* Other files: unused.
+
+Mbed Crypto 1.0.0
+-----------------
+
+Tags: mbedcrypto-1.0.0d4, mbedcrypto-1.0.0
+
+Released in February 2019. <br>
+Integrated in Mbed OS 5.12.
+
+Supported integrations:
+
+* [PSA platform](#file-namespace-on-a-psa-platform-for-1.0.0)
+* [library using PSA ITS](#file-namespace-on-its-as-a-library-for-1.0.0)
+* [library using C stdio](#file-namespace-on-stdio-for-1.0.0)
+
+Supported features:
+
+* [Persistent transparent keys](#key-file-format-for-1.0.0) designated by a [key identifier and owner](#key-names-for-1.0.0).
+* [Nonvolatile random seed](#nonvolatile-random-seed-file-format-for-1.0.0) on ITS only.
+
+Backward compatibility commitments: TBD
+
+### Key names for 1.0.0
+
+Information about each key is stored in a dedicated file designated by the key identifier. In integrations where there is no concept of key owner (in particular, in library integrations), the key identifier is exactly the key identifier as defined in the PSA Cryptography API specification (`psa_key_id_t`). In integrations where there is a concept of key owner (integration into a service for example), the key identifier is made of an owner identifier (its semantics and type are integration specific) and of the key identifier (`psa_key_id_t`) from the key owner point of view.
+
+The way in which the file name is constructed from the key identifier depends on the storage backend. The content of the file is described [below](#key-file-format-for-1.0.0).
+
+* Library integration: the key file name is just the key identifier as defined in the PSA crypto specification. This is a 32-bit value.
+* PSA service integration: the key file name is `(uint64_t)owner_uid << 32 | key_id` where `key_id` is the key identifier from the owner point of view and `owner_uid` (of type `int32_t`) is the calling partition identifier provided to the server by the partition manager. This is a 64-bit value.
+
+### Key file format for 1.0.0
+
+The layout is identical to [0.1.0](#key-file-format-for-0.1.0) so far. However note that the encoding of key types, algorithms and key material has changed, therefore the storage format is not compatible (despite using the same value in the version field so far).
+
+### Nonvolatile random seed file format for 1.0.0
+
+The nonvolatile random seed file contains a seed for the random generator. If present, it is rewritten at each boot as part of the random generator initialization.
+
+The file format is just the seed as a byte string with no metadata or encoding of any kind.
+
+This is unchanged since [the feature was introduced in Mbed Crypto 0.1.0](#nonvolatile-random-seed-file-format-for-0.1.0).
+
+### File namespace on a PSA platform for 1.0.0
+
+Assumption: ITS provides a 64-bit file identifier namespace. The Crypto service can use arbitrary file identifiers and no other part of the system accesses the same file identifier namespace.
+
+Assumption: the owner identifier is a nonzero value of type `int32_t`.
+
+* Files 0 through 0xffffff51, 0xffffff53 through 0xffffffff: unused, reserved for internal use of the crypto library or crypto service.
+* File 0xffffff52 (`PSA_CRYPTO_ITS_RANDOM_SEED_UID`): [nonvolatile random seed](#nonvolatile-random-seed-file-format-for-0.1.0).
+* Files 0x100000000 through 0xffffffffffff: [content](#key-file-format-for-1.0.0) of the [key whose identifier is the file identifier](#key-names-for-1.0.0). The upper 32 bits determine the owner.
+
+### File namespace on ITS as a library for 1.0.0
+
+Assumption: ITS provides a 64-bit file identifier namespace. The entity using the crypto library can use arbitrary file identifiers and no other part of the system accesses the same file identifier namespace.
+
+This is a library integration, so there is no owner. The key file identifier is identical to the key identifier.
+
+* File 0: unused.
+* Files 1 through 0xfffeffff: [content](#key-file-format-for-1.0.0) of the [key whose identifier is the file identifier](#key-names-for-1.0.0).
+* File 0xffffff52 (`PSA_CRYPTO_ITS_RANDOM_SEED_UID`): [nonvolatile random seed](#nonvolatile-random-seed-file-format-for-1.0.0).
+* Files 0xffff0000 through 0xffffff51, 0xffffff53 through 0xffffffff, 0x100000000 through 0xffffffffffffffff: unused.
+
+### File namespace on stdio for 1.0.0
+
+This is a library integration, so there is no owner. The key file identifier is identical to the key identifier.
+
+[Identical to 0.1.0](#file-namespace-on-stdio-for-0.1.0).
+
+### Upgrade from 0.1.0 to 1.0.0.
+
+* Delete files 1 through 0xfffeffff, which contain keys in a format that is no longer supported.
+
+### Suggested changes to make before 1.0.0
+
+The library integration and the PSA platform integration use different sets of file names. This is annoyingly non-uniform. For example, if we want to store non-key files, we have room in different ranges (0 through 0xffffffff on a PSA platform, 0xffff0000 through 0xffffffffffffffff in a library integration).
+
+It would simplify things to always have a 32-bit owner, with a nonzero value, and thus reserve the range 0–0xffffffff for internal library use.
+
+Mbed Crypto 1.1.0
+-----------------
+
+Tags: mbedcrypto-1.1.0
+
+Released in early June 2019. <br>
+Integrated in Mbed OS 5.13.
+
+Changes since [1.0.0](#mbed-crypto-1.0.0):
+
+* The stdio backend for storage has been replaced by an implementation of [PSA ITS over stdio](#file-namespace-on-stdio-for-1.1.0).
+* [Some changes in the key file format](#key-file-format-for-1.1.0).
+
+### File namespace on stdio for 1.1.0
+
+Assumption: C stdio, allowing names containing lowercase letters, digits and underscores, of length up to 23.
+
+An undocumented build-time configuration value `PSA_ITS_STORAGE_PREFIX` allows storing the key files in a directory other than the current directory. This value is simply prepended to the file name (so it must end with a directory separator to put the keys in a different directory).
+
+* `PSA_ITS_STORAGE_PREFIX "tempfile.psa_its"`: used as a temporary file. Must be writable. May be overwritten or deleted if present.
+* `sprintf(PSA_ITS_STORAGE_PREFIX "%016llx.psa_its", key_id)`: a key or non-key file. The `key_id` in the name is the 64-bit file identifier, which is the [key identifier](#key-names-for-mbed-tls-2.25.0) for a key file or some reserved identifier for a non-key file (currently: only the [nonvolatile random seed](#nonvolatile-random-seed-file-format-for-1.0.0)). The contents of the file are:
+    * Magic header (8 bytes): `"PSA\0ITS\0"`
+    * File contents.
+
+### Key file format for 1.1.0
+
+The key file format is identical to [1.0.0](#key-file-format-for-1.0.0), except for the following changes:
+
+* A new policy field, marked as [NEW:1.1.0] below.
+* The encoding of key types, algorithms and key material has changed, therefore the storage format is not compatible (despite using the same value in the version field so far).
+
+A self-contained description of the file layout follows.
+
+All integers are encoded in little-endian order in 8-bit bytes.
+
+The layout of a key file is:
+
+* magic (8 bytes): `"PSA\0KEY\0"`
+* version (4 bytes): 0
+* type (4 bytes): `psa_key_type_t` value
+* policy usage flags (4 bytes): `psa_key_usage_t` value
+* policy usage algorithm (4 bytes): `psa_algorithm_t` value
+* policy enrollment algorithm (4 bytes): `psa_algorithm_t` value [NEW:1.1.0]
+* key material length (4 bytes)
+* key material: output of `psa_export_key`
+* Any trailing data is rejected on load.
+
+Mbed Crypto TBD
+---------------
+
+Tags: TBD
+
+Released in TBD 2019. <br>
+Integrated in Mbed OS TBD.
+
+### Changes introduced in TBD
+
+* The layout of a key file now has a lifetime field before the type field.
+* Key files can store references to keys in a secure element. In such key files, the key material contains the slot number.
+
+### File namespace on a PSA platform on TBD
+
+Assumption: ITS provides a 64-bit file identifier namespace. The Crypto service can use arbitrary file identifiers and no other part of the system accesses the same file identifier namespace.
+
+Assumption: the owner identifier is a nonzero value of type `int32_t`.
+
+* Files 0 through 0xfffeffff: unused.
+* Files 0xffff0000 through 0xffffffff: reserved for internal use of the crypto library or crypto service. See [non-key files](#non-key-files-on-tbd).
+* Files 0x100000000 through 0xffffffffffff: [content](#key-file-format-for-1.0.0) of the [key whose identifier is the file identifier](#key-names-for-1.0.0). The upper 32 bits determine the owner.
+
+### File namespace on ITS as a library on TBD
+
+Assumption: ITS provides a 64-bit file identifier namespace. The entity using the crypto library can use arbitrary file identifiers and no other part of the system accesses the same file identifier namespace.
+
+This is a library integration, so there is no owner. The key file identifier is identical to the key identifier.
+
+* File 0: unused.
+* Files 1 through 0xfffeffff: [content](#key-file-format-for-1.0.0) of the [key whose identifier is the file identifier](#key-names-for-1.0.0).
+* Files 0xffff0000 through 0xffffffff: reserved for internal use of the crypto library or crypto service. See [non-key files](#non-key-files-on-tbd).
+* Files 0x100000000 through 0xffffffffffffffff: unused.
+
+### Non-key files on TBD
+
+File identifiers in the range 0xffff0000 through 0xffffffff are reserved for internal use in Mbed Crypto.
+
+* Files 0xfffffe02 through 0xfffffeff (`PSA_CRYPTO_SE_DRIVER_ITS_UID_BASE + lifetime`): secure element driver storage. The content of the file is the secure element driver's persistent data.
+* File 0xffffff52 (`PSA_CRYPTO_ITS_RANDOM_SEED_UID`): [nonvolatile random seed](#nonvolatile-random-seed-file-format-for-1.0.0).
+* File 0xffffff54 (`PSA_CRYPTO_ITS_TRANSACTION_UID`): [transaction file](#transaction-file-format-for-tbd).
+* Other files are unused and reserved for future use.
+
+### Key file format for TBD
+
+All integers are encoded in little-endian order in 8-bit bytes except where otherwise indicated.
+
+The layout of a key file is:
+
+* magic (8 bytes): `"PSA\0KEY\0"`.
+* version (4 bytes): 0.
+* lifetime (4 bytes): `psa_key_lifetime_t` value.
+* type (4 bytes): `psa_key_type_t` value.
+* policy usage flags (4 bytes): `psa_key_usage_t` value.
+* policy usage algorithm (4 bytes): `psa_algorithm_t` value.
+* policy enrollment algorithm (4 bytes): `psa_algorithm_t` value.
+* key material length (4 bytes).
+* key material:
+    * For a transparent key: output of `psa_export_key`.
+    * For an opaque key (unified driver interface): driver-specific opaque key blob.
+    * For an opaque key (key in a secure element): slot number (8 bytes), in platform endianness.
+* Any trailing data is rejected on load.
+
+### Transaction file format for TBD
+
+The transaction file contains data about an ongoing action that cannot be completed atomically. It exists only if there is an ongoing transaction.
+
+All integers are encoded in platform endianness.
+
+All currently existing transactions concern a key in a secure element.
+
+The layout of a transaction file is:
+
+* type (2 bytes): the [transaction type](#transaction-types-on-tbd).
+* unused (2 bytes)
+* lifetime (4 bytes): `psa_key_lifetime_t` value that corresponds to a key in a secure element.
+* slot number (8 bytes): `psa_key_slot_number_t` value. This is the unique designation of the key for the secure element driver.
+* key identifier (4 bytes in a library integration, 8 bytes on a PSA platform): the internal representation of the key identifier. On a PSA platform, this encodes the key owner in the same way as [in file identifiers for key files](#file-namespace-on-a-psa-platform-on-tbd)).
+
+#### Transaction types on TBD
+
+* 0x0001: key creation. The following locations may or may not contain data about the key that is being created:
+    * The slot in the secure element designated by the slot number.
+    * The file containing the key metadata designated by the key identifier.
+    * The driver persistent data.
+* 0x0002: key destruction. The following locations may or may not still contain data about the key that is being destroyed:
+    * The slot in the secure element designated by the slot number.
+    * The file containing the key metadata designated by the key identifier.
+    * The driver persistent data.
+
+Mbed Crypto TBD
+---------------
+
+Tags: TBD
+
+Released in TBD 2020. <br>
+Integrated in Mbed OS TBD.
+
+### Changes introduced in TBD
+
+* The type field has been split into a type and a bits field of 2 bytes each.
+
+### Key file format for TBD
+
+All integers are encoded in little-endian order in 8-bit bytes except where otherwise indicated.
+
+The layout of a key file is:
+
+* magic (8 bytes): `"PSA\0KEY\0"`.
+* version (4 bytes): 0.
+* lifetime (4 bytes): `psa_key_lifetime_t` value.
+* type (2 bytes): `psa_key_type_t` value.
+* bits (2 bytes): `psa_key_bits_t` value.
+* policy usage flags (4 bytes): `psa_key_usage_t` value.
+* policy usage algorithm (4 bytes): `psa_algorithm_t` value.
+* policy enrollment algorithm (4 bytes): `psa_algorithm_t` value.
+* key material length (4 bytes).
+* key material:
+    * For a transparent key: output of `psa_export_key`.
+    * For an opaque key (unified driver interface): driver-specific opaque key blob.
+    * For an opaque key (key in a secure element): slot number (8 bytes), in platform endianness.
+* Any trailing data is rejected on load.
+
+Mbed TLS 2.25.0
+---------------
+
+Tags: `mbedtls-2.25.0`, `mbedtls-2.26.0`, `mbedtls-2.27.0`, `mbedtls-2.28.0`, `mbedtls-3.0.0`, `mbedtls-3.1.0`
+
+First released in December 2020.
+
+Note: this is the first version that is officially supported. The version number is still 0.
+
+Backward compatibility commitments: we promise backward compatibility for stored keys when Mbed TLS is upgraded from x to y if x >= 2.25 and y < 4. See [`BRANCHES.md`](../../BRANCHES.md) for more details.
+
+Supported integrations:
+
+* [PSA platform](#file-namespace-on-a-psa-platform-on-mbed-tls-2.25.0)
+* [library using PSA ITS](#file-namespace-on-its-as-a-library-on-mbed-tls-2.25.0)
+* [library using C stdio](#file-namespace-on-stdio-for-mbed-tls-2.25.0)
+
+Supported features:
+
+* [Persistent keys](#key-file-format-for-mbed-tls-2.25.0) designated by a [key identifier and owner](#key-names-for-mbed-tls-2.25.0). Keys can be:
+    * Transparent, stored in the export format.
+    * Opaque, using the PSA driver interface with statically registered drivers. The driver determines the content of the opaque key blob.
+    * Opaque, using the deprecated secure element interface with dynamically registered drivers (`MBEDTLS_PSA_CRYPTO_SE_C`). The driver picks a slot number which is stored in the place of the key material.
+* [Nonvolatile random seed](#nonvolatile-random-seed-file-format-for-mbed-tls-2.25.0) on ITS only.
+
+### Changes introduced in Mbed TLS 2.25.0
+
+* The numerical encodings of `psa_key_type_t`, `psa_key_usage_t` and `psa_algorithm_t` have changed.
+
+### File namespace on a PSA platform on Mbed TLS 2.25.0
+
+Assumption: ITS provides a 64-bit file identifier namespace. The Crypto service can use arbitrary file identifiers and no other part of the system accesses the same file identifier namespace.
+
+Assumption: the owner identifier is a nonzero value of type `int32_t`.
+
+* Files 0 through 0xfffeffff: unused.
+* Files 0xffff0000 through 0xffffffff: reserved for internal use of the crypto library or crypto service. See [non-key files](#non-key-files-on-mbed-tls-2.25.0).
+* Files 0x100000000 through 0xffffffffffff: [content](#key-file-format-for-mbed-tls-2.25.0) of the [key whose identifier is the file identifier](#key-names-for-mbed-tls-2.25.0). The upper 32 bits determine the owner.
+
+### File namespace on ITS as a library on Mbed TLS 2.25.0
+
+Assumption: ITS provides a 64-bit file identifier namespace. The entity using the crypto library can use arbitrary file identifiers and no other part of the system accesses the same file identifier namespace.
+
+This is a library integration, so there is no owner. The key file identifier is identical to the key identifier.
+
+* File 0: unused.
+* Files 1 through 0xfffeffff: [content](#key-file-format-for-mbed-tls-2.25.0) of the [key whose identifier is the file identifier](#key-names-for-mbed-tls-2.25.0).
+* Files 0xffff0000 through 0xffffffff: reserved for internal use of the crypto library or crypto service. See [non-key files](#non-key-files-on-mbed-tls-2.25.0).
+* Files 0x100000000 through 0xffffffffffffffff: unused.
+
+### File namespace on stdio for Mbed TLS 2.25.0
+
+Assumption: C stdio, allowing names containing lowercase letters, digits and underscores, of length up to 23.
+
+An undocumented build-time configuration value `PSA_ITS_STORAGE_PREFIX` allows storing the key files in a directory other than the current directory. This value is simply prepended to the file name (so it must end with a directory separator to put the keys in a different directory).
+
+* `PSA_ITS_STORAGE_PREFIX "tempfile.psa_its"`: used as a temporary file. Must be writable. May be overwritten or deleted if present.
+* `sprintf(PSA_ITS_STORAGE_PREFIX "%016llx.psa_its", key_id)`: a key or non-key file. The `key_id` in the name is the 64-bit file identifier, which is the [key identifier](#key-names-for-mbed-tls-2.25.0) for a key file or some reserved identifier for a [non-key file](#non-key-files-on-mbed-tls-2.25.0). The contents of the file are:
+    * Magic header (8 bytes): `"PSA\0ITS\0"`
+    * File contents.
+
+### Key names for Mbed TLS 2.25.0
+
+Information about each key is stored in a dedicated file designated by the key identifier. In integrations where there is no concept of key owner (in particular, in library integrations), the key identifier is exactly the key identifier as defined in the PSA Cryptography API specification (`psa_key_id_t`). In integrations where there is a concept of key owner (integration into a service for example), the key identifier is made of an owner identifier (its semantics and type are integration specific) and of the key identifier (`psa_key_id_t`) from the key owner point of view.
+
+The way in which the file name is constructed from the key identifier depends on the storage backend. The content of the file is described [below](#key-file-format-for-mbed-tls-2.25.0).
+
+* Library integration: the key file name is just the key identifier as defined in the PSA crypto specification. This is a 32-bit value which must be in the range 0x00000001..0x3fffffff (`PSA_KEY_ID_USER_MIN`..`PSA_KEY_ID_USER_MAX`).
+* PSA service integration: the key file name is `(uint64_t)owner_uid << 32 | key_id` where `key_id` is the key identifier from the owner point of view and `owner_uid` (of type `int32_t`) is the calling partition identifier provided to the server by the partition manager. This is a 64-bit value.
+
+### Key file format for Mbed TLS 2.25.0
+
+All integers are encoded in little-endian order in 8-bit bytes except where otherwise indicated.
+
+The layout of a key file is:
+
+* magic (8 bytes): `"PSA\0KEY\0"`.
+* version (4 bytes): 0.
+* lifetime (4 bytes): `psa_key_lifetime_t` value.
+* type (2 bytes): `psa_key_type_t` value.
+* bits (2 bytes): `psa_key_bits_t` value.
+* policy usage flags (4 bytes): `psa_key_usage_t` value.
+* policy usage algorithm (4 bytes): `psa_algorithm_t` value.
+* policy enrollment algorithm (4 bytes): `psa_algorithm_t` value.
+* key material length (4 bytes).
+* key material:
+    * For a transparent key: output of `psa_export_key`.
+    * For an opaque key (unified driver interface): driver-specific opaque key blob.
+    * For an opaque key (key in a dynamic secure element): slot number (8 bytes), in platform endianness.
+* Any trailing data is rejected on load.
+
+### Non-key files on Mbed TLS 2.25.0
+
+File identifiers that are outside the range of persistent key identifiers are reserved for internal use by the library. The only identifiers currently in use have the owner id (top 32 bits) set to 0.
+
+* Files 0xfffffe02 through 0xfffffeff (`PSA_CRYPTO_SE_DRIVER_ITS_UID_BASE + lifetime`): dynamic secure element driver storage. The content of the file is the secure element driver's persistent data.
+* File 0xffffff52 (`PSA_CRYPTO_ITS_RANDOM_SEED_UID`): [nonvolatile random seed](#nonvolatile-random-seed-file-format-for-mbed-tls-2.25.0).
+* File 0xffffff54 (`PSA_CRYPTO_ITS_TRANSACTION_UID`): [transaction file](#transaction-file-format-for-mbed-tls-2.25.0).
+* Other files are unused and reserved for future use.
+
+### Nonvolatile random seed file format for Mbed TLS 2.25.0
+
+[Identical to Mbed Crypto 0.1.0](#nonvolatile-random-seed-file-format-for-0.1.0).
+
+### Transaction file format for Mbed TLS 2.25.0
+
+The transaction file contains data about an ongoing action that cannot be completed atomically. It exists only if there is an ongoing transaction.
+
+All integers are encoded in platform endianness.
+
+All currently existing transactions concern a key in a dynamic secure element.
+
+The layout of a transaction file is:
+
+* type (2 bytes): the [transaction type](#transaction-types-on-mbed-tls-2.25.0).
+* unused (2 bytes)
+* lifetime (4 bytes): `psa_key_lifetime_t` value that corresponds to a key in a secure element.
+* slot number (8 bytes): `psa_key_slot_number_t` value. This is the unique designation of the key for the secure element driver.
+* key identifier (4 bytes in a library integration, 8 bytes on a PSA platform): the internal representation of the key identifier. On a PSA platform, this encodes the key owner in the same way as [in file identifiers for key files](#file-namespace-on-a-psa-platform-on-mbed-tls-2.25.0)).
+
+#### Transaction types on Mbed TLS 2.25.0
+
+* 0x0001: key creation. The following locations may or may not contain data about the key that is being created:
+    * The slot in the secure element designated by the slot number.
+    * The file containing the key metadata designated by the key identifier.
+    * The driver persistent data.
+* 0x0002: key destruction. The following locations may or may not still contain data about the key that is being destroyed:
+    * The slot in the secure element designated by the slot number.
+    * The file containing the key metadata designated by the key identifier.
+    * The driver persistent data.
diff --git a/docs/architecture/psa-crypto-implementation-structure.md b/docs/architecture/psa-crypto-implementation-structure.md
new file mode 100644
index 0000000..d7e4f9c
--- /dev/null
+++ b/docs/architecture/psa-crypto-implementation-structure.md
@@ -0,0 +1,175 @@
+PSA Cryptography API implementation and PSA driver interface
+===========================================================
+
+## Introduction
+
+The [PSA Cryptography API specification](https://armmbed.github.io/mbed-crypto/psa/#application-programming-interface) defines an interface to cryptographic operations for which the Mbed TLS library provides a reference implementation. The PSA Cryptography API specification is complemented by the PSA driver interface specification which defines an interface for cryptoprocessor drivers.
+
+This document describes the high level organization of the Mbed TLS PSA Cryptography API implementation which is tightly related to the PSA driver interface.
+
+## High level organization of the Mbed TLS PSA Cryptography API implementation
+In one sentence, the Mbed TLS PSA Cryptography API implementation is made of a core and PSA drivers as defined in the PSA driver interface. The key point is that software cryptographic operations are organized as PSA drivers: they interact with the core through the PSA driver interface.
+
+### Rationale
+
+* Addressing software and hardware cryptographic implementations through the same C interface reduces the core code size and its call graph complexity. The core and its dispatching to software and hardware implementations are consequently easier to test and validate.
+* The organization of the software cryptographic implementations in drivers promotes modularization of those implementations.
+* As hardware capabilities, software cryptographic functionalities can be described by a JSON driver description file as defined in the PSA driver interface.
+* Along with JSON driver description files, the PSA driver specification defines the deliverables for a driver to be included into the Mbed TLS PSA Cryptography implementation. This provides a natural framework to integrate third party or alternative software implementations of cryptographic operations.
+
+## The Mbed TLS PSA Cryptography API implementation core
+
+The core implements all the APIs as defined in the PSA Cryptography API specification but does not perform on its own any cryptographic operation. The core relies on PSA drivers to actually
+perform the cryptographic operations. The core is responsible for:
+
+* the key store.
+* checking PSA API arguments and translating them into valid arguments for the necessary calls to the PSA driver interface.
+* dispatching the cryptographic operations to the appropriate PSA drivers.
+
+The sketch of an Mbed TLS PSA cryptographic API implementation is thus:
+```C                                                                            
+psa_status_t psa_api( ... )
+{
+    psa_status_t status;
+
+    /* Pre driver interface call processing: validation of arguments, building
+     * of arguments for the call to the driver interface, ... */
+
+    ...
+
+    /* Call to the driver interface */
+    status = psa_driver_wrapper_<entry_point>( ... );
+    if( status != PSA_SUCCESS )
+        return( status );
+
+    /* Post driver interface call processing: validation of the values returned
+     * by the driver, finalization of the values to return to the caller,
+     * clean-up in case of error ... */
+}
+```
+The code of most PSA APIs is expected to match precisely the above layout. However, it is likely that the code structure of some APIs will be more complicated with several calls to the driver interface, mainly to encompass a larger variety of hardware designs. For example, to encompass hardware accelerators that are capable of verifying a MAC and those that are only capable of computing a MAC, the psa_mac_verify() API could call first psa_driver_wrapper_mac_verify() and then fallback to psa_driver_wrapper_mac_compute().
+
+The implementations of `psa_driver_wrapper_<entry_point>` functions are generated by the build system based on the JSON driver description files of the various PSA drivers making up the Mbed TLS PSA Cryptography API implementation. The implementations are splited into two parts. The static ones are generated in a psa_crypto_driver_wrappers.h header file, the non-static ones are generated in a psa_crypto_driver_wrappers_no_static.c C file and the function prototypes declared in a psa_crypto_driver_wrappers_no_static.h header file.
+
+The psa_driver_wrapper_<entry_point>() functions dispatch cryptographic operations to accelerator drivers, secure element drivers as well as to the software implementations of cryptographic operations.
+
+Note that the implementation allows to build the library with only a C compiler by shipping a generated file corresponding to a pure software implementation. The driver entry points and their code in this generated file are guarded by pre-processor directives based on PSA_WANT_xyz macros (see [Conditional inclusion of cryptographic mechanism through the PSA API in Mbed TLS](psa-conditional-inclusion-c.html). That way, it is possible to compile and include in the library only the desired cryptographic operations.
+
+### Key creation
+
+Key creation implementation in Mbed TLS PSA core is articulated around three internal functions: psa_start_key_creation(), psa_finish_key_creation() and psa_fail_key_creation(). Implementations of key creation PSA APIs, namely psa_import_key(), psa_generate_key(), psa_key_derivation_output_key() and psa_copy_key() go by the following sequence:
+    1. Check the input parameters.
+    2. Call psa_start_key_creation() that allocates a key slot, prepares it with the specified key attributes, and in case of a volatile key assign it a volatile key identifier.
+    3. Generate or copy the key material into the key slot. This entails the allocation of the buffer to store the key material.
+    4. Call psa_finish_key_creation() that mostly saves persistent keys into persistent storage.
+
+In case of any error occurring at step 3 or 4, psa_fail_key_creation() is called. It wipes and cleans the slot especially the key material: reset to zero of the RAM memory that contained the key material, free the allocated buffer.
+
+
+## Mbed TLS PSA Cryptography API implementation drivers
+
+A driver of the Mbed TLS PSA Cryptography API implementation (Mbed TLS PSA driver in the following) is a driver in the sense that it is compliant with the PSA driver interface specification. But it is not an actual driver that drives some hardware. It implements cryptographic operations purely in software.
+
+An Mbed TLS PSA driver C file is named psa_crypto_<driver_name>.c and its associated header file psa_crypto_<driver_name>.h. The functions implementing a driver entry point as defined in the PSA driver interface specification are named as mbedtls_psa_<driver name>_<entry point>(). As an example, the psa_crypto_rsa.c and psa_crypto_rsa.h are the files containing the Mbed TLS PSA driver implementing RSA cryptographic operations. This RSA driver implements among other entry points the "import_key" entry point. The function implementing this entry point is named mbedtls_psa_rsa_import_key().
+
+## How to implement a new cryptographic mechanism
+
+Summary of files to modify when adding a new algorithm or key type:
+
+* [ ] PSA Crypto API draft, if not already done — [PSA standardization](#psa-standardization)
+* [ ] `include/psa/crypto_values.h` or `include/psa/crypto_extra.h` — [New functions and macros](#new-functions-and-macros)
+* [ ] `include/psa/crypto_config.h`, `tests/include/test/drivers/crypto_config_test_driver_extension.h` — [Preprocessor symbols](#preprocessor-symbols)
+* Occasionally `library/check_crypto_config.h` — [Preprocessor symbols](#preprocessor-symbols)
+* [ ] `include/mbedtls/config_psa.h` — [Preprocessor symbols](#preprocessor-symbols)
+* [ ] `library/psa_crypto.c`, `library/psa_crypto_*.[hc]` — [Implementation of the mechanisms](#implementation-of-the-mechanisms)
+* [ ] `include/psa/crypto_builtin_*.h` — [Translucent data structures](#translucent-data-structures)
+* [ ] `tests/suites/test_suite_psa_crypto_metadata.data` — [New functions and macros](#new-functions-and-macros)
+* (If adding `PSA_IS_xxx`) `tests/suites/test_suite_psa_crypto_metadata.function` — [New functions and macros](#new-functions-and-macros)
+* [ ] `tests/suites/test_suite_psa_crypto*.data`, `tests/suites/test_suite_psa_crypto*.function` — [Unit tests](#unit-tests)
+* [ ] `scripts/mbedtls_dev/crypto_knowledge.py`, `scripts/mbedtls_dev/asymmetric_key_data.py` — [Unit tests](#unit-tests)
+* [ ] `ChangeLog.d/*.txt` — changelog entry
+
+Summary of files to modify when adding new API functions:
+
+* [ ] `include/psa/crypto.h` and `include/psa/crypto_sizes.h`, or `include/psa/crypto_extra.h` — [New functions and macros](#new-functions-and-macros)
+* [ ] `library/psa_crypto.c`, `scripts/data_files/driver_templates/*.jinja` — [Implementation of the mechanisms](#implementation-of-the-mechanisms)
+* [ ] If adding stateful functions: `include/psa/crypto_struct.h`, `include/psa/crypto_builtin_*.h`, `include/psa/crypto_driver_contexts_*.h` — [Translucent data structures](#translucent-data-structures)
+* [ ] `tests/suites/test_suite_psa_crypto.data`, `tests/suites/test_suite_psa_crypto.function`, `tests/suites/test_suite_psa_crypto_driver_wrappers.*` — [Unit tests](#unit-tests)
+
+Note that this is just a basic guide. In some cases, you won't need to change all the files listed here. In some cases, you may need to change other files.
+
+### PSA standardization
+
+Typically, if there's enough demand for a cryptographic mechanism in Mbed TLS, there's enough demand for it to be part of the official PSA Cryptography specification. Therefore the first step before implementing a new mechanism should be to approach the PSA Cryptography working group in Arm for standardization.
+
+At the time of writing, all cryptographic mechanisms that are accessible through `psa_xxx` APIs in in Mbed TLS are current or upcoming PSA standards. Mbed TLS implements some extensions to the PSA API that offer extra integration customization or extra key policies.
+
+Mbed TLS routinely implements cryptographic mechanisms that are not yet part of a published PSA standard, but that are scheduled to be part of a future version of the standard. The Mbed TLS implementation validates the feasibility of the upcoming PSA standard. The PSA Cryptography working group and the Mbed TLS development team communicate during the elaboration of the new interfaces.
+
+### New functions and macros
+
+If a mechanism requires new functions, they should follow the design guidelines in the PSA Cryptography API specification.
+
+Functions that are part of the current or upcoming API are declared in `include/psa/crypto.h`, apart from structure accessors defined in `include/psa/crypto_struct.h`. Functions that have output buffers have associated sufficient-output-size macros in `include/psa/crypto_sizes.h`.
+
+Constants (algorithm identifiers, key type identifiers, etc.) and associated destructor macros (e.g. `PSA_IS_xxx()`) are defined in `include/psa/crypto_values.h`.
+
+Functions and macros that are not intended for standardization, or that are at a stage where the draft standard might still evolve significantly, are declared in `include/psa/crypto_extra.h`.
+
+The PSA Cryptography API specification defines both names and values for certain kinds of constants: algorithms (`PSA_ALG_xxx`), key types (`PSA_KEY_TYPE_xxx`), ECC curve families (`PSA_ECC_FAMILY_xxx`), DH group families (`PSA_DH_FAMILY_xxx`). If Mbed TLS defines an algorithm or a key type that is not part of a current or upcoming PSA standard, pick a value with the `VENDOR` flag set. If Mbed TLS defines an ECC curve or DH group family that is not part of a current or upcoming PSA standard, define a vendor key type and use the family identifier only with this vendor key type.
+
+New constants must have a test case in `tests/suites/test_suite_psa_crypto_metadata.data` that verifies that `PSA_IS_xxx` macros behave properly with the new constant. New `PSA_IS_xxx` macros must be declared in `tests/suites/test_suite_psa_crypto_metadata.function`.
+
+### Preprocessor symbols
+
+Each cryptographic mechanism is optional and can be selected by the application at build time. For each feature `PSA_ttt_xxx`:
+
+* The feature is available to applications when the preprocessor symbol `PSA_WANT_ttt_xxx` is defined. These symbols are set:
+    * If `MBEDTLS_PSA_CRYPTO_CONFIG` is disabled: based on the available mechanisms in Mbed TLS, deduced from `mbedtls/mbedtls_config.h` by code in `include/mbedtls/config_psa.h`.
+    * if `MBEDTLS_PSA_CRYPTO_CONFIG` is enabled: in the application configuration file `include/psa/crypto_config.h` (or `MBEDTLS_PSA_CRYPTO_CONFIG_FILE`, plus `MBEDTLS_PSA_CRYPTO_USER_CONFIG_FILE`), with code in `include/mbedtls/config_psa.h` deducing the necessary underlying `MBEDTLS_xxx` symbols.
+*  For transparent keys (keys that are not in a secure element), the feature is implemented by Mbed TLS if `MBEDTLS_PSA_BUILTIN_ttt_xxx` is defined, and by an accelerator driver if `MBEDTLS_PSA_ACCEL_ttt_xxx` is defined. `MBEDTLS_PSA_BUILTIN_ttt_xxx` constants are set in `include/mbedtls/config_psa.h` based on the application requests `PSA_WANT_ttt_xxx` and the accelerator driver declarations `MBEDTLS_PSA_ACCEL_ttt_xxx`.
+* For the testing of the driver dispatch code, `tests/include/test/drivers/crypto_config_test_driver_extension.h` sets additional `MBEDTLS_PSA_ACCEL_xxx` symbols.
+
+For more details, see *[Conditional inclusion of cryptographic mechanism through the PSA API in Mbed TLS](../proposed/psa-conditional-inclusion-c.html)*.
+
+Some mechanisms require other mechanisms. For example, you can't do GCM without a block cipher, or RSA-PSS without RSA keys. When mechanism A requires mechanism B, `include/mbedtls/config_psa.h` ensures that B is enabled whenever A is enabled. When mechanism A requires at least one of a set {B1, B2, B3, ...} but there is no particular reason why enabling A would enable any of the specific Bi's, it's up to the application to choose Bi's and the file `library/check_crypto_config.h` contains compile-time constraints to ensure that at least one Bi is enabled.
+
+### Implementation of the mechanisms
+
+The general structure of a cryptographic operation function is:
+
+1. API function defined in `library/psa_crypto.c`. The entry point performs generic checks that don't depend on whether the mechanism is implemented in software or in a driver and looks up keys in the key store.
+2. Driver dispatch code in `scripts/data_files/driver_templates/psa_crypto_driver_wrappers.h.jinja`, `scripts/data_files/driver_templates/psa_crypto_driver_wrappers_no_static.c.jinja` or files included from there.
+3. Built-in implementation in `library/psa_crypto_*.c` (with function declarations in the corresponding `.h` file). These files typically contain the implementation of modes of operation over basic building blocks that are defined elsewhere. For example, HMAC is implemented in `library/psa_crypto_mac.c` but the underlying hash functions are implemented in `library/sha*.c` and `library/md*.c`.
+4. Basic cryptographic building blocks in `library/*.c`.
+
+When implementing a new algorithm or key type, there are typically things to change in `library/crypto.c` (e.g. buffer size calculations, algorithm/key-type compatibility) and in the built-in implementation, but not in the driver dispatch code.
+
+### Translucent data structures
+
+Some mechanisms require state to be kept between function calls. Keys and key-like data is kept in the key store, which PSA manages internally. Other state, for example the state of multipart operations, is kept in structures allocated by the caller.
+
+The size of operation structures needs to be known at compile time, since callers may allocate them on the stack. Therefore these structures are defined in a public header: `include/psa/crypto_struct.h` for the parts that are independent of the underlying implementation, `include/psa/crypto_builtin_*` for parts that are specific to the Mbed TLS built-in implementation, `include/psa/crypto_driver_*.h` for structures implemented by drivers.
+
+### Unit tests
+
+A number of unit tests are automatically generated by `tests/scripts/generate_psa_tests.py` based on the algorithms and key types declared in `include/psa/crypto_values.h` and `include/psa/crypto_extra.h`:
+
+* Attempt to create a key with a key type that is not supported.
+* Attempt to perform an operation with a combination of key type and algorithm that is not valid or not supported.
+* Storage and retrieval of a persistent key.
+
+When adding a new key type or algorithm:
+
+* `scripts/mbedtls_dev/crypto_knowledge.py` contains knowledge about the compatibility of key types, key sizes and algorithms.
+* `scripts/mbedtls_dev/asymmetric_key_data.py` contains valid key data for asymmetric key types.
+
+Other things need to be tested manually, either in `tests/suites/test_sutie_psa_crypto.data` or in another file. For example (this is not an exhaustive list):
+
+* Known answer tests.
+* Potential edge cases (e.g. data less/equal/more than the block size, number equal to zero in asymmetric cryptography).
+* Tests with invalid keys (e.g. wrong size or format).
+* Tests with invalid data (e.g. wrong size or format, output buffer too small, invalid padding).
+* For new functions: incorrect function call sequence, driver dispatch (in `tests/suites/test_suite_psa_crypto_driver_wrappers.*`).
+* For key derivation algorithms: variation on the sequence of input steps, variation on the output size.
+
diff --git a/docs/architecture/psa-migration/md-cipher-dispatch.md b/docs/architecture/psa-migration/md-cipher-dispatch.md
new file mode 100644
index 0000000..eda65a3
--- /dev/null
+++ b/docs/architecture/psa-migration/md-cipher-dispatch.md
@@ -0,0 +1,616 @@
+PSA migration strategy for hashes and ciphers
+=============================================
+
+## Introduction
+
+This document discusses a migration strategy for code that is not subject to `MBEDTLS_USE_PSA_CRYPTO`, is currently using legacy cryptography APIs, and should transition to PSA, without a major version change.
+
+### Relationship with the main strategy document
+
+This is complementary to the main [strategy document](strategy.html) and is intended as a refinement. However, at this stage, there may be contradictions between the strategy proposed here and some of the earlier strategy.
+
+A difference between the original strategy and the current one is that in this work, we are not treating PSA as a black box. We can change experimental features, and we can call internal interfaces.
+
+## Requirements
+
+### User stories
+
+#### Backward compatibility user story
+
+As a developer of an application that uses Mbed TLS's interfaces (including legacy crypto),  
+I want Mbed TLS to preserve backward compatibility,  
+so that my code keeps working in new minor versions of Mbed TLS.
+
+#### Interface design user story
+
+As a developer of library code that uses Mbed TLS to perform cryptographic operations,  
+I want to know which functions to call and which feature macros to check,  
+so that my code works in all Mbed TLS configurations.
+
+Note: this is the same problem we face in X.509 and TLS.
+
+#### Hardware accelerator vendor user stories
+
+As a vendor of a platform with hardware acceleration for some crypto,  
+I want to build Mbed TLS in a way that uses my hardware wherever relevant,  
+so that my customers maximally benefit from my hardware.
+
+As a vendor of a platform with hardware acceleration for some crypto,  
+I want to build Mbed TLS without software that replicates what my hardware does,  
+to minimize the code size.
+
+#### Maintainer user stories
+
+As a maintainer of Mbed TLS,  
+I want to have clear rules for when to use which interface,  
+to avoid bugs in “unusual” configurations.
+
+As a maintainer of Mbed TLS,  
+I want to avoid duplicating code,  
+because this is inefficient and error-prone.
+
+### Use PSA more
+
+In the long term, all code using cryptography should use PSA interfaces, to benefit from PSA drivers, allow eliminating legacy interfaces (less code size, less maintenance). However, this can't be done without breaking [backward compatibility](#backward-compatibility).
+
+The goal of this work is to arrange for more non-PSA interfaces to use PSA interfaces under the hood, without breaking code in the cases where this doesn't work. Using PSA interfaces has two benefits:
+
+* Where a PSA driver is available, it likely has better performance, and sometimes better security, than the built-in software implementation.
+* In many scenarios, where a PSA driver is available, this allows removing the software implementation altogether.
+* We may be able to get rid of some redundancies, for example the duplication between the implementations of HMAC in `md.c` and in `psa_crypto_mac.c`, and HKDF in `hkdf.c` and `psa_crypto.c`.
+
+### Correct dependencies
+
+Traditionally, to determine whether a cryptographic mechanism was available, you had to check whether the corresponding Mbed TLS module or submodule was present: `MBEDTLS_SHA256_C` for SHA256, `MBEDTLS_AES_C && MBEDTLS_CIPHER_MODE_CBC` for AES-CBC, etc. In code that uses the PSA interfaces, this needs to change to `PSA_WANT_xxx` symbols.
+
+### Backward compatibility
+
+All documented behavior must be preserved, except for interfaces currently described as experimental or unstable. Those interfaces can change, but we should minimize disruption by providing a transition path for reasonable use cases.
+
+#### Changeable configuration options
+
+The following configuration options are described as experimental, and are likely to change at least marginally:
+
+* `MBEDTLS_PSA_CRYPTO_CLIENT`: “This interface is experimental and may change or be removed without notice.” In practice we don't want to remove this, but we may constrain how it's used.
+* `MBEDTLS_PSA_CRYPTO_DRIVERS`: “This interface is experimental. We intend to maintain backward compatibility with application code that relies on drivers, but the driver interfaces may change without notice.” In practice, this may mean constraints not only on how to write drivers, but also on how to integrate drivers into code that is platform code more than application code.
+* `MBEDTLS_PSA_CRYPTO_CONFIG`: “This feature is still experimental and is not ready for production since it is not completed.” We may want to change this, for example, to automatically enable more mechanisms (although this wouldn't be considered a backward compatibility break anyway, since we don't promise that you will not get a feature if you don't enable its `PSA_WANT_xxx`).
+
+### Non-goals
+
+It is not a goal at this stage to make more code directly call `psa_xxx` functions. Rather, the goal is to make more code call PSA drivers where available. How dispatch is done is secondary.
+
+## Problem analysis
+
+### Scope analysis
+
+#### Limitations of `MBEDTLS_USE_PSA_CRYPTO`
+
+The option `MBEDTLS_USE_PSA_CRYPTO` causes parts of the library to call the PSA API instead of legacy APIs for cryptographic calculations. `MBEDTLS_USE_PSA_CRYPTO` only applies to `pk.h`, X.509 and TLS. When this option is enabled, applications must call `psa_crypto_init()` before calling any of the functions in these modules.
+
+In this work, we want two things:
+
+* Make non-covered modules call PSA, but only [when this will actually work](#why-psa-is-not-always-possible). This effectively brings those modules to a partial use-PSA behavior (benefiting from PSA accelerators when they're usable) regardless of whether the option is enabled.
+* Call PSA when a covered module calls a non-covered module which calls another module, for example X.509 calling pk for PSS verification which calls RSA which calculates a hash ([see issue \#6497](https://github.com/Mbed-TLS/mbedtls/issues/6497)). This effectively extends the option to modules that aren't directly covered.
+
+#### Classification of callers
+
+We can classify code that implements or uses cryptographic mechanisms into several groups:
+
+* Software implementations of primitive cryptographic mechanisms. These are not expected to change.
+* Software implementations of constructed cryptographic mechanisms (e.g. HMAC, CTR_DRBG, RSA (calling a hash for PSS/OAEP, and needing to know the hash length in PKCS1v1.5 sign/verify), …). These need to keep working whenever a legacy implementation of the auxiliary mechanism is available, regardless of whether a PSA implementation is also available.
+* Code implementing the PSA crypto interface. This is not expected to change, except perhaps to expose some internal functionality to overhauled glue code.
+* Code that's subject to `MBEDTLS_USE_PSA_CRYPTO`: `pk.h`, X.509, TLS (excluding parts specific TLS 1.3).
+* Code that always uses PSA for crypto: TLS 1.3 (except things common with 1.2), LMS.
+
+For the purposes of this work, three domains emerge:
+
+* **Legacy domain**: does not interact with PSA. Implementations of hashes, of cipher primitives, of arithmetic.
+* **Mixed domain**: does not currently use PSA, but should [when possible](#why-psa-is-not-always-possible). This consists of the constructed cryptographic primitives (except LMS), as well as pk, X.509 and TLS when `MBEDTLS_USE_PSA_CRYPTO` is disabled.
+* **PSA domain**: includes pk, X.509 and TLS when `MBEDTLS_USE_PSA_CRYPTO` is enabled. Also TLS 1.3, LMS.
+
+#### Non-use-PSA modules
+
+The following modules in Mbed TLS call another module to perform cryptographic operations which, in the long term, will be provided through a PSA interface, but cannot make any PSA-related assumption.
+
+Hashes and HMAC (after the work on driver-only hashes):
+
+* entropy (hashes via MD-light)
+* ECDSA (HMAC\_DRBG; `md.h` exposed through API)
+* ECJPAKE (hashes via MD-light; `md.h` exposed through API)
+* MD (hashes and HMAC)
+* HKDF (HMAC via `md.h`; `md.h` exposed through API)
+* HMAC\_DRBG (hashes and HMAC via `md.h`; `md.h` exposed through API)
+* PKCS12 (hashes via MD-light)
+* PKCS5 (HMAC via `md.h`; `md.h` exposed through API)
+* PKCS7 (hashes via MD)
+* RSA (hash via MD-light for PSS and OAEP; `md.h` exposed through API)
+* PEM (MD5 hash via MD-light)
+
+Symmetric ciphers and AEADs (before work on driver-only cipher):
+
+* PEM:
+  * AES, DES or 3DES in CBC mode without padding, decrypt only (!).
+  * Currently using low-level non-generic APIs.
+  * No hard dependency, features guarded by `AES_C` resp. `DES_C`.
+  * Functions called: `setkey_dec()` + `crypt_cbc()`.
+* PKCS12:
+  * In practice: 2DES or 3DES in CBC mode with PKCS7 padding, decrypt only
+    (when called from pkparse).
+  * In principle: any cipher-mode (default padding), passed an
+    `mbedtls_cipher_type_t` as an argument, no documented restriction.
+  * Cipher, generically, selected from ASN.1 or function parameters;
+    no documented restriction but in practice TODO (inc. padding and
+    en/decrypt, look at standards and tests)
+  * Unconditional dependency on `CIPHER_C` in `check_config.h`.
+  * Note: `cipher.h` exposed through API.
+  * Functions called: `setup`, `setkey`, `set_iv`, `reset`, `update`, `finish` (in sequence, once).
+* PKCS5 (PBES2, `mbedtls_pkcs5_pbes2()`):
+  * 3DES or DES in CBC mode with PKCS7 padding, both encrypt and decrypt.
+  * Note: could also be AES in the future, see #7038.
+  * Unconditional dependency on `CIPHER_C` in `check_config.h`.
+  * Functions called: `setup`, `setkey`, `crypt`.
+* CTR\_DRBG:
+  * AES in ECB mode, encrypt only.
+  * Currently using low-level non-generic API (`aes.h`).
+  * Unconditional dependency on `AES_C` in `check_config.h`.
+  * Functions called: `setkey_enc`, `crypt_ecb`.
+* CCM:
+  * AES, Camellia or Aria in ECB mode, encrypt only.
+  * Unconditional dependency on `AES_C || CAMELLIA_C || ARIA_C` in `check_config.h`.
+  * Unconditional dependency on `CIPHER_C` in `check_config.h`.
+  * Note: also called by `cipher.c` if enabled.
+  * Functions called: `info`, `setup`, `setkey`, `update` (several times) - (never finish)
+* CMAC:
+  * AES or DES in ECB mode, encrypt only.
+  * Unconditional dependency on `AES_C || DES_C` in `check_config.h`.
+  * Unconditional dependency on `CIPHER_C` in `check_config.h`.
+  * Note: also called by `cipher.c` if enabled.
+  * Functions called: `info`, `setup`, `setkey`, `update` (several times) - (never finish)
+* GCM:
+  * AES, Camellia or Aria in ECB mode, encrypt only.
+  * Unconditional dependency on `AES_C || CAMELLIA_C || ARIA_C` in `check_config.h`.
+  * Unconditional dependency on `CIPHER_C` in `check_config.h`.
+  * Note: also called by `cipher.c` if enabled.
+  * Functions called: `info`, `setup`, `setkey`, `update` (several times) - (never finish)
+* NIST\_KW:
+  * AES in ECB mode, both encryt and decrypt.
+  * Unconditional dependency on `AES_C || DES_C` in `check_config.h`.
+  * Unconditional dependency on `CIPHER_C` in `check_config.h`.
+  * Note: also called by `cipher.c` if enabled.
+  * Note: `cipher.h` exposed through API.
+  * Functions called: `info`, `setup`, `setkey`, `update` (several times) - (never finish)
+* Cipher:
+  * potentially any cipher/AEAD in any mode and any direction
+
+Note: PSA cipher is built on Cipher, but PSA AEAD directly calls the underlying AEAD modules (GCM, CCM, ChachaPoly).
+
+### Difficulties
+
+#### Why PSA is not always possible
+
+Here are some reasons why calling `psa_xxx()` to perform a hash or cipher calculation might not be desirable in some circumstances, explaining why the application would arrange to call the legacy software implementation instead.
+
+* `MBEDTLS_PSA_CRYPTO_C` is disabled.
+* There is a PSA driver which has not been initialized (this happens in `psa_crypto_init()`).
+* For ciphers, the keystore is not initialized yet, and Mbed TLS uses a custom implementation of PSA ITS where the file system is not accessible yet (because something else needs to happen first, and the application takes care that it happens before it calls `psa_crypto_init()`). A possible workaround may be to dispatch to the internal functions that are called after the keystore lookup, rather than to the PSA API functions (but this is incompatible with `MBEDTLS_PSA_CRYPTO_CLIENT`).
+* The requested mechanism is enabled in the legacy interface but not in the PSA interface. This was not really intended, but is possible, for example, if you enable `MBEDTLS_MD5_C` for PEM decoding with PBKDF1 but don't want `PSA_ALG_WANT_MD5` because it isn't supported for `PSA_ALG_RSA_PSS` and `PSA_ALG_DETERMINISTIC_ECDSA`.
+* `MBEDTLS_PSA_CRYPTO_CLIENT` is enabled, and the client has not yet activated the connection to the server (this happens in `psa_crypto_init()`).
+* `MBEDTLS_PSA_CRYPTO_CLIENT` is enabled, but the operation is part of the implementation of an encrypted communication with the crypto service, or the local implementation is faster because it avoids a costly remote procedure call.
+
+#### Indirect knowledge
+
+Consider for example the code in `rsa.c` to perform an RSA-PSS signature. It needs to calculate a hash. If `mbedtls_rsa_rsassa_pss_sign()` is called directly by application code, it is supposed to call the built-in implementation: calling a PSA accelerator would be a behavior change, acceptable only if this does not add a risk of failure or performance degradation ([PSA is impossible or undesirable in some circumstances](#why-psa-is-not-always-possible)). Note that this holds regardless of the state of `MBEDTLS_USE_PSA_CRYPTO`, since `rsa.h` is outside the scope of `MBEDTLS_USE_PSA_CRYPTO`. On the other hand, if `mbedtls_rsa_rsassa_pss_sign()` is called from X.509 code, it should use PSA to calculate hashes. It doesn't, currently, which is [bug \#6497](https://github.com/Mbed-TLS/mbedtls/issues/6497).
+
+Generally speaking, modules in the mixed domain:
+
+* must call PSA if called by a module in the PSA domain;
+* must not call PSA (or must have a fallback) if their caller is not in the PSA domain and the PSA call is not guaranteed to work.
+
+#### Non-support guarantees: requirements
+
+Generally speaking, just because some feature is not enabled in `mbedtls_config.h` or `psa_config.h` doesn't guarantee that it won't be enabled in the build. We can enable additional features through `build_info.h`.
+
+If `PSA_WANT_xxx` is disabled, this should guarantee that attempting xxx through the PSA API will fail. This is generally guaranteed by the test suite `test_suite_psa_crypto_not_supported` with automatically enumerated test cases, so it would be inconvenient to carve out an exception.
+
+### Technical requirements
+
+Based on the preceding analysis, the core of the problem is: for code in the mixed domain (see [“Classification of callers”](#classification-of-callers)), how do we handle a cryptographic mechanism? This has several related subproblems:
+
+* How the mechanism is encoded (e.g. `mbedtls_md_type_t` vs `const *mbedtls_md_info_t` vs `psa_algorithm_t` for hashes).
+* How to decide whether a specific algorithm or key type is supported (eventually based on `MBEDTLS_xxx_C` vs `PSA_WANT_xxx`).
+* How to obtain metadata about algorithms (e.g. hash/MAC/tag size, key size).
+* How to perform the operation (context type, which functions to call).
+
+We need a way to decide this based on the available information:
+
+* Who's the ultimate caller — see [indirect knowledge](#indirect-knowledge) — which is not actually available.
+* Some parameter indicating which algorithm to use.
+* The available cryptographic implementations, based on preprocessor symbols (`MBEDTLS_xxx_C`, `PSA_WANT_xxx`, `MBEDTLS_PSA_ACCEL_xxx`, etc.).
+* Possibly additional runtime state (for example, we might check whether `psa_crypto_init` has been called).
+
+And we need to take care of the [the cases where PSA is not possible](#why-psa-is-not-always-possible): either make sure the current behavior is preserved, or (where allowed by backward compatibility) document a behavior change and, preferably, a workaround.
+
+### Working through an example: RSA-PSS
+
+Let us work through the example of RSA-PSS which calculates a hash, as in [see issue \#6497](https://github.com/Mbed-TLS/mbedtls/issues/6497).
+
+RSA is in the [mixed domain](#classification-of-callers). So:
+
+* When called from `psa_sign_hash` and other PSA functions, it must call the PSA hash accelerator if there is one.
+* When called from user code, it must call the built-in hash implementation if PSA is not available (regardless of whether this is because `MBEDTLS_PSA_CRYPTO_C` is disabled, or because `PSA_WANT_ALG_xxx` is disabled for this hash, or because there is an accelerator driver which has not been initialized yet).
+
+RSA knows which hash algorithm to use based on a parameter of type `mbedtls_md_type_t`. (More generally, all mixed-domain modules that take an algorithm specification as a parameter take it via a numerical type, except HMAC\_DRBG and HKDF which take a `const mbedtls_md_info_t*` instead, and CMAC which takes a `const mbedtls_cipher_info_t *`.)
+
+#### Double encoding solution
+
+A natural solution is to double up the encoding of hashes in `mbedtls_md_type_t`. Pass `MBEDTLS_MD_SHA256` and `md` will dispatch to the legacy code, pass a new constant `MBEDTLS_MD_SHA256_USE_PSA` and `md` will dispatch through PSA.
+
+This maximally preserves backward compatibility, but then no non-PSA code benefits from PSA accelerators, and there's little potential for removing the software implementation.
+
+#### Availability of hashes in RSA-PSS
+
+Here we try to answer the question: As a caller of RSA-PSS via `rsa.h`, how do I know whether it can use a certain hash?
+
+* For a caller in the legacy domain: if e.g. `MBEDTLS_SHA256_C` is enabled, then I want RSA-PSS to support SHA-256. I don't care about negative support. So `MBEDTLS_SHA256_C` must imply support for RSA-PSS-SHA-256. It must work at all times, regardless of the state of PSA (e.g. drivers not initialized).
+* For a caller in the PSA domain: if e.g. `PSA_WANT_ALG_SHA_256` is enabled, then I want RSA-PSS to support SHA-256, provided that `psa_crypto_init()` has been called. In some limited cases, such as `test_suite_psa_crypto_not_supported` when PSA implements RSA-PSS in software, we care about negative support: if `PSA_WANT_ALG_SHA_256` is disabled then `psa_verify_hash` must reject `PSA_WANT_ALG_SHA_256`. This can be done at the level of PSA before it calls the RSA module, though, so it doesn't have any implication on the RSA module. As far as `rsa.c` is concerned, what matters is that `PSA_WANT_ALG_SHA_256` implies that SHA-256 is supported after `psa_crypto_init()` has been called.
+* For a caller in the mixed domain: requirements depend on the caller. Whatever solution RSA has to determine the availability of algorithms will apply to its caller as well.
+
+Conclusion so far: RSA must be able to do SHA-256 if either `MBEDTLS_SHA256_C` or `PSA_WANT_ALG_SHA_256` is enabled. If only `PSA_WANT_ALG_SHA_256` and not `MBEDTLS_SHA256_C` is enabled (which implies that PSA's SHA-256 comes from an accelerator driver), then SHA-256 only needs to work if `psa_crypto_init()` has been called.
+
+#### More in-depth discussion of compile-time availability determination
+
+The following combinations of compile-time support are possible:
+
+* `MBEDTLS_PSA_CRYPTO_CLIENT`. Then calling PSA may or may not be desirable for performance. There are plausible use cases where only the server has access to an accelerator so it's best to call the server, and plausible use cases where calling the server has overhead that negates the savings from using acceleration, if there are savings at all. In any case, calling PSA only works if the connection to the server has been established, meaning `psa_crypto_init` has been called successfully. In the rest of this case enumeration, assume `MBEDTLS_PSA_CRYPTO_CLIENT` is disabled.
+* No PSA accelerator. Then just call `mbedtls_sha256`, it's all there is, and it doesn't matter (from an API perspective) exactly what call chain leads to it.
+* PSA accelerator, no software implementation. Then we might as well call the accelerator, unless it's important that the call fails. At the time of writing, I can't think of a case where we would want to guarantee that if `MBEDTLS_xxx_C` is not enabled, but xxx is enabled through PSA, then a request to use algorithm xxx through some legacy interface must fail.
+* Both PSA acceleration and the built-in implementation. In this case, we would prefer PSA for the acceleration, but we can only do this if the accelerator driver is working. For hashes, it's enough to assume the driver is initialized; we've [considered requiring hash drivers to work without initialization](https://github.com/Mbed-TLS/mbedtls/pull/6470). For ciphers, this is more complicated because the cipher functions require the keystore, and plausibly a cipher accelerator might want entropy (for side channel countermeasures) which might not be available at boot time.
+
+Note that it's a bit tricky to determine which algorithms are available. In the case where there is a PSA accelerator but no software implementation, we don't want the preprocessor symbols to indicate that the algorithm is available through the legacy domain, only through the PSA domain. What does this mean for the interfaces in the mixed domain? They can't guarantee the availability of the algorithm, but they must try if requested.
+
+### Designing an interface for hashes
+
+In this section, we specify a hash metadata and calculation for the [mixed domain](#classification-of-callers), i.e. code that can be called both from legacy code and from PSA code.
+
+#### Availability of hashes
+
+Generalizing the analysis in [“Availability of hashes in RSA-PSS”](#availability-of-hashes-in-RSA-PSS):
+
+A hash is available through the mixed-domain interface iff either of the following conditions is true:
+
+* A legacy hash interface is available and the hash algorithm is implemented in software.
+* PSA crypto is enabled and the hash algorithm is implemented via PSA.
+
+We could go further and make PSA accelerators available to legacy callers that call any legacy hash interface, e.g. `md.h` or `shaX.h`. There is little point in doing this, however: callers should just use the mixed-domain interface.
+
+#### Implications between legacy availability and PSA availability
+
+* When `MBEDTLS_PSA_CRYPTO_CONFIG` is disabled, all legacy mechanisms are automatically enabled through PSA. Users can manually enable PSA mechanisms that are available through accelerators but not through legacy, but this is not officially supported (users are not supposed to manually define PSA configuration symbols when `MBEDTLS_PSA_CRYPTO_CONFIG` is disabled).
+* When `MBEDTLS_PSA_CRYPTO_CONFIG` is enabled, there is no mandatory relationship between PSA support and legacy support for a mechanism. Users can configure legacy support and PSA support independently. Legacy support is automatically enabled if PSA support is requested, but only if there is no accelerator.
+
+It is strongly desirable to allow mechanisms available through PSA but not legacy: this allows saving code size when an accelerator is present.
+
+There is no strong reason to allow mechanisms available through legacy but not PSA when `MBEDTLS_PSA_CRYPTO_C` is enabled. This would only save at best a very small amount of code size in the PSA dispatch code. This may be more desirable when `MBEDTLS_PSA_CRYPTO_CLIENT` is enabled (having a mechanism available only locally and not in the crypto service), but we do not have an explicit request for this and it would be entirely reasonable to forbid it.
+
+In this analysis, we have not found a compelling reason to require all legacy mechanisms to also be available through PSA. However, this can simplify both the implementation and the use of dispatch code thanks to some simplifying properties:
+
+* Mixed-domain code can call PSA code if it knows that `psa_crypto_init()` has been called, without having to inspect the specifics of algorithm support.
+* Mixed-domain code can assume that PSA buffer calculations work correctly for all algorithms that it supports.
+
+#### Shape of the mixed-domain hash interface
+
+We now need to create an abstraction for mixed-domain hash calculation. (We could not create an abstraction, but that would require every piece of mixed-domain code to replicate the logic here. We went that route in Mbed TLS 3.3, but it made it effectively impossible to get something that works correctly.)
+
+Requirements: given a hash algorithm,
+
+* Obtain some metadata about it (size, block size).
+* Calculate the hash.
+* Set up a multipart operation to calculate the hash. The operation must support update, finish, reset, abort, clone.
+
+The existing interface in `md.h` is close to what we want, but not perfect. What's wrong with it?
+
+* It has an extra step of converting from `mbedtls_md_type_t` to `const mbedtls_md_info_t *`.
+* It includes extra fluff such as names and HMAC. This costs code size.
+* The md module has some legacy baggage dating from when it was more open, which we don't care about anymore. This may cost code size.
+
+These problems are easily solvable.
+
+* `mbedtls_md_info_t` can become a very thin type. We can't remove the extra function call from the source code of callers, but we can make it a very thin abstraction that compilers can often optimize.
+* We can make names and HMAC optional. The mixed-domain hash interface won't be the full `MBEDTLS_MD_C` but a subset.
+* We can optimize `md.c` without making API changes to `md.h`.
+
+### Scope reductions and priorities for 3.x
+
+This section documents things that we chose to temporarily exclude from the scope in the 3.x branch (which will eventually be in scope again after 4.0) as well as things we chose to prioritize if we don't have time to support everything.
+
+#### Don't support PK, X.509 and TLS without `MBEDTLS_USE_PSA_CRYPTO`
+
+We do not need to support driver-only hashes and ciphers in PK. X.509 and TLS without `MBEDTLS_USE_PSA_CRYPTO`. Users who want to take full advantage of drivers will need to enabled this macro.
+
+Note that this applies to TLS 1.3 as well, as some uses of hashes and all uses of ciphers there are common with TLS 1.2, hence governed by `MBEDTLS_USE_PSA_CRYPTO`, see [this macro's extended documentation](../../docs/use-psa-crypto.html).
+
+This will go away naturally in 4.0 when this macros is not longer an option (because it's always on).
+
+#### Don't support for `MBEDTLS_PSA_CRYPTO_CLIENT` without `MBEDTLS_PSA_CRYPTO_C`
+
+We generally don't really support builds with `MBEDTLS_PSA_CRYPTO_CLIENT` without `MBEDTLS_PSA_CRYPTO_C`. For example, both `MBEDTLS_USE_PSA_CRYPTO` and `MBEDTLS_SSL_PROTO_TLS1_3` require `MBEDTLS_PSA_CRYPTO_C`, while in principle they should only require `MBEDTLS_PSA_CRYPTO_CLIENT`.
+
+Considering this existing restriction which we do not plan to lift before 4.0, it is acceptable driver-only hashes and cipher support to have the same restriction in 3.x.
+
+It is however desirable for the design to keep support for `MBEDTLS_PSA_CRYPTO_CLIENT` in mind, in order to avoid making it more difficult to add in the future.
+
+#### For cipher: prioritize constrained devices and modern TLS
+
+The primary target is a configuration like TF-M's medium profile, plus TLS with only AEAD ciphersuites.
+
+This excludes things like:
+- Support for encrypted PEM, PKCS5 and PKCS12 encryption, and PKCS8 encrypted keys in PK parse. (Not widely used on highly constrained devices.)
+- Support for NIST-KW. (Same justification.)
+- Support for CMAC. (Same justification, plus can be directly accelerated.)
+- Support for CBC ciphersuites in TLS. (They've been recommended against for a while now.)
+
+### Dual-dispatch for block cipher primitives
+
+Considering the priorities stated above, initially we want to support GCM, CCM and CTR-DRBG. All three of them use the block cipher primitive only in the encrypt direction. Currently, GCM and CCM use the Cipher layer in order to work with AES, Aria and Camellia (DES is excluded by the standards due to its smaller block size) and CTR-DRBG directly uses the low-level API from `aes.h`. In all cases, access to the "block cipher primitive" is done by using "ECB mode" (which for both Cipher and `aes.h` only allows a single block, contrary to PSA which implements actual ECB mode).
+
+The two AEAD modes, GCM and CCM, have very similar needs and positions in the stack, strongly suggesting using the same design for both. On the other hand, there are a number of differences between CTR-DRBG and them.
+- CTR-DRBG only uses AES (and there is no plan to extend it to other block ciphers at the moment), while GCM and CCM need to work with 3 block ciphers already.
+- CTR-DRBG holds a special position in the stack: most users don't care about it per se, they only care about getting random numbers - in fact PSA users don't even need to know what DRBG is used. In particular, no part of the stack is asking questions like "is CTR-DRBG-AES available?" - an RNG needs to be available and that's it - contrary to similar questions about AES-GCM etc. which are asked for example by TLS.
+
+So, it makes sense to use different designs for CTR-DRBG on one hand, and GCM/CCM on the other hand:
+- CTR-DRBG can just check if `AES_C` is present and "fall back" to PSA if not.
+- GCM and CCM need an common abstraction layer that allows:
+  - Using AES, Aria or Camellia in a uniform way.
+  - Dispatching to built-in or driver.
+
+The abstraction layer used by GCM and CCM may either be a new internal module, or a subset of the existing Cipher API, extended with the ability to dispatch to a PSA driver.
+
+Reasons for making this layer's API a subset of the existing Cipher API:
+- No need to design, implement and test a new module. (Will need to test the new subset though, as well as the extended behaviour.)
+- No code change in GCM and CCM - only need to update dependencies.
+- No risk for code duplication between a potential new module and Cipher: source-level, and in in particular in builds that still have `CIPHER_C` enabled. (Compiled-code duplication could be avoided by excluding the new module in such builds, though.)
+- If want to support other users of Cipher later (such as NIST-KW, CMAC, PKCS5 and PKCS12), we can just extend dual-dispatch support to other modes/operations in Cipher and keep those extra modules unchanged as well.
+
+Possible costs of re-using (a subset of) the existing Cipher API instead of defining a new one:
+- We carry over costs associated with `cipher_info_t` structures. (Currently the info structure is used for 3 things: (1) to check if the cipher is supported, (2) to check its block size, (3) because `setup()` requires it).
+- We carry over questionable implementation decisions, like dynamic allocation of context.
+
+Those costs could be avoided by refactoring (parts of) Cipher, but that would probably mean either:
+- significant differences in how the `cipher.h` API is implemented between builds with the full Cipher or only a subset;
+- or more work to apply the simplifications to all of Cipher.
+
+Prototyping both approaches showed better code size savings and cleaner code with a new internal module (see section "Internal "block cipher" abstraction (Cipher light)" below).
+
+## Specification
+
+### MD light
+
+#### Definition of MD light
+
+MD light is a subset of `md.h` that implements the hash calculation interface described in ”[Designing an interface for hashes](#designing-an-interface-for-hashes)”. It is activated by `MBEDTLS_MD_LIGHT` in `mbedtls_config.h`.
+
+The following things enable MD light automatically in `build_info.h`:
+
+* A [mixed-domain](#classification-of-callers) module that needs to calculate hashes is enabled.
+* `MBEDTLS_MD_C` is enabled.
+
+MD light includes the following types:
+
+* `mbedtls_md_type_t`
+* `mbedtls_md_info_t`
+* `mbedtls_md_context_t`
+
+MD light includes the following functions:
+
+* `mbedtls_md_info_from_type`
+* `mbedtls_md_init`
+* `mbedtls_md_free`
+* `mbedtls_md_setup` — but `hmac` must be 0 if `MBEDTLS_MD_C` is disabled.
+* `mbedtls_md_clone`
+* `mbedtls_md_get_size`
+* `mbedtls_md_get_type`
+* `mbedtls_md_starts`
+* `mbedtls_md_update`
+* `mbedtls_md_finish`
+* `mbedtls_md`
+
+Unlike the full MD, MD light does not support null pointers as `mbedtls_md_context_t *`. At least some functions still need to support null pointers as `const mbedtls_md_info_t *` because this arises when you try to use an unsupported algorithm (`mbedtls_md_info_from_type` returns `NULL`).
+
+#### MD algorithm support macros
+
+For each hash algorithm, `md.h` defines a macro `MBEDTLS_MD_CAN_xxx` whenever the corresponding hash is available through MD light. These macros are only defined when `MBEDTLS_MD_LIGHT` is enabled. Per “[Availability of hashes](#availability-of-hashes)”, `MBEDTLS_MD_CAN_xxx` is enabled if:
+
+* the corresponding `MBEDTLS_xxx_C` is defined; or
+* one of `MBEDTLS_PSA_CRYPTO_C` or `MBEDTLS_PSA_CRYPTO_CLIENT` is enabled, and the corresponding `PSA_WANT_ALG_xxx` is enabled.
+
+Note that some algorithms have different spellings in legacy and PSA. Since MD is a legacy interface, we'll use the legacy names. Thus, for example:
+
+```
+#if defined(MBEDTLS_MD_LIGHT)
+#if defined(MBEDTLS_SHA256_C) || \
+    (defined(MBEDTLS_PSA_CRYPTO_C) && PSA_WANT_ALG_SHA_256)
+#define MBEDTLS_MD_CAN_SHA256
+#endif
+#endif
+```
+
+Note: in the future, we may want to replace `defined(MBEDTLS_PSA_CRYPTO_C)`
+with `defined(MBEDTLS_PSA_CRYTO_C) || defined(MBEDTLS_PSA_CRYPTO_CLIENT)` but
+for now this is out of scope.
+
+#### MD light internal support macros
+
+* If at least one hash has a PSA driver, define `MBEDTLS_MD_SOME_PSA`.
+* If at least one hash has a legacy implementation, defined `MBEDTLS_MD_SOME_LEGACY`.
+
+#### Support for PSA in the MD context
+
+An MD context needs to contain either a legacy module's context (or a pointer to one, as is the case now), or a PSA context (or a pointer to one).
+
+I am inclined to remove the pointer indirection, but this means that an MD context would always be as large as the largest supported hash context. So for the time being, this specification keeps a pointer. For uniformity, PSA will also have a pointer (we may simplify this later).
+
+```
+enum {
+    MBEDTLS_MD_ENGINE_LEGACY,
+    MBEDTLS_MD_ENGINE_PSA,
+} mbedtls_md_engine_t; // private type
+
+typedef struct mbedtls_md_context_t {
+    mbedtls_md_type_t type;
+#if defined(MBEDTLS_MD_SOME_PSA)
+    mbedtls_md_engine_t engine;
+#endif
+    void *md_ctx; // mbedtls_xxx_context or psa_hash_operation
+#if defined(MBEDTLS_MD_C)
+    void *hmac_ctx;
+#endif
+} mbedtls_md_context_t;
+```
+
+All fields are private.
+
+The `engine` field is almost redundant with knowledge about `type`. However, when an algorithm is available both via a legacy module and a PSA accelerator, we will choose based on the runtime availability of the accelerator when the context is set up. This choice needs to be recorded in the context structure.
+
+#### Inclusion of MD info structures
+
+MD light needs to support hashes that are only enabled through PSA. Therefore the `mbedtls_md_info_t` structures must be included based on `MBEDTLS_MD_CAN_xxx` instead of just the legacy module.
+
+The same criterion applies in `mbedtls_md_info_from_type`.
+
+#### Conversion to PSA encoding
+
+The implementation needs to convert from a legacy type encoding to a PSA encoding.
+
+```
+static inline psa_algorithm_t psa_alg_of_md_info(
+    const mbedtls_md_info_t *md_info );
+```
+
+#### Determination of PSA support at runtime
+
+```
+int psa_can_do_hash(psa_algorithm_t hash_alg);
+```
+
+The job of this private function is to return 1 if `hash_alg` can be performed through PSA now, and 0 otherwise. It is only defined on algorithms that are enabled via PSA.
+
+As a starting point, return 1 if PSA crypto's driver subsystem has been initialized.
+
+Usage note: for algorithms that are not enabled via PSA, calling `psa_can_do_hash` is generally safe: whether it returns 0 or 1, you can call a PSA hash function on the algorithm and it will return `PSA_ERROR_NOT_SUPPORTED`.
+
+#### Support for PSA dispatch in hash operations
+
+Each function that performs some hash operation or context management needs to know whether to dispatch via PSA or legacy.
+
+If given an established context, use its `engine` field.
+
+If given an algorithm as an `mbedtls_md_type_t type` (possibly being the `type` field of a `const mbedtls_md_info_t *`):
+
+* If there is a PSA accelerator for this hash and `psa_can_do_hash(alg)`, call the corresponding PSA function, and if applicable set the engine to `MBEDTLS_MD_ENGINE_PSA`. (Skip this is `MBEDTLS_MD_SOME_PSA` is not defined.)
+* Otherwise dispatch to the legacy module based on the type as currently done. (Skip this is `MBEDTLS_MD_SOME_LEGACY` is not defined.)
+* If no dispatch is possible, return `MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE`.
+
+Note that this assumes that an operation that has been started via PSA can be completed. This implies that `mbedtls_psa_crypto_free` must not be called while an operation using PSA is in progress. Document this.
+
+#### Error code conversion
+
+After calling a PSA function, MD light calls `mbedtls_md_error_from_psa` to convert its status code.
+
+### Support all legacy algorithms in PSA
+
+As discussed in [“Implications between legacy availability and PSA availability”](#implications-between-legacy-availability-and-psa-availability), we require the following property:
+
+> If an algorithm has a legacy implementation, it is also available through PSA.
+
+When `MBEDTLS_PSA_CRYPTO_CONFIG` is disabled, this is already the case. When is enabled, we will now make it so as well. Change `include/mbedtls/config_psa.h` accordingly.
+
+### MD light optimizations
+
+This section is not necessary to implement MD light, but will cut down its code size.
+
+#### Split names out of MD light
+
+Remove hash names from `mbedtls_md_info_t`. Use a simple switch-case or a separate list to implement `mbedtls_md_info_from_string` and `mbedtls_md_get_name`.
+
+#### Remove metadata from the info structure
+
+In `mbedtls_md_get_size` and in modules that want a hash's block size, instead of looking up hash metadata in the info structure, call the PSA macros.
+
+#### Optimize type conversions
+
+To allow optimizing conversions between `mbedtls_md_type_t` and `psa_algorithm_t`, renumber the `mbedtls_md_type_t` enum so that the values are the 8 lower bits of the PSA encoding.
+
+With this optimization,
+```
+static inline psa_algorithm_t psa_alg_of_md_info(
+    const mbedtls_md_info_t *md_info )
+{
+    if( md_info == NULL )
+        return( PSA_ALG_NONE );
+    return( PSA_ALG_CATEGORY_HASH | md_info->type );
+}
+```
+
+Work in progress on this conversion is at https://github.com/gilles-peskine-arm/mbedtls/tree/hash-unify-ids-wip-1
+
+#### Unify HMAC with PSA
+
+PSA has its own HMAC implementation. In builds with both `MBEDTLS_MD_C` and `PSA_WANT_ALG_HMAC` not fully provided by drivers, we should have a single implementation. Replace the one in `md.h` by calls to the PSA driver interface. This will also give mixed-domain modules access to HMAC accelerated directly by a PSA driver (eliminating the need to a HMAC interface in software if all supported hashes have an accelerator that includes HMAC support).
+
+### Improving support for `MBEDTLS_PSA_CRYPTO_CLIENT`
+
+So far, MD light only dispatches to PSA if an algorithm is available via `MBEDTLS_PSA_CRYPTO_C`, not if it's available via `MBEDTLS_PSA_CRYPTO_CLIENT`. This is acceptable because `MBEDTLS_USE_PSA_CRYPTO` requires `MBEDTLS_PSA_CRYPTO_C`, hence mixed-domain code never invokes PSA.
+
+The architecture can be extended to support `MBEDTLS_PSA_CRYPTO_CLIENT` with a little extra work. Here is an overview of the task breakdown, which should be fleshed up after we've done the first [migration](#migration-to-md-light):
+
+* Compile-time dependencies: instead of checking `defined(MBEDTLS_PSA_CRYPTO_C)`, check `defined(MBEDTLS_PSA_CRYPTO_C) || defined(MBEDTLS_PSA_CRYPTO_CLIENT)`.
+* Implementers of `MBEDTLS_PSA_CRYPTO_CLIENT` will need to provide `psa_can_do_hash()` (or a more general function `psa_can_do`) alongside `psa_crypto_init()`. Note that at this point, it will become a public interface, hence we won't be able to change it at a whim.
+
+### Internal "block cipher" abstraction (previously known as "Cipher light")
+
+#### Definition
+
+The new module is automatically enabled in `config_adjust_legacy_crypto.h` by modules that need
+it (namely: CCM, GCM) only when `CIPHER_C` is not available, or the new module
+is needed for PSA dispatch (see next section). Note: CCM and GCM currently
+depend on the full `CIPHER_C` (enforced by `check_config.h`); this hard
+dependency would be replaced by the above auto-enablement.
+
+The following API functions are offered:
+```
+void mbedtls_block_cipher_init(mbedtls_block_cipher_context_t *ctx);
+void mbedtls_block_cipher_free(mbedtls_block_cipher_context_t *ctx);
+int mbedtls_block_cipher_setup(mbedtls_block_cipher_context_t *ctx,
+                               mbedtls_cipher_id_t cipher_id);
+int mbedtls_block_cipher_setkey(mbedtls_block_cipher_context_t *ctx,
+                                const unsigned char *key,
+                                unsigned key_bitlen);
+int mbedtls_block_cipher_encrypt(mbedtls_block_cipher_context_t *ctx,
+                                 const unsigned char input[16],
+                                 unsigned char output[16]);
+```
+
+The only supported ciphers are AES, ARIA and Camellia. They are identified by
+an `mbedtls_cipher_id_t` in the `setup()` function, because that's how they're
+identifed by callers (GCM/CCM).
+
+#### Block cipher dual dispatch
+
+Support for dual dispatch in the new internal module `block_cipher` is extremely similar to that in MD light.
+
+A block cipher context contains either a legacy module's context (AES, ARIA, Camellia) or a PSA key identifier; it has a field indicating which one is in use. All fields are private.
+
+The `engine` field is almost redundant with knowledge about `type`. However, when an algorithm is available both via a legacy module and a PSA accelerator, we will choose based on the runtime availability of the accelerator when the context is set up. This choice needs to be recorded in the context structure.
+
+Support is determined at runtime using the new internal function
+```
+int psa_can_do_cipher(psa_key_type_t key_type, psa_algorithm_t cipher_alg);
+```
+
+The job of this private function is to return 1 if `hash_alg` can be performed through PSA now, and 0 otherwise. It is only defined on algorithms that are enabled via PSA. As a starting point, return 1 if PSA crypto's driver subsystem has been initialized.
+
+Each function in the module needs to know whether to dispatch via PSA or legacy. All functions consult the context's `engine` field, except `setup()` which will set it according to the key type and the return value of `psa_can_do_cipher()` as discussed above.
+
+Note that this assumes that an operation that has been started via PSA can be completed. This implies that `mbedtls_psa_crypto_free` must not be called while an operation using PSA is in progress.
+
+After calling a PSA function, `block_cipher` functions call `mbedtls_cipher_error_from_psa` to convert its status code.
diff --git a/docs/architecture/psa-migration/outcome-analysis.sh b/docs/architecture/psa-migration/outcome-analysis.sh
new file mode 100755
index 0000000..1805a3c
--- /dev/null
+++ b/docs/architecture/psa-migration/outcome-analysis.sh
@@ -0,0 +1,140 @@
+#!/bin/sh
+
+# This script runs tests before and after a PR and analyzes the results in
+# order to highlight any difference in the set of tests skipped.
+#
+# It can be used to check the first testing criterion mentioned in strategy.md,
+# end of section "Supporting builds with drivers without the software
+# implementation", namely: the sets of tests skipped in the default config and
+# the full config must be the same before and after the PR.
+#
+# USAGE:
+# - First, commit any uncommited changes. (Also, see warning below.)
+# - Then launch --> [SKIP_SSL_OPT=1] docs/architecture/psa-migration/outcome-analysis.sh
+#     - SKIP_SSL_OPT=1 can optionally be set to skip ssl-opt.sh tests
+#
+# WARNING: this script checks out a commit other than the head of the current
+# branch; it checks out the current branch again when running successfully,
+# but while the script is running, or if it terminates early in error, you
+# should be aware that you might be at a different commit than expected.
+#
+# NOTE: you can comment out parts that don't need to be re-done when
+# re-running this script (for example "get numbers before this PR").
+
+set -eu
+
+: ${SKIP_SSL_OPT:=0}
+
+cleanup() {
+    make clean
+    git checkout -- include/mbedtls/mbedtls_config.h include/psa/crypto_config.h
+}
+
+record() {
+    export MBEDTLS_TEST_OUTCOME_FILE="$PWD/outcome-$1.csv"
+    rm -f $MBEDTLS_TEST_OUTCOME_FILE
+
+    make check
+
+    if [ $SKIP_SSL_OPT -eq 0 ]; then
+        make -C programs ssl/ssl_server2 ssl/ssl_client2 \
+            test/udp_proxy test/query_compile_time_config
+        tests/ssl-opt.sh
+    fi
+}
+
+# save current HEAD.
+# Note: this can optionally be updated to
+#   HEAD=$(git branch --show-current)
+# when using a Git version above 2.22
+HEAD=$(git rev-parse --abbrev-ref HEAD)
+
+# get the numbers before this PR for default and full
+cleanup
+git checkout $(git merge-base HEAD development)
+
+record "before-default"
+
+cleanup
+
+scripts/config.py full
+record "before-full"
+
+# get the numbers now for default and full
+cleanup
+git checkout $HEAD
+
+record "after-default"
+
+cleanup
+
+scripts/config.py full
+record "after-full"
+
+cleanup
+
+# analysis
+
+populate_suites () {
+    SUITES=''
+    make generated_files >/dev/null
+    data_files=$(cd tests/suites && echo *.data)
+    for data in $data_files; do
+        suite=${data%.data}
+        SUITES="$SUITES $suite"
+    done
+    make neat
+
+    if [ $SKIP_SSL_OPT -eq 0 ]; then
+        SUITES="$SUITES ssl-opt"
+        extra_files=$(cd tests/opt-testcases && echo *.sh)
+        for extra in $extra_files; do
+            suite=${extra%.sh}
+            SUITES="$SUITES $suite"
+        done
+    fi
+}
+
+compare_suite () {
+    ref="outcome-$1.csv"
+    new="outcome-$2.csv"
+    suite="$3"
+
+    pattern_suite=";$suite;"
+    total=$(grep -c "$pattern_suite" "$ref")
+    sed_cmd="s/^.*$pattern_suite\(.*\);SKIP.*/\1/p"
+    sed -n "$sed_cmd" "$ref" > skipped-ref
+    sed -n "$sed_cmd" "$new" > skipped-new
+    nb_ref=$(wc -l <skipped-ref)
+    nb_new=$(wc -l <skipped-new)
+
+    name=${suite#test_suite_}
+    printf "%40s: total %4d; skipped %4d -> %4d\n" \
+            $name       $total       $nb_ref $nb_new
+    if diff skipped-ref skipped-new | grep '^> '; then
+        ret=1
+    else
+        ret=0
+    fi
+    rm skipped-ref skipped-new
+    return $ret
+}
+
+compare_builds () {
+    printf "\n*** Comparing $1 -> $2 ***\n"
+    failed=''
+    for suite in $SUITES; do
+        if compare_suite "$1" "$2" "$suite"; then :; else
+            failed="$failed $suite"
+        fi
+    done
+    if [ -z "$failed" ]; then
+        printf "No coverage gap found.\n"
+    else
+        printf "Suites with less coverage:%s\n" "$failed"
+    fi
+}
+
+populate_suites
+compare_builds before-default after-default
+compare_builds before-full after-full
diff --git a/docs/architecture/psa-migration/psa-legacy-bridges.md b/docs/architecture/psa-migration/psa-legacy-bridges.md
new file mode 100644
index 0000000..912344e
--- /dev/null
+++ b/docs/architecture/psa-migration/psa-legacy-bridges.md
@@ -0,0 +1,344 @@
+Bridges between legacy and PSA crypto APIs
+==========================================
+
+## Introduction
+
+### Goal of this document
+
+This document explores the needs of applications that use both Mbed TLS legacy crypto interfaces and PSA crypto interfaces. Based on [requirements](#requirements), we [analyze gaps](#gap-analysis) and [API design](#api-design).
+
+This is a design document. The target audience is library maintainers. See the companion document [“Transitioning to the PSA API”](../../psa-transition.md) for a user focus on the same topic.
+
+### Keywords
+
+* [TODO] A part of the analysis that isn't finished.
+* [OPEN] Open question: a specific aspect of the design where there are several plausible decisions.
+* [ACTION] A finalized part of the design that will need to be carried out.
+
+### Context
+
+Mbed TLS 3.x supports two cryptographic APIs:
+
+* The legacy API `mbedtls_xxx` is inherited from PolarSSL.
+* The PSA API `psa_xxx` was introduced in Mbed TLS 2.17.
+
+Mbed TLS is gradually shifting from the legacy API to the PSA API. Mbed TLS 4.0 will be the first version where the PSA API is considered the main API, and large parts of the legacy API will be removed.
+
+In Mbed TLS 4.0, the cryptography will be provided by a separate project [TF-PSA-Crypto](https://github.com/Mbed-TLS/TF-PSA-Crypto). For simplicity, in this document, we just refer to the whole as “Mbed TLS”.
+
+### Document history
+
+This document was originally written when preparing Mbed TLS 3.6. Mbed TLS 3.6 includes both PSA and legacy APIs covering largely overlapping ground. Many legacy APIs will be removed in Mbed TLS 4.0.
+
+## Requirements
+
+### Why mix APIs?
+
+There is functionality that is tied to one API and is not directly available in the other API:
+
+* Only PSA fully supports PSA accelerators and secure element integration.
+* Only PSA supports isolating cryptographic material in a secure service.
+* The legacy API has features that are not present (yet) in PSA, notably parsing and formatting asymmetric keys.
+
+The legacy API can partially leverage PSA features via `MBEDTLS_USE_PSA_CRYPTO`, but this has limited scope.
+
+In addition, many applications cannot be migrated in a single go. For large projects, it is impractical to rewrite a significant part of the code all at once. (For example, Mbed TLS itself will have taken more than 6 years to transition.) Projects that use one or more library in addition to Mbed TLS must follow the evolution of these libraries, each of which might have its own pace.
+
+### Where mixing happens
+
+Mbed TLS can be, and normally is, built with support for both APIs. Therefore no special effort is necessary to allow an application to use both APIs.
+
+Special effort is necessary to use both APIs as part of the implementation of the same feature. From an informal analysis of typical application requirements, we identify four parts of the use of cryptography which can be provided by different APIs:
+
+* Metadata manipulation: parsing and producing encrypted or signed files, finding mutually supported algorithms in a network protocol negotiation, etc.
+* Key management: parsing, generating, deriving and formatting cryptographic keys.
+* Data manipulation other than keys. In practice, most data formats within the scope of the legacy crypto APIs are trivial (ciphertexts, hashes, MACs, shared secrets). The one exception is ECDSA signatures.
+* Cryptographic operations: hash, sign, encrypt, etc.
+
+From this, we deduce the following requirements:
+
+* Convert between PSA and legacy metadata.
+* Creating a key with the legacy API and consuming it in the PSA API.
+* Creating a key with the PSA API and consuming it in the legacy API.
+* Manipulating data formats, other than keys, where the PSA API is lacking.
+
+### Scope limitations
+
+The goal of this document is to bridge the legacy API and the PSA API. The goal is not to provide a PSA way to do everything that is currently possible with the legacy API. The PSA API is less flexible in some regards, and extending it is out of scope in the present study.
+
+With respect to the legacy API, we do not consider functionality of low-level modules for individual algorithms. Our focus is on applications that use high-level legacy crypto modules (md, cipher, pk) and need to combine that with uses of the PSA APIs.
+
+## Gap analysis
+
+The document [“Transitioning to the PSA API”](../../psa-transition.md) enumerates the public header files in Mbed TLS 3.4 and the API elements (especially enums and functions) that they provide, listing PSA equivalents where they exist. There are gaps in two cases:
+
+* Where the PSA equivalents do not provide the same functionality. A typical example is parsing and formatting asymmetric keys.
+* To convert between data representations used by legacy APIs and data representations used by PSA APIs.
+
+Based on “[Where mixing happens](#where-mixing-happens)”, we focus the gap analysis on two topics: metadata and keys. This chapter explores the gaps in each family of cryptographic mechanisms.
+
+### Generic metadata gaps
+
+#### Need for error code conversion
+
+Do we need public functions to convert between `MBEDTLS_ERR_xxx` error codes and `PSA_ERROR_xxx` error codes? We have such functions for internal use.
+
+Mbed TLS needs these conversions because it has many functions that expose one API (legacy/API) but are implemented on top of the other API. Most applications would convert legacy and PSA error code to their own error codes, and converting between `MBEDTLS_ERR_xxx` error codes and `PSA_ERROR_xxx` is not particularly helpful for that. Application code might need such conversion functions when implementing an X.509 or TLS callback (returning `MBEDTLS_ERR_xxx`) on top of PSA functions, but this is a very limited use case.
+
+Conclusion: no need for public error code conversion functions.
+
+### Hash gap analysis
+
+Hashes do not involve keys, and involves no nontrivial data format. Therefore the only gap is with metadata, namely specifying a hash algorithm.
+
+Hashes are often used as building blocks for other mechanisms (HMAC, signatures, key derivation, etc.). Therefore metadata about hashes is relevant not only when calculating hashes, but also when performing many other cryptographic operations.
+
+Gap: functions to convert between `psa_algorithm_t` hash algorithms and `mbedtls_md_type_t`. Such functions exist in Mbed TLS 3.5 (`mbedtls_md_psa_alg_from_type`, `mbedtls_md_type_from_psa_alg`) but they are declared only in private headers.
+
+### MAC gap analysis
+
+[TODO]
+
+### Cipher and AEAD gap analysis
+
+[TODO]
+
+### Key derivation gap analysis
+
+[TODO]
+
+### Random generation gap analysis
+
+[TODO]
+
+### Asymmetric cryptography gap analysis
+
+#### Asymmetric cryptography metadata
+
+The legacy API only has generic support for two key types: RSA and ECC, via the pk module. ECC keys can also be further classified according to their curve. The legacy API also supports DHM (Diffie-Hellman-Merkle = FFDH: finite-field Diffie-Hellman) keys, but those are not integrated in the pk module.
+
+An RSA or ECC key can potentially be used for different algorithms in the scope of the pk module:
+
+* RSA: PKCS#1v1.5 signature, PSS signature, PKCS#1v1.5 encryption, OAEP encryption.
+* ECC: ECDSA signature (randomized or deterministic), ECDH key agreement (via `mbedtls_pk_ec`).
+
+ECC keys are also involved in EC-JPAKE, but this happens internally: the EC-JPAKE interface only needs one piece of metadata, namely, to identify a curve.
+
+Since there is no algorithm that can be used with multiple types, and PSA keys have a policy that (for the most part) limits them to one algorithm, there does not seem to be a need to convert between legacy and PSA asymmetric key types on their own. The useful metadata conversions are:
+
+* Selecting an **elliptic curve**.
+
+  This means converting between an `mbedtls_ecp_group_id` and a pair of `{psa_ecc_family_t; size_t}`.
+
+  This is fulfilled by `mbedtls_ecc_group_to_psa` and `mbedtls_ecc_group_from_psa`, which were introduced into the public API between Mbed TLS 3.5 and 3.6 ([#8664](https://github.com/Mbed-TLS/mbedtls/pull/8664)).
+
+* Selecting A **DHM group**.
+
+  PSA only supports predefined groups, whereas legacy only supports ad hoc groups. An existing application referring to `MBEDTLS_DHM_RFC7919_FFDHExxx` values would need to refer to `PSA_DH_FAMILY_RFC7919`; an existing application using arbitrary groups cannot migrate to PSA.
+
+* Simultaneously supporting **a key type and an algorithm**.
+
+  On the legacy side, this is an `mbedtls_pk_type_t` value and more. For ECDSA, the choice between randomized and deterministic is made at compile time. For RSA, the choice of encryption or signature algorithm is made either by configuring the underlying `mbedtls_rsa_context` or when calling the operation function.
+
+  On the PSA side, this is a `psa_key_type_t` value and an algorithm which is normally encoded as policy information in a `psa_key_attributes_t`. The algorithm is also needed in its own right when calling operation functions.
+
+#### Using a legacy key pair or public key with PSA
+
+There are several scenarios where an application has a legacy key pair or public key (`mbedtls_pk_context`) and needs to create a PSA key object (`psa_key_id_t`).
+
+Reasons for first creating a legacy key object, where it's impossible or impractical to directly create a PSA key:
+
+* A very common case where the input is a legacy key object is parsing. PSA does not (yet) have an equivalent of the `mbedtls_pk_parse_xxx` functions.
+* The PSA key creation interface is less flexible in some cases. In particular, PSA RSA key generation does not (yet) allow choosing the public exponent.
+* The pk object may be created by a part of the application (or a third-party library) that hasn't been migrated to the PSA API yet.
+
+Reasons for needing a PSA key object:
+
+* Using the key with third-party interface that takes a PSA key identifier as input. (Mbed TLS itself has a few TLS functions that take PSA key identifiers, but as of Mbed TLS 3.5, it is always possible to use a legacy key instead.)
+* Benefiting from a PSA accelerator, or from PSA's world separation, even without `MBEDTLS_USE_PSA_CRYPTO`. (Not a priority scenario: we generally expect people to activate `MBEDTLS_USE_PSA_CRYPTO` at an early stage of their migration to PSA.)
+
+Gap: a way to create a PSA key object from an `mbedtls_pk_context`. This partially exists in the form of `mbedtls_pk_wrap_as_opaque`, but it is not fully satisfactory, for reasons that are detailed in “[API to create a PSA key from a PK context](#api-to-create-a-psa-key-from-a-pk-context)” below.
+
+#### Using a PSA key as a PK context
+
+There are several scenarios where an application has a PSA key and needs to use it through an interface that wants an `mbedtls_pk_context` object. Typically, there is an existing key in the PSA key store (possibly in a secure element and non-exportable), and the key needs to be used in an interface that requires a `mbedtls_pk_context *` input, such as Mbed TLS's X.509 and TLS APIs or a similar third-party interface, or the `mbedtls_pk_write_xxx` interfaces which do not (yet) have PSA equivalents.
+
+There is a function `mbedtls_pk_setup_opaque` that mostly does this. However, it has several limitations:
+
+* It creates a PK key of type `MBEDTLS_PK_OPAQUE` that wraps the PSA key. This is good enough in some scenarios, but not others. For example, it's ok for pkwrite, because we've upgraded the pkwrite code to handle `MBEDTLS_PK_OPAQUE`. That doesn't help users of third-party libraries that haven't yet been upgraded.
+* It ties the lifetime of the PK object to the PSA key, which is error-prone: if the PSA key is destroyed but the PK object isn't, there is no way to reliably detect any subsequent misuse of the PK object.
+* It is only available under `MBEDTLS_USE_PSA_CRYPTO`. This is not a priority concern, since we generally expect people to activate `MBEDTLS_USE_PSA_CRYPTO` at an early stage of their migration to PSA. However, this function is useful to use specific PSA keys in X.509/TLS regardless of whether X.509/TLS use the PSA API for all cryptographic operations, so this is a wart in the current API.
+
+It therefore appears that we need two ways to “convert” a PSA key to PK:
+
+* Wrapping, which is what `mbedtls_pk_setup_opaque` does. This works for any PSA key but is limited by the key's lifetime and creates a PK object with limited functionality.
+* Copying, which requires a new function. This requires an exportable key but creates a fully independent, fully functional PK object.
+
+Gap: a way to copy a PSA key into a PK context. This can only be expected to work if the PSA key is exportable.
+
+After some discussion, have not identified anything we want to change in the behavior of `mbedtls_pk_setup_opaque`. We only want to generalize it to non-`MBEDTLS_USE_PSA_CRYPTO` and to document it better.
+
+#### Signature formats
+
+The pk module uses signature formats intended for X.509. The PSA module uses the simplest sensible signature format.
+
+* For RSA, the formats are the same.
+* For ECDSA, PSA uses a fixed-size concatenation of (r,s), whereas X.509 and pk use an ASN.1 DER encoding of the sequence (r,s).
+
+Gap: We need APIs to convert between these two formats. The conversion code already exists under the hood, but it's in pieces that can't be called directly.
+
+There is a design choice here: do we provide conversions functions for ECDSA specifically, or do we provide conversion functions that take an algorithm as argument and just happen to be a no-op with RSA? One factor is plausible extensions. These conversions functions will remain useful in Mbed TLS 4.x and perhaps beyond. We will at least add EdDSA support, and its signature encoding is the fixed-size concatenation (r,s) even in X.509. We may well also add support for some post-quantum signatures, and their concrete format is still uncertain.
+
+Given the uncertainty, it would be nice to provide a sufficiently generic interface to convert between the PSA and the pk signature format, parametrized by the algorithm. However, it is difficult to predict exactly what parameters are needed. For example, converting from an ASN.1 ECDSA signature to (r,s) requires the knowledge of the curve, or at least the curve's size. Therefore we are not going to add a generic function at this stage.
+
+For ECDSA, there are two plausible APIs: follow the ASN.1/X.509 write/parse APIs, or present an ordinary input/output API. The ASN.1 APIs are the way they are to accommodate nested TLV structures. But ECDSA signatures do not appear nested in TLV structures in either TLS (there's just a signature field) or X.509 (the signature is inside a BITSTRING, not directly in a SEQUENCE). So there does not seem to be a need for an ASN.1-like API for the ASN.1 format, just the format conversion itself in a buffer that just contains the signature.
+
+#### Asymmetric cryptography TODO
+
+[TODO] Other gaps?
+
+## New APIs
+
+This section presents new APIs to implement based on the [gap analysis](#gap-analysis).
+
+### General notes
+
+Each action to implement a function entails:
+
+* Implement the library function.
+* Document it precisely, including error conditions.
+* Unit-test it.
+* Mention it where relevant in the PSA transition guide.
+
+### Hash APIs
+
+Based on the [gap analysis](#hash-gap-analysis):
+
+[ACTION] [#8340](https://github.com/Mbed-TLS/mbedtls/issues/8340) Move `mbedtls_md_psa_alg_from_type` and `mbedtls_md_type_from_psa_alg` from `library/md_psa.h` to `include/mbedtls/md.h`.
+
+### MAC APIs
+
+[TODO]
+
+### Cipher and AEAD APIs
+
+[TODO]
+
+### Key derivation APIs
+
+[TODO]
+
+### Random generation APIs
+
+[TODO]
+
+### Asymmetric cryptography APIs
+
+#### Asymmetric cryptography metadata APIs
+
+Based on the [gap analysis](#asymmetric-cryptography-metadata):
+
+* No further work is needed about RSA specifically. The amount of metadata other than hashes is sufficiently small to be handled in ad hoc ways in applications, and hashes have [their own conversions](#hash-apis).
+* No further work is needed about ECC specifically. We have just added adequate functions.
+* No further work is needed about DHM specifically. There is no good way to translate the relevant information.
+* [OPEN] Is there a decent way to convert between `mbedtls_pk_type_t` plus extra information, and `psa_key_type_t` plus policy information? The two APIs are different in crucial ways, with different splits between key type, policy information and operation algorithm.
+  Thinking so far: there isn't really a nice way to present this conversion. For a specific key, `mbedtls_pk_get_psa_attributes` and `mbedtls_pk_copy_from_psa` do the job.
+
+#### API to create a PSA key from a PK context
+
+Based on the [gap analysis](#using-a-legacy-key-pair-or-public-key-with-psa):
+
+Given an `mbedtls_pk_context`, we want a function that creates a PSA key with the same key material and algorithm. “Same key material” is straightforward, but “same algorithm” is not, because a PK context has incomplete algorithm information. For example, there is no way to distinguish between an RSA key that is intended for signature or for encryption. Between algorithms of the same nature, there is no way to distinguish a key intended for PKCS#1v1.5 and one intended for PKCS#1v2.1 (OAEP/PSS): this is indicated in the underlying RSA context, but the indication there is only a default that can be overridden by calling `mbedtls_pk_{sign,verify}_ext`. Also there is no way to distinguish between `PSA_ALG_RSA_PKCS1V15_SIGN(hash_alg)` and `PSA_ALG_RSA_PKCS1V15_SIGN_RAW`: in the legacy interface, this is only determined when actually doing a signature/verification operation. Therefore the function that creates the PSA key needs extra information to indicate which algorithm to put in the key's policy.
+
+When creating a PSA key, apart from the key material, the key is determined by attributes, which fall under three categories:
+
+* Type and size. These are directly related to the key material and can be deduced from it if the key material is in a structured format, which is the case with an `mbedtls_pk_context` input.
+* Policy. This includes the chosen algorithm, which as discussed above cannot be fully deduced from the `mbedtls_pk_context` object. Just choosing one algorithm is problematic because it doesn't allow implementation-specific extensions, such as Mbed TLS's enrollment algorithm. The intended usage flags cannot be deduced from the PK context either, but the conversion function could sensibly just enable all the relevant usage flags. Users who want a more restrictive usage can call `psa_copy_key` and `psa_destroy_key` to obtain a PSA key object with a more restrictive usage.
+* Persistence and location. This is completely orthogonal to the information from the `mbedtls_pk_context` object. It is convenient, but not necessary, for the conversion function to allow customizing these aspects. If it doesn't, users can call the conversion function and then call `psa_copy_key` and `psa_destroy_key` to move the key to its desired location.
+
+To allow the full flexibility around policies, and make the creation of a persistent key more convenient, the conversion function shall take a `const psa_key_attributes_t *` input, like all other functions that create a PSA key. In addition, there shall be a helper function to populate a `psa_key_attributes_t` with a sensible default. This lets the caller choose a more flexible, or just different usage policy, unlike the default-then-copy approach which only allows restricting the policy.
+
+This is close to the existing function `mbedtls_pk_wrap_as_opaque`, but does not bake in the implementation-specific consideration that a PSA key has exactly two algorithms, and also allows the caller to benefit from default for the policy in more cases.
+
+[ACTION] [#8708](https://github.com/Mbed-TLS/mbedtls/issues/8708) Implement `mbedtls_pk_get_psa_attributes` and `mbedtls_pk_import_into_psa` as described below. These functions are available whenever `MBEDTLS_PK_C` and `MBEDTLS_PSA_CRYPTO_CLIENT` are both defined. Deprecate `mbedtls_pk_wrap_as_opaque`.
+
+```
+int mbedtls_pk_get_psa_attributes(const mbedtls_pk_context *pk,
+                                  psa_key_usage_flags_t usage,
+                                  psa_key_attributes_t *attributes);
+int mbedtls_pk_import_into_psa(const mbedtls_pk_context *pk,
+                               const psa_key_attributes_t *attributes,
+                               mbedtls_svc_key_id_t *key_id);
+```
+
+* `mbedtls_pk_get_psa_attributes` does not change the id/lifetime fields of the attributes (which indicate a volatile key by default).
+    * [OPEN] Or should it reset them to 0? Resetting is more convenient for the case where the pk key is a `MBEDTLS_PK_OPAQUE`. But that's an uncommon use case. It's probably less surprising if this function leaves the lifetime-related alone, since its job is to set the type-related and policy-related attributes.
+* `mbedtls_pk_get_psa_attributes` sets the type and size based on what's in the pk context.
+    * The key type is a key pair if the context contains a private key and the indicated usage is a private-key usage. The key type is a public key if the context only contains a public key, in which case a private-key usage is an error.
+* `mbedtls_pk_get_psa_attributes` sets the usage flags based on the `usage` parameter. It extends the usage to other usage that is possible:
+    * `EXPORT` and `COPY` are always set.
+    * If `SIGN_{HASH,MESSAGE}` is set then so is `VERIFY_{HASH,MESSAGE}`.
+    * If `DECRYPT` is set then so is `ENCRYPT`.
+    * It is an error if `usage` has more than one flag set, or has a usage that is incompatible with the key type.
+* `mbedtls_pk_get_psa_attributes` sets the algorithm usage policy based on information in the key object and on `usage`.
+    * For an RSA key with the `MBEDTLS_RSA_PKCS_V15` padding mode, the algorithm policy is `PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH)` for a sign/verify usage, and `PSA_ALG_RSA_PKCS1V15_CRYPT` for an encrypt/decrypt usage.
+    * For an RSA key with the `MBEDTLS_RSA_PKCS_V21` padding mode, the algorithm policy is `PSA_ALG_RSA_PSS_ANY_SALT(PSA_ALG_ANY_HASH)` for a sign/verify usage, and `PSA_ALG_RSA_OAEP(hash)` for an encrypt/decrypt usage where `hash` is from the RSA key's parameters. (Note that `PSA_ALG_ANY_HASH` is only allowed in signature algorithms.)
+    * For an `MBEDTLS_PK_ECKEY` or `MBEDTLS_PK_ECDSA` with a sign/verify usage, the algorithm policy is `PSA_ALG_DETERMINISTIC_ECDSA` if `MBEDTLS_ECDSA_DETERMINISTIC` is enabled and `PSA_ALG_ECDSA` otherwise. In either case, the hash policy is `PSA_ALG_ANY_HASH`.
+    * For an `MBEDTLS_PK_ECKEY` or `MBEDTLS_PK_ECDKEY_DH` with the usage `PSA_KEY_USAGE_DERIVE`, the algorithm is `PSA_ALG_ECDH`.
+    * For a `MBEDTLS_PK_OPAQUE`, this function reads the attributes of the existing PK key and copies them (without overriding the lifetime and key identifier in `attributes`), then applies a public-key restriction if needed.
+        * Public-key restriction: if `usage` is a public-key usage, change the type to the corresponding public-key type, and remove private-key usage flags from the usage flags read from the existing key.
+* `mbedtls_pk_import_into_psa` checks that the type field in the attributes is consistent with the content of the `mbedtls_pk_context` object (RSA/ECC, and availability of the private key).
+    * The key type can be a public key even if the private key is available.
+* `mbedtls_pk_import_into_psa` does not need to check the bit-size in the attributes: `psa_import_key` will do enough checks.
+* `mbedtls_pk_import_into_psa` does not check that the policy in the attributes is sensible. That's on the user.
+
+#### API to copy a PSA key to a PK context
+
+Based on the [gap analysis](#using-a-psa-key-as-a-pk-context):
+
+[ACTION] [#8709](https://github.com/Mbed-TLS/mbedtls/issues/8709) Implement `mbedtls_pk_copy_from_psa` as described below.
+
+```
+int mbedtls_pk_copy_from_psa(mbedtls_svc_key_id_t key_id,
+                             mbedtls_pk_context *pk);
+```
+
+* `pk` must be initialized, but not set up.
+* It is an error if the key is neither a key pair nor a public key.
+* It is an error if the key is not exportable.
+* The resulting pk object has a transparent type, not `MBEDTLS_PK_OPAQUE`. That's `MBEDTLS_PK_RSA` for RSA keys (since pk objects don't use `MBEDTLS_PK_RSASSA_PSS` as a type), and `MBEDTLS_PK_ECKEY` for ECC keys (following the example of pkparse).
+* Once this function returns, the pk object is completely independent of the PSA key.
+* Calling `mbedtls_pk_sign`, `mbedtls_pk_verify`, `mbedtls_pk_encrypt`, `mbedtls_pk_decrypt` on the resulting pk context will perform an algorithm that is compatible with the PSA key's primary algorithm policy (`psa_get_key_algorithm`) if that is a matching operation type (sign/verify, encrypt/decrypt), but with no restriction on the hash (as if the policy had `PSA_ALG_ANY_HASH` instead of a specific hash, and with `PSA_ALG_RSA_PKCS1V15_SIGN_RAW` merged with `PSA_ALG_RSA_PKCS1V15_SIGN(hash_alg)`).
+    * For ECDSA, the choice of deterministic vs randomized will be based on the compile-time setting `MBEDTLS_ECDSA_DETERMINISTIC`, like `mbedtls_pk_sign` today.
+    * For an RSA key, the output key will allow both encrypt/decrypt and sign/verify regardless of the original key's policy. The original key's policy determines the output key's padding mode.
+    * The primary intent of this requirement is to allow an application to switch to PSA for creating the key material (for example to benefit from a PSA accelerator driver, or to start using a secure element), without modifying the code that consumes the key. For RSA keys, the PSA primary algorithm policy is how one conveys the same information as RSA key padding information in the legacy API. Convey this in the documentation.
+
+#### API to create a PK object that wraps a PSA key
+
+Based on the [gap analysis](#using-a-psa-key-as-a-pk-context):
+
+[ACTION] [#8712](https://github.com/Mbed-TLS/mbedtls/issues/8712) Clarify the documentation of `mbedtls_pk_setup_opaque` regarding which algorithms the resulting key will perform with `mbedtls_pk_sign`, `mbedtls_pk_verify`, `mbedtls_pk_encrypt`, `mbedtls_pk_decrypt`.
+
+[ACTION] [#8710](https://github.com/Mbed-TLS/mbedtls/issues/8710) Provide `mbedtls_pk_setup_opaque` whenever `MBEDTLS_PSA_CRYPTO_CLIENT` is enabled, not just when `MBEDTLS_USE_PSA_CRYPTO` is enabled. This is nice-to-have, not critical. Update `use-psa-crypto.md` accordingly.
+
+[OPEN] What about `mbedtls_pk_sign_ext` and  `mbedtls_pk_verify_ext`?
+
+#### API to convert between signature formats
+
+Based on the [gap analysis](#signature-formats):
+
+[ACTION] [#7765](https://github.com/Mbed-TLS/mbedtls/issues/7765) Implement `mbedtls_ecdsa_raw_to_der` and `mbedtls_ecdsa_der_to_raw` as described below.
+
+```
+int mbedtls_ecdsa_raw_to_der(size_t bits,
+                             const unsigned char *raw, size_t raw_len,
+                             unsigned char *der, size_t der_size, size_t *der_len);
+int mbedtls_ecdsa_der_to_raw(size_t bits,
+                             const unsigned char *der, size_t der_len,
+                             unsigned char *raw, size_t raw_size, size_t *raw_len);
+```
+
+* These functions convert between the signature format used by `mbedtls_pk_{sign,verify}{,_ext}` and the signature format used by `psa_{sign,verify}_{hash,message}`.
+* The input and output buffers can overlap.
+* The `bits` parameter is necessary in the DER-to-raw direction because the DER format lacks leading zeros, so something else needs to convey the size of (r,s). The `bits` parameter is redundant in the raw-to-DER direction, but we have it anyway because [it helps catch errors](https://github.com/Mbed-TLS/mbedtls/pull/8681#discussion_r1445980971), and it isn't a burden on the caller because the information is readily available in practice.
+* Should these functions rely on the ASN.1 module? We experimented [calling ASN.1 functions](https://github.com/Mbed-TLS/mbedtls/pull/8681), [reimplementing simpler ASN.1 functions](https://github.com/Mbed-TLS/mbedtls/pull/8696), and [providing the functions from the ASN.1 module](https://github.com/Mbed-TLS/mbedtls/pull/8703). Providing the functions from the ASN.1 module [won on a compromise of code size and simplicity](https://github.com/Mbed-TLS/mbedtls/issues/7765#issuecomment-1893670015).
diff --git a/docs/architecture/psa-migration/psa-limitations.md b/docs/architecture/psa-migration/psa-limitations.md
new file mode 100644
index 0000000..29d7c53
--- /dev/null
+++ b/docs/architecture/psa-migration/psa-limitations.md
@@ -0,0 +1,337 @@
+This document lists current limitations of the PSA Crypto API (as of version
+1.1) that may impact our ability to (1) use it for all crypto operations in
+TLS and X.509 and (2) support isolation of all long-term secrets in TLS (that
+is, goals G1 and G2 in [strategy.md](strategy.md) in the same directory).
+
+This is supposed to be a complete list, based on a exhaustive review of crypto
+operations done in TLS and X.509 code, but of course it's still possible that
+subtle-but-important issues have been missed. The only way to be really sure
+is, of course, to actually do the migration work.
+
+Limitations relevant for G1 (performing crypto operations)
+==========================================================
+
+Restartable (aka interruptible) ECC operations
+----------------------------------------------
+
+Support for interruptible ECDSA sign/verify was added to PSA in Mbed TLS 3.4.
+However, support for interruptible ECDH is not present yet. Also, PK, X.509 and
+TLS have not yet been adapted to take advantage of the new PSA APIs. See:
+- <https://github.com/Mbed-TLS/mbedtls/issues/7292>;
+- <https://github.com/Mbed-TLS/mbedtls/issues/7293>;
+- <https://github.com/Mbed-TLS/mbedtls/issues/7294>.
+
+Currently, when `MBEDTLS_USE_PSA_CRYPTO` and `MBEDTLS_ECP_RESTARTABLE` are
+both enabled, some operations that should be restartable are not (ECDH in TLS
+1.2 clients using ECDHE-ECDSA), as they are using PSA instead, and some
+operations that should use PSA do not (signature generation & verification) as
+they use the legacy API instead, in order to get restartable behaviour.
+
+Things that are in the API but not implemented yet
+--------------------------------------------------
+
+PSA Crypto has an API for FFDH, but it's not implemented in Mbed TLS yet.
+(Regarding FFDH, see the next section as well.) See issue [3261][ffdh] on
+github.
+
+[ffdh]: https://github.com/Mbed-TLS/mbedtls/issues/3261
+
+Arbitrary parameters for FFDH
+-----------------------------
+
+(See also the first paragraph in the previous section.)
+
+Currently, the PSA Crypto API can only perform FFDH with a limited set of
+well-known parameters (some of them defined in the spec, but implementations
+are free to extend that set).
+
+TLS 1.2 (and earlier) on the other hand have the server send explicit
+parameters (P and G) in its ServerKeyExchange message. This has been found to
+be suboptimal for security, as it is prohibitively hard for the client to
+verify the strength of these parameters. This led to the development of RFC
+7919 which allows use of named groups in TLS 1.2 - however as this is only an
+extension, servers can still send custom parameters if they don't support the
+extension.
+
+In TLS 1.3 the situation will be simpler: named groups are the only
+option, so the current PSA Crypto API is a good match for that. (Not
+coincidentally, all the groups used by RFC 7919 and TLS 1.3 are included
+in the PSA specification.)
+
+There are several options here:
+
+1. Implement support for custom FFDH parameters in PSA Crypto: this would pose
+   non-trivial API design problem, but most importantly seems backwards, as
+the crypto community is moving away from custom FFDH parameters. (Could be
+done any time.)
+2. Drop the DHE-RSA and DHE-PSK key exchanges in TLS 1.2 when moving to PSA.
+   (For people who want some algorithmic variety in case ECC collapses, FFDH
+would still be available in TLS 1.3, just not in 1.2.) (Can only be done in
+4.0 or another major version.)
+3. Variant of the precedent: only drop client-side support. Server-side is
+   easy to support in terms of API/protocol, as the server picks the
+parameters: we just need remove the existing `mbedtls_ssl_conf_dh_param_xxx()`
+APIs and tell people to use `mbedtls_ssl_conf_groups()` instead. (Can only be
+done in 4.0 or another major version.)
+4. Implement RFC 7919, support DHE-RSA and DHE-PSK only in conjunction with it
+   when moving to PSA. Server-side would work as above; unfortunately
+client-side the only option is to offer named groups and break the handshake
+if the server didn't take on our offer. This is not fully satisfying, but is
+perhaps the least unsatisfying option in terms of result; it's also probably
+the one that requires the most work, but it would deliver value beyond PSA
+migration by implementing RFC 7919. (Implementing RFC 7919 could be done any
+time; making it mandatory can only be done in 4.0 or another major version.)
+
+As of early 2023, the plan is to go with option 2 in Mbed TLS 4.0, which has
+been announced on the mailing-list and got no push-back, see
+<https://github.com/Mbed-TLS/mbedtls/issues/5278>.
+
+RSA-PSS parameters
+------------------
+
+RSA-PSS signatures are defined by PKCS#1 v2, re-published as RFC 8017
+(previously RFC 3447).
+
+As standardized, the signature scheme takes several parameters, in addition to
+the hash algorithm potentially used to hash the message being signed:
+- a hash algorithm used for the encoding function
+- a mask generation function
+  - most commonly MGF1, which in turn is parametrized by a hash algorithm
+- a salt length
+- a trailer field - the value is fixed to 0xBC by PKCS#1 v2.1, but was left
+  configurable in the original scheme; 0xBC is used everywhere in practice.
+
+Both the existing `mbedtls_` API and the PSA API support only MGF1 as the
+generation function (and only 0xBC as the trailer field), but there are
+discrepancies in handling the salt length and which of the various hash
+algorithms can differ from each other.
+
+### API comparison
+
+- RSA:
+  - signature: `mbedtls_rsa_rsassa_pss_sign()`
+    - message hashed externally
+    - encoding hash = MGF1 hash (from context, or argument = message hash)
+    - salt length: always using the maximum legal value
+  - signature: `mbedtls_rsa_rsassa_pss_sign_ext()`
+    - message hashed externally
+    - encoding hash = MGF1 hash (from context, or argument = message hash)
+    - salt length: specified explicitly
+  - verification: `mbedtls_rsassa_pss_verify()`
+    - message hashed externally
+    - encoding hash = MGF1 hash (from context, or argument = message hash)
+    - salt length: any valid length accepted
+  - verification: `mbedtls_rsassa_pss_verify_ext()`
+    - message hashed externally
+    - encoding hash = MGF1 hash from dedicated argument
+    - expected salt length: specified explicitly, can specify "ANY"
+- PK:
+  - signature: not supported
+  - verification: `mbedtls_pk_verify_ext()`
+    - message hashed externally
+    - encoding hash = MGF1 hash, specified explicitly
+    - expected salt length: specified explicitly, can specify "ANY"
+- PSA:
+  - algorithm specification:
+    - hash alg used for message hashing, encoding and MGF1
+    - salt length can be either "standard" (<= hashlen, see note) or "any"
+  - signature generation:
+    - salt length: always <= hashlen (see note) and random salt
+  - verification:
+    - salt length: either <= hashlen (see note), or any depending on algorithm
+
+Note: above, "<= hashlen" means that hashlen is used if possible, but if it
+doesn't fit because the key is too short, then the maximum length that fits is
+used.
+
+The RSA/PK API is in principle more flexible than the PSA Crypto API. The
+following sub-sections study whether and how this matters in practice.
+
+### Use in X.509
+
+RFC 4055 Section 3.1 defines the encoding of RSA-PSS that's used in X.509.
+It allows independently specifying the message hash (also used for encoding
+hash), the MGF (and its hash if MGF1 is used), and the salt length (plus an
+extra parameter "trailer field" that doesn't vary in practice"). These can be
+encoded as part of the key, and of the signature. If both encoding are
+presents, all values must match except possibly for the salt length, where the
+value from the signature parameters is used.
+
+In Mbed TLS, RSA-PSS parameters can be parsed and displayed for various
+objects (certificates, CRLs, CSRs). During parsing, the following properties
+are enforced:
+- the extra "trailer field" parameter must have its default value
+- the mask generation function is MGF1
+- encoding hash = message hashing algorithm (may differ from MGF1 hash)
+
+When it comes to cryptographic operations, only two things are supported:
+- verifying the signature on a certificate from its parent;
+- verifying the signature on a CRL from the issuing CA.
+
+The verification is done using `mbedtls_pk_verify_ext()`.
+
+Note: since X.509 parsing ensures that message hash = encoding hash, and
+`mbedtls_pk_verify_ext()` uses encoding hash = mgf1 hash, it looks like all
+three hash algorithms must be equal, which would be good news as it would
+match a limitation of the PSA API.
+
+It is unclear what parameters people use in practice. It looks like by default
+OpenSSL picks saltlen = keylen - hashlen - 2 (tested with openssl 1.1.1f).
+The `certtool` command provided by GnuTLS seems to be picking saltlen = hashlen
+by default (tested with GnuTLS 3.6.13). FIPS 186-4 requires 0 <= saltlen <=
+hashlen.
+
+### Use in TLS
+
+In TLS 1.2 (or lower), RSA-PSS signatures are never used, except via X.509.
+
+In TLS 1.3, RSA-PSS signatures can be used directly in the protocol (in
+addition to indirect use via X.509). It has two sets of three signature
+algorithm identifiers (for SHA-256, SHA-384 and SHA-512), depending of what
+the OID of the public key is (rsaEncryption or RSASSA-PSS).
+
+In both cases, it specifies that:
+- the mask generation function is MGF1
+- all three hashes are equal
+- the length of the salt MUST be equal to the length of the digest algorithm
+
+When signing, the salt length picked by PSA is the one required by TLS 1.3
+(unless the key is unreasonably small).
+
+When verifying signatures, PSA will by default enforce the salt len is the one
+required by TLS 1.3.
+
+### Current testing - X509
+
+All test files use the default trailer field of 0xBC, as enforced by our
+parser. (There's a negative test for that using the
+`x509_parse_rsassa_pss_params` test function and hex data.)
+
+Files with "bad" in the name are expected to be invalid and rejected in tests.
+
+**Test certificates:**
+
+server9-bad-mgfhash.crt (announcing mgf1(sha224), signed with another mgf)
+         Hash Algorithm: sha256
+         Mask Algorithm: mgf1 with sha224
+          Salt Length: 0xDE
+server9-bad-saltlen.crt (announcing saltlen = 0xDE, signed with another len)
+         Hash Algorithm: sha256
+         Mask Algorithm: mgf1 with sha256
+          Salt Length: 0xDE
+server9-badsign.crt (one bit flipped in the signature)
+         Hash Algorithm: sha1 (default)
+         Mask Algorithm: mgf1 with sha1 (default)
+          Salt Length: 0xEA
+server9-defaults.crt
+         Hash Algorithm: sha1 (default)
+         Mask Algorithm: mgf1 with sha1 (default)
+          Salt Length: 0x14 (default)
+server9-sha224.crt
+         Hash Algorithm: sha224
+         Mask Algorithm: mgf1 with sha224
+          Salt Length: 0xE2
+server9-sha256.crt
+         Hash Algorithm: sha256
+         Mask Algorithm: mgf1 with sha256
+          Salt Length: 0xDE
+server9-sha384.crt
+         Hash Algorithm: sha384
+         Mask Algorithm: mgf1 with sha384
+          Salt Length: 0xCE
+server9-sha512.crt
+         Hash Algorithm: sha512
+         Mask Algorithm: mgf1 with sha512
+          Salt Length: 0xBE
+server9-with-ca.crt
+         Hash Algorithm: sha1 (default)
+         Mask Algorithm: mgf1 with sha1 (default)
+          Salt Length: 0xEA
+server9.crt
+         Hash Algorithm: sha1 (default)
+         Mask Algorithm: mgf1 with sha1 (default)
+          Salt Length: 0xEA
+
+These certificates are signed with a 2048-bit key. It appears that they are
+all using saltlen = keylen - hashlen - 2, except for server9-defaults which is
+using saltlen = hashlen.
+
+**Test CRLs:**
+
+crl-rsa-pss-sha1-badsign.pem
+         Hash Algorithm: sha1 (default)
+         Mask Algorithm: mgf1 with sha1 (default)
+          Salt Length: 0xEA
+crl-rsa-pss-sha1.pem
+         Hash Algorithm: sha1 (default)
+         Mask Algorithm: mgf1 with sha1 (default)
+          Salt Length: 0xEA
+crl-rsa-pss-sha224.pem
+         Hash Algorithm: sha224
+         Mask Algorithm: mgf1 with sha224
+          Salt Length: 0xE2
+crl-rsa-pss-sha256.pem
+         Hash Algorithm: sha256
+         Mask Algorithm: mgf1 with sha256
+          Salt Length: 0xDE
+crl-rsa-pss-sha384.pem
+         Hash Algorithm: sha384
+         Mask Algorithm: mgf1 with sha384
+          Salt Length: 0xCE
+crl-rsa-pss-sha512.pem
+         Hash Algorithm: sha512
+         Mask Algorithm: mgf1 with sha512
+          Salt Length: 0xBE
+
+These CRLs are signed with a 2048-bit key. It appears that they are
+all using saltlen = keylen - hashlen - 2.
+
+**Test CSRs:**
+
+server9.req.sha1
+         Hash Algorithm: sha1 (default)
+         Mask Algorithm: mgf1 with sha1 (default)
+          Salt Length: 0x6A
+server9.req.sha224
+         Hash Algorithm: sha224
+         Mask Algorithm: mgf1 with sha224
+          Salt Length: 0x62
+server9.req.sha256
+         Hash Algorithm: sha256
+         Mask Algorithm: mgf1 with sha256
+          Salt Length: 0x5E
+server9.req.sha384
+         Hash Algorithm: sha384
+         Mask Algorithm: mgf1 with sha384
+          Salt Length: 0x4E
+server9.req.sha512
+         Hash Algorithm: sha512
+         Mask Algorithm: mgf1 with sha512
+          Salt Length: 0x3E
+
+These CSRs are signed with a 2048-bit key. It appears that they are
+all using saltlen = keylen - hashlen - 2.
+
+### Possible courses of action
+
+There's no question about what to do with TLS (any version); the only question
+is about X.509 signature verification. Options include:
+
+1. Doing all verifications with `PSA_ALG_RSA_PSS_ANY_SALT` - while this
+   wouldn't cause a concrete security issue, this would be non-compliant.
+2. Doing verifications with `PSA_ALG_RSA_PSS` when we're lucky and the encoded
+   saltlen happens to match hashlen, and falling back to `ANY_SALT` otherwise.
+Same issue as with the previous point, except more contained.
+3. Reject all certificates with saltlen != hashlen. This includes all
+   certificates generated with OpenSSL using the default parameters, so it's
+probably not acceptable.
+4. Request an extension to the PSA Crypto API and use one of the above options
+   in the meantime. Such an extension seems inconvenient and not motivated by
+strong security arguments, so it's unclear whether it would be accepted.
+
+Since Mbed TLS 3.4, option 1 is implemented.
+
+Limitations relevant for G2 (isolation of long-term secrets)
+============================================================
+
+Currently none.
diff --git a/docs/architecture/psa-migration/strategy.md b/docs/architecture/psa-migration/strategy.md
new file mode 100644
index 0000000..b985a77
--- /dev/null
+++ b/docs/architecture/psa-migration/strategy.md
@@ -0,0 +1,486 @@
+This document explains the strategy that was used so far in starting the
+migration to PSA Crypto and mentions future perspectives and open questions.
+
+Goals
+=====
+
+Several benefits are expected from migrating to PSA Crypto:
+
+G1. Use PSA Crypto drivers when available.
+G2. Allow isolation of long-term secrets (for example, private keys).
+G3. Allow isolation of short-term secrets (for example, TLS session keys).
+G4. Have a clean, unified API for Crypto (retire the legacy API).
+G5. Code size: compile out our implementation when a driver is available.
+
+As of Mbed TLS 3.2, most of (G1) and all of (G2) is implemented when
+`MBEDTLS_USE_PSA_CRYPTO` is enabled. For (G2) to take effect, the application
+needs to be changed to use new APIs. For a more detailed account of what's
+implemented, see `docs/use-psa-crypto.md`, where new APIs are about (G2), and
+internal changes implement (G1).
+
+As of early 2023, work towards G5 is in progress: Mbed TLS 3.3 and 3.4 saw
+some improvements in this area, and more will be coming in future releases.
+
+Generally speaking, the numbering above doesn't mean that each goal requires
+the preceding ones to be completed.
+
+
+Compile-time options
+====================
+
+We currently have a few compile-time options that are relevant to the migration:
+
+- `MBEDTLS_PSA_CRYPTO_C` - enabled by default, controls the presence of the PSA
+  Crypto APIs.
+- `MBEDTLS_USE_PSA_CRYPTO` - disabled by default (enabled in "full" config),
+  controls usage of PSA Crypto APIs to perform operations in X.509 and TLS
+(G1 above), as well as the availability of some new APIs (G2 above).
+- `PSA_CRYPTO_CONFIG` - disabled by default, supports builds with drivers and
+  without the corresponding software implementation (G5 above).
+
+The reasons why `MBEDTLS_USE_PSA_CRYPTO` is optional and disabled by default
+are:
+- it's not fully compatible with `MBEDTLS_ECP_RESTARTABLE`: you can enable
+  both, but then you won't get the full effect of RESTARTBLE (see the
+documentation of this option in `mbedtls_config.h`);
+- to avoid a hard/default dependency of TLS, X.509 and PK on
+  `MBEDTLS_PSA_CRYPTO_C`, for backward compatibility reasons:
+  - When `MBEDTLS_PSA_CRYPTO_C` is enabled and used, applications need to call
+    `psa_crypto_init()` before TLS/X.509 uses PSA functions. (This prevents us
+from even enabling the option by default.)
+  - `MBEDTLS_PSA_CRYPTO_C` has a hard dependency on `MBEDTLS_ENTROPY_C ||
+    MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` but it's
+    currently possible to compile TLS and X.509 without any of the options.
+    Also, we can't just auto-enable `MBEDTLS_ENTROPY_C` as it doesn't build
+    out of the box on all platforms, and even less
+    `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` as it requires a user-provided RNG
+    function.
+
+The downside of this approach is that until we are able to make
+`MBDEDTLS_USE_PSA_CRYPTO` non-optional (always enabled), we have to maintain
+two versions of some parts of the code: one using PSA, the other using the
+legacy APIs. However, see next section for strategies that can lower that
+cost. The rest of this section explains the reasons for the
+incompatibilities mentioned above.
+
+At the time of writing (early 2022) it is unclear what could be done about the
+backward compatibility issues, and in particular if the cost of implementing
+solutions to these problems would be higher or lower than the cost of
+maintaining dual code paths until the next major version. (Note: these
+solutions would probably also solve other problems at the same time.)
+
+### `MBEDTLS_ECP_RESTARTABLE`
+
+Currently this option controls not only the presence of restartable APIs in
+the crypto library, but also their use in the TLS and X.509 layers. Since PSA
+Crypto does not support restartable operations, there's a clear conflict: the
+TLS and X.509 layers can't both use only PSA APIs and get restartable
+behaviour.
+
+Support for restartable (aka interruptible) ECDSA sign/verify operation was
+added to PSA in Mbed TLS 3.4, but support for ECDH is not present yet.
+
+It will then require follow-up work to make use of the new PSA APIs in
+PK/X.509/TLS in all places where we currently allow restartable operations.
+
+### Backward compatibility issues with making `MBEDTLS_USE_PSA_CRYPTO` always on
+
+1. Existing applications may not be calling `psa_crypto_init()` before using
+   TLS, X.509 or PK. We can try to work around that by calling (the relevant
+part of) it ourselves under the hood as needed, but that would likely require
+splitting init between the parts that can fail and the parts that can't (see
+<https://github.com/ARM-software/psa-crypto-api/pull/536> for that).
+2. It's currently not possible to enable `MBEDTLS_PSA_CRYPTO_C` in
+   configurations that don't have `MBEDTLS_ENTROPY_C`, and we can't just
+auto-enable the latter, as it won't build or work out of the box on all
+platforms. There are two kinds of things we'd need to do if we want to work
+around that:
+   1. Make it possible to enable the parts of PSA Crypto that don't require an
+      RNG (typically, public key operations, symmetric crypto, some key
+management functions (destroy etc)) in configurations that don't have
+`ENTROPY_C`. This requires going through the PSA code base to adjust
+dependencies. Risk: there may be annoying dependencies, some of which may be
+surprising.
+   2. For operations that require an RNG, provide an alternative function
+      accepting an explicit `f_rng` parameter (see #5238), that would be
+available in entropy-less builds. (Then code using those functions still needs
+to have one version using it, for entropy-less builds, and one version using
+the standard function, for driver support in build with entropy.)
+
+See <https://github.com/Mbed-TLS/mbedtls/issues/5156>.
+
+Taking advantage of the existing abstractions layers - or not
+=============================================================
+
+The Crypto library in Mbed TLS currently has 3 abstraction layers that offer
+algorithm-agnostic APIs for a class of algorithms:
+
+- MD for messages digests aka hashes (including HMAC)
+- Cipher for symmetric ciphers (included AEAD)
+- PK for asymmetric (aka public-key) cryptography (excluding key exchange)
+
+Note: key exchange (FFDH, ECDH) is not covered by an abstraction layer.
+
+These abstraction layers typically provide, in addition to the API for crypto
+operations, types and numerical identifiers for algorithms (for
+example `mbedtls_cipher_mode_t` and its values). The
+current strategy is to keep using those identifiers in most of the code, in
+particular in existing structures and public APIs, even when
+`MBEDTLS_USE_PSA_CRYPTO` is enabled. (This is not an issue for G1, G2, G3
+above, and is only potentially relevant for G4.)
+
+The are multiple strategies that can be used regarding the place of those
+layers in the migration to PSA.
+
+Silently call to PSA from the abstraction layer
+-----------------------------------------------
+
+- Provide a new definition (conditionally on `USE_PSA_CRYPTO`) of wrapper
+  functions in the abstraction layer, that calls PSA instead of the legacy
+crypto API.
+- Upside: changes contained to a single place, no need to change TLS or X.509
+  code anywhere.
+- Downside: tricky to implement if the PSA implementation is currently done on
+  top of that layer (dependency loop).
+
+This strategy is currently (early 2023) used for all operations in the PK
+layer; the MD layer uses a variant where it dispatches to PSA if a driver is
+available and the driver subsystem has been initialized, regardless of whether
+`USE_PSA_CRYPTO` is enabled; see `md-cipher-dispatch.md` in the same directory
+for details.
+
+This strategy is not very well suited to the Cipher layer, as the PSA
+implementation is currently done on top of that layer.
+
+This strategy will probably be used for some time for the PK layer, while we
+figure out what the future of that layer is: parts of it (parse/write, ECDSA
+signatures in the format that X.509 & TLS want) are not covered by PSA, so
+they will need to keep existing in some way. (Also, the PK layer is a good
+place for dispatching to either PSA or `mbedtls_xxx_restartable` while that
+part is not covered by PSA yet, if we decide to do that.)
+
+Replace calls for each operation
+--------------------------------
+
+- For every operation that's done through this layer in TLS or X.509, just
+  replace function call with calls to PSA (conditionally on `USE_PSA_CRYPTO`)
+- Upside: conceptually simple, and if the PSA implementation is currently done
+  on top of that layer, avoids concerns about dependency loops.
+- Upside: opens the door to building TLS/X.509 without that layer, saving some
+  code size.
+- Downside: TLS/X.509 code has to be done for each operation.
+
+This strategy is currently (early 2023) used for the MD layer and the Cipher
+layer in X.509 and TLS. Crypto modules however always call to MD which may
+then dispatch to PSA, see `md-cipher-dispatch.md`.
+
+Opt-in use of PSA from the abstraction layer
+--------------------------------------------
+
+- Provide a new way to set up a context that causes operations on that context
+  to be done via PSA.
+- Upside: changes mostly contained in one place, TLS/X.509 code only needs to
+  be changed when setting up the context, but not when using it. In
+  particular, no changes to/duplication of existing public APIs that expect a
+  key to be passed as a context of this layer (eg, `mbedtls_pk_context`).
+- Upside: avoids dependency loop when PSA implemented on top of that layer.
+- Downside: when the context is typically set up by the application, requires
+  changes in application code.
+
+This strategy is not useful when no context is used, for example with the
+one-shot function `mbedtls_md()`.
+
+There are two variants of this strategy: one where using the new setup
+function also allows for key isolation (the key is only held by PSA,
+supporting both G1 and G2 in that area), and one without isolation (the key is
+still stored outside of PSA most of the time, supporting only G1).
+
+This strategy, with support for key isolation, is currently (early 2022) used for
+private-key operations in the PK layer - see `mbedtls_pk_setup_opaque()`. This
+allows use of PSA-held private ECDSA keys in TLS and X.509 with no change to
+the TLS/X.509 code, but a contained change in the application.
+
+This strategy, without key isolation, was also previously used (until 3.1
+included) in the Cipher layer - see `mbedtls_cipher_setup_psa()`. This allowed
+use of PSA for cipher operations in TLS with no change to the application
+code, and a contained change in TLS code. (It only supported a subset of
+ciphers.)
+
+Note: for private key operations in the PK layer, both the "silent" and the
+"opt-in" strategy can apply, and can complement each other, as one provides
+support for key isolation, but at the (unavoidable) code of change in
+application code, while the other requires no application change to get
+support for drivers, but fails to provide isolation support.
+
+Summary
+-------
+
+Strategies currently (early 2022) used with each abstraction layer:
+
+- PK (for G1): silently call PSA
+- PK (for G2): opt-in use of PSA (new key type)
+- Cipher (G1): replace calls at each call site
+- MD (G1, X.509 and TLS): replace calls at each call site (depending on
+  `USE_PSA_CRYPTO`)
+- MD (G5): silently call PSA when a driver is available, see
+  `md-cipher-dispatch.md`.
+
+
+Supporting builds with drivers without the software implementation
+==================================================================
+
+This section presents a plan towards G5: save code size by compiling out our
+software implementation when a driver is available.
+
+Let's expand a bit on the definition of the goal: in such a configuration
+(driver used, software implementation and abstraction layer compiled out),
+we want:
+
+a. the library to build in a reasonably-complete configuration,
+b. with all tests passing,
+c. and no more tests skipped than the same configuration with software
+   implementation.
+
+Criterion (c) ensures not only test coverage, but that driver-based builds are
+at feature parity with software-based builds.
+
+We can roughly divide the work needed to get there in the following steps:
+
+0. Have a working driver interface for the algorithms we want to replace.
+1. Have users of these algorithms call to PSA or an abstraction layer than can
+   dispatch to PSA, but not the low-level legacy API, for all operations.
+(This is G1, and for PK, X.509 and TLS this is controlled by
+`MBEDTLS_USE_PSA_CRYPTO`.) This needs to be done in the library and tests.
+2. Have users of these algorithms not depend on the legacy API for information
+   management (getting a size for a given algorithm, etc.)
+3. Adapt compile-time guards used to query availability of a given algorithm;
+   this needs to be done in the library (for crypto operations and data) and
+tests.
+
+Note: the first two steps enable use of drivers, but not by themselves removal
+of the software implementation.
+
+Note: the fact that step 1 is not achieved for all of libmbedcrypto (see
+below) is the reason why criterion (a) has "a reasonably-complete
+configuration", to allow working around internal crypto dependencies when
+working on other parts such as X.509 and TLS - for example, a configuration
+without RSA PKCS#1 v2.1 still allows reasonable use of X.509 and TLS.
+
+Note: this is a conceptual division that will sometimes translate to how the
+work is divided into PRs, sometimes not. For example, in situations where it's
+not possible to achieve good test coverage at the end of step 1 or step 2, it
+is preferable to group with the next step(s) in the same PR until good test
+coverage can be reached.
+
+**Status as of end of March 2023 (shortly after 3.4):**
+
+- Step 0 is achieved for most algorithms, with only a few gaps remaining.
+- Step 1 is achieved for most of PK, X.509, and TLS when
+  `MBEDTLS_USE_PSA_CRYPTO` is enabled with only a few gaps remaining (see
+  docs/use-psa-crypto.md).
+- Step 1 is achieved for the crypto library regarding hashes: everything uses
+  MD (not low-level hash APIs), which then dispatches to PSA if applicable.
+- Step 1 is not achieved for all of the crypto library when it come to
+  ciphers. For example,`ctr_drbg.c` calls the legacy API `mbedtls_aes`.
+- Step 2 is achieved for most of X.509 and TLS (same gaps as step 1) when
+  `MBEDTLS_USE_PSA_CRYPTO` is enabled.
+- Step 3 is done for hashes and top-level ECC modules (ECDSA, ECDH, ECJPAKE).
+
+**Strategy for step 1:**
+
+Regarding PK, X.509, and TLS, this is mostly achieved with only a few gaps.
+(The strategy was outlined in the previous section.)
+
+Regarding libmbedcrypto:
+- for hashes and ciphers, see `md-cipher-dispatch.md` in the same directory;
+- for ECC, we have no internal uses of the top-level algorithms (ECDSA, ECDH,
+  ECJPAKE), however they all depend on `ECP_C` which in turn depends on
+`BIGNUM_C`. So, direct calls from TLS, X.509 and PK to ECP and Bignum will
+need to be replaced; see <https://github.com/Mbed-TLS/mbedtls/issues/6839> and
+linked issues for a summary of intermediate steps and open points.
+
+**Strategy for step 2:**
+
+The most satisfying situation here is when we can just use the PSA Crypto API
+for information management as well. However sometimes it may not be
+convenient, for example in parts of the code that accept old-style identifiers
+(such as `mbedtls_md_type_t`) in their API and can't assume PSA to be
+compiled in (such as `rsa.c`).
+
+When using an existing abstraction layer such as MD, it can provide
+information management functions. In other cases, information that was in a
+low-level module but logically belongs in a higher-level module can be moved
+to that module (for example, TLS identifiers of curves and there conversion
+to/from PSA or legacy identifiers belongs in TLS, not `ecp.c`).
+
+**Strategy for step 3:**
+
+There are currently two (complementary) ways for crypto-using code to check if a
+particular algorithm is supported: using `MBEDTLS_xxx` macros, and using
+`PSA_WANT_xxx` macros. For example, PSA-based code that want to use SHA-256
+will check for `PSA_WANT_ALG_SHA_256`, while legacy-based code that wants to
+use SHA-256 will check for `MBEDTLS_SHA256_C` if using the `mbedtls_sha256`
+API, or for `MBEDTLS_MD_C && MBEDTLS_SHA256_C` if using the `mbedtls_md` API.
+
+Code that obeys `MBEDTLS_USE_PSA_CRYPTO` will want to use one of the two
+dependencies above depending on whether `MBEDTLS_USE_PSA_CRYPTO` is defined:
+if it is, the code want the algorithm available in PSA, otherwise, it wants it
+available via the legacy API(s) is it using (MD and/or low-level).
+
+As much as possible, we're trying to create for each algorithm a single new
+macro that can be used to express dependencies everywhere (except pure PSA
+code that should always use `PSA_WANT`). For example, for hashes this is the
+`MBEDTLS_MD_CAN_xxx` family. For ECC algorithms, we have similar
+`MBEDTLS_PK_CAN_xxx` macros.
+
+Note that in order to achieve that goal, even for code that obeys
+`USE_PSA_CRYPTO`, it is useful to impose that all algorithms that are
+available via the legacy APIs are also available via PSA.
+
+Executing step 3 will mostly consist of using the right dependency macros in
+the right places (once the previous steps are done).
+
+**Note on testing**
+
+Since supporting driver-only builds is not about adding features, but about
+supporting existing features in new types of builds, testing will not involve
+adding cases to the test suites, but instead adding new components in `all.sh`
+that build and run tests in newly-supported configurations. For example, if
+we're making some part of the library work with hashes provided only by
+drivers when `MBEDTLS_USE_PSA_CRYPTO` is defined, there should be a place in
+`all.sh` that builds and run tests in such a configuration.
+
+There is however a risk, especially in step 3 where we change how dependencies
+are expressed (sometimes in bulk), to get things wrong in a way that would
+result in more tests being skipped, which is easy to miss. Care must be
+taken to ensure this does not happen. The following criteria can be used:
+
+1. The sets of tests skipped in the default config and the full config must be
+  the same before and after the PR that implements step 3. This is tested
+manually for each PR that changes dependency declarations by using the script
+`outcome-analysis.sh` in the present directory.
+2. The set of tests skipped in the driver-only build is the same as in an
+  equivalent software-based configuration. This is tested automatically by the
+CI in the "Results analysis" stage, by running
+`tests/scripts/analyze_outcomes.py`. See the
+`analyze_driver_vs_reference_xxx` actions in the script and the comments above
+their declaration for how to do that locally.
+
+
+Migrating away from the legacy API
+==================================
+
+This section briefly introduces questions and possible plans towards G4,
+mainly as they relate to choices in previous stages.
+
+The role of the PK/Cipher/MD APIs in user migration
+---------------------------------------------------
+
+We're currently taking advantage of the existing PK layer in order
+to reduce the number of places where library code needs to be changed. It's
+only natural to consider using the same strategy (with the PK, MD and Cipher
+layers) for facilitating migration of application code.
+
+Note: a necessary first step for that would be to make sure PSA is no longer
+implemented of top of the concerned layers
+
+### Zero-cost compatibility layer?
+
+The most favourable case is if we can have a zero-cost abstraction (no
+runtime, RAM usage or code size penalty), for example just a bunch of
+`#define`s, essentially mapping `mbedtls_` APIs to their `psa_` equivalent.
+
+Unfortunately that's unlikely to fully work. For example, the MD layer uses the
+same context type for hashes and HMACs, while the PSA API (rightfully) has
+distinct operation types. Similarly, the Cipher layer uses the same context
+type for unauthenticated and AEAD ciphers, which again the PSA API
+distinguishes.
+
+It is unclear how much value, if any, a zero-cost compatibility layer that's
+incomplete (for example, for MD covering only hashes, or for Cipher covering
+only AEAD) or differs significantly from the existing API (for example,
+introducing new context types) would provide to users.
+
+### Low-cost compatibility layers?
+
+Another possibility is to keep most or all of the existing API for the PK, MD
+and Cipher layers, implemented on top of PSA, aiming for the lowest possible
+cost. For example, `mbedtls_md_context_t` would be defined as a (tagged) union
+of `psa_hash_operation_t` and `psa_mac_operation_t`, then `mbedtls_md_setup()`
+would initialize the correct part, and the rest of the functions be simple
+wrappers around PSA functions. This would vastly reduce the complexity of the
+layers compared to the existing (no need to dispatch through function
+pointers, just call the corresponding PSA API).
+
+Since this would still represent a non-zero cost, not only in terms of code
+size, but also in terms of maintenance (testing, etc.) this would probably
+be a temporary solution: for example keep the compatibility layers in 4.0 (and
+make them optional), but remove them in 5.0.
+
+Again, this provides the most value to users if we can manage to keep the
+existing API unchanged. Their might be conflicts between this goal and that of
+reducing the cost, and judgment calls may need to be made.
+
+Note: when it comes to holding public keys in the PK layer, depending on how
+the rest of the code is structured, it may be worth holding the key data in
+memory controlled by the PK layer as opposed to a PSA key slot, moving it to a
+slot only when needed (see current `ecdsa_verify_wrap` when
+`MBEDTLS_USE_PSA_CRYPTO` is defined)  For example, when parsing a large
+number, N, of X.509 certificates (for example the list of trusted roots), it
+might be undesirable to use N PSA key slots for their public keys as long as
+the certs are loaded. OTOH, this could also be addressed by merging the "X.509
+parsing on-demand" (#2478), and then the public key data would be held as
+bytes in the X.509 CRT structure, and only moved to a PK context / PSA slot
+when it's actually used.
+
+Note: the PK layer actually consists of two relatively distinct parts: crypto
+operations, which will be covered by PSA, and parsing/writing (exporting)
+from/to various formats, which is currently not fully covered by the PSA
+Crypto API.
+
+### Algorithm identifiers and other identifiers
+
+It should be easy to provide the user with a bunch of `#define`s for algorithm
+identifiers, for example `#define MBEDTLS_MD_SHA256 PSA_ALG_SHA_256`; most of
+those would be in the MD, Cipher and PK compatibility layers mentioned above,
+but there might be some in other modules that may be worth considering, for
+example identifiers for elliptic curves.
+
+### Lower layers
+
+Generally speaking, we would retire all of the low-level, non-generic modules,
+such as AES, SHA-256, RSA, DHM, ECDH, ECP, bignum, etc, without providing
+compatibility APIs for them. People would be encouraged to switch to the PSA
+API. (The compatibility implementation of the existing PK, MD, Cipher APIs
+would mostly benefit people who already used those generic APis rather than
+the low-level, alg-specific ones.)
+
+### APIs in TLS and X.509
+
+Public APIs in TLS and X.509 may be affected by the migration in at least two
+ways:
+
+1. APIs that rely on a legacy `mbedtls_` crypto type: for example
+   `mbedtls_ssl_conf_own_cert()` to configure a (certificate and the
+associated) private key. Currently the private key is passed as a
+`mbedtls_pk_context` object, which would probably change to a `psa_key_id_t`.
+Since some users would probably still be using the compatibility PK layer, it
+would need a way to easily extract the PSA key ID from the PK context.
+
+2. APIs the accept list of identifiers: for example
+   `mbedtls_ssl_conf_curves()` taking a list of `mbedtls_ecp_group_id`s. This
+could be changed to accept a list of pairs (`psa_ecc_family_t`, size) but we
+should probably take this opportunity to move to a identifier independent from
+the underlying crypto implementation and use TLS-specific identifiers instead
+(based on IANA values or custom enums), as is currently done in the new
+`mbedtls_ssl_conf_groups()` API, see #4859).
+
+Testing
+-------
+
+An question that needs careful consideration when we come around to removing
+the low-level crypto APIs and making PK, MD and Cipher optional compatibility
+layers is to be sure to preserve testing quality. A lot of the existing test
+cases use the low level crypto APIs; we would need to either keep using that
+API for tests, or manually migrate tests to the PSA Crypto API. Perhaps a
+combination of both, perhaps evolving gradually over time.
diff --git a/docs/architecture/psa-migration/syms.sh b/docs/architecture/psa-migration/syms.sh
new file mode 100755
index 0000000..6c9686e
--- /dev/null
+++ b/docs/architecture/psa-migration/syms.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+#
+# Copyright The Mbed TLS Contributors
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+#
+# Purpose
+#
+# Show external links in built libraries (X509 or TLS) or modules. This is
+# usually done to list Crypto dependencies or to check modules'
+# interdependencies.
+#
+# Usage:
+# - build the library with debug symbols and the config you're interested in
+#   (default, full minus MBEDTLS_USE_PSA_CRYPTO, full, etc.)
+# - launch this script with 1 or more arguments depending on the analysis' goal:
+#     - if only 1 argument is used (which is the name of the used config,
+#       ex: full), then the analysis is done on libmbedx509 and libmbedtls
+#       libraries by default
+#     - if multiple arguments are provided, then modules' names (ex: pk,
+#       pkparse, pkwrite, etc) are expected after the 1st one and the analysis
+#       will be done on those modules instead of the libraries.
+
+set -eu
+
+# list mbedtls_ symbols of a given type in a static library
+syms() {
+    TYPE="$1"
+    FILE="$2"
+
+    nm "$FILE" | sed -n "s/[0-9a-f ]*${TYPE} \(mbedtls_.*\)/\1/p" | sort -u
+}
+
+# Check if the provided name refers to a module or library and return the
+# same path with proper extension
+get_file_with_extension() {
+    BASE=$1
+    if [ -f $BASE.o ]; then
+        echo $BASE.o
+    elif [ -f $BASE.a ]; then
+        echo $BASE.a
+    fi
+}
+
+# create listings for the given library
+list() {
+    NAME="$1"
+    FILE=$(get_file_with_extension "library/${NAME}")
+    PREF="${CONFIG}-$NAME"
+
+    syms '[TRrD]' $FILE > ${PREF}-defined
+    syms U $FILE > ${PREF}-unresolved
+
+    diff ${PREF}-defined ${PREF}-unresolved \
+        | sed -n 's/^> //p' > ${PREF}-external
+    sed 's/mbedtls_\([^_]*\).*/\1/' ${PREF}-external \
+        | uniq -c | sort -rn > ${PREF}-modules
+
+    rm ${PREF}-defined ${PREF}-unresolved
+}
+
+CONFIG="${1:-unknown}"
+
+# List of modules to check is provided as parameters
+if [ $# -gt 1 ]; then
+    shift 1
+    ITEMS_TO_CHECK="$@"
+else
+    ITEMS_TO_CHECK="libmbedx509 libmbedtls"
+fi
+
+for ITEM in $ITEMS_TO_CHECK; do
+    list $ITEM
+done
diff --git a/docs/architecture/psa-migration/testing.md b/docs/architecture/psa-migration/testing.md
new file mode 100644
index 0000000..f205c16
--- /dev/null
+++ b/docs/architecture/psa-migration/testing.md
@@ -0,0 +1,99 @@
+Testing strategy for `MBEDTLS_USE_PSA_CRYPTO`
+=============================================
+
+This document records the testing strategy used so far in implementing
+`MBEDTLS_USE_PSA_CRYPTO`.
+
+
+General considerations
+----------------------
+
+There needs to be at least one build in `all.sh` that enables
+`MBEDTLS_USE_PSA_CRYPTO` and runs the full battery of tests; currently that's
+ensured by the fact that `scripts/config.py full` enables
+`MBEDTLS_USE_PSA_CRYPTO`. There needs to be at least one build with
+`MBEDTLS_USE_PSA_CRYPTO` disabled (as long as it's optional); currently that's
+ensured by the fact that it's disabled in the default config.
+
+Generally, code review is enough to ensure that PSA APIs are indeed used where
+they should be when `MBEDTLS_USE_PSA_CRYPTO` is enabled.
+
+However, when it comes to TLS, we also have the option of using debug messages
+to confirm which code path is taken. This is generally unnecessary, except when
+a decision is made at run-time about whether to use the PSA or legacy code
+path. (For example, for record protection, previously (until 3.1), some ciphers were supported
+via PSA while some others weren't, with a run-time fallback. In this case, it's
+good to have a debug message checked by the test case to confirm that the
+right decision was made at run-time, i. e. that we didn't use the fallback for
+ciphers that are supposed to be supported.)
+
+
+New APIs meant for application use
+----------------------------------
+
+For example, `mbedtls_pk_setup_opaque()` is meant to be used by applications
+in order to create PK contexts that can then be passed to existing TLS and
+X.509 APIs (which remain unchanged).
+
+In that case, we want:
+
+- unit testing of the new API and directly-related APIs - for example:
+  - in `test_suite_pk` we have a new test function `pk_psa_utils` that exercises
+    `mbedtls_pk_setup_opaque()` and checks that various utility functions
+  (`mbedtls_pk_get_type()` etc.) work and the functions that are expected to
+  fail (`mbedtls_pk_verify()` etc) return the expected error.
+  - in `test_suite_pk` we modified the existing `pk_psa_sign` test function to
+    check that signature generation works as expected
+  - in `test_suite_pkwrite` we should have a new test function checking that
+    exporting (writing out) the public part of the key works as expected and
+    that exporting the private key fails as expected.
+- integration testing of the new API with each existing API which should
+  accepts a context created this way - for example:
+  - in `programs/ssl/ssl_client2` a new option `key_opaque` that causes the
+    new API to be used, and one or more tests in `ssl-opt.sh` using that.
+    (We should have the same server-side.)
+  - in `test_suite_x509write` we have a new test function
+    `x509_csr_check_opaque()` checking integration of the new API with the
+    existing `mbedtls_x509write_csr_set_key()`. (And also
+    `mbedtls_x509write_crt_set_issuer_key()` since #5710.)
+
+For some APIs, for example with `mbedtls_ssl_conf_psk_opaque()`, testing in
+`test_suite_ssl` was historically not possible, so we only have testing in
+`ssl-opt.sh`.
+
+New APIs meant for internal use
+-------------------------------
+
+For example, `mbedtls_cipher_setup_psa()` (no longer used, soon to be
+deprecated - #5261) was meant to be used by the TLS layer, but probably not
+directly by applications.
+
+In that case, we want:
+
+- unit testing of the new API and directly-related APIs - for example:
+  - in `test_suite_cipher`, the existing test functions `auth_crypt_tv` and
+    `test_vec_crypt` gained a new parameter `use_psa` and corresponding test
+    cases
+- integration testing:
+  - usually already covered by existing tests for higher-level modules:
+    - for example simple use of `mbedtls_cipher_setup_psa()` in TLS is already
+      covered by running the existing TLS tests in a build with
+      `MBEDTLS_USA_PSA_CRYPTO` enabled
+  - however if use of the new API in higher layers involves more logic that
+    use of the old API, specific integrations test may be required
+    - for example, the logic to fall back from `mbedtls_cipher_setup_psa()` to
+      `mbedtls_cipher_setup()` in TLS is tested by `run_test_psa` in
+      `ssl-opt.sh`.
+
+Internal changes
+----------------
+
+For example, use of PSA to compute the TLS 1.2 PRF.
+
+Changes in this category rarely require specific testing, as everything should
+be already be covered by running the existing tests in a build with
+`MBEDTLS_USE_PSA_CRYPTO` enabled; however we need to make sure the existing
+test have sufficient coverage, and improve them if necessary.
+
+However, if additional logic is involved, or there are run-time decisions about
+whether to use the PSA or legacy code paths, specific tests might be in order.
diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md
new file mode 100644
index 0000000..ef3a6b0
--- /dev/null
+++ b/docs/architecture/psa-shared-memory.md
@@ -0,0 +1,685 @@
+PSA API functions and shared memory
+===================================
+
+## Introduction
+
+This document discusses the security architecture of systems where PSA API functions might receive arguments that are in memory that is shared with an untrusted process. On such systems, the untrusted process might access a shared memory buffer while the cryptography library is using it, and thus cause unexpected behavior in the cryptography code.
+
+### Core assumptions
+
+We assume the following scope limitations:
+
+* Only PSA Crypto API functions are in scope (including Mbed TLS extensions to the official API specification). Legacy crypto, X.509, TLS, or any other function which is not called `psa_xxx` is out of scope.
+* We only consider [input buffers](https://arm-software.github.io/psa-api/crypto/1.1/overview/conventions.html#input-buffer-sizes) and [output buffers](https://arm-software.github.io/psa-api/crypto/1.1/overview/conventions.html#output-buffer-sizes). Any other data is assumed to be in non-shared memory.
+
+## System architecture discussion
+
+### Architecture overview
+
+We consider a system that has memory separation between partitions: a partition can't access another partition's memory directly. Partitions are meant to be isolated from each other: a partition may only affect the integrity of another partition via well-defined system interfaces. For example, this can be a Unix/POSIX-like system that isolates processes, or isolation between the secure world and the non-secure world relying on a mechanism such as TrustZone, or isolation between secure-world applications on such a system.
+
+More precisely, we consider such a system where our PSA Crypto implementation is running inside one partition, called the **crypto service**. The crypto service receives remote procedure calls (RPC) from other partitions, validates their arguments (e.g. validation of key identifier ownership), and calls a PSA Crypto API function. This document is concerned with environments where the arguments passed to a PSA Crypto API function may be in shared memory (as opposed to environments where the inputs are always copied into memory that is solely accessible by the crypto service before calling the API function, and likewise with output buffers after the function returns).
+
+When the data is accessible to another partition, there is a risk that this other partition will access it while the crypto implementation is working. Although this could be prevented by suspending the whole system while crypto is working, such a limitation is rarely desirable and most systems don't offer a way to do it. (Even systems that have absolute thread priorities, and where crypto has a higher priority than any untrusted partition, may be vulnerable due to having multiple cores or asynchronous data transfers with peripherals.)
+
+The crypto service must guarantee that it behaves as if the rest of the world was suspended while it is executed. A behavior that is only possible if an untrusted entity accesses a buffer while the crypto service is processing the data is a security violation.
+
+### Risks and vulnerabilities
+
+We consider a security architecture with two or three entities:
+
+* a crypto service, which offers PSA crypto API calls over RPC (remote procedure call) using shared memory for some input or output arguments;
+* a client of the crypto service, which makes a RPC to the crypto service;
+* in some scenarios, a client of the client, which makes a RPC to the crypto client which re-shares the memory with the crypto service.
+
+The behavior of RPC is defined for in terms of values of inputs and outputs. This models an ideal world where the content of input and output buffers is not accessible outside the crypto service while it is processing an RPC. It is a security violation if the crypto service behaves in a way that cannot be achieved by setting the inputs before the RPC call, and reading the outputs after the RPC call is finished.
+
+#### Read-read inconsistency
+
+If an input argument is in shared memory, there is a risk of a **read-read inconsistency**:
+
+1. The crypto code reads part of the input and validates it, or injects it into a calculation.
+2. The client (or client's client) modifies the input.
+3. The crypto code reads the same part again, and performs an action which would be impossible if the input had had the same value all along.
+
+Vulnerability example (parsing): suppose the input contains data with a type-length-value or length-value encoding (for example, importing an RSA key). The crypto code reads the length field and checks that it fits within the buffer. (This could be the length of the overall data, or the length of an embedded field) Later, the crypto code reads the length again and uses it without validation. A malicious client can modify the length field in the shared memory between the two reads and thus cause a buffer overread on the second read.
+
+Vulnerability example (dual processing): consider an RPC to perform authenticated encryption, using a mechanism with an encrypt-and-MAC structure. The authenticated encryption implementation separately calculates the ciphertext and the MAC from the plaintext. A client sets the plaintext input to `"PPPP"`, then starts the RPC call, then changes the input buffer to `"QQQQ"` while the crypto service is working.
+
+* Any of `enc("PPPP")+mac("PPPP")`, `enc("PPQQ")+mac("PPQQ")` or `enc("QQQQ")+mac("QQQQ")` are valid outputs: they are outputs that can be produced by this authenticated encryption RPC.
+* If the authenticated encryption calculates the ciphertext before the client changes the output buffer and calculates the MAC after that change, reading the input buffer again each time, the output will be `enc("PPPP")+mac("QQQQ")`. There is no input that can lead to this output, hence this behavior violates the security guarantees of the crypto service.
+
+#### Write-read inconsistency
+
+If an output argument is in shared memory, there is a risk of a **write-read inconsistency**:
+
+1. The crypto code writes some intermediate data into the output buffer.
+2. The client (or client's client) modifies the intermediate data.
+3. The crypto code reads the intermediate data back and continues the calculation, leading to an outcome that would not be possible if the intermediate data had not been modified.
+
+Vulnerability example: suppose that an RSA signature function works by formatting the data in place in the output buffer, then applying the RSA private-key operation in place. (This is how `mbedtls_rsa_pkcs1_sign` works.) A malicious client may write badly formatted data into the buffer, so that the private-key operation is not a valid signature (e.g. it could be a decryption), violating the RSA key's usage policy.
+
+Vulnerability example with chained calls: we consider the same RSA signature operation as before. In this example, we additionally assume that the data to sign comes from an attestation application which signs some data on behalf of a final client: the key and the data to sign are under the attestation application's control, and the final client must not be able to obtain arbitrary signatures. The final client shares an output buffer for the signature with the attestation application, and the attestation application re-shares this buffer with the crypto service. A malicious final client can modify the intermediate data and thus sign arbitrary data.
+
+#### Write-write disclosure
+
+If an output argument is in shared memory, there is a risk of a **write-write disclosure**:
+
+1. The crypto code writes some intermediate data into the output buffer. This intermediate data must remain confidential.
+2. The client (or client's client) reads the intermediate data.
+3. The crypto code overwrites the intermediate data.
+
+Vulnerability example with chained calls (temporary exposure): an application encrypts some data, and lets its clients store the ciphertext. Clients may not have access to the plaintext. To save memory, when it calls the crypto service, it passes an output buffer that is in the final client's memory. Suppose the encryption mechanism works by copying its input to the output buffer then encrypting in place (for example, to simplify considerations related to overlap, or because the implementation relies on a low-level API that works in place). In this scenario, the plaintext is exposed to the final client while the encryption in progress, which violates the confidentiality of the plaintext.
+
+Vulnerability example with chained calls (backtrack): we consider a provisioning application that provides a data encryption service on behalf of multiple clients, using a single shared key. Clients are not allowed to access each other's data. The provisioning application isolates clients by including the client identity in the associated data. Suppose that an AEAD decryption function processes the ciphertext incrementally by simultaneously writing the plaintext to the output buffer and calculating the tag. (This is how AEAD decryption usually works.) At the end, if the tag is wrong, the decryption function wipes the output buffer. Assume that the output buffer for the plaintext is shared from the client to the provisioning application, which re-shares it with the crypto service. A malicious client can read another client (the victim)'s encrypted data by passing the ciphertext to the provisioning application, which will attempt to decrypt it with associated data identifying the requesting client. Although the operation will fail beacuse the tag is wrong, the malicious client still reads the victim plaintext.
+
+#### Write-read feedback
+
+If a function both has an input argument and an output argument in shared memory, and processes its input incrementally to emit output incrementally, the following sequence of events is possible:
+
+1. The crypto code processes part of the input and writes the corresponding part of the output.
+2. The client reads the early output and uses that to calculate the next part of the input.
+3. The crypto code processes the rest of the input.
+
+There are cryptographic mechanisms for which this breaks security properties. An example is [CBC encryption](https://link.springer.com/content/pdf/10.1007/3-540-45708-9_2.pdf): if the client can choose the content of a plaintext block after seeing the immediately preceding ciphertext block, this gives the client a decryption oracle. This is a security violation if the key policy only allowed the client to encrypt, not to decrypt.
+
+TODO: is this a risk we want to take into account? Although this extends the possible behaviors of the one-shot interface, the client can do the same thing legitimately with the multipart interface.
+
+### Possible countermeasures
+
+In this section, we briefly discuss generic countermeasures.
+
+#### Copying
+
+Copying is a valid countermeasure. It is conceptually simple. However, it is often unattractive because it requires additional memory and time.
+
+Note that although copying is very easy to write into a program, there is a risk that a compiler (especially with whole-program optimization) may optimize the copy away, if it does not understand that copies between shared memory and non-shared memory are semantically meaningful.
+
+Example: the PSA Firmware Framework 1.0 forbids shared memory between partitions. This restriction is lifted in version 1.1 due to concerns over RAM usage.
+
+#### Careful accesses
+
+The following rules guarantee that shared memory cannot result in a security violation other than [write-read feedback](#write-read-feedback):
+
+* Never read the same input twice at the same index.
+* Never read back from an output.
+* Never write to the output twice at the same index.
+    * This rule can usefully be relaxed in many circumstances. It is ok to write data that is independent of the inputs (and not otherwise confidential), then overwrite it. For example, it is ok to zero the output buffer before starting to process the input.
+
+These rules are very difficult to enforce.
+
+Example: these are the rules that a GlobalPlatform TEE Trusted Application (application running on the secure side of TrustZone on Cortex-A) must follow.
+
+## Protection requirements
+
+### Responsibility for protection
+
+A call to a crypto service to perform a crypto operation involves the following components:
+
+1. The remote procedure call framework provided by the operating system.
+2. The code of the crypto service.
+3. The code of the PSA Crypto dispatch layer (also known as the core), which is provided by Mbed TLS.
+4. The driver implementing the cryptographic mechanism, which may be provided by Mbed TLS (built-in driver) or by a third-party driver.
+
+The [PSA Crypto API specification](https://arm-software.github.io/psa-api/crypto/1.1/overview/conventions.html#stability-of-parameters) puts the responsibility for protection on the implementation of the PSA Crypto API, i.e. (3) or (4).
+
+> In an environment with multiple threads or with shared memory, the implementation carefully accesses non-overlapping buffer parameters in order to prevent any security risk resulting from the content of the buffer being modified or observed during the execution of the function. (...)
+
+In Mbed TLS 2.x and 3.x up to and including 3.5.0, there is no defense against buffers in shared memory. The responsibility shifts to (1) or (2), but this is not documented.
+
+In the remainder of this chapter, we will discuss how to implement this high-level requirement where it belongs: inside the implementation of the PSA Crypto API. Note that this allows two possible levels: in the dispatch layer (independently of the implementation of each mechanism) or in the driver (specific to each implementation).
+
+#### Protection in the dispatch layer
+
+The dispatch layer has no control over how the driver layer will access buffers. Therefore the only possible protection at this layer method is to ensure that drivers have no access to shared memory. This means that any buffer located in shared memory must be copied into or out of a buffer in memory owned by the crypto service (heap or stack). This adds inefficiency, mostly in terms of RAM usage.
+
+For buffers with a small static size limit, this is something we often do for convenience, especially with output buffers. However, as of Mbed TLS 3.5.0, it is not done systematically.
+
+It is ok to skip the copy if it is known for sure that a buffer is not in shared memory. However, the location of the buffer is not under the control of Mbed TLS. This means skipping the copy would have to be a compile-time or run-time option which has to be set by the application using Mbed TLS. This is both an additional maintenance cost (more code to analyze, more testing burden), and a residual security risk in case the party who is responsible for setting this option does not set it correctly. As a consequence, Mbed TLS will not offer this configurability unless there is a compelling argument.
+
+#### Protection in the driver layer
+
+Putting the responsibility for protection in the driver layer increases the overall amount of work since there are more driver implementations than dispatch implementations. (This is true even inside Mbed TLS: almost all API functions have multiple underlying implementations, one for each algorithm.) It also increases the risk to the ecosystem since some drivers might not protect correctly. Therefore having drivers be responsible for protection is only a good choice if there is a definite benefit to it, compared to allocating an internal buffer and copying. An expected benefit in some cases is that there are practical protection methods other than copying.
+
+Some cryptographic mechanisms are naturally implemented by processing the input in a single pass, with a low risk of ever reading the same byte twice, and by writing the final output directly into the output buffer. For such mechanism, it is sensible to mandate that drivers respect these rules.
+
+In the next section, we will analyze how susceptible various cryptographic mechanisms are to shared memory vulnerabilities.
+
+### Susceptibility of different mechanisms
+
+#### Operations involving small buffers
+
+For operations involving **small buffers**, the cost of copying is low. For many of those, the risk of not copying is high:
+
+* Any parsing of formatted data has a high risk of [read-read inconsistency](#read-read-inconsistency).
+* An internal review shows that for RSA operations, it is natural for an implementation to have a [write-read inconsistency](#write-read-inconsistency) or a [write-write disclosure](#write-write-disclosure).
+
+Note that in this context, a “small buffer” is one with a size limit that is known at compile time, and small enough that copying the data is not prohibitive. For example, an RSA key fits in a small buffer. A hash input is not a small buffer, even if it happens to be only a few bytes long in one particular call.
+
+The following buffers are considered small buffers:
+
+* Any input or output directly related to asymmetric cryptography (signature, encryption/decryption, key exchange, PAKE), including key import and export.
+    * Note that this does not include inputs or outputs that are not processed by an asymmetric primitives, for example the message input to `psa_sign_message` or `psa_verify_message`.
+* Cooked key derivation output.
+* The output of a hash or MAC operation.
+
+**Design decision: the dispatch layer shall copy all small buffers**.
+
+#### Symmetric cryptography inputs with small output
+
+Message inputs to hash, MAC and key derivation operations are at a low risk of [read-read inconsistency](#read-read-inconsistency) because they are unformatted data, and for all specified algorithms, it is natural to process the input one byte at a time.
+
+**Design decision: require symmetric cryptography drivers to read their input without a risk of read-read inconsistency**.
+
+TODO: what about IV/nonce inputs? They are typically small, but don't necessarily have a static size limit (e.g. GCM recommends a 12-byte nonce, but also allows large nonces).
+
+#### Key derivation outputs
+
+Key derivation typically emits its output as a stream, with no error condition detected after setup other than operational failures (e.g. communication failure with an accelerator) or running out of data to emit (which can easily be checked before emitting any data, since the data size is known in advance).
+
+(Note that this is about raw byte output, not about cooked key derivation, i.e. deriving a structured key, which is considered a [small buffer](#operations-involving-small-buffers).)
+
+**Design decision: require key derivation drivers to emit their output without reading back from the output buffer**.
+
+#### Cipher and AEAD
+
+AEAD decryption is at risk of [write-write disclosure](#write-write-disclosure) when the tag does not match.
+
+AEAD encryption and decryption are at risk of [read-read inconsistency](#read-read-inconsistency) if they process the input multiple times, which is natural in a number of cases:
+
+* when encrypting with an encrypt-and-authenticate or authenticate-then-encrypt structure (one read to calculate the authentication tag and another read to encrypt);
+* when decrypting with an encrypt-then-authenticate structure (one read to decrypt and one read to calculate the authentication tag);
+* with SIV modes (not yet present in the PSA API, but likely to come one day) (one full pass to calculate the IV, then another full pass for the core authenticated encryption);
+
+Cipher and AEAD outputs are at risk of [write-read inconsistency](#write-read-inconsistency) and [write-write disclosure](#write-write-disclosure) if they are implemented by copying the input into the output buffer with `memmove`, then processing the data in place. In particular, this approach makes it easy to fully support overlapping, since `memmove` will take care of overlapping cases correctly, which is otherwise hard to do portably (C99 does not offer an efficient, portable way to check whether two buffers overlap).
+
+**Design decision: the dispatch layer shall allocate an intermediate buffer for cipher and AEAD plaintext/ciphertext inputs and outputs**.
+
+Note that this can be a single buffer for the input and the output if the driver supports in-place operation (which it is supposed to, since it is supposed to support arbitrary overlap, although this is not always the case in Mbed TLS, a [known issue](https://github.com/Mbed-TLS/mbedtls/issues/3266)). A side benefit of doing this intermediate copy is that overlap will be supported.
+
+For all currently implemented AEAD modes, the associated data is only processed once to calculate an intermediate value of the authentication tag.
+
+**Design decision: for now, require AEAD drivers to read the additional data without a risk of read-read inconsistency**. Make a note to revisit this when we start supporting an SIV mode, at which point the dispatch layer shall copy the input for modes that are not known to be low-risk.
+
+#### Message signature
+
+For signature algorithms with a hash-and-sign framework, the input to sign/verify-message is passed to a hash, and thus can follow the same rules as [symmetric cryptography inputs with small output](#symmetric-cryptography-inputs-with-small-output). This is also true for `PSA_ALG_RSA_PKCS1V15_SIGN_RAW`, which is the only non-hash-and-sign signature mechanism implemented in Mbed TLS 3.5. This is not true for PureEdDSA (`#PSA_ALG_PURE_EDDSA`), which is not yet implemented: [PureEdDSA signature](https://www.rfc-editor.org/rfc/rfc8032#section-5.1.6) processes the message twice. (However, PureEdDSA verification only processes the message once.)
+
+**Design decision: for now, require sign/verify-message drivers to read their input without a risk of read-read inconsistency**. Make a note to revisit this when we start supporting PureEdDSA, at which point the dispatch layer shall copy the input for algorithms such as PureEdDSA that are not known to be low-risk.
+
+## Design of shared memory protection
+
+This section explains how Mbed TLS implements the shared memory protection strategy summarized below.
+
+### Shared memory protection strategy
+
+* The core (dispatch layer) shall make a copy of the following buffers, so that drivers do not receive arguments that are in shared memory:
+    * Any input or output from asymmetric cryptography (signature, encryption/decryption, key exchange, PAKE), including key import and export.
+    * Plaintext/ciphertext inputs and outputs for cipher and AEAD.
+    * The output of a hash or MAC operation.
+    * Cooked key derivation output.
+
+* A document shall explain the requirements on drivers for arguments whose access needs to be protected:
+    * Hash and MAC input.
+    * Cipher/AEAD IV/nonce (to be confirmed).
+    * AEAD associated data (to be confirmed).
+    * Key derivation input (excluding key agreement).
+    * Raw key derivation output (excluding cooked key derivation output).
+
+* The built-in implementations of cryptographic mechanisms with arguments whose access needs to be protected shall protect those arguments.
+
+Justification: see “[Susceptibility of different mechanisms](#susceptibility-of-different-mechanisms)”.
+
+### Implementation of copying
+
+Copy what needs copying. This is broadly straightforward, however there are a few things to consider.
+
+#### Compiler optimization of copies
+
+It is unclear whether the compiler will attempt to optimize away copying operations.
+
+Once the copying code is implemented, it should be evaluated to see whether compiler optimization is a problem. Specifically, for the major compilers supported by Mbed TLS:
+* Write a small program that uses a PSA function which copies inputs or outputs.
+* Build the program with link-time optimization / full-program optimization enabled (e.g. `-flto` with `gcc`). Try also enabling the most extreme optimization options such as `-Ofast` (`gcc`) and `-Oz` (`clang`).
+* Inspect the generated code with `objdump` or a similar tool to see if copying operations are preserved.
+
+If copying behaviour is preserved by all major compilers then assume that compiler optimization is not a problem.
+
+If copying behaviour is optimized away by the compiler, further investigation is needed. Experiment with using the `volatile` keyword to force the compiler not to optimize accesses to the copied buffers. If the `volatile` keyword is not sufficient, we may be able to use compiler or target-specific techniques to prevent optimization, for example memory barriers or empty `asm` blocks. These may be implemented and verified for important platforms while retaining a C implementation that is likely to be correct on most platforms as a fallback - the same approach taken by the constant-time module.
+
+**Open questions: Will the compiler optimize away copies? If so, can it be prevented from doing so in a portable way?**
+
+#### Copying code
+
+We may either copy buffers on an ad-hoc basis using `memcpy()` in each PSA function, or use a unified set of functions for copying input and output data. The advantages of the latter are obvious:
+
+* Any test hooks need only be added in one place.
+* Copying code must only be reviewed for correctness in one place, rather than in all functions where it occurs.
+* Copy bypass is simpler as we can just replace these functions with no-ops in a single place.
+* Any complexity needed to prevent the compiler optimizing copies away does not have to be duplicated.
+
+On the other hand, the only advantage of ad-hoc copying is slightly greater flexibility.
+
+**Design decision: Create a unified set of functions for copying input and output data.**
+
+#### Copying in multipart APIs
+
+Multipart APIs may follow one of 2 possible approaches for copying of input:
+
+##### 1. Allocate a buffer and copy input on each call to `update()`
+
+This is simple and mirrors the approach for one-shot APIs nicely. However, allocating memory in the middle of a multi-part operation is likely to be bad for performance. Multipart APIs are designed in part for systems that do not have time to perform an operation at once, so introducing poor performance may be a problem here.
+
+**Open question: Does memory allocation in `update()` cause a performance problem? If so, to what extent?**
+
+##### 2. Allocate a buffer at the start of the operation and subdivide calls to `update()`
+
+In this approach, input and output buffers are allocated at the start of the operation that are large enough to hold the expected average call to `update()`. When `update()` is called with larger buffers than these, the PSA API layer makes multiple calls to the driver, chopping the input into chunks of the temporary buffer size and filling the output from the results until the operation is finished.
+
+This would be more complicated than approach (1) and introduces some extra issues. For example, if one of the intermediate calls to the driver's `update()` returns an error, it is not possible for the driver's state to be rolled back to before the first call to `update()`. It is unclear how this could be solved.
+
+However, this approach would reduce memory usage in some cases and prevent memory allocation during an operation. Additionally, since the input and output buffers would be fixed-size it would be possible to allocate them statically, avoiding the need for any dynamic memory allocation at all.
+
+**Design decision: Initially use approach (1) and treat approach (2) as an optimization to be done if necessary.**
+
+### Validation of copying
+
+#### Validation of copying by review
+
+This is fairly self-explanatory. Review all functions that use shared memory and ensure that they each copy memory. This is the simplest strategy to implement but is less reliable than automated validation.
+
+#### Validation of copying with memory pools
+
+Proposed general idea: have tests where the test code calling API functions allocates memory in a certain pool, and code in the library allocates memory in a different pool. Test drivers check that needs-copying arguments are within the library pool, not within the test pool.
+
+#### Validation of copying by memory poisoning
+
+Proposed general idea: in test code, “poison” the memory area used by input and output parameters that must be copied. Poisoning means something that prevents accessing memory while it is poisoned. This could be via memory protection (allocate with `mmap` then disable access with `mprotect`), or some kind of poisoning for an analyzer such as MSan or Valgrind.
+
+In the library, the code that does the copying temporarily unpoisons the memory by calling a test hook.
+
+```c
+static void copy_to_user(void *copy_buffer, void *const input_buffer, size_t length) {
+#if defined(MBEDTLS_TEST_HOOKS)
+    if (memory_poison_hook != NULL) {
+        memory_poison_hook(copy_buffer, length);
+    }
+#endif
+    memcpy(copy_buffer, input_buffer, length);
+#if defined(MBEDTLS_TEST_HOOKS)
+    if (memory_unpoison_hook != NULL) {
+        memory_unpoison_hook(copy_buffer, length);
+    }
+#endif
+}
+```
+The reason to poison the memory before calling the library, rather than after the copy-in (and symmetrically for output buffers) is so that the test will fail if we forget to copy, or we copy the wrong thing. This would not be the case if we relied on the library's copy function to do the poisoning: that would only validate that the driver code does not access the memory on the condition that the copy is done as expected.
+
+##### Options for implementing poisoning
+
+There are several different ways that poisoning could be implemented:
+
+1. Using Valgrind's memcheck tool. Valgrind provides a macro `VALGRIND_MAKE_MEM_NO_ACCESS` that allows manual memory poisoning. Valgrind memory poisoning is already used for constant-flow testing in Mbed TLS.
+2. Using Memory Sanitizer (MSan), which allows us to mark memory as uninitialized. This is also used for constant-flow testing. It is suitable for input buffers only, since it allows us to detect when a poisoned buffer is read but not when it is written.
+3. Using Address Sanitizer (ASan). This provides `ASAN_POISON_MEMORY_REGION` which marks memory as inaccessible.
+4. Allocating buffers separate pages and calling `mprotect()` to set pages as inaccessible. This has the disadvantage that we will have to manually ensure that buffers sit in their own pages, which likely means making a copy.
+5. Filling buffers with random data, keeping a copy of the original. For input buffers, keep a copy of the original and copy it back once the PSA function returns. For output buffers, fill them with random data and keep a separate copy of it. In the memory poisoning hooks, compare the copy of random data with the original to ensure that the output buffer has not been written directly.
+
+Approach (2) is insufficient for the full testing we require as we need to be able to check both input and output buffers.
+
+Approach (5) is simple and requires no extra tooling. It is likely to have good performance as it does not use any sanitizers. However, it requires the memory poisoning test hooks to maintain extra copies of the buffers, which seems difficult to implement in practice. Additionally, it does not precisely test the property we want to validate, so we are relying on the tests to fail if given random data as input. It is possible (if unlikely) that the PSA function will access the poisoned buffer without causing the test to fail. This becomes more likely when we consider test cases that call PSA functions on incorrect inputs to check that the correct error is returned. For these reasons, this memory poisoning approach seems unsuitable.
+
+All three remaining approaches are suitable for our purposes. However, approach (4) is more complex than the other two. To implement it, we would need to allocate poisoned buffers in separate memory pages. They would require special handling and test code would likely have to be designed around this special handling.
+
+Meanwhile, approaches (1) and (3) are much more convenient. We are simply required to call a special macro on some buffer that was allocated by us and the sanitizer takes care of everything else. Of these two, ASan appears to have a limitation related to buffer alignment. From code comments quoted in [the documentation](https://github.com/google/sanitizers/wiki/AddressSanitizerManualPoisoning):
+
+> This function is not guaranteed to poison the whole region - it may poison only subregion of [addr, addr+size) due to ASan alignment restrictions.
+
+Specifically, ASan will round the buffer size down to 8 bytes before poisoning due to details of its implementation. For more information on this, see [Microsoft documentation of this feature](https://learn.microsoft.com/en-us/cpp/sanitizers/asan-runtime?view=msvc-170#alignment-requirements-for-addresssanitizer-poisoning).
+
+It should be possible to work around this by manually rounding buffer lengths up to the nearest multiple of 8 in the poisoning function, although it's remotely possible that this will cause other problems. Valgrind does not appear to have this limitation (unless Valgrind is simply more poorly documented). However, running tests under Valgrind causes a much greater slowdown compared with ASan. As a result, it would be beneficial to implement support for both Valgrind and ASan, to give the extra flexibility to choose either performance or accuracy as required. This should be simple as both have very similar memory poisoning interfaces.
+
+**Design decision: Implement memory poisoning tests with both Valgrind's memcheck and ASan manual poisoning.**
+
+##### Validation with new tests
+
+Validation with newly created tests would be simpler to implement than using existing tests, since the tests can be written to take into account memory poisoning. It is also possible to build such a testsuite using existing tests as a starting point - `mbedtls_test_psa_exercise_key` is a test helper that already exercises many PSA operations on a key. This would need to be extended to cover operations without keys (e.g. hashes) and multipart operations, but it provides a good base from which to build all of the required testing.
+
+Additionally, we can ensure that all functions are exercised by automatically generating test data files.
+
+##### Validation with existing tests
+
+An alternative approach would be to integrate memory poisoning validation with existing tests. This has two main advantages:
+
+* All of the tests are written already, potentially saving development time.
+* The code coverage of these tests is greater than would be achievable writing new tests from scratch. In practice this advantage is small as buffer copying will take place in the dispatch layer. The tests are therefore independent of the values of parameters passed to the driver, so extra coverage in these parameters does not gain anything.
+
+It may be possible to transparently implement memory poisoning so that existing tests can work without modification. This would be achieved by replacing the implementation of `malloc()` with one that allocates poisoned buffers. However, there are some difficulties with this:
+
+* Not all buffers allocated by tests are used as inputs and outputs to PSA functions being tested.
+* Those buffers that are inputs to a PSA function need to be unpoisoned right up until the function is called, so that they can be filled with input data.
+* Those buffers that are outputs from a PSA function need to be unpoisoned straight after the function returns, so that they can be read to check the output is correct.
+
+These issues may be solved by creating some kind of test wrapper around every PSA function call that poisons the memory. However, it is unclear how straightforward this will be in practice. If this is simple to achieve, the extra coverage and time saved on new tests will be a benefit. If not, writing new tests is the best strategy.
+
+**Design decision: Add memory poisoning transparently to existing tests.**
+
+#### Discussion of copying validation
+
+Of all discussed approaches, validation by memory poisoning appears as the best. This is because it:
+
+* Does not require complex linking against different versions of `malloc()` (as is the case with the memory pool approach).
+* Allows automated testing (unlike the review approach).
+
+**Design decision: Use a memory poisoning approach to validate copying.**
+
+### Shared memory protection requirements
+
+TODO: write document and reference it here.
+
+### Validation of careful access for built-in drivers
+
+For PSA functions whose inputs and outputs are not copied, it is important that we validate that the builtin drivers are correctly accessing their inputs and outputs so as not to cause a security issue. Specifically, we must check that each memory location in a shared buffer is not accessed more than once by a driver function. In this section we examine various possible methods for performing this validation.
+
+Note: We are focusing on read-read inconsistencies for now, as most of the cases where we aren't copying are inputs.
+
+#### Review
+
+As with validation of copying, the simplest method of validation we can implement is careful code review. This is the least desirable method of validation for several reasons:
+
+1. It is tedious for the reviewers.
+2. Reviewers are prone to make mistakes (especially when performing tedious tasks).
+3. It requires engineering time linear in the number of PSA functions to be tested.
+4. It cannot assure the quality of third-party drivers, whereas automated tests can be ported to any driver implementation in principle.
+
+If all other approaches turn out to be prohibitively difficult, code review exists as a fallback option. However, it should be understood that this is far from ideal.
+
+#### Tests using `mprotect()`
+
+Checking that a memory location is not accessed more than once may be achieved by using `mprotect()` on a Linux system to cause a segmentation fault whenever a memory access happens. Tests based on this approach are sketched below.
+
+##### Linux mprotect+ptrace
+
+Idea: call `mmap` to allocate memory for arguments and `mprotect` to deny or reenable access. Use `ptrace` from a parent process to react to SIGSEGV from a denied access. On SIGSEGV happening in the faulting region:
+
+1. Use `ptrace` to execute a `mprotect` system call in the child to enable access. TODO: How? `ptrace` can modify registers and memory in the child, which includes changing parameters of a syscall that's about to be executed, but not directly cause the child process to execute a syscall that it wasn't about to execute.
+2. Use `ptrace` with `PTRACE_SINGLESTEP` to re-execute the failed load/store instrution.
+3. Use `ptrace` to execute a `mprotect` system call in the child to disable access.
+4. Use `PTRACE_CONT` to resume the child execution.
+
+Record the addresses that are accessed. Mark the test as failed if the same address is read twice.
+
+##### Debugger + mprotect
+
+Idea: call `mmap` to allocate memory for arguments and `mprotect` to deny or reenable access. Use a debugger to handle SIGSEGV (Gdb: set signal catchpoint). If the segfault was due to accessing the protected region:
+
+1. Execute `mprotect` to allow access.
+2. Single-step the load/store instruction.
+3. Execute `mprotect` to disable access.
+4. Continue execution.
+
+Record the addresses that are accessed. Mark the test as failed if the same address is read twice. This part might be hard to do in the gdb language, so we may want to just log the addresses and then use a separate program to analyze the logs, or do the gdb tasks from Python.
+
+#### Instrumentation (Valgrind)
+
+An alternative approach is to use a dynamic instrumentation tool (the most obvious being Valgrind) to trace memory accesses and check that each of the important memory addresses is accessed no more than once.
+
+Valgrind has no tool specifically that checks the property that we are looking for. However, it is possible to generate a memory trace with Valgrind using the following:
+
+```
+valgrind --tool=lackey --trace-mem=yes --log-file=logfile ./myprogram
+```
+This will execute `myprogram` and dump a record of every memory access to `logfile`, with its address and data width. If `myprogram` is a test that does the following:
+
+1. Set up input and output buffers for a PSA function call.
+2. Leak the start and end address of each buffer via `print()`.
+3. Write data into the input buffer exactly once.
+4. Call the PSA function.
+5. Read data from the output buffer exactly once.
+
+Then it should be possible to parse the output from the program and from Valgrind and check that each location was accessed exactly twice: once by the program's setup and once by the PSA function.
+
+#### Fixed Virtual Platform testing
+
+It may be possible to measure double accesses by running tests on a Fixed Virtual Platform such as Corstone 310 ecosystem FVP, available [here](https://developer.arm.com/downloads/-/arm-ecosystem-fvps). There exists a pre-packaged example program for the Corstone 310 FVP available as part of the Open IoT SDK [here](https://git.gitlab.arm.com/iot/open-iot-sdk/examples/sdk-examples/-/tree/main/examples/mbedtls/cmsis-rtx/corstone-310) that could provide a starting point for a set of tests.
+
+Running on an FVP allows two approaches to careful-access testing:
+
+* Convenient scripted use of a debugger with [Iris](https://developer.arm.com/documentation/101196/latest/). This allows memory watchpoints to be set, perhaps more flexibly than with GDB.
+* Tracing of all memory accesses with [Tarmac Trace](https://developer.arm.com/documentation/100964/1123/Plug-ins-for-Fast-Models/TarmacTrace). To validate the single-access properties, the [processor memory access trace source](https://developer.arm.com/documentation/100964/1123/Plug-ins-for-Fast-Models/TarmacTrace/Processor-memory-access-trace) can be used to output all memory accesses happening on the FVP. This output can then be easily parsed and processed to ensure that the input and output buffers are accessed only once. The addresses of buffers can either be leaked by the program through printing to the serial port or set to fixed values in the FVP's linker script.
+
+#### Discussion of careful-access validation
+
+The best approach for validating the correctness of memory accesses is an open question that requires further investigation. To answer this question, each of the test strategies discussed above must be prototyped as follows:
+
+1. Take 1-2 days to create a basic prototype of a test that uses the approach.
+2. Document the prototype - write a short guide that can be followed to arrive at the same prototype.
+3. Evaluate the prototype according to its usefulness. The criteria of evaluation should include:
+   * Ease of implementation - Was the prototype simple to implement? Having implemented it, is it simple to extend it to do all of the required testing?
+   * Flexibility - Could the prototype be extended to cover other careful-access testing that may be needed in future?
+   * Performance - Does the test method perform well? Will it cause significant slowdown to CI jobs?
+   * Ease of reproduction - Does the prototype require a particular platform or tool to be set up? How easy would it be for an external user to run the prototype?
+   * Comprehensibility - Accounting for the lower code quality of a prototype, would developers unfamiliar with the tests based on the prototype be able to understand them easily?
+   * Portability - How well can this approach be ported to multiple platforms? This would allow us to ensure that there are no double-accesses due to a bug that only affects a specific target.
+
+Once each prototype is complete, choose the best approach to implement the careful-access testing. Implement tests using this approach for each of the PSA interfaces that require careful-access testing:
+
+* Hash
+* MAC
+* AEAD (additional data only)
+* Key derivation
+* Asymmetric signature (input only)
+
+##### New vs existing tests
+
+Most of the test methods discussed above need extra setup. Some require leaking of buffer bounds, predictable memory access patterns or allocation of special buffers. FVP testing even requires the tests to be run on a non-host target.
+
+With this complexity in mind it does not seem feasible to run careful-access tests using existing testsuites. Instead, new tests should be written that exercise the drivers in the required way. Fortunately, the only interfaces that need testing are hash, MAC, AEAD (testing over AD only), Key derivation and Asymmetric signature, which limits the number of new tests that must be written.
+
+#### Validation of validation for careful-access
+
+In order to ensure that the careful-access validation works, it is necessary to write tests to check that we can correctly detect careful-access violations when they occur. To do this, write a test function that:
+
+* Reads its input multiple times at the same location.
+* Writes to its output multiple times at the same location.
+
+Then, write a careful-access test for this function and ensure that it fails.
+
+## Analysis of argument protection in built-in drivers
+
+TODO: analyze the built-in implementations of mechanisms for which there is a requirement on drivers. By code inspection, how satisfied are we that they meet the requirement?
+
+## Copy bypass
+
+For efficiency, we are likely to want mechanisms to bypass the copy and process buffers directly in builds that are not affected by shared memory considerations.
+
+Expand this section to document any mechanisms that bypass the copy.
+
+Make sure that such mechanisms preserve the guarantees when buffers overlap.
+
+## Detailed design
+
+### Implementation by module
+
+Module | Input protection strategy | Output protection strategy | Notes
+---|---|---|---
+Hash and MAC | Careful access | Careful access | Low risk of multiple-access as the input and output are raw unformatted data.
+Cipher | Copying | Copying |
+AEAD | Copying (careful access for additional data) | Copying |
+Key derivation | Careful access | Careful access |
+Asymmetric signature | Careful access | Copying | Inputs to signatures are passed to a hash. This will no longer hold once PureEdDSA support is implemented.
+Asymmetric encryption | Copying | Copying |
+Key agreement | Copying | Copying |
+PAKE | Copying | Copying |
+Key import / export | Copying | Copying | Keys may be imported and exported in DER format, which is a structured format and therefore susceptible to read-read inconsistencies and potentially write-read inconsistencies.
+
+### Copying functions
+
+As discussed in [Copying code](#copying-code), it is simpler to use a single unified API for copying. Therefore, we create the following functions:
+
+* `psa_crypto_copy_input(const uint8_t *input, size_t input_length, uint8_t *input_copy, size_t input_copy_length)`
+* `psa_crypto_copy_output(const uint8_t *output_copy, size_t output_copy_length, uint8_t *output, size_t output_length)`
+
+These seem to be a repeat of the same function, however it is useful to retain two separate functions for input and output parameters so that we can use different test hooks in each when using memory poisoning for tests.
+
+Given that the majority of functions will be allocating memory on the heap to copy, it is helpful to build convenience functions that allocate the memory as well.
+
+In order to keep track of allocated copies on the heap, we can create new structs:
+
+```c
+typedef struct psa_crypto_local_input_s {
+    uint8_t *buffer;
+    size_t length;
+} psa_crypto_local_input_t;
+
+typedef struct psa_crypto_local_output_s {
+    uint8_t *original;
+    uint8_t *buffer;
+    size_t length;
+} psa_crypto_local_output_t;
+```
+
+These may be used to keep track of input and output copies' state, and ensure that their length is always stored with them. In the case of output copies, we keep a pointer to the original buffer so that it is easy to perform a writeback to the original once we have finished outputting.
+
+With these structs we may create 2 pairs of functions, one pair for input copies:
+
+```c
+psa_status_t psa_crypto_local_input_alloc(const uint8_t *input, size_t input_len,
+                                          psa_crypto_local_input_t *local_input);
+
+void psa_crypto_local_input_free(psa_crypto_local_input_t *local_input);
+```
+
+* `psa_crypto_local_input_alloc()` calls `calloc()` to allocate a new buffer of length `input_len`, copies the contents across from `input`. It then stores `input_len` and the pointer to the copy in the struct `local_input`.
+* `psa_crypto_local_input_free()` calls `free()` on the local input that is referred to by `local_input` and sets the pointer in the struct to `NULL`.
+
+We also create a pair of functions for output copies:
+
+```c
+psa_status_t psa_crypto_local_output_alloc(uint8_t *output, size_t output_len,
+                                           psa_crypto_local_output_t *local_output);
+
+psa_status_t psa_crypto_local_output_free(psa_crypto_local_output_t *local_output);
+```
+
+* `psa_crypto_local_output_alloc()` calls `calloc()` to allocate a new buffer of length `output_len` and stores `output_len` and the pointer to the buffer in the struct `local_output`. It also stores a pointer to `output` in `local_output->original`.
+* `psa_crypto_local_output_free()` copies the contents of the output buffer `local_output->buffer` into the buffer `local_output->original`, calls `free()` on `local_output->buffer` and sets it to `NULL`.
+
+Some PSA functions may not use these convenience functions as they may have local optimizations that reduce memory usage. For example, ciphers may be able to use a single intermediate buffer for both input and output.
+
+In order to abstract the management of the copy state further, to make it simpler to add, we create the following 6 convenience macros:
+
+For inputs:
+
+* `LOCAL_INPUT_DECLARE(input, input_copy_name)`, which declares and initializes a `psa_crypto_local_input_t` and a pointer with the name `input_copy_name` in the current scope.
+* `LOCAL_INPUT_ALLOC(input, input_size, input_copy)`, which tries to allocate an input using `psa_crypto_local_input_alloc()`. On failure, it sets an error code and jumps to an exit label. On success, it sets `input_copy` to point to the copy of the buffer.
+* `LOCAL_INPUT_FREE(input, input_copy)`, which frees the input copy using `psa_crypto_local_input_free()` and sets `input_copy` to `NULL`.
+
+For outputs:
+
+* `LOCAL_OUTPUT_DECLARE(output, output_copy_name)`, analogous to `LOCAL_INPUT_DECLARE()` for `psa_crypto_local_output_t`.
+* `LOCAL_OUTPUT_ALLOC(output, output_size, output_copy)`, analogous to `LOCAL_INPUT_ALLOC()` for outputs, calling `psa_crypto_local_output_alloc()`.
+* `LOCAL_OUTPUT_FREE(output, output_copy)`, analogous to `LOCAL_INPUT_FREE()` for outputs. If the `psa_crypto_local_output_t` is in an invalid state (the copy pointer is valid, but the original pointer is `NULL`) this macro sets an error status.
+
+These macros allow PSA functions to have copying added while keeping the code mostly unmodified. Consider a hypothetical PSA function:
+
+```c
+psa_status_t psa_foo(const uint8_t *input, size_t input_length,
+                     uint8_t *output, size_t output_size, size_t *output_length)
+{
+    /* Do some operation on input and output */
+}
+```
+
+By changing the name of the input and output parameters, we can retain the original variable name as the name of the local copy while using a new name (e.g. with the suffix `_external`) for the original buffer. This allows copying to be added near-seamlessly as follows:
+
+```c
+psa_status_t psa_foo(const uint8_t *input_external, size_t input_length,
+                     uint8_t *output_external, size_t output_size, size_t *output_length)
+{
+    psa_status_t status;
+
+    LOCAL_INPUT_DECLARE(input_external, input);
+    LOCAL_OUTPUT_DECLARE(output_external, output);
+
+    LOCAL_INPUT_ALLOC(input_external, input);
+    LOCAL_OUTPUT_ALLOC(output_external, output);
+
+    /* Do some operation on input and output */
+
+exit:
+    LOCAL_INPUT_FREE(input_external, input);
+    LOCAL_OUTPUT_FREE(output_external, output);
+}
+```
+
+A second advantage of using macros for the copying (other than simple convenience) is that it allows copying to be easily disabled by defining alternate macros that function as no-ops. Since buffer copying is specific to systems where shared memory is passed to PSA functions, it is useful to be able to disable it where it is not needed, to save code size.
+
+To this end, the macros above are defined conditionally on a new config option, `MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS`, which may be set whenever PSA functions are assumed to have exclusive access to their input and output buffers. When `MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS` is set, the macros do not perform copying.
+
+### Implementation of copying validation
+
+As discussed in the [design exploration of copying validation](#validation-of-copying), the best strategy for validation of copies appears to be validation by memory poisoning, implemented using Valgrind and ASan.
+
+To perform memory poisoning, we must implement the functions alluded to in [Validation of copying by memory poisoning](#validation-of-copying-by-memory-poisoning):
+```c
+void mbedtls_test_memory_poison(const unsigned char *ptr, size_t size);
+void mbedtls_test_memory_unpoison(const unsigned char *ptr, size_t size);
+```
+This should poison or unpoison the given buffer, respectively.
+
+* `mbedtls_test_memory_poison()` is equivalent to calling `VALGRIND_MAKE_MEM_NOACCESS(ptr, size)` or `ASAN_POISON_MEMORY_REGION(ptr, size)`.
+* `mbedtls_test_memory_unpoison()` is equivalent to calling `VALGRIND_MAKE_MEM_DEFINED(ptr, size)` or `ASAN_UNPOISON_MEMORY_REGION(ptr, size)`.
+
+The PSA copying function must then have test hooks implemented as outlined in [Validation of copying by memory poisoning](#validation-of-copying-by-memory-poisoning).
+
+As discussed in [the design exploration](#validation-with-existing-tests), the preferred approach for implementing copy-testing is to implement it transparently using existing tests. This is specified in more detail below.
+
+#### Transparent allocation-based memory poisoning
+
+In order to implement transparent memory poisoning we require a wrapper around all PSA function calls that poisons any input and output buffers.
+
+The easiest way to do this is to create wrapper functions that poison the memory and then `#define` PSA function names to be wrapped versions of themselves. For example, to replace `psa_aead_update()`:
+```c
+psa_status_t mem_poison_psa_aead_update(psa_aead_operation_t *operation,
+                                        const uint8_t *input,
+                                        size_t input_length,
+                                        uint8_t *output,
+                                        size_t output_size,
+                                        size_t *output_length)
+{
+    mbedtls_test_memory_poison(input, input_length);
+    mbedtls_test_memory_poison(output, output_size);
+    psa_status_t status = psa_aead_update(operation, input, input_length,
+                                          output, output_size, output_length);
+    mbedtls_test_memory_unpoison(input, input_length);
+    mbedtls_test_memory_unpoison(output, output_size);
+
+    return status;
+}
+
+#define psa_aead_update(...) mem_poison_psa_aead_update(__VA_ARGS__)
+```
+
+There now exists a more generic mechanism for making exactly this kind of transformation - the PSA test wrappers, which exist in the files `tests/include/test/psa_test_wrappers.h` and `tests/src/psa_test_wrappers.c`. These are wrappers around all PSA functions that allow testing code to be inserted at the start and end of a PSA function call.
+
+The test wrappers are generated by a script, although they are not automatically generated as part of the build process. Instead, they are checked into source control and must be manually updated when functions change by running `tests/scripts/generate_psa_wrappers.py`.
+
+Poisoning code is added to these test wrappers where relevant in order to pre-poison and post-unpoison the parameters to the functions.
+
+#### Configuration of poisoning tests
+
+Since the memory poisoning tests will require the use of interfaces specific to the sanitizers used to poison memory, they must only be enabled when we are building with ASan or Valgrind. For now, we can auto-detect ASan at compile-time and set an option: `MBEDTLS_TEST_MEMORY_CAN_POISON`. When this option is enabled, we build with memory-poisoning support. This enables transparent testing with ASan without needing any extra configuration options.
+
+Auto-detection and memory-poisoning with Valgrind is left for future work.
+
+#### Validation of validation for copying
+
+To make sure that we can correctly detect functions that access their input/output buffers rather than the copies, it would be best to write a test function that misbehaves and test it with memory poisoning. Specifically, the function should:
+
+* Read its input buffer and after calling the input-buffer-copying function to create a local copy of its input.
+* Write to its output buffer before and after calling the output-buffer-copying function to copy-back its output.
+
+Then, we could write a test that uses this function with memory poisoning and ensure that it fails. Since we are expecting a failure due to memory-poisoning, we would run this test separately from the rest of the memory-poisoning testing.
+
+This testing is implemented in `programs/test/metatest.c`, which is a program designed to check that test failures happen correctly. It may be run via the script `tests/scripts/run-metatests.sh`.
diff --git a/docs/architecture/psa-storage-resilience.md b/docs/architecture/psa-storage-resilience.md
new file mode 100644
index 0000000..2f190d3
--- /dev/null
+++ b/docs/architecture/psa-storage-resilience.md
@@ -0,0 +1,536 @@
+# PSA storage resilience design
+
+## Introduction
+
+The PSA crypto subsystem includes a persistent key store. It is possible to create a persistent key and read it back later. This must work even if the underlying storage exhibits non-nominal behavior. In this document, _resilience_ means correct behavior of the key store even under if the underlying platform behaves in a non-nominal, but still partially controlled way.
+
+At this point, we are only concerned about one specific form of resilience: to a system crash or power loss. That is, we assume that the underlying platform behaves nominally, except that occasionally it may restart. In the field, this can happen due to a sudden loss of power.
+
+This document explores the problem space, defines a library design and a test design.
+
+## Resilience goals for API functions
+
+**Goal: PSA Crypto API functions are atomic and committing.**
+
+_Atomic_ means that when an application calls an API function, as far as the application is concerned, at any given point in time, the system is either in a state where the function has not started yet, or in a state where the function has returned. The application never needs to worry about an intermediate state.
+
+_Committing_ means that when a function returns, the data has been written to the persistent storage. As a consequence, if the system restarts during a sequence of storage modifications $M_1, M_2, \ldots, M_n$, we know that when the system restarts, a prefix of the sequence has been performed. For example, there will never be a situation where $M_2$ has been performed but not $M_1$.
+
+The committing property is important not only for sequences of operations, but also when reporting the result of an operation to an external system. For example, if a key creation function in the PSA Crypto API reports to the application that a key has been created, and the application reports to a server that the key has been created, it is guaranteed that the key exists even if the system restarts.
+
+## Assumptions on the underlying file storage
+
+PSA relies on a PSA ITS (Internal Trusted Storage) interface, which exposes a simple API. There are two functions to modify files:
+
+* `set()` writes a whole file (either creating it, or replacing the previous content).
+* `remove()` removes a file (returning a specific error code if the file does not exist).
+
+**Assumption: the underlying ITS functions are atomic and committing.**
+
+Since the underlying functions are atomic, the content of a file is always a version that was previously passed to `set()`. We do not try to handle the case where a file might be partially written.
+
+## Overview of API functions
+
+For a transparent key, all key management operations (creation or destruction) on persistent keys rely on a single call to the underlying storage (`set()` for a key creation, `remove()` for a key destruction). This also holds for an opaque key stored in a secure element that does not have its own key store: in this case, the core stores a wrapped (i.e. encrypted) copy of the key material, but this does not impact how the core interacts with the storage. Other API functions do not modify the storage.
+
+The following case requires extra work related to resilience:
+
+* [Key management for stateful secure element keys](#designing-key-management-for-secure-element-keys).
+
+As a consequence, apart from the listed cases, the API calls inherit directly from the [resilience properties of the underyling storage](#assumptions-on-the-underlying-file-storage). We do not need to take any special precautions in the library design, and we do not need to perform any testing of resilience for transparent keys.
+
+(This section was last updated for Mbed TLS 3.4.0 implementing PSA Crypto API 1.1.)
+
+## Designing key management for secure element keys
+
+In this section, we use “(stateful) secure element key” to mean a key stored in a stateful secure element, i.e. a secure element that stores keys. This excludes keys in a stateleess secure element for which the core stores a wrapped copy of the key. We study the problem of how key management in stateful secure elements interacts with storage and explore the design space.
+
+### Assumptions on stateful secure elements
+
+**Assumption: driver calls for key management in stateful secure elements are atomic and committing.**
+
+(For stateless secure elements, this assumption is vacuously true.)
+
+### Dual management of keys: the problem
+
+For a secure element key, key management requires a commitment on both sites. For example, consider a successful key creation operation:
+
+1. The core sends a request to the secure element to create a key.
+2. The secure element modifies its key store to create the key.
+3. The secure element reports to the core that the key has been created.
+4. The core reports to the application that the key has been created.
+
+If the core loses power between steps 1 and 2, the key does not exist yet. This is fine from an application's perspective since the core has not committed to the key's existence, but the core needs to take care not to leave resources in storage that are related to the non-existent key. If the core loses power between steps 2 and 3, the key exists in the secure element. From an application's perspective, the core may either report that the key exists or that it does not exist, but in the latter case, the core needs to free the key in the secure element, to avoid leaving behind inaccessible resources.
+
+As a consequence, the content of the storage cannot remain the same between the end of step 1 and the end of step 3, since the core must behave differently depending on whether step 2 has taken place.
+
+Accomplishing a transaction across system boundaries is a well-known problem in database management, with a well-known solution: two-phase commit.
+
+### Overview of two-phase commit with stateful secure elements
+
+With a key in a stateful secure element, a successful creation process goes as follows (see [“Key management in a secure element with storage” in the driver interface specification](../../proposed/psa-driver-interface.html#key-management-in-a-secure-element-with-storage)):
+
+1. The core calls the driver's `"allocate_key"` entry point.
+2. The driver allocates a unique identifier _D_ for the key. This is unrelated to the key identifier _A_ used by the application interface. This step must not modify the state of the secure element.
+3. The core updates the storage to indicate that key identifier _A_ has the identifier _D_ in the driver, and that _A_ is in a half-created state.
+4. The core calls the driver's key creation entry point, passing it the driver's chosen identifier _D_.
+5. The driver creates the key in the secure element. When this happens, it concludes the voting phase of the two-phase commit: effectively, the secure element decides to commit. (It is however possible to revert this commitment by giving the secure element the order to destroy the key.)
+6. The core updates the storage to indicate that _A_ is now in a fully created state. This concludes the commit phase of the two-phase commit.
+
+If there is a loss of power:
+
+* Before step 3: the system state has not changed at all. As far as the world is concerned, the key creation attempt never happened.
+* Between step 3 and step 6: upon restart, the core needs to find out whether the secure element completed step 5 or not, and reconcile the state of the storage with the state of the secure element.
+* After step 6: the key has been created successfully.
+
+Key destruction goes as follows:
+
+1. The core updates the storage indicating that the key is being destroyed.
+2. The core calls the driver's `"destroy_key"` entry point.
+3. The secure element destroys the key.
+4. The core updates the storage to indicate that the key has been destroyed.
+
+If there is a loss of power:
+
+* Before step 1: the system state has not changed at all. As far as the world is concerned, the key destruction attempt never happened.
+* Between step 1 and step 4: upon restart, the core needs to find out whether the secure element completed step 3 or not, and reconcile the state of the storage with the state of the secure element.
+* After step 4: the key has been destroyed successfully.
+
+In both cases, upon restart, the core needs to perform a transaction recovery. When a power loss happens, the core decides whether to commit or abort the transaction.
+
+Note that the analysis in this section assumes that the driver does not update its persistent state during a key management operation (or at least not in a way that is influences the key management process — for example, it might renew an authorization token).
+
+### Optimization considerations for transactions
+
+We assume that power failures are rare. Therefore we will primarily optimize for the normal case. Transaction recovery needs to be practical, but does not have to be fully optimized.
+
+The main quantity we will optimize for is the number of storage updates in the nominal case. This is good for performance because storage writes are likely to dominate the runtime in some hardware configurations where storage writes are slow and communication with the secure element is fast, for key management operations that require a small amount of computation. In addition, minimizing the number of storage updates is good for the longevity of flash media.
+
+#### Information available during recovery
+
+The PSA ITS API does not support enumerating files in storage: an ITS call can only access one file identifier. Therefore transaction recovery cannot be done by traversing files whose name is or encodes the key identifier. It must start by traversing a small number of files whose names are independent of the key identifiers involved.
+
+#### Minimum effort for a transaction
+
+Per the [assumptions on the underlying file storage](#assumptions-on-the-underlying-file-storage), each atomic operation in the internal storage concerns a single file: either removing it, or setting its content. Furthermore there is no way to enumerate the files in storage.
+
+A key creation function must transform the internal storage from a state where file `id` does not exist, to a state where file `id` exists and has its desired final content (containing the key attributes and the driver's key identifier). The situation is similar with key destruction, except that the initial and final states are exchanged. Neither the initial state nor the final state reference `id` otherwise.
+
+For a key that is not in a stateful element, the transaction consists of a single write operation. As discussed previously, this is not possible with a stateful secure element because the state of the internal storage needs to change both before and after the state change in the secure element. No other single-write algorithm works.
+
+If there is a power failure around the time of changing the state of the secure element, there must be information in the internal storage that indicates that key `id` has a transaction in progress. The file `id` cannot be used for this purpose because there is no way to enumerate all keys (and even if there was, it would not be practical). Therefore the transaction will need to modify some other file `t` with a fixed name (a name that doesn't depend on the key). Since the final system state should be identical to the initial state except for the file `id`, the minimum number of storage operations for a transaction is 3:
+
+* Write (create or update) a file `t` referencing `id`.
+* Write the final state of `id`.
+* Restore `t` to its initial state.
+
+The strategies discussed in the [overview above](#overview-of-two-phase-commit-with-stateful-secure-elements) follow this pattern, with `t` being the file containing the transaction list that the recovery consults. We have just proved that this pattern is optimal.
+
+Note that this pattern requires the state of `id` to be modified only once. In particular, if a key management involves writing an intermediate state for `id` before modifying the secure element state and writing a different state after that, this will require a total of 4 updates to internal storage. Since we want to minimize the number of storage updates, we will not explore designs that involved updating `id` twice or more.
+
+### Recovery strategies
+
+When the core starts, it needs to know about transaction(s) that need to be resumed. This information will be stored in a persistent “transaction list”, with one entry per key. In this section, we explore recovery strategies, and we determine what the transaction list needs to contain as well as when it needs to be updated. Other sections will explore the format of the transaction list, as well as how many keys it needs to contain.
+
+#### Exploring the recovery decision tree
+
+There are four cases for recovery when a transaction is in progress. In each case, the core can either decide to commit the transaction (which may require replaying the interrupted part) or abort it (which may require a rewind in the secure element). It may call the secure element driver's `"get_key_attributes"` entry point to find out whether the key is present.
+
+* Key creation, key not present in the secure element:
+    * Committing means replaying the driver call in the key creation. This requires all the input, for example the data to import. This seems impractical in general. Also, the second driver call require a new call to `"allocate_key"` which will in general changing the key's driver identifier, which complicates state management in the core. Given the likely complexity, we exclude this strategy.
+    * Aborting means removing any trace of the key creation.
+* Key creation, key present in the secure element:
+    * Committing means finishing the update of the core's persistent state, as would have been done if the transaction had not been interrupted.
+    * Aborting means destroying the key in the secure element and removing any local storage used for that key.
+* Key destruction, key not present in the secure element:
+    * Committing means finishing the update of the core's persistent state, as would have been done if the transaction had not been interrupted, by removing any remaining local storage used for that key.
+    * Aborting would mean re-creating the key in the secure element, which is impossible in general since the key material is no longer present.
+* Key destruction, key present in the secure element:
+    * Committing means finishing the update of the core's persistent state, as would have been done if the transaction had not been interrupted, by removing any remaining local storage used for that key and destroying the key in the secure element.
+    * Aborting means keeping the key. This requires no action on the secure element, and is only practical locally if the local storage is intact.
+
+#### Comparing recovery strategies
+
+From the analysis above, assuming that all keys are treated in the same way, there are 4 possible strategies.
+
+* [Always follow the state of the secure element](#exploring-the-follow-the-secure-element-strategy). This requires the secure element driver to have a `"get_key_attributes"` entry point. Recovery means resuming the operation where it left off. For key creation, this means that the key metadata needs to be saved before calling the secure element's key creation entry point.
+* Minimize the information processing: [always destroy the key](#exploring-the-always-destroy-strategy), i.e. abort all key creations and commit all key destructions. This does not require querying the state of the secure element. This does not require any special precautions to preserve information about the key during the transaction. It simplifies recovery in that the recovery process might not even need to know whether it's recovering a key creation or a key destruction.
+* Follow the state of the secure element for key creation, but always go ahead with key destruction. This requires the secure element driver to have a `"get_key_attributes"` entry point. Compared to always following the state of the secure element, this has the advantage of maximizing the chance that a command to destroy key material is effective. Compared to always destroying the key, this has a performance advantage if a key creation is interrupted. These do not seem like decisive advantages, so we will not consider this strategy further.
+* Always abort key creation, but follow the state of the secure element for key destruction. I can't think of a good reason to choose this strategy.
+
+Requiring the driver to have a `"get_key_attributes"` entry point is potentially problematic because some secure elements don't have room to store key attributes: a key slot always exists, and it's up to the user to remember what, if anything, they put in it. The driver has to remember anyway, so that it can find a free slot when creating a key. But with a recovery strategy that doesn't involve a `"get_key_attributes"` entry point, the driver design is easier: the driver doesn't need to protect the information about slots in use against a power failure, the core takes care of that.
+
+#### Exploring the follow-the-secure-element strategy
+
+Each entry in the transaction list contains the API key identifier, the key lifetime (or at least the location), the driver key identifier (not constant-size), and an indication of whether the key is being created or destroyed.
+
+For key creation, we have all the information to store in the key file once the `"allocate_key"` call returns. We must store all the information that will go in the key file before calling the driver's key creation entry point. Therefore the normal sequence of operations is:
+
+1. Call the driver's `"allocate_key"` entry point.
+2. Add the key to the transaction list, indicating that it is being created.
+3. Write the key file.
+4. Call the driver's key creation entry point.
+5. Remove the key from the transaction list.
+
+During recovery, for each key in the transaction list that was being created:
+
+* If the key exists in the secure element, just remove it from the transaction list.
+* If the key does not exist in the secure element, first remove the key file if it is present, then remove the key from the transaction list.
+
+For key destruction, we need to preserve the key file until after the key has been destroyed. Therefore the normal sequence of operations is:
+
+1. Add the key to the transaction list, indicating that it is being destroyed.
+2. Call the driver's `"destroy_key"` entry point.
+3. Remove the key file.
+4. Remove the key from the transaction list.
+
+During recovery, for each key in the transaction list that was being created:
+
+* If the key exists in the secure element, call the driver's `"destroy_key"` entry point, then remove the key file, and finally remote the key from the transaction lits.
+* If the key does not exist in the secure element, remove the key file if it is still present, then remove the key from the transaction list.
+
+#### Exploring the always-destroy strategy
+
+Each entry in the transaction list contains the API key identifier, the key lifetime (or at least the location), and the driver key identifier (not constant-size).
+
+For key creation, we do not need to store the key's metadata until it has been created in the secure element. Therefore the normal sequence of operations is:
+
+1. Call the driver's `"allocate_key"` entry point.
+2. Add the key to the transaction list.
+3. Call the driver's key creation entry point.
+4. Write the key file.
+5. Remove the key from the transaction list.
+
+For key destruction, we can remove the key file before contacting the secure element. Therefore the normal sequence of operations is:
+
+1. Add the key to the transaction list.
+2. Remove the key file.
+3. Call the driver's `"destroy_key"` entry point.
+4. Remove the key from the transaction list.
+
+Recovery means removing all traces of all keys on the transaction list. This means following the destruction process, starting after the point where the key has been added to the transaction list, and ignoring any failure of a removal action if the item to remove does not exist:
+
+1. Remove the key file, treating `DOES_NOT_EXIST` as a success.
+2. Call the driver's `"destroy_key"` entry point, treating `DOES_NOT_EXIST` as a success.
+3. Remove the key from the transaction list.
+
+#### Always-destroy strategy with a simpler transaction file
+
+We can modify the [always-destroy strategy](#exploring-the-always-destroy-strategy) to make the transaction file simpler: if we ensure that the key file always exists if the key exists in the secure element, then the transaction list does not need to include the driver key identifier: it can be read from the key file.
+
+For key creation, we need to store the key's metadata before creating in the secure element. Therefore the normal sequence of operations is:
+
+1. Call the driver's `"allocate_key"` entry point.
+2. Add the key to the transaction list.
+3. Write the key file.
+4. Call the driver's key creation entry point.
+5. Remove the key from the transaction list.
+
+For key destruction, we need to contact the secure element before removing the key file. Therefore the normal sequence of operations is:
+
+1. Add the key to the transaction list.
+2. Call the driver's `"destroy_key"` entry point.
+3. Remove the key file.
+4. Remove the key from the transaction list.
+
+Recovery means removing all traces of all keys on the transaction list. This means following the destruction process, starting after the point where the key has been added to the transaction list, and ignoring any failure of a removal action if the item to remove does not exist:
+
+1. Load the driver key identifier from the key file. If the key file does not exist, skip to step 4.
+2. Call the driver's `"destroy_key"` entry point, treating `DOES_NOT_EXIST` as a success.
+3. Remove the key file, treating `DOES_NOT_EXIST` as a success.
+4. Remove the key from the transaction list.
+
+Compared with the basic always-destroy strategy:
+
+* The transaction file handling is simpler since its entries have a fixed size.
+* The flow of information is somewhat different from transparent keys and keys in stateless secure elements: we aren't just replacing “create the key material” by “tell the secure element to create the key material”, those happen at different times. But there's a different flow for stateful secure elements anyway, since the call to `"allocate_key"` has no analog in the stateless secure element or transparent cases.
+
+#### Assisting secure element drivers with recovery
+
+The actions of the secure element driver may themselves be non-atomic. So the driver must be given a chance to perform recovery.
+
+To simplify the design of the driver, the core should guarantee that the driver will know if a transaction was in progress and the core cannot be sure about the state of the secure element. Merely calling a read-only entry point such as `"get_key_attributes"` does not provide enough information to the driver for it to know that it should actively perform recovery related to that key.
+
+This gives an advantage to the “always destroy” strategy. Under this strategy, if the key might be in a transitional state, the core will request a key destruction from the driver. This means that, if the driver has per-key auxiliary data to clean up, it can bundle that as part of the key's destruction.
+
+### Testing non-atomic processes
+
+In this section, we discuss how to test non-atomic processes that must implement an atomic and committing interface. As discussed in [“Overview of API functions”](#overview-of-api-functions), this concerns key management in stateful secure elements.
+
+#### Naive test strategy for non-atomic processes
+
+Non-atomic processes consist of a series of atomic, committing steps.
+
+Our general strategy to test them is as follows: every time there is a modification of persistent state, either in storage or in the (simulated) secure element, try both the nominal case and simulating a power loss. If a power loss occurs, restart the system (i.e. clean up and call `psa_crypto_init()`), and check that the system ends up in a consistent state.
+
+Note that this creates a binary tree of possibilities: after each state modification, there may or may not be a restart, and after that different state modifications may occur, each of which may or may not be followed by a restart.
+
+For example, consider testing of one key creation operation (see [“Overview of two-phase commit with stateful secure elements”](#overview-of-two-phase-commit-with-stateful-secure-elements), under the simplifying assumption that each storage update step, as well as the recovery after a restart, each make a single (atomic) storage modification and no secure element access. The nominal case consists of three state modifications: storage modification (start transaction), creation on the secure element, storage modification (commit transaction). We need to test the following sequences:
+
+* Start transaction, restart, recovery.
+* Start transaction, secure element operation, restart, recovery.
+* Start transaction, secure element operation, commit transaction.
+
+If, for example, recovery consists of two atomic steps, the tree of possibilities expands and may be infinite:
+
+* Start transaction, restart, recovery step 1, restart, recovery step 1, recovery step 2.
+* Start transaction, restart, recovery step 1, restart, recovery step 1, restart, recovery step 1, recovery step 2.
+* Start transaction, restart, recovery step 1, restart, recovery step 1, restart, recovery step 1, restart, recovery step 1, recovery step 2.
+* etc.
+* Start transaction, secure element operation, restart, ...
+* Start transaction, secure element operation, commit transaction.
+
+In order to limit the possibilities, we need to make some assumptions about the recovery step. For example, if we have confidence that recovery step 1 is idempotent (i.e. doing it twice is the same as doing it once), we don't need to test what happens in execution sequences that take recovery step 1 more than twice in a row.
+
+### Splitting normal behavior and transaction recovery
+
+We introduce an abstraction level in transaction recovery:
+
+* Normal operation must maintain a certain invariant on the state of the world (internal storage and secure element).
+* Transaction recovery is defined over all states of the world that satisfy this invariant.
+
+This separation of concerns greatly facilitates testing, since it is now split into two parts:
+
+* During the testing of normal operation, we can use read-only invasive testing to ensure that the invariant is maintained. No modification of normal behavior (such as simulated power failures) is necessary.
+* Testing of transaction recovery is independent of how the system state was reached. We only need to artificially construct a representative sample of system states that match the invariant. Transaction recovery is itself an operation that must respect the invariant, and so we do not need any special testing for the case of an interrupted recovery.
+
+Another benefit of this approach is that it is easier to specify and test what happens if the library is updated on a device with leftovers from an interrupted transaction. We will require and test that the new version of the library supports recovery of the old library's states, without worrying how those states were reached.
+
+#### Towards an invariant for transactions
+
+As discussed in the section [“Recovery strategies”](#recovery-strategies), the information about active transactions is stored in a transaction list file. The name of the transaction list file does not depend on the identifiers of the keys in the list, but there may be more than one transaction list, for example one per secure element. If so, each transaction list can be considered independently.
+
+When no transaction is in progress, the transaction list does not exist, or is empty. The empty case must be supported because this is the initial state of the filesystem. When no transaction is in progress, the state of the secure element must be consistent with references to keys in that secure element contained in key files. More generally, if a key is not in the transaction list, then the key must be present in the secure element if and only if the key file is in the internal storage.
+
+For the purposes of the state invariant, it matters whether the transaction list file contains the driver key identifier, or if the driver key identifier is only stored in the key file. This is because the core needs to know the driver key id in order to access the secure element. If the transaction list does not contain the driver key identifier, and the key file does not exist, the key must not be present in the secure element.
+
+We thus have two scenarios, each with their own invariant: one where the transaction list contains only key identifiers, and one where it also contains the secure element's key identifier (as well as the location of the secure element if this is not encoded in the name of the transaction list file).
+
+#### Storage invariant if the transaction list contains application key identifiers only
+
+Invariants:
+
+* If the file `id` does not exist, then no resources corresponding to that key are in a secure element. This holds whether `id` is in the transaction list or not.
+* If `id` is not in the transaction list and the file `id` exists and references a key in a stateful secure element, then the key is present in the secure element.
+
+If `id` is in the transaction list and the file `id` exists, the key may or may not be present in the secure element.
+
+The invariant imposes constraints on the [order of operations for the two-phase commit](#overview-of-two-phase-commit-with-stateful-secure-elements): key creation must create `id` before calling the secure element's key creation entry point, and key destruction must remove `id` after calling the secure element's key destruction entry point.
+
+For recovery:
+
+* If the file `id` does not exist, then nothing needs to be done for recovery, other than removing `id` from the transaction list.
+* If the file `id` exists:
+    * It is correct to destroy the key in the secure element (treating a `DOES_NOT_EXIST` error as a success), then remove `id`.
+    * It is correct to check whether the key exists in the secure element, and if it does, keep it and keep `id`. If not, remove `id` from the internal storage.
+
+#### Storage invariant if the transaction list contains driver key identifiers
+
+Invariants:
+
+* If `id` is not in the transaction list and the file `id` does not exist, then no resources corresponding to that key are in a secure element.
+* If `id` is not in the transaction list and the file `id` exists, then the key is present in the secure element.
+
+If `id` is in the transaction list, neither the state of `id` in the internal storage nor the state of the key in the secure element is known.
+
+For recovery:
+
+* If the file `id` does not exist, then destroy the key in the secure element (treating a `DOES_NOT_EXIST` error as a success).
+* If the file `id` exists:
+    * It is correct to destroy the key in the secure element (treating a `DOES_NOT_EXIST` error as a success), then remove `id`.
+    * It is correct to check whether the key exists in the secure element, and if it does, keep it and keep `id`. If not, remove `id` from the internal storage.
+
+#### Coverage of states that respect the invariant
+
+For a given key, we have to consider three a priori independent boolean states:
+
+* Whether the key file exists.
+* Whether the key is in the secure element.
+* Whether the key is in the transaction list.
+
+There is full coverage for one key if we have tests of recovery for the states among these $2^3 = 8$ possibilities that satisfy the storage invariant.
+
+In addition, testing should adequately cover the case of multiple keys in the transaction list. How much coverage is adequate depends on the layout of the list as well as white-box considerations of how the list is manipulated.
+
+### Choice of a transaction design
+
+#### Chosen transaction algorithm
+
+Based on [“Optimization considerations for transactions”](#optimization-considerations-for-transactions), we choose a transaction algorithm that consists in the following operations:
+
+1. Add the key identifier to the transaction list.
+2. Call the secure element's key creation or destruction entry point.
+3. Remove the key identifier from the transaction list.
+
+In addition, before or after step 2, create or remove the key file in the internal storage.
+
+In order to conveniently support multiple transactions at the same time, we pick the simplest possible layout for the transaction list: a simple array of key identifiers. Since the transaction list does not contain the driver key identifier:
+
+* During key creation, create the key file in internal storage in the internal storage before calling the secure element's key creation entry point.
+* During key destruction, call the secure element's key destruction entry point before removing the key file in internal storage.
+
+This choice of algorithm does not require the secure element driver to have a `"get_key_attributes"` entry point.
+
+#### Chosen storage invariant
+
+The [storage invariant](#storage-invariant-if-the-transaction-list-contains-application-key-identifiers-only) is as follows:
+
+* If the file `id` does not exist, then no resources corresponding to that key are in a secure element. This holds whether `id` is in the transaction list or not.
+* If `id` is not in the transaction list and the file `id` exists and references a key in a stateful secure element, then the key is present in the secure element.
+* If `id` is in the transaction list and a key exists by that identifier, the key's location is a stateful secure element.
+
+#### Chosen recovery process
+
+To [assist secure element drivers with recovery](#assisting-secure-element-drivers-with-recovery), we pick the [always-destroy recovery strategy with a simple transaction file](#always-destroy-strategy-with-a-simpler-transaction-file). The the recovery process is as follows:
+
+* If the file `id` does not exist, then nothing needs to be done for recovery, other than removing `id` from the transaction list.
+* If the file `id` exists, call the secure element's key destruction entry point (treating a `DOES_NOT_EXIST` error as a success), then remove `id`.
+
+## Specification of key management in stateful secure elements
+
+This section only concerns stateful secure elements as discussed in [“Designing key management for secure element keys”](#designing-key-management-for-secure-element-keys), i.e. secure elements with an `"allocate_key"` entry point. The design follows the general principle described in [“Overview of two-phase commit with stateful secure elements”](#overview-of-two-phase-commit-with-stateful-secure-elements) and the specific choices justified in [“Choice of a transaction design”](choice-of-a-transaction-design).
+
+### Transaction list file manipulation
+
+The transaction list is a simple array of key identifiers.
+
+To add a key identifier to the list:
+
+1. Load the current list from the transaction list if it exists and it is not already cached in memory.
+2. Append the key identifier to the array.
+3. Write the updated list file.
+
+To remove a key identifier from the list:
+
+1. Load the current list if it is not already cached in memory. It is an error if the file does not exist since it must contain this identifier.
+2. Remove the key identifier from the array. If it wasn't the last element in array, move array elements to fill the hole.
+3. If the list is now empty, remove the transaction list file. Otherwise write the updated list to the file.
+
+### Key creation process in the core
+
+Let _A_ be the application key identifier.
+
+1. Call the driver's `"allocate_key"` entry point, obtaining the driver key identifier _D_ chosen by the driver.
+2. Add _A_ [to the transaction list file](#transaction-list-file-manipulation).
+3. Create the key file _A_ in the internal storage. Note that this is done at a different time from what happens when creating a transparent key or a key in a stateless secure element: in those cases, creating the key file happens after the actual creation of the key material.
+4. Call the secure element's key creation entry point.
+5. Remove _A_ [from the transaction list file](#transaction-list-file-manipulation).
+
+If any step fails:
+
+* If the secure element's key creation entry point has been called and succeeded, call the secure element's destroy entry point.
+* If the key file has been created in the internal storage, remove it.
+* Remove the key from the transaction list.
+
+Note that this process is identical to key destruction, except that the key is already in the transaction list.
+
+### Key destruction process in the core
+
+Let _A_ be the application key identifier.
+
+We assume that the key is loaded in a key slot in memory: the core needs to know the key's location in order to determine whether the key is in a stateful secure element, and if so to know the driver key identifier. A possible optimization would be to load only that information in local variables, without occupying a key store; this has the advantage that key destruction works even if the key store is full.
+
+1. Add _A_ [to the transaction list file](#transaction-list-file-manipulation).
+2. Call the secure element's `"destroy_key"` entry point.
+3. Remove the key file _A_ from the internal storage.
+4. Remove _A_ [from the transaction list file](#transaction-list-file-manipulation).
+5. Free the corresponding key slot in memory.
+
+If any step fails, remember the error but continue the process, to destroy the resources associated with the key as much as is practical.
+
+### Transaction recovery
+
+For each key _A_ in the transaction list file, if the file _A_ exists in the internal storage:
+
+1. Load the key into a key slot in memory (to get its location and the driver key identifier, although we could get the location from the transaction list).
+2. Call the secure element's `"destroy_key"` entry point.
+3. Remove the key file _A_ from the internal storage.
+4. Remove _A_ [from the transaction list file](#transaction-list-file-manipulation).
+5. Free the corresponding key slot in memory.
+
+The transaction list file can be processed in any order.
+
+It is correct to update the transaction list after recovering each key, or to only delete the transaction list file once the recovery is over.
+
+### Concrete format of the transaction list file
+
+The transaction list file contains a [fixed header](#transaction-list-header-format) followed by a list of [fixed-size elements](#transaction-list-element-format).
+
+The file uid is `PSA_CRYPTO_ITS_TRANSACTION_LIST_UID` = 0xffffff53.
+
+#### Transaction list header format
+
+* Version (2 bytes): 0x0003. (Chosen to differ from the first two bytes of a [dynamic secure element transaction file](#dynamic-secure-element-transaction-file), to reduce the risk of a mix-up.)
+* Key name size (2 bytes): `sizeof(psa_storage_uid_t)`. Storing this size avoids reading bad data if Mbed TLS is upgraded to a different integration that names keys differently.
+
+#### Transaction list element format
+
+In practice, there will rarely be more than one active transaction at a time, so the size of an element is not critical for efficiency. Therefore, in addition to the key identifier which is required, we add some potentially useful information in case it becomes useful later. We do not put the driver key identifier because its size is not a constant.
+
+* Key id: `sizeof(psa_storage_uid_t)` bytes.
+* Key lifetime: 4 bytes (`sizeof(psa_key_lifetime_t)`). Currently unused during recovery.
+* Operation type: 1 byte. Currently unused during recovery.
+    * 0: destroy key.
+    * 1: import key.
+    * 2: generate key.
+    * 3: derive key.
+    * 4: import key.
+* Padding: 3 bytes. Reserved for future use. Currently unused during recovery.
+
+#### Dynamic secure element transaction file
+
+Note that the code base already references a “transaction file” (`PSA_CRYPTO_ITS_TRANSACTION_UID` = 0xffffff54), used by dynamic secure elements (feature enabled with `MBEDTLS_PSA_CRYPTO_SE_C`). This is a deprecated feature that has not been fully implemented: when this feature is enabled, the transaction file gets written during transactions, but if it exists when PSA crypto starts, `psa_crypto_init()` fails because [recovery has never been implemented](https://github.com/ARMmbed/mbed-crypto/issues/218).
+
+For the new kind of secure element driver, we pick a different file name to avoid any mixup.
+
+## Testing key management in secure elements
+
+### Instrumentation for checking the storage invariant
+
+#### Test hook locations
+
+When `MBEDTLS_TEST_HOOKS` is enabled, each call to `psa_its_set()` or `psa_its_remove()` also calls a test hook, passing the file UID as an argument to the hook.
+
+When a stateful secure element driver is present in the build, we use this hook to verify that the storage respects the [storage invariant](#chosen-storage-invariant). In addition, if there is some information about key ongoing operation (set explicitly by the test function as a global variable in the test framework), the hook tests that the content of the storage is compatible with the ongoing operation.
+
+#### Test hook behavior
+
+The storage invariant check cannot check all keys in storage, and does not need to (for example, it would be pointless to check anything about transparent keys). It checks the following keys:
+
+* When invoked from the test hook on a key file: on that key.
+* When invoked from the test hook on the transaction file: on all the keys listed in the transaction file.
+* When invoked from a test secure element: on the specified key.
+
+#### Test hook extra data
+
+Some tests set global variables to indicate which persistent keys they manipulate. We instrument at least some of these tests to also indicate what operation is in progress on the key. See the GitHub issues or the source code for details.
+
+### Testing of transaction recovery
+
+When no secure element driver is present in the build, the presence of a transaction list file during initialization is an error.
+
+#### Recovery testing process
+
+When the stateful test secure element driver is present in the build, we run test cases on a representative selection of states of the internal storage and the test secure element. Each test case for transaction recovery has the following form:
+
+1. Create the initial state:
+    * Create a transaction list file with a certain content.
+    * Create key files that we want to have in the test.
+    * Call the secure element test driver to create keys without going throught the PSA API.
+2. Call `psa_crypto_init()`. Expect success if the initial state satisfies the [storage invariant](#chosen-storage-invariant) and failure otherwise.
+3. On success, check that the expected keys exist, and that keys that are expected to have been destroyed by recovery do not exist.
+4. Clean up the storage and the secure element test driver's state.
+
+#### States to test recovery on
+
+For a given key located in a secure element, the following combination of states are possible:
+
+* Key file: present, absent.
+* Key in secure element: present, absent.
+* Key in the transaction file: no, creation (import), destruction.
+
+We test all $2 \times 2 \times 3 = 12$ possibilities, each in its own test case. In each case, call the test function that checks the storage invariant and check that its result is as expected. Then, if the storage invariant is met, follow the [recovery testing process](#recovery-testing-process).
+
+In addition, have at least one positive test case for each creation method other than import, to ensure that we don't reject a valid value.
+
+Note: testing of a damaged filesystem (including a filesystem that doesn't meet the invariant) is out of scope of the present document.
diff --git a/docs/architecture/psa-thread-safety/key-slot-state-transitions.png b/docs/architecture/psa-thread-safety/key-slot-state-transitions.png
new file mode 100644
index 0000000..08e4cc0
--- /dev/null
+++ b/docs/architecture/psa-thread-safety/key-slot-state-transitions.png
Binary files differ
diff --git a/docs/architecture/psa-thread-safety/psa-thread-safety.md b/docs/architecture/psa-thread-safety/psa-thread-safety.md
new file mode 100644
index 0000000..edb94c5
--- /dev/null
+++ b/docs/architecture/psa-thread-safety/psa-thread-safety.md
@@ -0,0 +1,367 @@
+# Thread-safety of the PSA subsystem
+
+Currently, PSA Crypto API calls in Mbed TLS releases are not thread-safe.
+
+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.
+
+Summary of recent work:
+
+- 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).
+- `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 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
+
+* 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.
+
+## Definitions
+
+*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*
+
+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.
+
+## Guarantees
+
+### Correctness out of the box
+
+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).
+
+We do not test or support calling other PSA API functions concurrently.
+
+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.
+
+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)).
+
+#### PSA Concurrent calling conventions
+
+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):
+
+> 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 `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 `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.
+
+### Backwards compatibility
+
+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.
+
+### Supported threading implementations
+
+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 (`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, it is down to the external implementation to ensure thread-safety, should threading be enabled.
+
+## 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.
+
+#### 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.
+
+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
+
+
+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. 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#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. 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.
+
+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 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
+
+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 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 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` 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.
+
+### 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.
+
+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 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 have not yet tested whether they rely on unprotected global resources. The key slot access in these operations is thread-safe.
+
+## 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.
+
+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. We need to write new test suites for concurrency testing.
+
+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 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 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.
+
+### 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 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
+
+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, 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
+
+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.
+
+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 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.
+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 would be non-thread-safe if the `thread-safe` property is set to true.
+
+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 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 here.
diff --git a/docs/architecture/testing/driver-interface-test-strategy.md b/docs/architecture/testing/driver-interface-test-strategy.md
new file mode 100644
index 0000000..380fd39
--- /dev/null
+++ b/docs/architecture/testing/driver-interface-test-strategy.md
@@ -0,0 +1,133 @@
+# Mbed TLS driver interface test strategy
+
+This document describes the test strategy for the driver interfaces in Mbed TLS. Mbed TLS has interfaces for secure element drivers, accelerator drivers and entropy drivers. This document is about testing Mbed TLS itself; testing drivers is out of scope.
+
+The driver interfaces are standardized through PSA Cryptography functional specifications.
+
+## Secure element driver interface testing
+
+### Secure element driver interfaces
+
+#### Opaque driver interface
+
+The [unified driver interface](../../proposed/psa-driver-interface.md) supports both transparent drivers (for accelerators) and opaque drivers (for secure elements).
+
+Drivers exposing this interface need to be registered at compile time by declaring their JSON description file.
+
+#### Dynamic secure element driver interface
+
+The dynamic secure element driver interface (SE interface for short) is defined by [`psa/crypto_se_driver.h`](../../../include/psa/crypto_se_driver.h). This is an interface between Mbed TLS and one or more third-party drivers.
+
+The SE interface consists of one function provided by Mbed TLS (`psa_register_se_driver`) and many functions that drivers must implement. To make a driver usable by Mbed TLS, the initialization code must call `psa_register_se_driver` with a structure that describes the driver. The structure mostly contains function pointers, pointing to the driver's methods. All calls to a driver function are triggered by a call to a PSA crypto API function.
+
+### SE driver interface unit tests
+
+This section describes unit tests that must be implemented to validate the secure element driver interface. Note that a test case may cover multiple requirements; for example a “good case” test can validate that the proper function is called, that it receives the expected inputs and that it produces the expected outputs.
+
+Many SE driver interface unit tests could be covered by running the existing API tests with a key in a secure element.
+
+#### SE driver registration
+
+This applies to dynamic drivers only.
+
+* Test `psa_register_se_driver` with valid and with invalid arguments.
+* Make at least one failing call to `psa_register_se_driver` followed by a successful call.
+* Make at least one test that successfully registers the maximum number of drivers and fails to register one more.
+
+#### Dispatch to SE driver
+
+For each API function that can lead to a driver call (more precisely, for each driver method call site, but this is practically equivalent):
+
+* Make at least one test with a key in a secure element that checks that the driver method is called. A few API functions involve multiple driver methods; these should validate that all the expected driver methods are called.
+* Make at least one test with a key that is not in a secure element that checks that the driver method is not called.
+* Make at least one test with a key in a secure element with a driver that does not have the requisite method (i.e. the method pointer is `NULL`) but has the substructure containing that method, and check that the return value is `PSA_ERROR_NOT_SUPPORTED`.
+* Make at least one test with a key in a secure element with a driver that does not have the substructure containing that method (i.e. the pointer to the substructure is `NULL`), and check that the return value is `PSA_ERROR_NOT_SUPPORTED`.
+* At least one test should register multiple drivers with a key in each driver and check that the expected driver is called. This does not need to be done for all operations (use a white-box approach to determine if operations may use different code paths to choose the driver).
+* At least one test should register the same driver structure with multiple lifetime values and check that the driver receives the expected lifetime value.
+
+Some methods only make sense as a group (for example a driver that provides the MAC methods must provide all or none). In those cases, test with all of them null and none of them null.
+
+#### SE driver inputs
+
+For each API function that can lead to a driver call (more precisely, for each driver method call site, but this is practically equivalent):
+
+* Wherever the specification guarantees parameters that satisfy certain preconditions, check these preconditions whenever practical.
+* If the API function can take parameters that are invalid and must not reach the driver, call the API function with such parameters and verify that the driver method is not called.
+* Check that the expected inputs reach the driver. This may be implicit in a test that checks the outputs if the only realistic way to obtain the correct outputs is to start from the expected inputs (as is often the case for cryptographic material, but not for metadata).
+
+#### SE driver outputs
+
+For each API function that leads to a driver call, call it with parameters that cause a driver to be invoked and check how Mbed TLS handles the outputs.
+
+* Correct outputs.
+* Incorrect outputs such as an invalid output length.
+* Expected errors (e.g. `PSA_ERROR_INVALID_SIGNATURE` from a signature verification method).
+* Unexpected errors. At least test that if the driver returns `PSA_ERROR_GENERIC_ERROR`, this is propagated correctly.
+
+Key creation functions invoke multiple methods and need more complex error handling:
+
+* Check the consequence of errors detected at each stage (slot number allocation or validation, key creation method, storage accesses).
+* Check that the storage ends up in the expected state. At least make sure that no intermediate file remains after a failure.
+
+#### Persistence of SE keys
+
+The following tests must be performed at least one for each key creation method (import, generate, ...).
+
+* Test that keys in a secure element survive `psa_close_key(); psa_open_key()`.
+* Test that keys in a secure element survive `mbedtls_psa_crypto_free(); psa_crypto_init()`.
+* Test that the driver's persistent data survives `mbedtls_psa_crypto_free(); psa_crypto_init()`.
+* Test that `psa_destroy_key()` does not leave any trace of the key.
+
+#### Resilience for SE drivers
+
+Creating or removing a key in a secure element involves multiple storage modifications (M<sub>1</sub>, ..., M<sub>n</sub>). If the operation is interrupted by a reset at any point, it must be either rolled back or completed.
+
+* For each potential interruption point (before M<sub>1</sub>, between M<sub>1</sub> and M<sub>2</sub>, ..., after M<sub>n</sub>), call `mbedtls_psa_crypto_free(); psa_crypto_init()` at that point and check that this either rolls back or completes the operation that was started.
+* This must be done for each key creation method and for key destruction.
+* This must be done for each possible flow, including error cases (e.g. a key creation that fails midway due to `OUT_OF_MEMORY`).
+* The recovery during `psa_crypto_init` can itself be interrupted. Test those interruptions too.
+* Two things need to be tested: the key that is being created or destroyed, and the driver's persistent storage.
+* Check both that the storage has the expected content (this can be done by e.g. using a key that is supposed to be present) and does not have any unexpected content (for keys, this can be done by checking that `psa_open_key` fails with `PSA_ERROR_DOES_NOT_EXIST`).
+
+This requires instrumenting the storage implementation, either to force it to fail at each point or to record successive storage states and replay each of them. Each `psa_its_xxx` function call is assumed to be atomic.
+
+### SE driver system tests
+
+#### Real-world use case
+
+We must have at least one driver that is close to real-world conditions:
+
+* With its own source tree.
+* Running on actual hardware.
+* Run the full driver validation test suite (which does not yet exist).
+* Run at least one test application (e.g. the Mbed OS TLS example).
+
+This requirement shall be fulfilled by the [Microchip ATECC508A driver](https://github.com/ARMmbed/mbed-os-atecc608a/).
+
+#### Complete driver
+
+We should have at least one driver that covers the whole interface:
+
+* With its own source tree.
+* Implementing all the methods.
+* Run the full driver validation test suite (which does not yet exist).
+
+A PKCS#11 driver would be a good candidate. It would be useful as part of our product offering.
+
+## Transparent driver interface testing
+
+The [unified driver interface](../../proposed/psa-driver-interface.md) defines interfaces for accelerators.
+
+### Test requirements
+
+#### Requirements for transparent driver testing
+
+Every cryptographic mechanism for which a transparent driver interface exists (key creation, cryptographic operations, …) must be exercised in at least one build. The test must verify that the driver code is called.
+
+#### Requirements for fallback
+
+The driver interface includes a fallback mechanism so that a driver can reject a request at runtime and let another driver handle the request. For each entry point, there must be at least three test runs with two or more drivers available with driver A configured to fall back to driver B, with one run where A returns `PSA_SUCCESS`, one where A returns `PSA_ERROR_NOT_SUPPORTED` and B is invoked, and one where A returns a different error and B is not invoked.
+
+## Entropy and randomness interface testing
+
+TODO
diff --git a/docs/architecture/testing/invasive-testing.md b/docs/architecture/testing/invasive-testing.md
new file mode 100644
index 0000000..464f761
--- /dev/null
+++ b/docs/architecture/testing/invasive-testing.md
@@ -0,0 +1,367 @@
+# Mbed TLS invasive testing strategy
+
+## Introduction
+
+In Mbed TLS, we use black-box testing as much as possible: test the documented behavior of the product, in a realistic environment. However this is not always sufficient.
+
+The goal of this document is to identify areas where black-box testing is insufficient and to propose solutions.
+
+This is a test strategy document, not a test plan. A description of exactly what is tested is out of scope.
+
+This document is structured as follows:
+
+* [“Rules”](#rules) gives general rules and is written for brevity.
+* [“Requirements”](#requirements) explores the reasons why invasive testing is needed and how it should be done.
+* [“Possible approaches”](#possible-approaches) discusses some general methods for non-black-box testing.
+* [“Solutions”](#solutions) explains how we currently solve, or intend to solve, specific problems.
+
+### TLS
+
+This document currently focuses on data structure manipulation and storage, which is what the crypto/keystore and X.509 parts of the library are about. More work is needed to fully take TLS into account.
+
+## Rules
+
+Always follow these rules unless you have a good reason not to. If you deviate, document the rationale somewhere.
+
+See the section [“Possible approaches”](#possible-approaches) for a rationale.
+
+### Interface design for testing
+
+Do not add test-specific interfaces if there's a practical way of doing it another way. All public interfaces should be useful in at least some configurations. Features with a significant impact on the code size or attack surface should have a compile-time guard.
+
+### Reliance on internal details
+
+In unit tests and in test programs, it's ok to include internal header files from `library/`. Do not define non-public interfaces in public headers. In contrast, sample programs must not include header files from `library/`.
+
+Sometimes it makes sense to have unit tests on functions that aren't part of the public API. Declare such functions in `library/*.h` and include the corresponding header in the test code. If the function should be `static` for optimization but can't be `static` for testing, declare it as `MBEDTLS_STATIC_TESTABLE`, and make the tests that use it depend on `MBEDTLS_TEST_HOOKS` (see [“rules for compile-time options”](#rules-for-compile-time-options)).
+
+If test code or test data depends on internal details of the library and not just on its documented behavior, add a comment in the code that explains the dependency. For example:
+
+> ```
+> /* This test file is specific to the ITS implementation in PSA Crypto
+>  * on top of stdio. It expects to know what the stdio name of a file is
+>  * based on its keystore name.
+>  */
+> ```
+
+> ```
+> # This test assumes that PSA_MAX_KEY_BITS (currently 65536-8 bits = 8191 bytes
+> # and not expected to be raised any time soon) is less than the maximum
+> # output from HKDF-SHA512 (255*64 = 16320 bytes).
+> ```
+
+### Rules for compile-time options
+
+If the most practical way to test something is to add code to the product that is only useful for testing, do so, but obey the following rules. For more information, see the [rationale](#guidelines-for-compile-time-options).
+
+* **Only use test-specific code when necessary.** Anything that can be tested through the documented API must be tested through the documented API.
+* **Test-specific code must be guarded by `#if defined(MBEDTLS_TEST_HOOKS)`**. Do not create fine-grained guards for test-specific code.
+* **Do not use `MBEDTLS_TEST_HOOKS` for security checks or assertions.** Security checks belong in the product.
+* **Merely defining `MBEDTLS_TEST_HOOKS` must not change the behavior**. It may define extra functions. It may add fields to structures, but if so, make it very clear that these fields have no impact on non-test-specific fields.
+* **Where tests must be able to change the behavior, do it by function substitution.** See [“rules for function substitution”](#rules-for-function-substitution) for more details.
+
+#### Rules for function substitution
+
+This section explains how to replace a library function `mbedtls_foo()` by alternative code for test purposes. That is, library code calls `mbedtls_foo()`, and there is a mechanism to arrange for these calls to invoke different code.
+
+Often `mbedtls_foo` is a macro which is defined to be a system function (like `mbedtls_calloc` or `mbedtls_fopen`), which we replace to mock or wrap the system function. This is useful to simulate I/O failure, for example. Note that if the macro can be replaced at compile time to support alternative platforms, the test code should be compatible with this compile-time configuration so that it works on these alternative platforms as well.
+
+Sometimes the substitutable function is a `static inline` function that does nothing (not a macro, to avoid accidentally skipping side effects in its parameters), to provide a hook for test code; such functions should have a name that starts with the prefix `mbedtls_test_hook_`. In such cases, the function should generally not modify its parameters, so any pointer argument should be const. The function should return void.
+
+With `MBEDTLS_TEST_HOOKS` set, `mbedtls_foo` is a global variable of function pointer type. This global variable is initialized to the system function, or to a function that does nothing. The global variable is defined in a header in the `library` directory such as `psa_crypto_invasive.h`. This is similar to the platform function configuration mechanism with `MBEDTLS_PLATFORM_xxx_ALT`.
+
+In unit test code that needs to modify the internal behavior:
+
+* The test function (or the whole test file) must depend on `MBEDTLS_TEST_HOOKS`.
+* At the beginning of the test function, set the global function pointers to the desired value.
+* In the test function's cleanup code, restore the global function pointers to their default value.
+
+## Requirements
+
+### General goals
+
+We need to balance the following goals, which are sometimes contradictory.
+
+* Coverage: we need to test behaviors which are not easy to trigger by using the API or which cannot be triggered deterministically, for example I/O failures.
+* Correctness: we want to test the actual product, not a modified version, since conclusions drawn from a test of a modified product may not apply to the real product.
+* Effacement: the product should not include features that are solely present for test purposes, since these increase the attack surface and the code size.
+* Portability: tests should work on every platform. Skipping tests on certain platforms may hide errors that are only apparent on such platforms.
+* Maintainability: tests should only enforce the documented behavior of the product, to avoid extra work when the product's internal or implementation-specific behavior changes. We should also not give the impression that whatever the tests check is guaranteed behavior of the product which cannot change in future versions.
+
+Where those goals conflict, we should at least mitigate the goals that cannot be fulfilled, and document the architectural choices and their rationale.
+
+### Problem areas
+
+#### Allocation
+
+Resource allocation can fail, but rarely does so in a typical test environment. How does the product cope if some allocations fail?
+
+Resources include:
+
+* Memory.
+* Files in storage (PSA API only — in the Mbed TLS API, black-box unit tests are sufficient).
+* Key slots (PSA API only).
+* Key slots in a secure element (PSA SE HAL).
+* Communication handles (PSA crypto service only).
+
+#### Storage
+
+Storage can fail, either due to hardware errors or to active attacks on trusted storage. How does the code cope if some storage accesses fail?
+
+We also need to test resilience: if the system is reset during an operation, does it restart in a correct state?
+
+#### Cleanup
+
+When code should clean up resources, how do we know that they have truly been cleaned up?
+
+* Zeroization of confidential data after use.
+* Freeing memory.
+* Freeing key slots.
+* Freeing key slots in a secure element.
+* Deleting files in storage (PSA API only).
+
+#### Internal data
+
+Sometimes it is useful to peek or poke internal data.
+
+* Check consistency of internal data (e.g. output of key generation).
+* Check the format of files (which matters so that the product can still read old files after an upgrade).
+* Inject faults and test corruption checks inside the product.
+
+## Possible approaches
+
+Key to requirement tables:
+
+* ++ requirement is fully met
+* \+ requirement is mostly met
+* ~ requirement is partially met but there are limitations
+* ! requirement is somewhat problematic
+* !! requirement is very problematic
+
+### Fine-grained public interfaces
+
+We can include all the features we want to test in the public interface. Then the tests can be truly black-box. The limitation of this approach is that this requires adding a lot of interfaces that are not useful in production. These interfaces have costs: they increase the code size, the attack surface, and the testing burden (exponentially, because we need to test all these interfaces in combination).
+
+As a rule, we do not add public interfaces solely for testing purposes. We only add public interfaces if they are also useful in production, at least sometimes. For example, the main purpose of `mbedtls_psa_crypto_free` is to clean up all resources in tests, but this is also useful in production in some applications that only want to use PSA Crypto during part of their lifetime.
+
+Mbed TLS traditionally has very fine-grained public interfaces, with many platform functions that can be substituted (`MBEDTLS_PLATFORM_xxx` macros). PSA Crypto has more opacity and less platform substitution macros.
+
+| Requirement | Analysis |
+| ----------- | -------- |
+| Coverage | ~ Many useful tests are not reasonably achievable |
+| Correctness | ++ Ideal |
+| Effacement | !! Requires adding many otherwise-useless interfaces |
+| Portability | ++ Ideal; the additional interfaces may be useful for portability beyond testing |
+| Maintainability | !! Combinatorial explosion on the testing burden |
+|                 | ! Public interfaces must remain for backward compatibility even if the test architecture changes |
+
+### Fine-grained undocumented interfaces
+
+We can include all the features we want to test in undocumented interfaces. Undocumented interfaces are described in public headers for the sake of the C compiler, but are described as “do not use” in comments (or not described at all) and are not included in Doxygen-rendered documentation. This mitigates some of the downsides of [fine-grained public interfaces](#fine-grained-public-interfaces), but not all. In particular, the extra interfaces do increase the code size, the attack surface and the test surface.
+
+Mbed TLS traditionally has a few internal interfaces, mostly intended for cross-module abstraction leakage rather than for testing. For the PSA API, we favor [internal interfaces](#internal-interfaces).
+
+| Requirement | Analysis |
+| ----------- | -------- |
+| Coverage | ~ Many useful tests are not reasonably achievable |
+| Correctness | ++ Ideal |
+| Effacement | !! Requires adding many otherwise-useless interfaces |
+| Portability | ++ Ideal; the additional interfaces may be useful for portability beyond testing |
+| Maintainability | ! Combinatorial explosion on the testing burden |
+
+### Internal interfaces
+
+We can write tests that call internal functions that are not exposed in the public interfaces. This is nice when it works, because it lets us test the unchanged product without compromising the design of the public interface.
+
+A limitation is that these interfaces must exist in the first place. If they don't, this has mostly the same downside as public interfaces: the extra interfaces increase the code size and the attack surface for no direct benefit to the product.
+
+Another limitation is that internal interfaces need to be used correctly. We may accidentally rely on internal details in the tests that are not necessarily always true (for example that are platform-specific). We may accidentally use these internal interfaces in ways that don't correspond to the actual product.
+
+This approach is mostly portable since it only relies on C interfaces. A limitation is that the test-only interfaces must not be hidden at link time (but link-time hiding is not something we currently do). Another limitation is that this approach does not work for users who patch the library by replacing some modules; this is a secondary concern since we do not officially offer this as a feature.
+
+| Requirement | Analysis |
+| ----------- | -------- |
+| Coverage | ~ Many useful tests require additional internal interfaces |
+| Correctness | + Does not require a product change |
+|             | ~ The tests may call internal functions in a way that does not reflect actual usage inside the product |
+| Effacement | ++ Fine as long as the internal interfaces aren't added solely for test purposes |
+| Portability | + Fine as long as we control how the tests are linked |
+|             | ~ Doesn't work if the users rewrite an internal module |
+| Maintainability | + Tests interfaces that are documented; dependencies in the tests are easily noticed when changing these interfaces |
+
+### Static analysis
+
+If we guarantee certain properties through static analysis, we don't need to test them. This puts some constraints on the properties:
+
+* We need to have confidence in the specification (but we can gain this confidence by evaluating the specification on test data).
+* This does not work for platform-dependent properties unless we have a formal model of the platform.
+
+| Requirement | Analysis |
+| ----------- | -------- |
+| Coverage | ~ Good for platform-independent properties, if we can guarantee them statically |
+| Correctness | + Good as long as we have confidence in the specification |
+| Effacement | ++ Zero impact on the code |
+| Portability | ++ Zero runtime burden |
+| Maintainability | ~ Static analysis is hard, but it's also helpful |
+
+### Compile-time options
+
+If there's code that we want to have in the product for testing, but not in production, we can add a compile-time option to enable it. This is very powerful and usually easy to use, but comes with a major downside: we aren't testing the same code anymore.
+
+| Requirement | Analysis |
+| ----------- | -------- |
+| Coverage | ++ Most things can be tested that way |
+| Correctness | ! Difficult to ensure that what we test is what we run |
+| Effacement | ++ No impact on the product when built normally or on the documentation, if done right |
+|             | ! Risk of getting “no impact” wrong |
+| Portability | ++ It's just C code so it works everywhere |
+|             | ~ Doesn't work if the users rewrite an internal module |
+| Maintainability | + Test interfaces impact the product source code, but at least they're clearly marked as such in the code |
+
+#### Guidelines for compile-time options
+
+* **Minimize the number of compile-time options.**<br>
+  Either we're testing or we're not. Fine-grained options for testing would require more test builds, especially if combinatorics enters the play.
+* **Merely enabling the compile-time option should not change the behavior.**<br>
+  When building in test mode, the code should have exactly the same behavior. Changing the behavior should require some action at runtime (calling a function or changing a variable).
+* **Minimize the impact on code**.<br>
+  We should not have test-specific conditional compilation littered through the code, as that makes the code hard to read.
+
+### Runtime instrumentation
+
+Some properties can be tested through runtime instrumentation: have the compiler or a similar tool inject something into the binary.
+
+* Sanitizers check for certain bad usage patterns (ASan, MSan, UBSan, Valgrind).
+* We can inject external libraries at link time. This can be a way to make system functions fail.
+
+| Requirement | Analysis |
+| ----------- | -------- |
+| Coverage | ! Limited scope |
+| Correctness | + Instrumentation generally does not affect the program's functional behavior |
+| Effacement | ++ Zero impact on the code |
+| Portability | ~ Depends on the method |
+| Maintainability | ~ Depending on the instrumentation, this may require additional builds and scripts |
+|                 | + Many properties come for free, but some require effort (e.g. the test code itself must be leak-free to avoid false positives in a leak detector) |
+
+### Debugger-based testing
+
+If we want to do something in a test that the product isn't capable of doing, we can use a debugger to read or modify the memory, or hook into the code at arbitrary points.
+
+This is a very powerful approach, but it comes with limitations:
+
+* The debugger may introduce behavior changes (e.g. timing). If we modify data structures in memory, we may do so in a way that the code doesn't expect.
+* Due to compiler optimizations, the memory may not have the layout that we expect.
+* Writing reliable debugger scripts is hard. We need to have confidence that we're testing what we mean to test, even in the face of compiler optimizations. Languages such as gdb make it hard to automate even relatively simple things such as finding the place(s) in the binary corresponding to some place in the source code.
+* Debugger scripts are very much non-portable.
+
+| Requirement | Analysis |
+| ----------- | -------- |
+| Coverage | ++ The sky is the limit |
+| Correctness | ++ The code is unmodified, and tested as compiled (so we even detect compiler-induced bugs) |
+|             | ! Compiler optimizations may hinder |
+|             | ~ Modifying the execution may introduce divergence |
+| Effacement | ++ Zero impact on the code |
+| Portability | !! Not all environments have a debugger, and even if they do, we'd need completely different scripts for every debugger |
+| Maintainability | ! Writing reliable debugger scripts is hard |
+|                 | !! Very tight coupling with the details of the source code and even with the compiler |
+
+## Solutions
+
+This section lists some strategies that are currently used for invasive testing, or planned to be used. This list is not intended to be exhaustive.
+
+### Memory management
+
+#### Zeroization testing
+
+Goal: test that `mbedtls_platform_zeroize` does wipe the memory buffer.
+
+Solution ([debugger](#debugger-based-testing)): implemented in `tests/scripts/test_zeroize.gdb`.
+
+Rationale: this cannot be tested by adding C code, because the danger is that the compiler optimizes the zeroization away, and any C code that observes the zeroization would cause the compiler not to optimize it away.
+
+#### Memory cleanup
+
+Goal: test the absence of memory leaks.
+
+Solution ([instrumentation](#runtime-instrumentation)): run tests with ASan. (We also use Valgrind, but it's slower than ASan, so we favor ASan.)
+
+Since we run many test jobs with a memory leak detector, each test function or test program must clean up after itself. Use the cleanup code (after the `exit` label in test functions) to free any memory that the function may have allocated.
+
+#### Robustness against memory allocation failure
+
+Solution: TODO. We don't test this at all at this point.
+
+#### PSA key store memory cleanup
+
+Goal: test the absence of resource leaks in the PSA key store code, in particular that `psa_close_key` and `psa_destroy_key` work correctly.
+
+Solution ([internal interface](#internal-interfaces)): in most tests involving PSA functions, the cleanup code explicitly calls `PSA_DONE()` instead of `mbedtls_psa_crypto_free()`. `PSA_DONE` fails the test if the key store in memory is not empty.
+
+Note there must also be tests that call `mbedtls_psa_crypto_free` with keys still open, to verify that it does close all keys.
+
+`PSA_DONE` is a macro defined in `psa_crypto_helpers.h` which uses `mbedtls_psa_get_stats()` to get information about the keystore content before calling `mbedtls_psa_crypto_free()`. This feature is mostly but not exclusively useful for testing, and may be moved under `MBEDTLS_TEST_HOOKS`.
+
+### PSA storage
+
+#### PSA storage cleanup on success
+
+Goal: test that no stray files are left over in the key store after a test that succeeded.
+
+Solution: TODO. Currently the various test suites do it differently.
+
+#### PSA storage cleanup on failure
+
+Goal: ensure that no stray files are left over in the key store even if a test has failed (as that could cause other tests to fail).
+
+Solution: TODO. Currently the various test suites do it differently.
+
+#### PSA storage resilience
+
+Goal: test the resilience of PSA storage against power failures.
+
+Solution: TODO.
+
+See the [secure element driver interface test strategy](driver-interface-test-strategy.html) for more information.
+
+#### Corrupted storage
+
+Goal: test the robustness against corrupted storage.
+
+Solution ([internal interface](#internal-interfaces)): call `psa_its` functions to modify the storage.
+
+#### Storage read failure
+
+Goal: test the robustness against read errors.
+
+Solution: TODO
+
+#### Storage write failure
+
+Goal: test the robustness against write errors (`STORAGE_FAILURE` or `INSUFFICIENT_STORAGE`).
+
+Solution: TODO
+
+#### Storage format stability
+
+Goal: test that the storage format does not change between versions (or if it does, an upgrade path must be provided).
+
+Solution ([internal interface](#internal-interfaces)): call internal functions to inspect the content of the file.
+
+Note that the storage format is defined not only by the general layout, but also by the numerical values of encodings for key types and other metadata. For numerical values, there is a risk that we would accidentally modify a single value or a few values, so the tests should be exhaustive. This probably requires some compile-time analysis (perhaps the automation for `psa_constant_names` can be used here). TODO
+
+### Other fault injection
+
+#### PSA crypto init failure
+
+Goal: test the failure of `psa_crypto_init`.
+
+Solution ([compile-time option](#compile-time-options)): replace entropy initialization functions by functions that can fail. This is the only failure point for `psa_crypto_init` that is present in all builds.
+
+When we implement the PSA entropy driver interface, this should be reworked to use the entropy driver interface.
+
+#### PSA crypto data corruption
+
+The PSA crypto subsystem has a few checks to detect corrupted data in memory. We currently don't have a way to exercise those checks.
+
+Solution: TODO. To corrupt a multipart operation structure, we can do it by looking inside the structure content, but only when running without isolation. To corrupt the key store, we would need to add a function to the library or to use a debugger.
+
diff --git a/docs/architecture/testing/psa-storage-format-testing.md b/docs/architecture/testing/psa-storage-format-testing.md
new file mode 100644
index 0000000..a5c1fd1
--- /dev/null
+++ b/docs/architecture/testing/psa-storage-format-testing.md
@@ -0,0 +1,127 @@
+# Mbed TLS PSA keystore format stability testing strategy
+
+## Introduction
+
+The PSA crypto subsystem includes a persistent key store. It is possible to create a persistent key and read it back later. This must work even if Mbed TLS has been upgraded in the meantime (except for deliberate breaks in the backward compatibility of the storage).
+
+The goal of this document is to define a test strategy for the key store that not only validates that it's possible to load a key that was saved with the version of Mbed TLS under test, but also that it's possible to load a key that was saved with previous versions of Mbed TLS.
+
+Interoperability is not a goal: PSA crypto implementations are not intended to have compatible storage formats. Downgrading is not required to work.
+
+## General approach
+
+### Limitations of a direct approach
+
+The goal of storage format stability testing is: as a user of Mbed TLS, I want to store a key under version V and read it back under version W, with W ≥ V.
+
+Doing the testing this way would be difficult because we'd need to have version V of Mbed TLS available when testing version W.
+
+An alternative, semi-direct approach consists of generating test data under version V, and reading it back under version W. Done naively, this would require keeping a large amount of test data (full test coverage multiplied by the number of versions that we want to preserve backward compatibility with).
+
+### Save-and-compare approach
+
+Importing and saving a key is deterministic. Therefore we can ensure the stability of the storage format by creating test cases under a version V of Mbed TLS, where the test case parameters include both the parameters to pass to key creation and the expected state of the storage after the key is created. The test case creates a key as indicated by the parameters, then compares the actual state of the storage with the expected state.
+
+In addition, the test case also loads the key and checks that it has the expected data and metadata. Import-and-save testing and load-and-check testing can be split into separate test functions with the same payloads.
+
+If the test passes with version V, this means that the test data is consistent with what the implementation does. When the test later runs under version W ≥ V, it creates and reads back a storage state which is known to be identical to the state that V would have produced. Thus, this approach validates that W can read storage states created by V.
+
+Note that it is the combination of import-and-save passing on version V and load-and-check passing on version W with the same data that proves that version W can read back what version V wrote. From the perspective of a particular version of the library, the import-and-save tests guarantee forward compatibility while the load-and-check tests guarantee backward compatibility.
+
+Use a similar approach for files other than keys where possible and relevant.
+
+### Keeping up with storage format evolution
+
+Test cases should normally not be removed from the code base: if something has worked before, it should keep working in future versions, so we should keep testing it.
+
+This cannot be enforced solely by looking at a single version of Mbed TLS, since there would be no indication that more test cases used to exist. It can only be enforced through review of library changes. The review is be assisted by a tool that compares the old and the new version, which is implemented in `scripts/abi_check.py`. This tool fails the CI if load-and-check test case disappears (changed test cases are raised as false positives).
+
+If the way certain keys are stored changes, and we don't deliberately decide to stop supporting old keys (which should only be done by retiring a version of the storage format), then we should keep the corresponding test cases in load-only mode: create a file with the expected content, load it and check the data that it contains.
+
+## Storage architecture overview
+
+The PSA subsystem provides storage on top of the PSA trusted storage interface. The state of the storage is a mapping from file identifier (a 64-bit number) to file content (a byte array). These files include:
+
+* [Key files](#key-storage) (files containing one key's metadata and, except for some secure element keys, key material).
+* The [random generator injected seed or state file](#random-generator-state) (`PSA_CRYPTO_ITS_RANDOM_SEED_UID`).
+* [Storage transaction file](#storage-transaction-resumption).
+* [Driver state files](#driver-state-files).
+
+For a more detailed description, refer to the [Mbed TLS storage specification](../mbed-crypto-storage-specification.md).
+
+In addition, Mbed TLS includes an implementation of the PSA trusted storage interface on top of C stdio. This document addresses the test strategy for [PSA ITS over file](#psa-its-over-file) in a separate section below.
+
+## Key storage testing
+
+This section describes the desired test cases for keys created with the current storage format version. When the storage format changes, if backward compatibility is desired, old test data should be kept as described under [“Keeping up with storage format evolution”](#keeping-up-with-storage-format-evolution).
+
+### Keystore layout
+
+Objective: test that the key file name corresponds to the key identifier.
+
+Method: Create a key with a given identifier (using `psa_import_key`) and verify that a file with the expected name is created, and no other. Repeat for different identifiers.
+
+### General key format
+
+Objective: test the format of the key file: which field goes where and how big it is.
+
+Method: Create a key with certain metadata with `psa_import_key`. Read the file content and validate that it has the expected layout, deduced from the storage specification. Repeat with different metadata. Ensure that there are test cases covering all fields.
+
+### Enumeration of test cases for keys
+
+Objective: ensure that the coverage is sufficient to have assurance that all keys are stored correctly. This requires a sufficient selection of key types, sizes, policies, etc.
+
+In particular, the tests must validate that each `PSA_xxx` constant that is stored in a key is covered by at least one test case:
+
+* Lifetimes: `PSA_KEY_LIFETIME_xxx`, `PSA_KEY_PERSISTENCE_xxx`, `PSA_KEY_LOCATION_xxx`.
+* Usage flags: `PSA_KEY_USAGE_xxx`.
+* Algorithms in policies: `PSA_ALG_xxx`.
+* Key types: `PSA_KEY_TYPE_xxx`, `PSA_ECC_FAMILY_xxx`, `PSA_DH_FAMILY_xxx`.
+
+In addition, the coverage of key material must ensure that any variation in key representation is detected. See [“Considerations on key material representations”](#Considerations-on-key-material-representations) for considerations regarding key types.
+
+Method: Each test case creates a key with `psa_import_key`, purges it from memory, then reads it back and exercises it.
+
+Generate test cases automatically based on an enumeration of available constants and some knowledge of what attributes (sizes, algorithms, …) and content to use for keys of a certain type.
+
+### Testing with alternative lifetime values
+
+Objective: have test coverage for lifetimes other than the default persistent lifetime (`PSA_KEY_LIFETIME_PERSISTENT`).
+
+Method:
+
+* For alternative locations: have tests conditional on the presence of a driver for that location.
+* For alternative persistence levels: have load-and-check tests for supported persistence levels. We may also want to have negative tests ensuring that keys with a not-supported persistence level are not accidentally created.
+
+### Considerations on key material representations
+
+The risks of incompatibilities in key representations depends on the key type and on the presence of drivers. Compatibility of and with drivers is currently out of scope of this document.
+
+Some types only have one plausible representation. Others admit alternative plausible representations (different encodings, or non-canonical representations).
+Here are some areas to watch for, with an identified risk of incompatibilities.
+
+* HMAC keys longer than the block size: pre-hashed or not?
+* DES keys: was parity enforced?
+* RSA keys: can invalid DER encodings (e.g. leading zeros, ignored sign bit) have been stored?
+* RSA private keys: can invalid CRT parameters have been stored?
+* Montgomery private keys: were they stored in masked form?
+
+## Random generator state
+
+TODO
+
+## Driver state files
+
+Not yet implemented.
+
+TODO
+
+## Storage transaction resumption
+
+Only relevant for secure element support. Not yet fully implemented.
+
+TODO
+
+## PSA ITS over file
+
+TODO
diff --git a/docs/architecture/testing/test-framework.md b/docs/architecture/testing/test-framework.md
new file mode 100644
index 0000000..80667df
--- /dev/null
+++ b/docs/architecture/testing/test-framework.md
@@ -0,0 +1,64 @@
+# Mbed TLS test framework
+
+This document is an overview of the Mbed TLS test framework and test tools.
+
+This document is incomplete. You can help by expanding it.
+
+## Unit tests
+
+See <https://mbed-tls.readthedocs.io/en/latest/kb/development/test_suites>
+
+### Unit test descriptions
+
+Each test case has a description which succinctly describes for a human audience what the test does. The first non-comment line of each paragraph in a `.data` file is the test description. The following rules and guidelines apply:
+
+* Test descriptions may not contain semicolons, line breaks and other control characters, or non-ASCII characters. <br>
+  Rationale: keep the tools that process test descriptions (`generate_test_code.py`, [outcome file](#outcome-file) tools) simple.
+* Test descriptions must be unique within a `.data` file. If you can't think of a better description, the convention is to append `#1`, `#2`, etc. <br>
+  Rationale: make it easy to relate a failure log to the test data. Avoid confusion between cases in the [outcome file](#outcome-file).
+* Test descriptions should be a maximum of **66 characters**. <br>
+  Rationale: 66 characters is what our various tools assume (leaving room for 14 more characters on an 80-column line). Longer descriptions may be truncated or may break a visual alignment. <br>
+  We have a lot of test cases with longer descriptions, but they should be avoided. At least please make sure that the first 66 characters describe the test uniquely.
+* Make the description descriptive. “foo: x=2, y=4” is more descriptive than “foo #2”. “foo: 0<x<y, both even” is even better if these inequalities and parities are why this particular test data was chosen.
+* Avoid changing the description of an existing test case without a good reason. This breaks the tracking of failures across CI runs, since this tracking is based on the descriptions.
+
+`tests/scripts/check_test_cases.py` enforces some rules and warns if some guidelines are violated.
+
+## TLS tests
+
+### SSL extension tests
+
+#### SSL test case descriptions
+
+Each test case in `ssl-opt.sh` has a description which succinctly describes for a human audience what the test does. The test description is the first parameter to `run_test`.
+
+The same rules and guidelines apply as for [unit test descriptions](#unit-test-descriptions). In addition, the description must be written on the same line as `run_test`, in double quotes, for the sake of `check_test_cases.py`.
+
+### SSL cipher suite tests
+
+Each test case in `compat.sh` has a description which succinctly describes for a human audience what the test does. The test description is `$TITLE` defined in `run_client`.
+
+The same rules and guidelines apply as for [unit test descriptions](#unit-test-descriptions). In addition, failure cause in `compat.sh` is not classified as `ssl-opt.sh`, so the information of failed log files are followed as prompt.
+
+## Running tests
+
+### Outcome file
+
+#### Generating an outcome file
+
+Unit tests, `ssl-opt.sh` and `compat.sh` record the outcome of each test case in a **test outcome file**. This feature is enabled if the environment variable `MBEDTLS_TEST_OUTCOME_FILE` is set. Set it to the path of the desired file.
+
+If you run `all.sh --outcome-file test-outcome.csv`, this collects the outcome of all the test cases in `test-outcome.csv`.
+
+#### Outcome file format
+
+The outcome file is in a CSV format using `;` (semicolon) as the delimiter and no quoting. This means that fields may not contain newlines or semicolons. There is no title line.
+
+The outcome file has 6 fields:
+
+* **Platform**: a description of the platform, e.g. `Linux-x86_64` or `Linux-x86_64-gcc7-msan`.
+* **Configuration**: a unique description of the configuration (`mbedtls_config.h`).
+* **Test suite**: `test_suite_xxx`, `ssl-opt` or `compat`.
+* **Test case**: the description of the test case.
+* **Result**: one of `PASS`, `SKIP` or `FAIL`.
+* **Cause**: more information explaining the result.
diff --git a/docs/architecture/tls13-support.md b/docs/architecture/tls13-support.md
new file mode 100644
index 0000000..d6fc19e
--- /dev/null
+++ b/docs/architecture/tls13-support.md
@@ -0,0 +1,435 @@
+TLS 1.3 support
+===============
+
+Overview
+--------
+
+Mbed TLS provides an implementation of the TLS 1.3 protocol. The TLS 1.3 support
+may be enabled using the MBEDTLS_SSL_PROTO_TLS1_3 configuration option.
+
+Support description
+-------------------
+
+- Overview
+
+  - Mbed TLS implements both the client and the server side of the TLS 1.3
+    protocol.
+
+  - Mbed TLS supports ECDHE key establishment.
+
+  - Mbed TLS supports DHE key establishment.
+
+  - Mbed TLS supports pre-shared keys for key establishment, pre-shared keys
+    provisioned externally as well as provisioned via the ticket mechanism.
+
+  - Mbed TLS supports session resumption via the ticket mechanism.
+
+  - Mbed TLS supports sending and receiving early data (0-RTT data).
+
+- Supported cipher suites: depends on the library configuration. Potentially
+  all of them:
+  TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256,
+  TLS_AES_128_CCM_SHA256 and TLS_AES_128_CCM_8_SHA256.
+
+- Supported ClientHello extensions:
+
+  | Extension                    | Support |
+  | ---------------------------- | ------- |
+  | server_name                  | YES     |
+  | max_fragment_length          | no      |
+  | status_request               | no      |
+  | supported_groups             | YES     |
+  | signature_algorithms         | YES     |
+  | use_srtp                     | no      |
+  | heartbeat                    | no      |
+  | alpn                         | YES     |
+  | signed_certificate_timestamp | no      |
+  | client_certificate_type      | no      |
+  | server_certificate_type      | no      |
+  | padding                      | no      |
+  | key_share                    | YES     |
+  | pre_shared_key               | YES     |
+  | psk_key_exchange_modes       | YES     |
+  | early_data                   | YES     |
+  | cookie                       | no      |
+  | supported_versions           | YES     |
+  | certificate_authorities      | no      |
+  | post_handshake_auth          | no      |
+  | signature_algorithms_cert    | no      |
+
+
+- Supported groups: depends on the library configuration.
+  Potentially all ECDHE groups:
+  secp256r1, x25519, secp384r1, x448 and secp521r1.
+
+  Potentially all DHE groups:
+  ffdhe2048, ffdhe3072, ffdhe4096, ffdhe6144 and ffdhe8192.
+
+- Supported signature algorithms (both for certificates and CertificateVerify):
+  depends on the library configuration.
+  Potentially:
+  ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512,
+  rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, rsa_pss_rsae_sha256,
+  rsa_pss_rsae_sha384 and rsa_pss_rsae_sha512.
+
+  Note that in absence of an application profile standard specifying otherwise
+  rsa_pkcs1_sha256, rsa_pss_rsae_sha256 and ecdsa_secp256r1_sha256 are
+  mandatory (see section 9.1 of the specification).
+
+- Supported versions:
+
+  - TLS 1.2 and TLS 1.3 with version negotiation on client and server side.
+
+  - TLS 1.2 and TLS 1.3 can be enabled in the build independently of each
+    other.
+
+- Compatibility with existing SSL/TLS build options:
+
+  The TLS 1.3 implementation is compatible with nearly all TLS 1.2
+  configuration options in the sense that when enabling TLS 1.3 in the library
+  there is rarely any need to modify the configuration from that used for
+  TLS 1.2. There are two exceptions though: the TLS 1.3 implementation requires
+  MBEDTLS_PSA_CRYPTO_C and MBEDTLS_SSL_KEEP_PEER_CERTIFICATE, so these options
+  must be enabled.
+
+  Most of the Mbed TLS SSL/TLS related options are not supported or not
+  applicable to the TLS 1.3 implementation:
+
+  | Mbed TLS configuration option            | Support |
+  | ---------------------------------------- | ------- |
+  | MBEDTLS_SSL_ALL_ALERT_MESSAGES           | yes     |
+  | MBEDTLS_SSL_ASYNC_PRIVATE                | no      |
+  | MBEDTLS_SSL_CONTEXT_SERIALIZATION        | no      |
+  | MBEDTLS_SSL_DEBUG_ALL                    | no      |
+  | MBEDTLS_SSL_ENCRYPT_THEN_MAC             | n/a     |
+  | MBEDTLS_SSL_EXTENDED_MASTER_SECRET       | n/a     |
+  | MBEDTLS_SSL_KEEP_PEER_CERTIFICATE        | no (1)  |
+  | MBEDTLS_SSL_RENEGOTIATION                | n/a     |
+  | MBEDTLS_SSL_MAX_FRAGMENT_LENGTH          | no      |
+  |                                          |         |
+  | MBEDTLS_SSL_SESSION_TICKETS              | yes     |
+  | MBEDTLS_SSL_SERVER_NAME_INDICATION       | yes     |
+  | MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH       | no      |
+  |                                          |         |
+  | MBEDTLS_ECP_RESTARTABLE                  | no      |
+  | MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED     | no      |
+  |                                          |         |
+  | MBEDTLS_KEY_EXCHANGE_PSK_ENABLED         | n/a (2) |
+  | MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED     | n/a     |
+  | MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED   | n/a     |
+  | MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED     | n/a     |
+  | MBEDTLS_KEY_EXCHANGE_RSA_ENABLED         | n/a     |
+  | MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED     | n/a     |
+  | MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED   | n/a     |
+  | MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED | n/a     |
+  | MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED  | n/a     |
+  | MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED    | n/a     |
+  | MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED     | n/a     |
+  |                                          |         |
+  | MBEDTLS_PSA_CRYPTO_C                     | no (1)  |
+  | MBEDTLS_USE_PSA_CRYPTO                   | yes     |
+
+  (1) These options must remain in their default state of enabled.
+  (2) See the TLS 1.3 specific build options section below.
+
+- TLS 1.3 specific build options:
+
+  - MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE enables the support for middlebox
+    compatibility mode as defined in section D.4 of RFC 8446.
+
+  - MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED enables the support for
+    the PSK key exchange mode as defined by RFC 8446. If it is the only key
+    exchange mode enabled, the TLS 1.3 implementation does not contain any code
+    related to key exchange protocols, certificates and signatures.
+
+  - MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED enables the
+    support for the ephemeral key exchange mode. If it is the only key exchange
+    mode enabled, the TLS 1.3 implementation does not contain any code related
+    to PSK based key exchange. The ephemeral key exchange mode requires at least
+    one of the key exchange protocol allowed by the TLS 1.3 specification, the
+    parsing and validation of x509 certificates and at least one signature
+    algorithm allowed by the TLS 1.3 specification for signature computing and
+    verification.
+
+  - MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED enables the
+    support for the PSK ephemeral key exchange mode. If it is the only key
+    exchange mode enabled, the TLS 1.3 implementation does not contain any code
+    related to certificates and signatures. The PSK ephemeral key exchange
+    mode requires at least one of the key exchange protocol allowed by the
+    TLS 1.3 specification.
+
+
+Coding rules checklist for TLS 1.3
+----------------------------------
+
+The following coding rules are aimed to be a checklist for TLS 1.3 upstreaming
+work to reduce review rounds and the number of comments in each round. They
+come along (do NOT replace) the project coding rules
+(https://mbed-tls.readthedocs.io/en/latest/kb/development/mbedtls-coding-standards). They have been
+established and discussed following the review of #4882 that was the
+PR upstreaming the first part of TLS 1.3 ClientHello writing code.
+
+TLS 1.3 specific coding rules:
+
+  - TLS 1.3 specific C modules, headers, static functions names are prefixed
+    with `ssl_tls13_`. The same applies to structures and types that are
+    internal to C modules.
+
+  - TLS 1.3 specific exported functions, structures and types are
+    prefixed with `mbedtls_ssl_tls13_`.
+
+  - Use TLS1_3 in TLS 1.3 specific macros.
+
+  - The names of macros and variables related to a field or structure in the
+    TLS 1.3 specification should contain as far as possible the field name as
+    it is in the specification. If the field name is "too long" and we prefer
+    to introduce some kind of abbreviation of it, use the same abbreviation
+    everywhere in the code.
+
+    Example 1: #define CLIENT_HELLO_RANDOM_LEN 32, macro for the length of the
+        `random` field of the ClientHello message.
+
+    Example 2 (consistent abbreviation): `mbedtls_ssl_tls13_write_sig_alg_ext()`
+        and `MBEDTLS_TLS_EXT_SIG_ALG`, `sig_alg` standing for
+        `signature_algorithms`.
+
+  - Regarding vectors that are represented by a length followed by their value
+    in the data exchanged between servers and clients:
+
+    - Use `<vector name>_len` for the name of a variable used to compute the
+      length in bytes of the vector, where <vector name> is the name of the
+      vector as defined in the TLS 1.3 specification.
+
+    - Use `p_<vector_name>_len` for the name of a variable intended to hold
+      the address of the first byte of the vector length.
+
+    - Use `<vector_name>` for the name of a variable intended to hold the
+      address of the first byte of the vector value.
+
+    - Use `<vector_name>_end` for the name of a variable intended to hold
+      the address of the first byte past the vector value.
+
+    Those idioms should lower the risk of mis-using one of the address in place
+    of another one which could potentially lead to some nasty issues.
+
+    Example: `cipher_suites` vector of ClientHello in
+             `ssl_tls13_write_client_hello_cipher_suites()`
+    ```
+    size_t cipher_suites_len;
+    unsigned char *p_cipher_suites_len;
+    unsigned char *cipher_suites;
+    ```
+
+  - Where applicable, use:
+    - the macros to extract a byte from a multi-byte integer MBEDTLS_BYTE_{0-8}.
+    - the macros to write in memory in big-endian order a multi-byte integer
+      MBEDTLS_PUT_UINT{8|16|32|64}_BE.
+    - the macros to read from memory a multi-byte integer in big-endian order
+      MBEDTLS_GET_UINT{8|16|32|64}_BE.
+    - the macro to check for space when writing into an output buffer
+      `MBEDTLS_SSL_CHK_BUF_PTR`.
+    - the macro to check for data when reading from an input buffer
+      `MBEDTLS_SSL_CHK_BUF_READ_PTR`.
+
+    The three first types, MBEDTLS_BYTE_{0-8}, MBEDTLS_PUT_UINT{8|16|32|64}_BE
+    and MBEDTLS_GET_UINT{8|16|32|64}_BE improve the readability of the code and
+    reduce the risk of writing or reading bytes in the wrong order.
+
+    The two last types, `MBEDTLS_SSL_CHK_BUF_PTR` and
+    `MBEDTLS_SSL_CHK_BUF_READ_PTR`, improve the readability of the code and
+    reduce the risk of error in the non-completely-trivial arithmetic to
+    check that we do not write or read past the end of a data buffer. The
+    usage of those macros combined with the following rule mitigate the risk
+    to read/write past the end of a data buffer.
+
+    Examples:
+    ```
+    hs_hdr[1] = MBEDTLS_BYTE_2( total_hs_len );
+    MBEDTLS_PUT_UINT16_BE( MBEDTLS_TLS_EXT_SUPPORTED_VERSIONS, p, 0 );
+    MBEDTLS_SSL_CHK_BUF_PTR( p, end, 7 );
+    ```
+
+  - To mitigate what happened here
+    (https://github.com/Mbed-TLS/mbedtls/pull/4882#discussion_r701704527) from
+    happening again, use always a local variable named `p` for the reading
+    pointer in functions parsing TLS 1.3 data, and for the writing pointer in
+    functions writing data into an output buffer and only that variable. The
+    name `p` has been chosen as it was already widely used in TLS code.
+
+  - When an TLS 1.3 structure is written or read by a function or as part of
+    a function, provide as documentation the definition of the structure as
+    it is in the TLS 1.3 specification.
+
+General coding rules:
+
+  - We prefer grouping "related statement lines" by not adding blank lines
+    between them.
+
+    Example 1:
+    ```
+    ret = ssl_tls13_write_client_hello_cipher_suites( ssl, buf, end, &output_len );
+    if( ret != 0 )
+        return( ret );
+    buf += output_len;
+    ```
+
+    Example 2:
+    ```
+    MBEDTLS_SSL_CHK_BUF_PTR( cipher_suites_iter, end, 2 );
+    MBEDTLS_PUT_UINT16_BE( cipher_suite, cipher_suites_iter, 0 );
+    cipher_suites_iter += 2;
+    ```
+
+  - Use macros for constants that are used in different functions, different
+    places in the code. When a constant is used only locally in a function
+    (like the length in bytes of the vector lengths in functions reading and
+    writing TLS handshake message) there is no need to define a macro for it.
+
+    Example: `#define CLIENT_HELLO_RANDOM_LEN 32`
+
+  - When declaring a pointer the dereferencing operator should be prepended to
+    the pointer name not appended to the pointer type:
+
+    Example: `mbedtls_ssl_context *ssl;`
+
+  - Maximum line length is 80 characters.
+
+    Exceptions:
+
+    - string literals can extend beyond 80 characters as we do not want to
+      split them to ease their search in the code base.
+
+    - A line can be more than 80 characters by a few characters if just looking
+      at the 80 first characters is enough to fully understand the line. For
+      example it is generally fine if some closure characters like ";" or ")"
+      are beyond the 80 characters limit.
+
+    If a line becomes too long due to a refactoring (for example renaming a
+    function to a longer name, or indenting a block more), avoid rewrapping
+    lines in the same commit: it makes the review harder. Make one commit with
+    the longer lines and another commit with just the rewrapping.
+
+  - When in successive lines, functions and macros parameters should be aligned
+    vertically.
+
+    Example:
+    ```
+    int mbedtls_ssl_start_handshake_msg( mbedtls_ssl_context *ssl,
+                                         unsigned hs_type,
+                                         unsigned char **buf,
+                                         size_t *buf_len );
+    ```
+
+  - When a function's parameters span several lines, group related parameters
+    together if possible.
+
+    For example, prefer:
+
+    ```
+    mbedtls_ssl_start_handshake_msg( ssl, hs_type,
+                                     buf, buf_len );
+    ```
+    over
+    ```
+    mbedtls_ssl_start_handshake_msg( ssl, hs_type, buf,
+                                     buf_len );
+    ```
+    even if it fits.
+
+
+Overview of handshake code organization
+---------------------------------------
+
+The TLS 1.3 handshake protocol is implemented as a state machine. The
+functions `mbedtls_ssl_tls13_handshake_{client,server}_step` are the top level
+functions of that implementation. They are implemented as a switch over all the
+possible states of the state machine.
+
+Most of the states are either dedicated to the processing or writing of an
+handshake message.
+
+The implementation does not go systematically through all states as this would
+result in too many checks of whether something needs to be done or not in a
+given state to be duplicated across several state handlers. For example, on
+client side, the states related to certificate parsing and validation are
+bypassed if the handshake is based on a pre-shared key and thus does not
+involve certificates.
+
+On the contrary, the implementation goes systematically though some states
+even if they could be bypassed if it helps in minimizing when and where inbound
+and outbound keys are updated. The `MBEDTLS_SSL_CLIENT_CERTIFICATE` state on
+client side is a example of that.
+
+The names of the handlers processing/writing an handshake message are
+prefixed with `(mbedtls_)ssl_tls13_{process,write}`. To ease the maintenance and
+reduce the risk of bugs, the code of the message processing and writing
+handlers is split into a sequence of stages.
+
+The sending of data to the peer only occurs in `mbedtls_ssl_handshake_step`
+between the calls to the handlers and as a consequence handlers do not have to
+care about the MBEDTLS_ERR_SSL_WANT_WRITE error code. Furthermore, all pending
+data are flushed before to call the next handler. That way, handlers do not
+have to worry about pending data when changing outbound keys.
+
+### Message processing handlers
+For message processing handlers, the stages are:
+
+* coordination stage: check if the state should be bypassed. This stage is
+optional. The check is either purely based on the reading of the value of some
+fields of the SSL context or based on the reading of the type of the next
+message. The latter occurs when it is not known what the next handshake message
+will be, an example of that on client side being if we are going to receive a
+CertificateRequest message or not. The intent is, apart from the next record
+reading to not modify the SSL context as this stage may be repeated if the
+next handshake message has not been received yet.
+
+* fetching stage: at this stage we are sure of the type of the handshake
+message we must receive next and we try to fetch it. If we did not go through
+a coordination stage involving the next record type reading, the next
+handshake message may not have been received yet, the handler returns with
+`MBEDTLS_ERR_SSL_WANT_READ` without changing the current state and it will be
+called again later.
+
+* pre-processing stage: prepare the SSL context for the message parsing. This
+stage is optional. Any processing that must be done before the parsing of the
+message or that can be done to simplify the parsing code. Some simple and
+partial parsing of the handshake message may append at that stage like in the
+ServerHello message pre-processing.
+
+* parsing stage: parse the message and restrict as much as possible any
+update of the SSL context. The idea of the pre-processing/parsing/post-processing
+organization is to concentrate solely on the parsing in the parsing function to
+reduce the size of its code and to simplify it.
+
+* post-processing stage: following the parsing, further update of the SSL
+context to prepare for the next incoming and outgoing messages. This stage is
+optional. For example, secret and key computations occur at this stage, as well
+as handshake messages checksum update.
+
+* state change: the state change is done in the main state handler to ease the
+navigation of the state machine transitions.
+
+
+### Message writing handlers
+For message writing handlers, the stages are:
+
+* coordination stage: check if the state should be bypassed. This stage is
+optional. The check is based on the value of some fields of the SSL context.
+
+* preparation stage: prepare for the message writing. This stage is optional.
+Any processing that must be done before the writing of the message or that can
+be done to simplify the writing code.
+
+* writing stage: write the message and restrict as much as possible any update
+of the SSL context. The idea of the preparation/writing/finalization
+organization is to concentrate solely on the writing in the writing function to
+reduce the size of its code and simplify it.
+
+* finalization stage: following the writing, further update of the SSL
+context to prepare for the next incoming and outgoing messages. This stage is
+optional. For example, handshake secret and key computation occur at that
+stage (ServerHello writing finalization), switching to handshake keys for
+outbound message on server side as well.
+
+* state change: the state change is done in the main state handler to ease
+the navigation of the state machine transitions.