Chapter 13: Trusted firmware secure services

Abstract

In this chapter, we will examine the TF-M security services and the API calls available to the Non-Secure application code, but first we will look at the TF-M Non-Secure client, which is used to pass the API calls across the isolation boundary.

Keywords

Trusted firmware secure services; Nonsecure client; Configuration; TF-M client operation; TF-M client test; Protected storage; Internal trusted storage; Attestation service; Audit service; Cryptography service; mbedCrypto

Introduction

In this chapter, we will examine the TF-M security services and the API calls available to the Non-Secure application code, but first we will look at the TF-M Non-Secure client, which is used to pass the API calls across the isolation boundary.

Nonsecure client

The Non-Secure code contains a client and API definitions that are used to communicate with the Secure Partition Manager. The client supports both the Secure Function (SFN) call and the Inter-Process Calling (IPC) methods. Whichever calling method is used, an API function is first used to populate a structure with the service ID and the passed parameters before making a call across the isolation boundary.

Configuration

The Non-Secure code must be configured with the TF-M client by selecting the TFM::Core option in the RTE and selecting the calling type to match the configuration of the Secure partition code.

Next, we must add the support files for each of the mandatory services that will be used by the Non-Secure code (Fig. 13.1). If you are using the Attestation service, you will also need to add the QCBOR and T_COSE libraries. The T_COSE encryption type must be set to match the configuration of the attestation service in the Secure Partition.

Fig. 13.1
Fig. 13.1 Trusted Firmware Non-Secure client services. Add the Trusted Firmware Non-Secure client services in the RTE manager. No permission required.

Once the files have been added to the RTE, you must include the Non-Secure interface header file in the application code.

          #include "tfm_ns_interface.h"

TF-M client operation

We can access a service in the Secure partition by adding the service header file, for example, the cryptography API.

          #include "psa/crypto.h"

Each of the services provide a client API function, which can be called directly from the application code.

psa:status_t psa:hash_setup(psa:hash_operation_t *operation,  psa:algorithm_t alg);

The operation structure is declared and initialized prior to calling the API function.

          psa:hash_operation_t operation = PSA_HASH_OPERATION_INIT;

The NS API function will populate a structure with the Secure Function ID of the required service, and a handle used to manage the call:

   struct tfm_crypto_pack_iovec iov = {     .sfn_id = TFM_CRYPTO_HASH_SETUP_SID,     .alg = alg,     .op_handle = operation->handle,    };

Then, place the calling parameters into input and output IO vector buffers, which are used to transfer data across the isolation boundary:

   psa:invec in_vec[] = {     {.base = &iov, .len = sizeof(struct tfm_crypto_pack_iovec)},    };    psa:outvec out_vec[] = {     {.base = &(operation->handle), .len = sizeof(uint32_t)},    };

The API function then calls a dispatcher function which takes the Secure function name and ID as well as the IO vectors:

status = API_DISPATCH(tfm_crypto_hash_setup,  TFM_CRYPTO_HASH_SETUP);

The underlying dispatcher function is specific to the TF-M calling model selected:

