/** * @defgroup sys_psa_crypto PSA Cryptographic API * @ingroup sys * @brief Implements the PSA Crypto API specification. * @see https://armmbed.github.io/mbed-crypto/html/ * * @warning This implementation is not complete and not yet thoroughly tested. * Please do not use this module in production, as it may introduce security issues. * * @note This implementation is not complete and will be successively expanded. * * About {#About} * ===== * This module implements the PSA Cryptography API Version 1.1 as specified * [here](https://armmbed.github.io/mbed-crypto/html/) and the PSA Status code API Version 1.0 * as specified [here](https://arm-software.github.io/psa-api/status-code/1.0/). * It provides an OS level access to cryptographic operations and supports software and hardware * backends as well as the use of secure elements. * The API automatically builds a hardware backend for an operation, if there's one available, * otherwise it falls back to software. Specific backends can be configured, if needed. * For configuration options see [Configuration](#configuration). * * PSA Crypto has an integrated key management module, which stores keys internally * without exposing them to applications. To learn how to use keys with PSA, * read [Using Keys](#using-keys). * * A basic usage and configuration example can be found in `examples/psa_crypto`. * For more usage instructions, please read the documentation. * * If you want to add your own crypto backend, see [Porting Guide](#porting-guide). * * Basic Usage * === * To use PSA Crypto, add `psa/crypto.h` to your includes. This will make all * operations and macros available. * * Call `psa_crypto_init()` before calling any other operation. * * ## Structure Initialization * Whenever you declare a PSA Crypto structure (e.g. operation contexts or key attributes), * it needs to be initialized with zeroes. A structure that is not initialized will be interpreted * by PSA as *active* and can not be used for a new operation. * The example function and macro shown below result in the same thing: A new, inactive structure. * * @code {.c} * // Choose one of these options * psa_hash_operation_t hash_op = psa_hash_operation_init(); * psa_hash_operation_t hash_op = PSA_HASH_OPERATION_INIT; * @endcode * * An already active operation can be set to zero by reinitializing it. It then becomes *inactive* * again and can be used for a new operation. * * When errors occur during execution, PSA resets the operation contexts and makes them * *inactive*, to prevent unauthorized access to an operation's state. * Users can also call `psa__abort()` anytime in between function calls to do the same. * * Using Keys {#using-keys} * === * PSA can only operate on keys, that are registered with and stored within the internal * key storage module. This means you need to either generate keys with PSA or * import an existing key. * For this purpose there are a number of * [key management functions](https://armmbed.github.io/mbed-crypto/html/api/keys/management.html) * (external link). * * ## Key Attributes * When creating a key for PSA, the implementation needs to know what kind of key it is * dealing with, what it can be used for, where it's supposed to be stored, etc. * That information needs to be specified in a set of * [Key Attributes](https://armmbed.github.io/mbed-crypto/html/api/keys/attributes.html) * (external link). * * The example below defines attributes for an AES-128 key, which can be used for CBC encryption * and decryption and will be stored in local volatile memory. * @code * // Initializes empty attributes structure * psa_key_attributes_t attributes = psa_key_attributes_init(); * * // Set all necessary attributes * psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE); * psa_set_key_type(&attributes, PSA_KEY_TYPE_AES); * psa_set_key_bits(&attributes, 128); * psa_set_key_algorithm(&attributes, PSA_ALG_CBC_NO_PADDING); * psa_set_key_usage_flags(&attributes, (PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT)); * @endcode * * After setting the attributes, an exiting key can be imported: * @code * uint8_t aes_key[] = { ... }; * psa_key_id_t key_id = 0; // Will be set by PSA Crypto * psa_status_t status = psa_import_key(&attributes, aes_key, sizeof(aes_key), &key_id); * @endcode * The PSA Crypto implementation will assign an identifier to the key and return it * via the `key_id` parameter. This identifier can then be used for operations with this * specific key. * @code * uint8_t PLAINTEXT[] = { ... }; * // Buffer sizes can be calculated with macros * size_t output_buf_size = PSA_CIPHER_ENCRYPT_OUTPUT_SIZE(PSA_KEY_TYPE_AES, PSA_ALG_CBC_NO_PADDING,sizeof(PLAINTEXT)); * uint8_t output_buffer[output_buf_size]; * * status = psa_cipher_encrypt(key_id, PSA_ALG_CBC_NO_PADDING, PLAINTEXT, sizeof(PLAINTEXT),output_buffer, sizeof(output_buffer), &output_length)); * @endcode * * All the supported key types, algorithms and usage flags can be found in the documentation. * * ### Key Lifetime {#key-lifetime} * #### Volatile vs. Persistent * The PSA API specifies two ways of storing keys: volatile and persistent. Volatile * keys will be stored only in RAM, which means they will be destroyed after application * termination or a device reset. * Persistent keys will also be written into flash memory for later access. To destroy * them they must be explicitly deleted with the `psa_destroy_key()` function. * * @note Persistent key storage can be optionally enabled on `native` and on the `nRF52840dk`. * For this, add `USEMODULE += psa_persistent_storage` to your application makefile * or `CONFIG_MODULE_PSA_PERSISTENT_STORAGE=y` to your `app.config.test` file. * Example: `tests/sys/psa_crypto_persistent_storage` * * @warning Be aware that the current implementation writes keys in plain text to flash memory. * Anyone with hardware access can read them. * * #### Lifetime Encoding * When creating a key, the user needs to specify a lifetime value, which actually consists * of two values: persistence and location. The location defines the actual memory location * of the key (e.g. whether the key will be stored in RAM, in a hardware protected memory slot * or on an external device like a secure element). * * The persistence value defines whether the key will be stored in RAM (volatile) * in flash (persistent). * Some default values that exist are: * - @ref PSA_KEY_LIFETIME_VOLATILE (stored in local, volatile memory) * - @ref PSA_KEY_LIFETIME_PERSISTENT (stored in local, persistent memory) * * Other lifetime values can be constructed with the macro * `PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(persistence, location)`. * All supported `PSA_KEY_PERSISTENCE_*` and `PSA_KEY_LOCATION_*` values can be combined. * * In addition to the location values defined by the specification, this implementation also * supports values for [Secure Elements](#secure-elements). * * Configuration {#configuration} * === * Currently there are two ways to configure PSA Crypto: Kconfig and Makefiles. An example for both * can be found in `RIOT/examples/psa_crypto`. * * ## Kconfig * We recommend using Kconfig and choosing your features in `menuconfig`. * You can access the GUI by calling * * @code * TEST_KCONFIG=1 BOARD= make menuconfig * @endcode * * from your application directory. * There you can find the available PSA features and options under `System->PSA Crypto`. * If you only select the operations you want to use (e.g. `PSA Ciphers->AES-128 CBC`), Kconfig * will automatically select the best backend for you depending on the board (e.g. a hardware * accelerator if it is available). Optionally you can force a custom backend. * * Further you can specify the exact number of keys you need to store (section `PSA Key Management * Configuration` in `menuconfig`), or choose your [Secure Element](#secure-elements) * configurations. * * Alternatively you can create an `app.config.test` file in your application folder * and choose your symbols there (see `examples/psa_crypto`). * * In the `app.config.test` file, modules can be chosen with the following syntax: * `CONFIG_MODULE_=y`, as shown below. * @code * CONFIG_MODULE_PSA_CRYPTO=y * CONFIG_MODULE_PSA_CIPHER=y * CONFIG_MODULE_PSA_CIPHER_AES_128_CBC=y * @endcode * * ## Makefiles * If you don't want to use Kconfig, you can use the traditional way in RIOT of selecting * modules in your application Makefile. * * Here you need to set the base module and individual modules for each operation you need. * The example below also chooses a default backend depending on your board. * @code * // Base module: this is required! * USEMODULE += psa_crypto * * USEMODULE += psa_cipher * USEMODULE += psa_cipher_aes_128_cbc * @endcode * * If desired, you can choose a specific backend at compile time. For this you need to specify * that you want to set a custom backend and then explicitly choose the one you want (see below). * @code * USEMODULE += psa_cipher_aes_128_cbc_custom_backend * USEMODULE += psa_cipher_aes_128_cbc_backend_riot * @endcode * * The currently available modules, are listed [below](#available-modules). * * ## Key Slot Types {#configuration-keys} * The key management of PSA keeps track of keys by storing them in virtual key slot * representations, along with their attributes. Since keys can come in various sizes, * it would be inefficient to allocate the same amount of memory for all keys. * To reduce the amount of memory used for key storage, PSA internally differentiates between * three types of key slots (see below). Depending on the operations your application uses, PSA will * automatically detect the key sizes needed and will allocate the required memory. * The number of key slots allocated of each type is set to five per default, but can be changed by * the user depending on their requirements. * * | Single Key Slot | Asymmetric Key Slot | Protected Key Slot | * |----------------|---------------------|--------------------| * | Single keys or unstructured data,
e.g. AES keys or asymmetric
public keys in local memory | Asymmetric key pairs
(private and public parts)
in local memory | Any keys stored on a secure
element or on-chip in
hardware protected memory | * * If you want to change the default number of allocated key slots you can do so by * updating the number in `menuconfig`, or adding them to the `app.config.test` file like so: * @code * CONFIG_PSA_SINGLE_KEY_COUNT=3 * CONFIG_PSA_ASYMMETRIC_KEYPAIR_COUNT=1 * CONFIG_PSA_PROTECTED_KEY_COUNT=2 * @endcode * * When using Makefiles, you can pass CFLAGS as shown below. * @code * CFLAGS += -DCONFIG_PSA_SINGLE_KEY_COUNT=3 * CFLAGS += -DCONFIG_PSA_ASYMMETRIC_KEYPAIR_COUNT=1 * CFLAGS += -DCONFIG_PSA_PROTECTED_KEY_COUNT=2 * @endcode * * @note The key slot count defines the maximum number of keys that can be cached in * RAM at runtime. It does not limit the number of persistent keys that can be stored * in flash memory. It is the user's responsibility to keep track of the number of * persistently stored keys. * * ## Available Modules {#available-modules} * Below are the currently available modules. * No matter which operation you need, you always have to choose the base module. * If you want to specify a backend other than the default, you need to select * `psa__custom_backend` in addition to the actual backend module. * * The names listed are are the version used in makefiles with the * `USEMODULE += ` syntax. * In Kconfig you don't need to know the exact names, you can simply choose the features in * `menuconfig`. * When using `app.config.test` files in your application directory, you need to write the * names in uppercase and add the prefix `CONFIG_MODULE_` to all of them. * * ### Key Storage * - Persistent Key Storage: psa_persistent_storage * * ### Asymmetric Crypto * - Base: psa_asymmetric * * #### NIST ECC P192 * - psa_asymmetric_ecc_p192r1 * - psa_asymmetric_ecc_p192r1_backend_periph * - psa_asymmetric_ecc_p192r1_custom_backend * - psa_asymmetric_ecc_p192r1_backend_microecc * * #### NIST ECC P192 * - psa_asymmetric_ecc_p256r1 * - psa_asymmetric_ecc_p256r1_backend_periph * - psa_asymmetric_ecc_p256r1_custom_backend * - psa_asymmetric_ecc_p256r1_backend_microecc * * #### Ed25519 * - psa_asymmetric_ecc_ed25519 * - psa_asymmetric_ecc_ed25519_backend_periph * - psa_asymmetric_ecc_ed25519_custom_backend * - psa_asymmetric_ecc_ed25519_backend_c25519 * * ### Ciphers * - Base: psa_cipher * * #### AES ECB * - psa_cipher_aes_128_ecb * - psa_cipher_aes_128_ecb_backend_riot * * #### AES CBC * - psa_cipher_aes_128_cbc * - psa_cipher_aes_128_cbc_backend_periph * - psa_cipher_aes_128_cbc_custom_backend * - psa_cipher_aes_128_cbc_backend_riot * - psa_cipher_aes_192_cbc * - psa_cipher_aes_192_cbc_custom_backend * - psa_cipher_aes_192_cbc_backend_riot * - psa_cipher_aes_256_cbc * - psa_cipher_aes_256_cbc_custom_backend * - psa_cipher_aes_256_cbc_backend_riot * * #### CHACHA20 * - psa_cipher_chacha20 * - psa_cipher_chacha20_backend_periph * - psa_cipher_chacha20_custom_backend * - psa_cipher_chacha20_backend_riot * * ### Hashes * - Base: psa_hash * * #### MD5 * - psa_hash_md5 * - psa_hash_md5_custom_backend * - psa_hash_md5_backend_riot * * #### SHA 1 * - psa_hash_sha_1 * - psa_hash_sha_1_backend_periph * - psa_hash_sha_1_custom_backend * - psa_hash_sha_1_backend_riot * * #### SHA 224 * - psa_hash_sha_224 * - psa_hash_sha_224_backend_periph * - psa_hash_sha_224_custom_backend * - psa_hash_sha_224_backend_riot * * #### SHA 256 * - psa_hash_sha_256 * - psa_hash_sha_256_backend_periph * - psa_hash_sha_256_custom_backend * - psa_hash_sha_256_backend_riot * * #### SHA 384 * - psa_hash_sha_384 * - psa_hash_sha_384_backend_periph * - psa_hash_sha_384_custom_backend * - psa_hash_sha_384_backend_riot * * #### SHA 512 * - psa_hash_sha_512 * - psa_hash_sha_512_backend_periph * - psa_hash_sha_512_custom_backend * - psa_hash_sha_512_backend_riot * * #### SHA 512/224 * - psa_hash_sha_512_224 * - psa_hash_sha_512_224_backend_periph * - psa_hash_sha_512_224_custom_backend * - psa_hash_sha_512_224_backend_riot * * #### SHA 512/256 * - psa_hash_sha_512_256 * - psa_hash_sha_512_256_backend_periph * - psa_hash_sha_512_256_custom_backend * - psa_hash_sha_512_256_backend_riot * * #### SHA 3/256 * - psa_hash_sha3_256 * - psa_hash_sha3_256_backend_periph * - psa_hash_sha3_256_custom_backend * - psa_hash_sha3_256_backend_riot * * #### SHA 3/384 * - psa_hash_sha3_384 * - psa_hash_sha3_384_backend_periph * - psa_hash_sha3_384_custom_backend * - psa_hash_sha3_384_backend_riot * * #### SHA 3/512 * - psa_hash_sha3_512 * - psa_hash_sha3_512_backend_periph * - psa_hash_sha3_512_custom_backend * - psa_hash_sha3_512_backend_riot * * ### MAC * - Base: psa_mac * * #### HMAC SHA 256 * - psa_mac_hmac_sha_256 * - psa_mac_hmac_sha_256_backend_periph * - psa_mac_hmac_sha_256_custom_backend * - psa_mac_hmac_sha_256_backend_riot * * ### Secure Elements * Base: * - psa_secure_element * - psa_secure_element_multiple * * #### SE Types * - psa_secure_element_ateccx08a * - psa_secure_element_ateccx08a_cipher_aes_128 * - psa_secure_element_ateccx08a_ecc_p256 * - psa_secure_element_ateccx08a_hmac_sha256 * * Random Number Generation {#rng} * === * Currently uses the [RIOT Random Module](#sys_random) as a backend. * See the documentation for configuration options. * * Secure Elements {#secure-elements} * === * * An example showing the use of SEs can be found in `examples/psa_crypto`. * * To use secure elements, you first need to assign a static location value to each device, * so PSA can find it. If you only use one device, you can use * `PSA_KEY_LOCATION_PRIMARY_SECURE_ELEMENT`. For additional devices this value must be within * the range of `PSA_KEY_LOCATION_SE_MIN` and `PSA_KEY_LOCATION_SE_MAX`. * When booting the system, the `auto_init` module in RIOT will automatically register the device * with the location with PSA Crypto. * * You can now import or create keys on the secure element by constructing a key lifetime containing * a device's location value. * * @code {.c} * psa_key_lifetime_t lifetime = * PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION (PSA_KEY_LIFETIME_VOLATILE, PSA_KEY_LOCATION_PRIMARY_SECURE_ELEMENT); * @endcode * * Some secure elements come with their own key management and device configurations. In this case * the configuration parameters must be passed to PSA Crypto during the registration. For this, you * need to define a `psa_se_config_t` structure containing the configuration. * PSA Crypto will use this structure to keep track of what types of keys are allowed on the device * and how much storage is available. * Where this structure should be placed, how it looks and what parameters are required depends * on the type of your device. * * A good place to define that structure and the location values is a drivers `_params.h` * file, but this may vary depending on how your device is integrated in RIOT. * * For detailed, device specific information, please check the device driver documentation or * the example. * * ## Available Devices and Drivers * - ATECCX08A: [Microchip Cryptoauthlib as a PSA backend](#psa-cryptoauthlib) * * ## Main SE Configuration * To use SEs, the appropriate modules must be chosen in Kconfig: * @code * CONFIG_PSA_SECURE_ELEMENT=y * CONFIG_PSA_SECURE_ELEMENT_ATECCX08A=y // device example * CONFIG_PSA_SECURE_ELEMENT_ATECCX08A_ECC_P256=y * @endcode * * or added to the the Makefile: * @code * USEMODULE += psa_secure_element * USEMODULE += psa_secure_element_ateccx08a // device example * USEMODULE += psa_secure_element_ateccx08a_ecc_p256 * @endcode * * This implementation supports the use of one or more secure elements (SE) as backends. In this * case the number of used secure elements must be specified (must be at least 2 and at most 255). * When using more than one SE, add * @code * CONFIG_PSA_SECURE_ELEMENT_MULTIPLE=y * CONFIG_PSA_MAX_SE_COUNT=2 // or any other number between 2 and 255 * @endcode * * or, respectively, * * @code * USEMODULE += psa_secure_element_multiple * CFLAGS += -DCONFIG_PSA_MAX_SE_COUNT=2 // or any other number between 2 and 255 * @endcode * * Porting Guide {#porting-guide} * === * This porting guide focuses on how to add your software library or hardware driver * as a backend to PSA Crypto without actually touching the PSA implementation. * We will provide some [general information](#porting-general) and then some case * examples for different kinds of backends: * - [Software Libraries](#porting-software) * - [Hardware Drivers](#porting-hardware) * - [Secure Elements](#porting-secure-elements) * * Some examples to look at are: * - [RIOT hash module](#sys_hashes) * - [RIOT cipher module](#sys_crypto) * - [Micro-ECC](#pkg_micro_ecc) * - [CryptoCell 310 driver](#pkg_driver_cryptocell_310). * * An example integrating a secure element can be found in the * [Cryptoauthlib Package](#pkg_cryptoauthlib). * * ## General Information {#porting-general} * ### Error Values * You should always check the status of your function calls and translate your library's or * driver's errors to PSA error values (please be as thorough as possible). * The PSA Crypto specification describes exactly what kind of error values should be returned * by which function. Please read the API documentation and comply with the instructions. * We recommend writing a`_to_psa_error()` function right in the beginning (see for * example `CRYS_to_psa_error()` in * `pkg/driver_cryptocell_310/psa_cryptocell_310/error_conversion.c`). * * ### The Build System * As mentioned before, there are two ways of selecting build time configurations in RIOT: Kconfig * and Makefiles. * Kconfig dependency resolution is currently an experimental feature and will at some point * replace Makefiles. Until then, our implementation needs to support both, which means we need * to define features and symbols in multiple places. * Luckily, the modules have the exact same names in both systems, which makes the transfer easier. * The examples below show both ways. * * ### Modules {#module-names} * In RIOT, module names are generated from path names, so if you create a directory for * your sourcefiles, the module name will be the same as the directory name. It is possible * to change that by declaring a new module name in the Makefile by adding the line * `MODULE := your_module_name`. * * If you leave it like this, all sourcefiles in the path corresponding to the module name will be * built (e.g. if you choose the module `hashes`, all files in `sys/hashes` will be included). * For better configurability it is possible to add submodules (see * `sys/hashes/psa_riot_hashes` for example). * In that case the base module name will be the directory name and each file inside the directory * becomes its own submodule that must be explicitly chosen. The module name will then be the * directory name with the file name as a postfix. * For example: * @code * USEMODULE += hashes * USEMODULE += psa_riot_hashes * USEMODULE += psa_riot_hashes_sha_256 * * will build the file at `sys/hashes/psa_riot_hashes/sha_256.c`, but none of the other files in * the directory. * * To enable submodules for your implementation add the following to the directory makefile: * @code * BASE_MODULE := psa_ * SUBMODULES := 1 * @endcode * * We also need to create so-called pseudomodules for each available submodule. * Those must follow the scheme `psa__`. * Where they are declared depends on where your module is located. Pseudomodules in `RIOT/sys` must * be added in `pseudomodules.inc.mk`. * When integrating packages or drivers, the pseudomodules can be added in the `Makefile.include` * file of the individual module's directory (see `pkg/micro-ecc/Makefile.include`). * * When adding backends to PSA Crypto, please name your modules in ways that fit within the * current naming scheme: `psa__`. Also, when adding software libraries and * hardware drivers, use the submodule approach. That makes PSA Crypto more configurable. * * The drawback of the submodule approach is, that if one of our sourcefiles depends on * another sourcefile in the same folder, we need to select it explicitly. For example, in * `pkg/driver_cryptocell_310/psa_cryptocell_310` you can see that there are some common source * files that all the others use (e.g. for hashes there is a `hashes_common.c` file). * * If that is the case for your driver, you need to make sure the modules are selected in * the Kconfig file as well as the `Makefile.dep` file (see `psa_cryptocell_310/Makefile.dep` or * `psa_cryptocell_310/Kconfig`). * * ### Adding Glue Code {#glue-code} * We define a number of wrapper APIs, which are called by PSA to invoke crypto backends. * Software libraries and hardware drivers use the same methods, secure elements are handled * in a different way (see [Case Example – Secure Elements](#porting-secure-elements) for details). * * The names, parameters and return values for wrapper methods are defined in header files in * `sys/psa_crypto/include/psa_.h`. * The functions declared in those files are the ones that are currently supported by this * PSA implementation. They will be extended in the future. * * You need to implement those functions with glue code calling your library or driver code * and converting types and error values between PSA and your backend. * Below is an example of how this might look (it's very reduced, your library may need * much more glue code). * @code {.c} * psa_status_t psa_ecc_p256r1_sign_hash(const psa_key_attributes_t *attributes, * psa_algorithm_t alg, const uint8_t *key_buffer, * size_t key_buffer_size, const uint8_t *hash, * size_t hash_length, uint8_t *signature, * size_t signature_size, size_t *signature_length) * { * int status = _(key_buffer, hash, hash_length, * signature, signature_length, curve); * * if (status != SUCCESS) { * return _status_to_psa_error(status); * } * * (void)alg; * (void)attributes; * (void)key_buffer_size; * return PSA_SUCCESS; * } * @endcode * * ### Operation Contexts * Some cryptographic operations use driver specific context to store the operation state in * between function calls. These must be defined somewhere. Examples can be found in * `pkg/driver_cryptocell_310/include/psa_periph_hashes_ctx.h` and * `sys/include/hashes/psa/riot_hashes.h`. * * When defining the contexts for a hardware driver, all you need to do is add a file called * `psa_periph__ctx.h` to your driver's include folder and define the available types * (see supported [types](#supported-types) below). * Those files are automatically included in `crypto_includes.h` and it is important that they * always have the same name for each algorithm. * * When defining the contexts for a software library, the headerfile should be called * `_.h` (e.g. `riot_hashes.h`) and must be added to `crypto_includes.h` as * shown below: * @code * #if IS_USED(MODULE_PSA__) * #include "/_.h" * #endif * @endcode * * When defining the context types, those must always depend on the specific algorithm module, * for example * @code * #if IS_USED(MODULE_PSA__HASHES_SHA_256) * #include "path/to/headerfile_containing_the_driver_context_definition" * * typedef psa_hashes_sha256_ctx_t; * #endif * @endcode * * #### Hashes {#supported-types} * - `psa_hashes_md5_ctx_t` * - `psa_hashes_sha1_ctx_t` * - `psa_hashes_sha224_ctx_t` * - `psa_hashes_sha256_ctx_t` * - `psa_hashes_sha384_ctx_t` * - `psa_hashes_sha512_ctx_t` * - `psa_hashes_sha512_224_ctx_t` * - `psa_hashes_sha512_256_ctx_t` * * #### Ciphers * - `psa_cipher_aes_128_ctx_t` * - `psa_cipher_aes_192_ctx_t` * - `psa_cipher_aes_256_ctx_t` * * Secure Elements need their own contexts. For this, * see [Case Example – Secure Elements](#porting-secure-elements). * * ## Adding a Backend * The integration of hardware drivers, software libraries and secure element drivers * differs a bit. Below we describe the necessary steps for each of them. * * ### Case Example – A Software Library {#porting-software} * Software libraries are the easiest backends, because they are not platform or hardware * specific. They can generally run on all platforms in RIOT and we can * combine different software backends for different operations (we could, for example, * use the Micro-ECC package for ECC NIST curves and the C25519 package for operations with * the Curve25519). * * Let's say we have an imaginary software library called `FancyCrypt` and want to use * it as a backend of PSA. We've already added it to RIOT as a third party package in * `pkg/fancycrypt`. * Our library provides hashes and elliptic curve operations and to make it accessible to * PSA Crypto we need to write wrappers for our API calls. * * First we create a folder called `psa_fancycrypt` in the package directory. Inside we create * a file with the name of each operation you want to integrate, e.g. `p256.c` and * `hashes_sha_224.c` (when adding operations, remember that the path of the files will also * be the [module name](#module-names), so please comply with the current naming scheme). * * In these files we need to implement the methods that are called by PSA as described * [above](#glue-code). * * #### Adding Makefiles * We add a Makefile to the `psa_fancycrypt` folder with the following content: * @code {.c} * BASE_MODULE := psa_fancycrypt * SUBMODULES := 1 * * include $(RIOTBASE)/Makefile.base * @endcode * * This tells RIOT that the `psa_fancycrypt` module has submodules, which can be selected * individually. * * In `pkg/fancycrypt` we now need to declare explicit pseudomodules in `Makefile.include` and add * the `psa_fancycrypt` folder to the source files and the `sys/psa_crypto/include` folder to the * includes. * These should be dependent on the PSA Crypto module as shown below. * * @code * ifneq (,$(filter psa_fancycrypt_%, $(USEMODULE))) * PSEUDOMODULES += psa_fancycrypt_hashes_sha_256 * PSEUDOMODULES += psa_fancycrypt_p256 * DIRS += $(RIOTPKG)/fancycrypt/psa_fancycrypt * INCLUDES += -I$(RIOTBASE)/sys/psa_crypto/include * endif * @endcode * * If the implementation has any dependencies, they need to be added in `Makefile.dep`, for example: * @code * USEMODULE += psa_fancycrypt * USEMODULE += psa_fancycrypt_error_conversion * * ifneq (,$(filter psa_fancycrypt_hashes_sha1,$(USEMODULE))) * USEMODULE += psa_fancycrypt_hashes_common * endif * @endcode * * #### Adding a Kconfig file * We add a file called `Kconfig` to the `psa_fancycrypt` folder. Here we declare * the modules for Kconfig like so: * @code * config MODULE_PSA_FANCYCRYPT_HASHES_SHA_256 * bool * depends on MODULE_PSA_CRYPTO * select MODULE_PSA_FANCYCRYPT * * config MODULE_PSA_FANCYCRYPT_P256 * bool * depends on MODULE_PSA_CRYPTO * select MODULE_PSA_FANCYCRYPT * * config MODULE_PSA_FANCYCRYPT * bool * @endcode * * If the implementation has any dependencies, we can select them in this Kconfig file: * @code * config MODULE_PSA_FANCYCRYPT_HASHES_SHA_256 * bool * depends on MODULE_PSA_CRYPTO * select MODULE_PSA_FANCYCRYPT * select MODULE_PSA_FANCYCRYPT_HASHES_COMMON * select MODULE_PSA_FANCYCRYPT_ERROR_CONVERSION * @endcode * * In `pkg/fancycrypt/Kconfig` we need to add the line * @code * rsource "psa_fancycrypt/Kconfig" * @endcode * at the bottom. * * #### Telling PSA Crypto about it * To be able to choose `fancycrypt` as a PSA backend, we need to add the option to the Kconfig * and Makefiles of the PSA Crypto Module. * * In `sys/psa_crypto/` we need to modify `Kconfig.asymmetric`, `sys/psa_crypto/Kconfig.hashes`, * `Makefile.dep` and `Makefile.include`. * * To `Kconfig.asymmetric` we need to add * @code * config MODULE_PSA_ASYMMETRIC_ECC_P256R1_BACKEND_FANCYCRYPT * bool "FancyCrypt Package" * select PACKAGE_FANCYCRYPT * select MODULE_PSA_FANCYCRYPT_P256 * @endcode * This will expose FancyCrypt as a backend option in PSA and then enable all the necessary * features, when users select it. * You need to do the same thing for the hash operation in `Kconfig.hashes`. * * To achieve the same thing with Makefiles we need to do this in two places: * In `Makefile.include` there are some existing pseudomodules for asymmetric crypto and hashes. * There we need to create the backend modules for FancyCrypt by adding * * @code * PSEUDOMODULES += psa_asymmetric_ecc_p256r1_backend_fancycrypt * @endcode * * and * * @code * PSEUDOMODULES += psa_hash_sha_256_backend_fancycrypt * @endcode * * The automatic module selection happens in `Makefile.dep`. To the place where exiting P256 curves * and hashes are selected we add cases for our backend modules: * * @code * ifneq (,$(filter psa_asymmetric_ecc_p256r1_backend_fancycrypt,$(USEMODULE))) * USEPKG += fancycrypt * USEMODULE += psa_fancycrypt * USEMODULE += psa_fancycrypt_p256 * endif * @endcode * * Now you should be able to select your package as a backend for PSA Crypto and use it to perform * operations. * * ### Case Example – A Hardware Driver {#porting-hardware} * The first steps of porting a hardware driver are the same as for the software library. * Only we skip the last part where we add the modules to the PSA Crypto Kconfig and Makefiles * and do something else instead. * * Hardware drivers are treated a little differently, mostly because they are tied to a specific * platform and users can not just choose a different driver for their accelerator. * Therefore we just want PSA Crypto to automatically use this driver whenever it runs on the * corresponding platform, which means that we have to add some additional options and features, * not only to the driver but also to the CPU it belongs to. * A good example for this is the [CryptoCell 310 driver](#pkg_driver_cryptocell_310) for the * accelerator on the [nRF52840 CPU](#cpu_nrf52). * * Now, let's say we have a CPU called `myCPU` with an on-chip accelerator called * `speedycrypt`. Let's say that `speedycrypt` provides hashes and ECC curves. * The vendor provides a driver, which we already have included in RIOT as a package. * Also we've followed the steps in the [glue code section](#glue-code) and provide a folder called * `pkg/driver_speedycrypt/psa_speedycrypt` with the required wrapper files. * We have also added the module names in a Kconfig file and in the Makefiles. * * #### Telling PSA Crypto about it * This is where we diverge from the software library example. If you take a look at the available * backends in PSA, you'll notice one with the postfix `*_BACKEND_PERIPH` for each available * algorithm. **Periph** here is short for *peripheral hardware accelerator*. * The `*_BACKEND_PERIPH` modules depend on the presence of such an accelerator. They are a generic * module for all crypto hardware accelerators and will automatically resolve to the driver that is * associated with the available accelerator. * * Before we're able to use it we need to tell RIOT that those hardware features exist for * our `myCPU` (see `cpu/nrf52/Kconfig` and `cpu/nrf52/Makefile.features` as an example). * In `cpu/myCPU` we add all the provided features as shown below. * * Files we need to touch: * - `cpu/myCPU/Makefile.features` * - `cpu/myCPU/Kconfig` * - `cpu/myCPU/periph/Makefile.dep` * - `cpu/myCPU/periph/Kconfig` * - When defining new features: `RIOT/kconfigs/Kconfig.features` * * **cpu/myCPU/Makefile.features:** * @code * FEATURES_PROVIDED += periph_speedycrypt // General feature for the accelerator * FEATURES_PROVIDED += periph_hash_sha_256 * FEATURES_PROVIDED += periph_ecc_p256r1 * @endcode * * **cpu/myCPU/Kconfig:** * @code * config CPU_FAM_MYCPU * bool * select CPU_SOME_FEATURES * ... * select HAS_PERIPH_HASH_SHA_256 * select HAS_PERIPH_ECC_P256R1 * select HAS_PERIPH_SPEEDYCRYPT * @endcode * The `HAS_PERIPH_*` symbols are defined in ``. If your device * provides capabilities that are not yet defined, you can add them to that file. * * Next we need to define selectable modules for this in the `cpu/myCPU/periph` folder, which * then automatically enable the driver. An example for this is `cpu/nrf52/periph`. * We add the following to the `cpu/myCPU/periph/Kconfig` file and `cpu/myCPU/periph/Makefile.dep`: * * **cpu/myCPU/periph/Makefile.dep:** * @code * ifneq (,$(filter periph_hash_sha_256,$(USEMODULE))) * USEPKG += driver_speedycrypt * USEMODULE += psa_speedycrypt_hashes_sha256 * endif * @endcode * * **cpu/myCPU/periph/Kconfig:** * @code * config MODULE_PERIPH_FANCYCRYPT * bool * depends on HAS_PERIPH_FANCYCRYPT * select PACKAGE_DRIVER_FANCYCRYPT * * config MODULE_PERIPH_HASH_SHA_256 * bool * depends on HAS_PERIPH_HASH_SHA_256 * select MODULE_PERIPH_SPEEDYCRYPT * select MODULE_PSA_SPEEDYCRYPT_HASHES_SHA256 * @endcode * * Here we basically say "If the user chooses the `periph_hash_sha_256 module`, also select the * `periph_speedycrypt` feature, which will then enable the speedycrypt driver". Of course you need * to do this for all your available features. * * Now, if you build PSA Crypto with default configurations, it should automatically detect that * your board has a hardware accelerator for hashes and ECC operations and build the hardware * driver as a backend. * * ### Case Example – A Secure Element Driver {#porting-secure-elements} * Secure elements (SEs) are handled almost completely separate from the other backends. When we use * software libraries or hardware drivers, we only build one implementation per algorithm. * When it comes to secure elements we want to be able to build them in addition to the other * backends and we may want to connect and use more than one of them at the same time. * Another difference is that when using software libraries and hardware drivers, PSA handles the * storage of key material. When using SEs, keys are stored on the SE, which means, we need * additional functionality for the key management. * * An existing example in RIOT is the Microchip ATECCX08A device family, whose driver can be found * in `pkg/cryptoauthlib`. * * PSA Crypto has an integrated SE driver registry, which stores all registered drivers in a list. * When an application calls a cryptographic operation that's supposed to be performed by a secure * element, the registry will find the correct driver in the list and PSA will invoke the operation. * Each driver is stored with a context that contains persistent as well as transient driver data. * Transient driver data can be anything the driver needs to function. Persistent data is supposed * to be used to keep track of how many keys are stored on the device and if there is still some * free space available. * * @note Currently PSA does not support persistent storage, so the persistent driver data * is not really persistent, yet. Once persistent storage is implemented, this data * will be stored, so the implementation can find already existing keys again after * a reboot. * * For this example we integrate an imaginary SE called `superSE`, which comes with a driver called * `superSE_lib`. Again, we assume that we have already added the driver as a package in RIOT and it * can be found at `pkg/superse_lib`. * * #### Adding the Glue Code * Secure element drivers need to implement a different API than the other backends. It is defined * [here](#sys_psa_crypto_se_driver). * In our package folder we now create a new folder called `psa_superse_driver` and add a source * file called `psa_superse_lib_driver.c`. Here we now implement glue code for all the cryptographic * operations our SE supports. * * You will notice that the SE interface also provides some key management functions. This is * because keys are stored on the device and PSA can not access the memory and key data itself, * but needs to tell the driver to do it. * * ### Operation Contexts * Some operations need driver specific contexts. For secure elements these are wrapped in types * defined in `crypto_contexts.h` (currently only `psa_se_cipher_context_t` is supported). * In this header file add operation contexts that belong to your driver to the available SE * context unions as shown in the example below: * * @code * typedef struct { * union driver_context { * unsigned dummy; * #if IS_USED(MODULE_PSA_SECURE_ELEMENT_ATECCX08A) || defined(DOXYGEN) * atca_aes_cbc_ctx_t atca_aes_cbc; * #endif * #if IS_USED(MODULE_PSA_SECURE_ELEMENT_SUPERSE) || defined(DOXYGEN) * superse_cipher_ctx_t superse_aes_cbc; * #endif * } drv_ctx; * } psa_se_cipher_context_t; * @endcode * * #### Allocation * The first thing PSA will do, when an application creates a key on an SE, is ask the driver to * find a free key slot on the device. This is what the `allocate` function is for. How exactly * the slot is allocated, depends on the driver. * It may be possible to query that information directly from the device. If that is not possible, * we can use the persistent data stored in the driver context. An example for this can be * found in `pkg/cryptoauthlib/psa_atca_driver/psa_atca_se_driver.c`. * This example requires the user to provide information about the configurations for each key slot, * which is then stored in the persistent driver data and used for key management (for a better * description read [Using Cryptoauthlib as a backend for PSA Crypto](#psa-cryptoauthlib)). * At this point you can decide what the best approach for your device is. * * The `allocate` function should then return some reference to the slot it has allocated * for the key (possibly a pointer or a slot number). Next PSA Crypto will invoke the `import` * or `generate` function to store a key. * * #### Using Persistent Data * When you want to use persistent data to keep track of keys, you should utilize the * `psa_se_config_t` structure, which is declared in `crypto_se_config.h`. * You can define a structure that can hold your device configuration and make sure it is available * then your SE is used. * * #### Making the Methods Available * At the bottom of the wrapper code, define structures with pointers to the available methods. * For example if you have implemented a `superse_allocate` and `superse_generate_key` function, * you need to add a `psa_drv_se_key_management_t` structure as shown below. Fill the unimplemented * methods with `NULL` pointers. * The last structure should be a `psa_drv_se_t` struct containing pointers to the other structures. * That one will be stored during driver registration to get access to all the implemented * functions. * * @code {.c} * static psa_drv_se_key_management_t superse_key_management = { * .p_allocate = superse_allocate, * .p_validate_slot_number = NULL, * .p_import = NULL, * .p_generate = superse_generate_key, * .p_destroy = NULL, * .p_export = NULL, * .p_export_public = NULL * }; * * psa_drv_se_t superse_methods = { * .hal_version = PSA_DRV_SE_HAL_VERSION, * .persistent_data_size = 0, * .p_init = NULL, * .key_management = &superse_key_management, * .mac = NULL, * .cipher = NULL, * .aead = NULL, * .asymmetric = NULL, * .derivation = NULL * }; * @endcode * * You should do this for all available functions. The structures for the functions are * declared in `sys/psa_crypto/include/psa_crypto_se_driver.h`. * * #### Driver Registration * At start-up all secure element drivers need to be registered with the PSA SE management module. * This happens by calling `psa_register_secure_element()` during the automatic driver * initialization in RIOT. * When you added support for our device to RIOT, you should have implemented an * `auto_init_` function, which initializes the connected devices. * In this function, after initializing a device, you should call `psa_register_secure_element()` * and pass the device's location value, and pointers to the `psa_drv_se_t` structure, * the persistent data and some device specific context. * An example implementation of this can be seen in `sys/auto_init/security/auto_init_atca.c`. * * #### Telling PSA Crypto about it * To be able to choose our `superSE` during configuration, we need to define the corresponding * modules in the Kconfig files and Makefiles. * * To `pkg/super_se_lib/Kconfig` we add something like * @code * config MODULE_PSA_SUPERSE_DRIVER * bool * depends on PACKAGE_SUPERSE_LIB * default y if MODULE_PSA_CRYPTO * select PSA_KEY_MANAGEMENT * @endcode * This tells the build system that whenever this driver and PSA Crypto are used at the same time, * the wrapper and the PSA key management module are needed, too. * * To `sys/psa_crypto/psa_se_mgmt/Kconfig` we add a menu for the SE like so: * @code * menuconfig MODULE_PSA_SECURE_ELEMENT_SUPERSE * bool "Our Vendor's SuperSE" * select PACKAGE_SUPERSE_LIB * depends on * help * * @endcode * This makes our driver selectable whenever an application configuration selects the PSA secure * element module. * * As described in the [Configuration Section](#configuration-keys), references to keys on secure * elements are stored by PSA in a different type of key slot than other keys. * The slot for protected keys usually only contains a slot number or address and not the actual * key, which requires a lot less memory space. * * **BUT:** If your secure element supports asymmetric cryptography and exports a public key part * during key generation, that key part must be stored somewhere. So when you choose an * asymmetric operation, the protected key slots will have the space to store a public * key. * * #### Dependencies * Secure Element operations also depend on the PSA modules. E.g. when you want to use an ECC * operation, you need to make sure that you also build the asymmetric PSA functions. * * For this we need to add the following to the `superSE` menu: * @code * config MODULE_PSA_SECURE_ELEMENT_SUPERSE_ECC_P256 * bool "Our Vendor's Elliptic Curve P256" * select PSA_KEY_SIZE_256 * select MODULE_PSA_ASYMMETRIC * depends on MODULE_PSA_SECURE_ELEMENT_SUPERSE * @endcode * This tells us, what size a key slot should have to store the public key. If your SE supports * other curves, you need to modify this accordingly or add more of them. * * Now we need to add the same to the Makefiles. In `Makefile.include` we add the source file path * and the PSA include folders and define the new available pseudomodules: * @code * ifneq (,$(filter psa_crypto,$(USEMODULE))) * DIRS += $(RIOTPKG)/superse_lib/psa_superse_driver * INCLUDES += -I$(RIOTBASE)/sys/psa_crypto/include * PSEUDOMODULES += psa_secure_element_superse * PSEUDOMODULES += psa_secure_element_superse_ecc_p256 * endif * @endcode * * In `Makefile.dep` we automatically add required modules when PSA Crypto and the ECC curve * module are chosen: * @code * ifneq (,$(filter psa_crypto,$(USEMODULE))) * USEMODULE += psa_superse_driver * endif * * ifneq (,$(filter psa_secure_element_superse_ecc_p256, $(USEMODULE))) * USEMODULE += psa_asymmetric * endif * @endcode * This needs to be done for all other supported operations (e.g. ATECCX08 operations in * `pkg/cryptoauthlib/Makefile.include`, `pkg/cryptoauthlib/Makefile.dep` and * `sys/psa_crypto/psa_se_mgmt/Kconfig`. Now the secure element should be available for use * with PSA Crypto. */