The PSA Cryptography API specification 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.
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.
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 sketch of an Mbed TLS PSA cryptographic API implementation is thus:
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. That way, it is possible to compile and include in the library only the desired cryptographic operations.
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.
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__(). 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().
Summary of files to modify when adding a new algorithm or key type:
include/psa/crypto_values.h
or include/psa/crypto_extra.h
— New functions and macrosinclude/psa/crypto_config.h
, tests/include/test/drivers/crypto_config_test_driver_extension.h
— Preprocessor symbolslibrary/check_crypto_config.h
— Preprocessor symbolsinclude/mbedtls/config_psa.h
— Preprocessor symbolslibrary/psa_crypto.c
, library/psa_crypto_*.[hc]
— Implementation of the mechanismsinclude/psa/crypto_builtin_*.h
— Translucent data structurestests/suites/test_suite_psa_crypto_metadata.data
— New functions and macrosPSA_IS_xxx
) tests/suites/test_suite_psa_crypto_metadata.function
— New functions and macrostests/suites/test_suite_psa_crypto*.data
, tests/suites/test_suite_psa_crypto*.function
— Unit testsscripts/mbedtls_dev/crypto_knowledge.py
, scripts/mbedtls_dev/asymmetric_key_data.py
— Unit testsChangeLog.d/*.txt
— changelog entrySummary 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 macroslibrary/psa_crypto.c
, scripts/data_files/driver_templates/*.jinja
— Implementation of the mechanismsinclude/psa/crypto_struct.h
, include/psa/crypto_builtin_*.h
, include/psa/crypto_driver_contexts_*.h
— Translucent data structurestests/suites/test_suite_psa_crypto.data
, tests/suites/test_suite_psa_crypto.function
, tests/suites/test_suite_psa_crypto_driver_wrappers.*
— Unit testsNote 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.
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.
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
.
Each cryptographic mechanism is optional and can be selected by the application at build time. For each feature PSA_ttt_xxx
:
PSA_WANT_ttt_xxx
is defined. These symbols are set: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
.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.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
.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.
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.
The general structure of a cryptographic operation function is:
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.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.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
.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.
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.
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
:
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):
tests/suites/test_suite_psa_crypto_driver_wrappers.*
).