define API_DISPATCH(sfn_name, sfn_id)  tfm_ns_interface:dispatch((veneer_fn)tfm_##sfn_name##_veneer,  (uint32_t)in_vec,  ARRAY_SIZE(in_vec),  (uint32_t)out_vec,  ARRAY_SIZE(out_vec))

The tfm_ns_interface:dispatch() dispatcher function will use an RTOS mutex to lock the critical code.

os_wrapper_mutex_acquire(ns_lock_handle,  OS_WRAPPER_WAIT_FOREVER)

The API call structure is sent to the secure veneer function. The veneer function uses a supervisor call to enter the processor privileged mode prior to entering the secure partition. It will also add the partition ID and pass the API call structure to the Secure Partition Manager (SPM).

              result = fn(arg0, arg1, arg2, arg3);

Once the message has been processed, the mutex is released.

          os_wrapper_mutex_release(ns_lock_handle)

The IPC calling method used the same initial function to add the SID and prepare the IOVEC buffers. However, to send the API call, it then uses the connectcalldisconnect functions to communicate with the SPM and the endpoint security service.

Once the IO vectors have been populated, the NS Client will connect to the secure partition via the SPM.

          psa:connect(FM_CRYPTO);

Once again the dispatcher function will be called:

          status = API_DISPATCH(tfm_crypto_hash_setup,TFM_CRYPTO_HASH_SETUP);

However, this time it is aliased to support the IPC communication model and will generate a psa:call() to the secure partition.

   #define API_DISPATCH(sfn_name, sfn_id)psa:call(ipc_handle, PSA_IPC_CALL,     in_vec, ARRAY_SIZE(in_vec),     out_vec, ARRAY_SIZE(out_vec))

The call function will place the size of the IOVEC buffers in the ctrl_params structure and then call the original dispatcher function.

   tfm_ns_interface:dispatch(           (veneer_fn)tfm_psa:call_veneer,           (uint32_t)handle,           (uint32_t)&ctrl_param,           (uint32_t)in_vec,           (uint32_t)out_vec);

When the transaction has finished, the connection to the secure partition is closed.

          psa:close();

Security services

The Secure Partition Trusted Firmware provides the PSA mandatory security services cryptography, storage, auditing, attestation, and firmware update. Each service has an init function that is invoked on startup and does not need to be part of the application code.

Each security service provides a header file that defines an API and a return value. As part of our secure coding practice, any application code should always check the return value. The return value is of the type:

  • psa:status_t.

Ideally, you should get PSA_SUCCESS as a result. If the function fails, there are a wide range of error values that should be logged for future diagnostic use. The full range of error codes can be found in the header file.

  • psa/error.h.

After any operation with the secure services, you must also wipe any associated memory and variables in the Non-Secure code, including data stored on the stack or heap. Each security service provides an abort function which should be called if the results of a secure function are no longer required. You should also destroy any keys that are no longer in active use.

Secure storage service

The Trusted Firmware provides two storage volumes in the Secure Partition. These are Protected Storage (PS) and Internal Trusted Storage (ITS). Protected Storage is intended to provide storage for application “data at rest” and provides protection against software and hardware attack. The Internal Trusted Storage volume provides the highest level of security for device intimate data such as cryptographic keys, firmware measurements, and other device secrets.

Protected storage

The Protected Storage volume will generally be a region of the microcontroller FLASH though it could be an external volume such as serial FLASH memory. The PS storage volume can be located in either the Secure or Non-Secure partition through PSA storage functions are always located in the Secure Partition. The Protected Storage is different from the other services in that it is run as a service within the Application RoT rather than the PSA RoT. The protected storage is capable of providing confidentiality, integrity, and replay protection. However, depending on the threat model and user requirements, data may be stored without confidentiality or replay protection, while data integrity is always validated.

The protected storage volume is accessed through an API provided to the Non-Secure code by adding the header file “psa/protected_storage.h” to the application Non-Secure code. This provides the following storage API (Table 13.1):

Table 13.1

Protected storage API functions.
FunctionDescription
psa:ps_getRetrieve data associated with a provided UID
psa:ps_setCreate a new or modify an existing key/value pair
psa:ps_get_infoRetrieve the metadata about the provided UID
psa:ps_removeRemove the provided UID and its associated data from the storage
psa:ps_createReserves storage for the specified UID.
psa:ps_set_extendedSets partial data into an asset based on the given identifier
psa:ps_get_supportReturns a bitmask with flags set for all the optional features supported by the implementation

When storing data, additional flags are used to control the security settings for a given data item. While confidentiality and anti-replay are optional, integrity is always applied.

In the case of public data, it is possible to disable encryption:

          PSA_STORAGE_FLAG_NO_CONFIDENTIALITY

Similarly, we can also disable replay protection if required.

          PSA_STORAGE_FLAG_NO_REPLAY_PROTECTION

We can also store an item as a constant so that one it has been written it cannot be updated. Such an immutable item can be created with the following flag.

          PSA_STORAGE_FLAG_WRITE_ONCE

Data items are stored as key value pairs, which are referenced through a Unique Identifier (UID) using the type:

          psa:storage_uid_tPS_Block = 0x01;

We can store a data item as follows:

   psa:status_t res ;   size_t PS_BlockSize =  20;   uint8_t PS_Blockdata[10] = "1234567890";   psa:storage_create_flags_t PS_CreateFlags = 0;   res = psa:ps_set( PS_Block, PS_BlockSize, PS_Blockdata, PS_CreateFlags);

When data are stored, depending on the implementation, the modifying flags may be ignored. For example, you may request no confidentiality, but the storage service may be configured to always encrypt the data. We can check the actual storage state using the following API call:

   struct psa:storage_info_t PS_block_info;   res = psa:ps_get_info(PS_Block, &PS_block_info);

which will return the capacity of the storage, the size of the data currently stored, and any flags that were set during creation.

If you need to create a storage item but only want to store part of the record, you must first create the item and then use the psa:ps_set_extended() function to add the data.

   size_t PS_DataSize = 10;   size_t PS_Offset = 10;   If( psa:ps_get_support() == PSA_STORAGE_SUPPORT_SET_EXTENDED )   {   res = psa:ps_create(PS_Block, PS_BlockSize, PS_CreateFlags);res = psa:ps_set_extended(PS_Block,PS_Offset, PS_DataSize,PS_Blockdata );   }

Data items can be retrieved or partially retrieved in a similar fashion and if necessary deleted.

   res =psa:ps_get(PS_Block, 0, PS_BlockSize, PS_Read_Blockdata, &PS_Read_Size);   res = psa:ps_remove(PS_Block);

Exercise: Protected storage

This exercise uses the Protected Storage API to save and retrieve data to the protected storage volume.

  • Locate Exercise 13.1 in the pack installer and press the copy button.
  • Make the nonsecure project the active project.
  • Batch build both projects and start the debugger.
  • Open the RTE and the TFM::Secure Service branch.

The protected storage API is enabled as an encrypted volume.

  • In the secure project, open TFM::tfm_config.h

Examine the configuration options for the protected storage volume (Fig. 13.2).

Fig. 13.2
Fig. 13.2 The secure code allows you to configure the protected storage volume and features.

We can control the FLASH volume by selecting an appropriate CMSIS-FLASH driver. It is also possible to enable rollback protection and control the maximum item storage size and number of items that can be saved to the protected storage volume.

The “Create Flash layout” option will force the secure service to format the FLASH volume and erase all existing data.

Validate FLASH Metadata will force the secure service to check the item metadata during a read operation to ensure that it is not corrupted or subject to a malicious change.

If you change any of these options, you must also consider how any change to the size of the protected storage volume will impact the memory map defined in the original CMSIS Zone project.

  • Open app_main.c and step through the code.

The app-main thread uses each of the Protected Storage functions to save, retrieve and extend blocks of data into the secure FLASH volume.

Internal trusted storage

The Internal Trusted Storage is used to store a small number of high-value secrets and consequently always provides confidentiality, integrity, and replay protection. The Internal Trusted Storage is run as a service within the PSA Root of Trust, and the data will be stored in a small volume located in the internal secure FLASH memory. The API is defined by including the header file:

          "psa/internal_trusted_storage.h"

and contains the following functions (Table 13.2):

Table 13.2

Internal trusted storage API functions.
FunctionDescription
psa:its_getReads a data entry
psa:its_setCreates a data entry and saves the application data
psa:its_get_infoReturns data entry meta data (size and capacity) in the form of psa:storage_info_t
psa:its_get_removeDeletes the data entry and frees the storage block

The Internal Trusted Storage functions operate with a similar but more limited API compared to the protected storage functions. When a data item is stored in the internal protected memory, the NO_CONFIDENTIALITY and PSA_STORAGE_FLAG_NO_REPLAY_PROTECTION have no effect, while the STORAGE_FLAG_WRITE_ONCE flag will always work.

We can now store secrets in the Internal Trusted Storage.

psa:status_t  psa:storage_create_flags_t ITS_CreateFlags = 0;  psa:storage_uid_t ITS_Block = 0x01;  size_t ITS_BlockSize =  20;  uint8_t ITS_Blockdata[10] = "1234567890";  uint8_t ITS_Read_Blockdata[10];  size_t ITS_Read_Size = 0;  res = psa:its_set(ITS_Block,ITS_BlockSize,  ITS_Blockdata,  ITS_CreateFlags);  res =psa:its_get(ITS_Block,0,  10,  ITS_Read_Blockdata,  &ITS_Read_Size);  psa:its_remove(ITS_Block);

Exercise: Internal trusted storage

In this exercise, we will use the Internal Trusted Storage to store and retrieve a block of data.

  • Locate Exercise 13.2 in the pack installer and press the copy button.
  • Make the nonsecure project the active project.
  • Batch build both projects and start the debugger.
  • Open the RTE and the TFM::Secure Service branch.

The Internal Trusted Storage API is enabled and defaults to an encrypted volume with rollback protection.

  • In the secure project, open TFM::tfm_config.h
  • Examine the configuration options for the Internal Trusted Storage volume (Fig. 13.3).
    Fig. 13.3
    Fig. 13.3 The secure code allows you to configure the ITS volume size and features.

Like the protected storage, we can define the maximum storage size for each volume of data and the number of items that can be stored in the volume. We can also access the FLASH storage volume by selecting the configured CMSIS-FLASH driver.

  • In the nonsecure project, open app_main.c and examine the code.

The app-main thread uses each of the Internal Trusted Storage functions to save retrieve and extend blocks of data into the secure FLASH volume.

  • Run the code to store and retrieve data in the PSA internal Trusted storage.

Cryptography service

In the cryptography chapters, we saw how to use different algorithms by creating projects with a flat memory model that can be used with any Arm-based microcontroller. With the introduction of TrustZone, in order to create a secure application, we need to place the low-level cryptographic algorithms and their associated secrets in the secure partition. To solve this problem, a separate library called mbedCrypto has been created. The mbedCrypto library provides a set of abstraction layers for each group of algorithms. This allows us to place the sensitive functions in the Secure partition while the mbedTLS code responsible for the TLS protocol can remain in the Non-Secure partition (Fig. 13.4). It is likely that more functionality within mbedTLS will migrate to mbedCrypto over time.

Fig. 13.4
Fig. 13.4 mbedTls and mbedCrypto. mbedCrypto provides a secure cryptography service to mbedTLS. No permission required.

The mbedCrypto library provides abstraction layers for the following groups of algorithms (Table 13.3):

Table 13.3

mbedCrypto abstraction layers.
Abstraction layer
Message DIGEST
Message authentication codes
Symmetric cipher
Key derivation
Random number
Key management
Asymmetric encryption
Hash and sign
Key agreement

Multipart operation

The symmetric cipher, MAC, and message digest abstraction layers may be used with a single block of data or are capable of working with large blocks of data or streaming data. A multipart operation consists of an initial setup function followed by a call to an update function. Once the data have been processed, the session can be terminated, and if appropriate, a final result can be calculated with a finish function.

To access the mbedCrypto functions, you must include the header “psa/crypto.h”.

Random

The mbedCrypto service provides a random function that are used to provide keying material and streams of random values (Table 13.4).

Table 13.4

mbedCrypto random function.
FunctionDescription
psa:generate_randomReturn a buffer of random data

We can fill a local buffer with random data as follows.

   uint8_t random[10] ;   status = psa:generate_random(random, sizeof(random));

Key management

In addition to the core cryptographic algorithms, mbedCrypto provides a rich API to create and manage encryption keys. By default, the Internal Trusted Storage is used to hold the encryption keys, but the API can be modified to target a secure key store provided by the underlying microcontroller. When using the key management API, the Non-Secure code and other security services reference the keys by identifiers, which are defined during the key generation process. When created, a key may be given a range of attributes and usage policies that allow it to be used by the Non-Secure code and/or other secure services. Each key is given a lifetime, which allows it to be persistent and be stored in the ITS until it is destroyed, or it may be a volatile key that has a lifetime limited to the current session. We can also control if a key is allowed to leave the Secure World by defining it as extractable or nonextractable. As an example, both a public and private key may be held in the Internal Trusted Storage. The private key would be labeled as nonextractable, while the public key would be defined as extractable. Once a key has been generated, an additional set of functions are used to manage its lifecycle, as shown in Table 13.5.

Table 13.5

mbedCrypto key management API.
FunctionDescription
psa:import_keyImport a key in binary format
psa:generate_keyGenerate a key or key pair
psa:copy_keyMake a copy of a key
psa:purge_keyRemove nonessential copies of key material from memory
psa:destroy_keyDestroy a key

The capabilities of a key are stored in a structure psa:key_attributes_t. This structure contains a set of elements that define the following features (Table 13.6).

Table 13.6

mbedCrypto key attributes.
AttributeDescription
IDThe Key ID
LifetimeWhether the key is persistent or volatile
TypeThe key type and its algorithm
KeysizeThe key size in bits
Usage flagsAllowed operations
AlgorithmThe algorithms that may be used by the key
LocationIf available, you can select additional storage volumes

When a key is created, we pass a unique integer value of type psa:key_id_t. This becomes the key ID that is used by the Non-Secure code to reference the key for all future operations. When a key is defined, we must also specify its usage policy as a set of flags (Table 13.7), which algorithms it can be used with and its bit size.

Table 13.7

mbedCrypto key usage flags.
Usage flagDescription
PSA_KEY_USAGE_EXPORTPermission to export the key
PSA_KEY_USAGE_COPYPermission to copy the key
PSA_KEY_USAGE_CACHEPermission for the implementation to cache the key
PSA_KEY_USAGE_ENCRYPTPermission to encrypt a message with the key
PSA_KEY_USAGE_DECRYPTPermission to decrypt a message with the key
PSA_KEY_USAGE_SIGN_MESSAGEPermission to sign a message with the key
PSA_KEY_USAGE_VERIFY_MESSAGEPermission to verify a message signature with the key
PSA_KEY_USAGE_SIGN_HASHPermission to sign a message hash with the key
PSA_KEY_USAGE_VERIFY_HASHPermission to verify a message hash with the key
PSA_KEY_USAGE_DERIVEPermission to derive other keys from this key

The Non-Secure code does not have access to the key and its attributes directly but can manage the key, and its attributes through a set of helper functions (Table 13.8). We again define the range of algorithms the key can be used for. Although this is already defined in key type, this second entry defines additional information necessary to use the algorithm, such as padding type.

Table 13.8

mbedCrypto attribute helper functions.
FunctionDescription
psa:set_key_idDeclare a key as persistent and set its key identifier
psa:get_key_idRetrieve the key identifier from key attributes
psa:set_key_lifetimeSet the lifetime of a persistent key
psa:get_key_lifetimeGet the lifetime of a persistent key
psa:set_key_usage_flagsDeclare usage flags for a key
psa:get_key_usage_flagsRetrieve the usage flags from key attributes
psa:set_key_algorithmDeclare the permitted algorithm policy for a key
psa:get_key_algorithmRetrieve the algorithm policy from key attributes
psa:set_key_typeDeclare the type of a key
psa:get_key_typeRetrieve the key type from key attributes
psa:set_key_bitsDeclare the size of a key
psa:get_key_bitsRetrieve the key size from key attributes

The PSA crypto library also provides a set of high-level functions, which can be used to initialize, read, and reset the key attributes as a whole (Table 13.9). The reset function should be called after any operations on the key to wipe the Non-Secure attribute memory.

Table 13.9

mbedCrypto key attribute functions.
FunctionDescription
psa:key_attributes_initReturn an initial value for a key attribute object
psa:get_key_attributesRetrieve the attributes of a key
psa:reset_key_attributesWipe the key attribute object to a freshly initialized state

Once a key has been created, it is stored in the Internal Trusted Storage. Within the Non-Secure code, it is then referenced by its ID. We can now use these functions to create a set of keys and store them in the ITS. First, declare and initialize the attributes object and the key ID:

   psa:key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;   psa:key_id_ttest_key_id;

To manage our key, we can create a key ID value:

          #define TEST_KEY_ID (psa:key_id_t) 1

This is stored in the attributes along with other the key parameters and policies.

   psa:set_key_id(&attributes,TEST_KEY_ID)   psa:set_key_usage_flags(&attributes,PSA_KEY_USAGE_ENCRYPT         PSA_KEY_USAGE_DECRYPT );
   psa:set_key_algorithm(&attributes, 0);   psa:set_key_type(&attributes, PSA_KEY_TYPE_AES);   psa:set_key_bits(&attributes, 128);   psa:set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT);

Now we can store an existing key with psa:key_import() or generate a symmetrical key using a random process in the secure service.

          status = psa:generate_key(&attributes, &test_key_id);

Once the key has been generated, the returned test_key_id will equal original define TEST_KEY_ID.

Once we have a set of keys, we can start to use the mbedCrypto algorithms.

Key derivation

For some algorithms, we will need to create ephemeral session keys. The mbedCrypto library provides a set of key derivation functions to manage these operations (Table 13.10).

Table 13.10

mbedCrypto key derivation functions.
FunctionDescription
psa:key_derivation_operation_initReturn an initial value for a key derivation operation object
psa:key_derivation_setupSet up a key derivation operation
psa:key_derivation_get_capacityRetrieve the current capacity of a key derivation operation
psa:key_derivation_set_capacitySet the maximum number of bytes the derivation can return
psa:key_derivation_input_bytesProvide an input for key derivation or key agreement
psa:key_derivation_input_keyProvide an input for key derivation in the form of a key
psa:key_derivation_key_agreementPerform a key agreement using a shared secret as input
psa:key_derivation_output_bytesRead some data from a key derivation operation
psa:key_derivation_output_keyDerive a key from an ongoing key derivation operation
psa:key_derivation_abortAbort a key derivation operation
psa:raw_key_agreementPerform a key agreement and return the raw shared secret

First create and initialize the key objects:

   psa:key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;   psa:key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT;

We must also create a salt to prevent lookup attacks:

          const unsigned char salt[] = {0x1,0x2,0x3,0x4};

We can also select the underlying algorithm used for the derivation:

          psa:algorithm_t alg = PSA_ALG_HKDF(PSA_ALG_SHA_256);

then define the characteristics of the required key:

   size_t derived_bits = 128;   size_t capacity = PSA_BITS_TO_BYTES(derived_bits);

and then define the id’s used to manage an existing and derived key:

   psa:key_id_ttest_key_aes = 1;   psa:key_id_t derived_key;

To derive the key, we can setup the key derivation algorithm and its bit size capacity:

   status = psa:key_derivation_setup(&operation, alg);   status = psa:key_derivation_set_capacity(&operation, capacity);

Then, add the keying material:

   status = psa:key_derivation_input_bytes(&operation,         PSA_KEY_DERIVATION_INPUT_SALTsalt, sizeof(salt));   status = psa:key_derivation_input_key(&operation,         PSA_KEY_DERIVATION_INPUT_SECRET, test_key_aes);   status = psa:key_derivation_input_bytes(&operation,         PSA_KEY_DERIVATION_INPUT_INFO, info,sizeof(info));

Then, define the key attributes:

    psa:set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT);    psa:set_key_algorithm(&attributes, PSA_ALG_CTR);    psa:set_key_type(&attributes, PSA_KEY_TYPE_AES);    psa:set_key_bits(&attributes, 128);

Finally, derive the key:

    status = psa:key_derivation_output_key(&attributes,&operation,&derived_key);

Symmetrical ciphers

The mbedCrypto service provides an abstraction layer for any symmetrical ciphers that have been enabled in the secure service (Table 13.11).

Table 13.11

mbedCrypto symmetrical cipher abstraction layer.
FunctionDescription
psa:cipher_encryptEncrypt a message using a symmetric cipher
psa:cipher_decryptDecrypt a message using a symmetric cipher
psa:cipher_operation_initReturn an initial value for a cipher operation object
psa:cipher_encrypt_setupSet the key for a multipart symmetric encryption operation
psa:cipher_decrypt_setupSet the key for a multipart symmetric decryption operation
psa:cipher_generate_ivGenerate an Initialization Vector (IV)
psa:cipher_set_ivSet the Initialization Vector (IV)
psa:cipher_updateEncrypt or decrypt a message fragment in an active cipher operation
psa:cipher_finishFinish encrypting or decrypting a message in a cipher operation
psa:cipher_abortAbort a cipher operation

To use a symmetrical cipher, we can first setup the attributes objects and define the chaining method:

   psa:status_t status;psa:key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;psa:algorithm_t alg = PSA_ALG_CBC_NO_PADDING;uint8_t plaintext[block_size] = “Hello World”;   uint8_t iv[block_size];   size_t iv_len;   uint8_t output[block_size];   size_t output_len;   psa:key_handle_t handle;   psa:cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT;

Now we can open a test key and setup the cipher for multipart operation:

          status = psa:cipher_encrypt_setup(&operation,test_key_aes, alg);

Before the first packet of plaintext, we must generate an IV:

   status = psa:cipher_generate_iv(&operation,iv,         sizeof(iv),         &iv_len);

Then we can iterate through the data:

   status = psa:cipher_update(&operation,plaintext,         sizeof(plaintext),         output,         sizeof(output),         &output_len);

Until we finish with the last packet:

   status = psa:cipher_finish(&operation,output + output_len,         sizeof(output) - output_len,         &output_len);

Authenticated encryption with associated data

The AEAD algorithm is also provided as a set of API functions (Table 13.12).

Table 13.12

mbedCrypto AEAD abstraction layer.
FunctionDescription
psa:aead_encryptProcess an authenticated encryption operation
psa:aead_decryptProcess an authenticated decryption operation
psa:aead_operation_initReturn an initial value for an AEAD operation object
psa:aead_encrypt_setupSet the key for a multipart authenticated encryption operation
psa:aead_decrypt_setupSet the key for a multipart authenticated decryption operation
psa:aead_generate_nonceGenerate a random nonce for an authenticated encryption operation
psa:aead_set_nonceSet the NONCE for an authenticated encryption or decryption operation
psa:aead_set_lengthsDeclare the lengths of the message and additional data for AEAD
psa:aead_update_adPass additional data to an active AEAD operation
psa:aead_updateEncrypt or decrypt a message fragment in an active AEAD operation
psa:aead_finishFinish encrypting a message in an AEAD operation
psa:aead_verifyFinish authenticating and decrypting a message in an AEAD operation
psa:aead_abortAbort an AEAD operation

We can use the AEAD algorithm to perform a simple encryption of a block of data. Here, we must provide the key ID, a NONCE, which must be unique for each session, the unencrypted associated data, and the input data, which will be encrypted:

   status = psa:aead_encrypt(test_key_aead,PSA_ALG_CCM,         nonce,         sizeof(nonce),         additional_data,         sizeof(additional_data),         input_data,         sizeof(input_data),         output_data,         output_size,         &output_length);

The same key and NONCE is used in the decryption function.

   status = psa:aead_decrypt(test_key_aead,PSA_ALG_CCM,         nonce,         sizeof(nonce),         additional_data,         sizeof(additional_data),         input_data,         sizeof(input_data),         output_data,         output_size,         &output_length);

We can also perform a multipart operation in a similar fashion to the symmetrical cipher above.

Message digest

The mbedCrypto library also provides an abstraction layer for the mbedTLS hashing algorithms (Table 13.13).

Table 13.13

mbedCrypto message digest abstraction layer.
FunctionDescription
psa:hash_computeCalculate the hash (digest) of a message
psa:hash_compareCalculate the hash (digest) of a message and compare it with a reference value
psa:hash_operation_initReturn an initial value for a hash operation object
psa:hash_setupSet up a multipart hash operation
psa:hash_updateAdd a message fragment to a multipart hash operation
psa:hash_finishFinish the calculation of the hash of a message
psa:hash_verifyFinish the calculation of the hash of a message and compare it with an expected value
psa:hash_abortAbort a hash operation
psa:hash_suspendHalt the hash operation and extract the intermediate state of the hash computation
psa:hash_resumeSet up a multipart hash operation using the hash suspend state from a previously suspended hash operation
psa:hash_cloneCopy the state of an ongoing hash operation to a new operation object

We can perform a multipart hashing operation by first setting up the hash operation with the selected hash algorithm:

          status = psa:hash_setup(&operation, alg);

Then we can apply packets of the data:

          status = psa:hash_update(&operation, input, sizeof(input));

Until we reach the end of the data block when we can generate the final hash:

   status = psa:hash_finish(&operation,actual_hash,         sizeof(actual_hash),         &actual_hash_len);

Or verify against an existing hash:

   status = psa:hash_verify(&operation,expected_hash,         expected_hash_len);

Message authentication codes (MAC)

A set of MAC functions are also available. The base hashing algorithm can be selected at the start of the MAC operation (Table 13.14).

Table 13.14

mbedCrypto MAC abstraction layer.
FunctionDescription
psa:mac_computeCalculate the Message Authentication Code (MAC) of a message
psa:mac_verifyCalculate the MAC of a message and compare it with a reference value
psa:mac_sign_setupSet up a multipart MAC calculation operation
psa:mac_verify_setupSet up a multipart MAC verification operation
psa:mac_updateAdd a message fragment to a multipart MAC operation
psa:mac_sign_finishFinish the calculation of the MAC of a message
psa:mac_verify_finishFinish the MAC calculation and compare it with an expected value
psa:mac_abortAbort a MAC operation

Like the hash algorithms, we can MAC a single packet of data or setup a multipart operation.

   psa:mac_compute(test_key_mac,PSA_ALG_HMAC(PSA_ALG_SHA_256),         test_buffer,sizeof(test_buffer),         &mac,mac_size,&mac_len);   psa:mac_verify(test_key_mac,PSA_ALG_HMAC(PSA_ALG_SHA_256),         test_buffer,sizeof(test_buffer),         &mac,mac_len);

Asymmetric signing and encryption

The mbedCrypto library also supports a range of asymmetric algorithms for encryptiondecryption and signing of data (Table 13.15). The abstraction layer supports both the RSA cryptosystem and the Diffie-Hellman key agreement algorithm. Signing can also be accomplished with RSA, DSA, or ECDSA.

Table 13.15

mbedCrypto Asymmetric Signing and Encryption abstraction layer.
FunctionDescription
psa:asymmetric_encryptEncrypt a short message with a public key
psa:asymmetric_decryptDecrypt a short message with a private key
psa:sign_messageHash and sign a message with a private key
psa:verify_messageVerify the signature and hash of a message with a public key
psa:sign_hashSign an already-calculated hash with a private key
psa:verify_hashVerify the signature of a hash or short message using a public key

Here we can sign a message using an already calculated hash. The key will define the algorithm though we must also specify a padding method:

   status = psa:sign_hash (pk_handle,         PSA_ALG_RSA_PKCS1V15_SIGN_RAW,         hash,         sizeof(hash),         signature,         sizeof(signature),         &signature_length);

Key agreement

The Key Agreement API allows us to calculate a shared secret by combining our key with the public key from a peer (Table 13.16). We can also calculate the secret and derive a symmetric encryption key with a single API call.

Table 13.16

mbedCrypto key agreement abstraction layer.
FunctionDescription
psa:raw_key_agreementPerform a key agreement and return the raw shared secret
psa:key_derivation_key_agreementPerform a key agreement and use the shared secret as input to a key derivation

Once we exchange public keys with a peer, it is possible to calculate a shared secret.

   psa:raw_key_agreement(PSA_ALG_ECDH,private_key,         &peer_key,         peer_key_length,         &output,         output_size,         &output_length);

Exercise: Cryptography service

The mbedCrypto Service is designed to run in the secure partition and provide essential cryptography algorithms to the nonsecure code. Mbed Crypto also provides a key storage and management API that allows secrets to be placed within the ITS where they can be referenced by the nonsecure code through ID’s. Additionally, the keys may be bound to a specific service and function.

  • Locate Exercise 13.3 in the pack installer and press the copy button.
  • Make the nonsecure project the active project.
  • Batch build both projects and start the debugger.
  • Open the RTE and the TFM::Secure Service branch.

Here the nonsecure crypto service is enabled. This adds the client and the mbedCrypto.h header file.

In the secure project open TFM::tfm_config.h (Fig. 13.5).

Fig. 13.5
Fig. 13.5 The secure code allows us to define the cryptography algorithms available to the nonsecure code.

This allows us to define how much memory is available to the cryptography service along with the size of the IO vectors used to pass data to and from the service. We can also disable unwanted algorithms.

  • Step through each of the following functions to exercise the cryptography algorithms provided by the mbedCrypto service. These are the same algorithms that we explored inChapters 45.
  •                     mbedCrypto_symmetric_key_management();
  •                     mbedCrypto_asymmetric_key_management();
  •                     mbedCrypto_hash();
  •                     mbedCrypto_asymmetric_cipher();
  •                     mbedCrypto_symmetrical_cipher();
  •                     mbedCrypto_AEAD();

The mbedCrypto service acts as an Oracle where the nonsecure code can request a cryptography service without any knowledge of the encryption key.

  • Batch build both projects.
  • Start the debugger and step through the code to see the mbedCrypto API in action.

Attestation

An attestation service is used to create a token that is used to prove its identity and capabilities. The token can then be used to access resources on a server in place of a formal login. Existing web token schemes based on CBOR and JSON encoding are currently available to access HTTPS resources. However, these schemes do not fully meet the requirements of an IoT device. A new attestation token scheme called Entity Attestation Token (EAT) is used within the PSA Trusted firmware, which provides sufficient flexibility for IoT devices.

An Entity Attestation Token is a means for an IoT device to identify itself to a server and describe its characteristics as a set of security claims. A token is a blob of binary data, which is signed using the ECDSA algorithm to provide a guarantee of authentication. Typically a token will be encoded using CBOR and then encrypted and signed. While a token will generally carry fixed security claims, it may also be used to transfer a limited amount of process data. A good example of this would be “Number of units consumed” in a metering application.

Attestation token

A token will encode a number of security claims that are used to describe its origin and capabilities to the server. The Internet Assigned Numbers Authority maintains a registry of claims which may be used as templates for any given application. If a suitable token is not available, you can create your own security claims and token description. The format shown below is currently used (Table 13.17).

Table 13.17

Attestation token header claims.
Attestation fieldsDescription
Boot seedA 32bit random value generated at startup
ChallengeA block of user supplied data
Implementation IDThe device PSA implementation ID
Instance IDThe device Instance ID
Profile IDProfile definition, currently defaults to PSA_IOT_PROFILE_1
Security lifecycleThe device current lifecycle state
Measurement descriptionThe Hash Algorithm used for the SW component measurements
SW componentsA set of claims for each Software component

A claim is then added for each software component, typically this will be the Application Image, Trusted Firmware, and the PSA bootloader. Each claim is made up of the measurements shown in Table 13.18.

Table 13.18

Software component attestation claim.
SW component fieldDescription
Measurement valueHash of the SW component
Signer IDID of the signing entity
SW componentName of the SW component
SW COMPONENT versionThe Version number in Major.Minor format

The token data will be encoded as a CBOR object and then will be encrypted and signed in a “CBOR Object Signing and Encryption” COSE format.

Attestation infrastructure

Alongside the token, a device will create an ECDSA key pair. The private key will be stored in the device itself, and the public key will be stored in a database that is part of an attestation server (Fig. 13.6).

Fig. 13.6
Fig. 13.6 Attestation key storage. The Initial Attestation private key is stored in the device while the public half is stored in a verification server. No permission required.

The attestation server may be a public service or a private part of your system. When an IoT device wishes to communicate with a server, it first sends its token to the server. The server does not read the token but blindly passes the token to the attestation verification service for authentication using the instance ID to identify the matching public key. The attestation server will return a validation result to the server. If the token is found to be valid, the server can examine the claims made in the token and make a decision to grant the service request made by the IoT device (Fig. 13.7).

Fig. 13.7
Fig. 13.7 Attestation infrastructure. An attestation token will pass through a cloud server to a verification service. The relying cloud service can then decide if it wants to onboard the device. No permission required.

The header file “psa/initial_attestation.h” provides the Non-Secure code with an API to access the Attestation token and public key. The available functions are shown in Table 13.19.

Table 13.19

Non Secure attestation API.
FunctionDescription
psa:initial_attest_get_tokenRetrieve the Initial Attestation Token
psa:initial_attest_get_token_sizeCalculate the size of an Initial Attestation Token
tfm_initial_attest_get_public_keyGet the attestation public key and associated elliptic curve

The Non-Secure code can create a token by providing a block of challenge data and then requesting the token.

   #define TEST_TOKEN_SIZE (0x200)   #define TEST_CHALLENGE_OBJ_SIZE (32u)   static uint8_t token_buffer[TEST_TOKEN_SIZE];   static uint8_t challenge_buffer[TEST_CHALLENGE_OBJ_SIZE] =   {//32 bytes of user data};   uint32_t token_size;   attest_err = psa:initial_attest_get_token_size(TEST_CHALLENGE_OBJ_SIZE,         &token_size);   attest_err = psa:initial_attest_get_token(challenge_buffer,         TEST_CHALLENGE_OBJ_SIZE,         token_buffer,         &token_size);

Exercise: Attestation token

In this exercise, we will create and validate an Entity Attestation Token (EAT).

  • Locate Exercise 13.4 in the pack installer and press the copy button
  • Make the nonsecure project the active project
  • Batch build both projects and start the debugger
  • Open the RTE and the TFM::Secure Service branch

The Attestation API is enabled along with the crypto service

  • In the secure project, open TFM::tfm_config.c (Fig. 13.8).
    Fig. 13.8
    Fig. 13.8 The attestation options allow you to add custom ‘claims’ and a key ID for the verification server.

The asymmetric key ID is the SHA256 hash of the attestation public key. This is used by the attestation verification service to retrieve the correct public key from its database.

The “Include Optional Claims” selection will be removed from the final release.

  • In the nonsecure project open app_main().

The attestation API is used to first get the size of a token and then generate the token before storing it onto the SD card. To generate the token we provide a challenge in the form of a block of random data. The challenge is included within the token to prove it has been freshly created.

  • Run the code to generate the token and store it on the SD card.

We can now validate the token using a set of Python scripts provided by the TFM component pack.

  • Open the following directory in a DOS command window.

<PATH>Keil_v5ARMPACKARMTFM<version> oolsiat-verifier

  • Install the necessary Python modules with the following command.

pip3 install . (note there is a dot (.) at the end of this command)

  • Now validate the token using the following command.

python check_iat <path>/token.cbr

This will give a pass or fail on the Token format.

  • We can also check the signature with the following command.

check_iat -k <path>/key.pem <path>/token.cbr

  • Finally we can also decompile the token.

decompile_token -k <path>/key.pem <path>/token.cbr

Auditing

The PSA audit functions provides a way for the secure code to write audit log messages, which can be retrieved by the Non-Secure code.

Secure partition

The secure services and partition manager can write messages to the audit log using the API shown in Table 13.20.

Table 13.20

Secure audit functions.
FunctionDescription
psa:audit_add_recordSecure only Add a record to the audit log

Non-secure partition

The Non-Secure code can monitor and read the audit log (Table 13.21) using the header file “psa/initial_audit_api.h”.

Table 13.21

Non-Secure audit functions.
FunctionDescription
psa:audit_retrieve_recordRetrieves a record at the specified index
psa:audit_get_infoReturns the total number and size of the records stored
psa:audit_get_record_infoReturns the size of the record at the specified index
psa:audit_delete_recordDeletes a record at the specified index

The Non-Secure code can read the audit log using this simple API.

   psa:audit_get_info(numRecords,totalSize);   for(n  =  0;n  <  numRecords;n  ++){   psa:audit_get_record_info(n,&curRecSize);   psa:audit_retrieve_record(n,sizeof(auditBuf),NULL,0,&curRecSize);   printf(“%s”, auditBuf);   psa:audit_delete_record(n);   }

Exercise: Audit

The audit log is generated secure partition and accessed by the nonsecure code. The nonsecure audit client provides a minimal API to manage the audit records.

  • Locate Exercise 13.5 in the pack installer and press the copy button.
  • Make the nonsecure project the active project.
  • Batch build both projects and start the debugger.
  • Open the RTE and the TFM::Secure Service branch.
  • Check that the audit logging client is enabled.

In the secure project the Audit logging is enabled in the RTE and currently has no further configuration options.

The client API provides functions to get the total number of audit records their total stored size and the size of a particular record.

psa:audit_get_info(&num_records, &stored_size);  psa:audit_get_record_info(0, &recordSize);

We can then read and delete a specific record.

psa:audit_retrieve_record(0,//record index  LOCAL_BUFFER_SIZE,//buffer size  0,//challenge token currently NULL  0,//challenge token size  &auditBuffer[0],//record buffer  &recordSize);//record buffer size
  • Build the project and start the debugger.
  • Step through the code in app_main() to see the audit API in action.

Lifecycle

Finally the Non-Secure code can request the current lifecycle state using the API call shown in Table 13.22 and provided by the header “psa/lifecycle.h”.

Table 13.22

Lifecycle API.
FunctionDescription
psa:rot_lifecycle_stateReturns the current lifecycle state of the PSA RoT

The lifecycle state is returned in bits [15:8] while bits [7:0] are reserved for future implementation defined states.

Provisioning

Once the TF-M software has been installed onto the Secure partition, it must be provisioned with all the necessary device secrets so that it fully enters into its operational lifecycle. This can be achieved during manufacture by installing a provisioning program into the NSPE. The provisioning program is used to download the necessary secrets into the SPE and the TF-M ITS storage. Table 13.23 shows the range of the TF-M device intimate data.

Table 13.23

Trusted Firmware provisioning requirements.
SecretTypeStorage
Hardware unique keySymmetric keyITS or device keystore
Initial attestation keyAsymmetric private key or symmetric keyITS or device keystore
Instance IDHash of initial attestation keyITS or device keystore
Implementation ID32Byte Platform IDImage
Hardware IDEANImage
TLS CA certX.509PS or Image
TLS device certX.509PS or Image
TLS device private keyAsymmetric private keyITS or device keystore
RoT keyAsymmetric public keyITS or device keystore
Device secure bootX.509 public keyDevice specific
Device debugX.509 public keyDevice Specific

Conclusion

In this chapter, we have seen how to communicate between the Application in the NSPE and the TF-M platform in the SPE. We have also seen the range of available services within the secure partitions and how to provision the TF-M for its operational lifetime. In the next chapter, we will look at adding the Second Stage Bootloader (BL2), which will allow us to perform essential updates over the lifetime of the device.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.239.76.211