Skip to content

Commit

Permalink
tfm: Move TF-M attestation data to provisioned OTP region
Browse files Browse the repository at this point in the history
Optional fields to TF-M attestation were previously stored in
tfm_otp_nv_counters region, which we were not able to provision.
This moves the psa_certification_reference to the provisioned
OTP-region and adds support for accessing the variable data in
bl_storage.h.

Verification service URL and profile may change with device
upgrades, for this reason they are added as Kconfigs.

Note that we still need to keep the tfm_otp_nv_counters region
when TFM_PARTITION_PROTECTED_STORAGE and
TFM_PS_ROLLBACK_PROTECTION are enabled. TF-M will increase
monotonic counters every time new data is written and given the
limited size of our OTP-region it would not support many updates.

NCSDK-17932

Signed-off-by: Markus Lassila <[email protected]>
  • Loading branch information
MarkusLassila committed Dec 20, 2024
1 parent 2f7cd3e commit 2cf35a2
Show file tree
Hide file tree
Showing 15 changed files with 437 additions and 129 deletions.
6 changes: 6 additions & 0 deletions cmake/sysbuild/provision_hex.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ function(provision application prefix_name)
set(mcuboot_counters_slots --mcuboot-counters-slots ${SB_CONFIG_MCUBOOT_HW_DOWNGRADE_PREVENTION_COUNTER_SLOTS})
endif()

if(SB_CONFIG_TFM_OTP_PSA_CERTIFICATE_REFERENCE AND SB_CONFIG_TFM_PSA_CERTIFICATE_REFERENCE_VALUE)
set(psa_certificate_reference --psa-certificate-reference ${SB_CONFIG_TFM_PSA_CERTIFICATE_REFERENCE_VALUE})
endif()

if(CONFIG_SECURE_BOOT)
add_custom_command(
OUTPUT
Expand All @@ -103,6 +107,7 @@ function(provision application prefix_name)
${monotonic_counter_arg}
${no_verify_hashes_arg}
${mcuboot_counters_slots}
${psa_certificate_reference}
--lcs-state-size ${lcs_state_struct_size}
DEPENDS
${PROVISION_KEY_DEPENDS}
Expand All @@ -126,6 +131,7 @@ function(provision application prefix_name)
--max-size ${CONFIG_PM_PARTITION_SIZE_PROVISION}
${mcuboot_counters_num}
${mcuboot_counters_slots}
${psa_certificate_reference}
--lcs-state-size ${lcs_state_struct_size}
DEPENDS
${PROVISION_KEY_DEPENDS}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,8 +369,13 @@ SUIT samples
Trusted Firmware-M (TF-M) samples
---------------------------------

|no_changes_yet_note|
* :ref:`tfm_psa_template` sample:

* Added support for the following attestation token fields:

* Profile definition
* PSA certificate reference (optional), configured using the :kconfig:option:`SB_CONFIG_TFM_OTP_PSA_CERTIFICATE_REFERENCE` sysbuild Kconfig option
* Verification service URL (optional), configured using the :kconfig:option:`CONFIG_TFM_ATTEST_VERIFICATION_SERVICE_URL` Kconfig option

Thread samples
--------------
Expand Down
92 changes: 83 additions & 9 deletions include/bl_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ typedef uint32_t lcs_reserved_t;
/* We truncate the 32 byte sha256 down to 16 bytes before storing it */
#define SB_PUBLIC_KEY_HASH_LEN 16

/* Supported collection types. */
enum collection_type {
BL_COLLECTION_TYPE_MONOTONIC_COUNTERS = 1,
BL_COLLECTION_TYPE_VARIABLE_DATA = 0x9312,
};

/* Counter used by NSIB to check the firmware version */
#define BL_MONOTONIC_COUNTERS_DESC_NSIB 0x1

Expand Down Expand Up @@ -88,23 +94,50 @@ struct monotonic_counter {
uint16_t description;
/* Number of entries in 'counter_slots' list. */
uint16_t num_counter_slots;
counter_t counter_slots[1];
counter_t counter_slots[];
};

/** Common part for all collections. */
struct collection {
uint16_t type;
uint16_t count;
};

/** The second data structure in the provision page. It has unknown length since
* 'counters' is repeated. Note that each entry in counters also has unknown
* length, and each entry can have different length from the others, so the
* entries beyond the first cannot be accessed via array indices.
* entries beyond the first cannot be accessed through array indices.
*/
struct counter_collection {
uint16_t type; /* Must be "monotonic counter". */
uint16_t num_counters; /* Number of entries in 'counters' list. */
struct monotonic_counter counters[1];
struct collection collection; /* Type must be BL_COLLECTION_TYPE_MONOTONIC_COUNTERS */
struct monotonic_counter counters[];
};

/* Variable data types. */
enum variable_data_type {
BL_VARIABLE_DATA_TYPE_PSA_CERTIFICATION_REFERENCE = 0x1
};
struct variable_data {
uint8_t type;
uint8_t length;
uint8_t data[];
};

/* The third data structure in the provision page. It has unknown length since
* 'variable_data' is repeated. The collection starts immediately after the
* counter collection. As the counter collection has unknown length, the start
* of the variable data collection must be calculated dynamically. Similarly,
* the entries in the variable data collection have unknown length, so they
* cannot be accessed through array indices.
*/
struct variable_data_collection {
struct collection collection; /* Type must be BL_COLLECTION_TYPE_VARIABLE_DATA */
struct variable_data variable_data[];
};

/** The first data structure in the bootloader storage. It has unknown length
* since 'key_data' is repeated. This data structure is immediately followed by
* struct counter_collection.
* struct counter_collection, which is then followed by struct variable_data_collection.
*/
struct bl_storage_data {
/* NB: When placed in OTP, reads must be 4 bytes and 4 byte aligned */
Expand All @@ -116,7 +149,28 @@ struct bl_storage_data {
struct {
uint32_t valid;
uint8_t hash[SB_PUBLIC_KEY_HASH_LEN];
} key_data[1];
} key_data[];

/* Monotonic counter collection:
* uint16_t type;
* uint16_t count;
* struct {
* uint16_t description;
* uint16_t num_counter_slots;
* uint16_t counter_slots[];
* } counters[];
*/

/* Variable data collection:
* uint16_t type;
* uint16_t count;
* struct {
* uint8_t type;
* uint8_t length;
* uint8_t data[];
* } variable_data[];
* uint8_t padding[]; // Padding to align to 4 bytes
*/
};

#define BL_STORAGE ((const volatile struct bl_storage_data *)(PM_PROVISION_ADDRESS))
Expand Down Expand Up @@ -150,7 +204,7 @@ uint32_t s1_address_read(void);
uint32_t num_public_keys_read(void);

/**
* @brief Function for reading number of public key data slots.
* @brief Function for verifying public keys.
*
* @retval 0 if all keys are ok.
* @retval -EHASHFF if one or more keys contains an aligned 0xFFFF.
Expand Down Expand Up @@ -259,12 +313,32 @@ int update_life_cycle_state(enum lcs next_lcs);
#if CONFIG_BUILD_WITH_TFM

/**
* Read the implementation id from OTP and copy it into a given buffer.
* Read the implementation ID from OTP and copy it into a given buffer.
*
* @param[out] buf Buffer that has at least BL_STORAGE_IMPLEMENTATION_ID_SIZE bytes
*/
void read_implementation_id_from_otp(uint8_t *buf);

/**
* @brief Read variable data from OTP.
*
* Variable data starts with variable data collection ID, followed by amount of variable data
* entries and the variable data entries themselves.
* [Collection ID][Variable count][Type][Variable data length][Variable data][Type]...
* 2 bytes 2 bytes 1 byte 1 byte 0-255 bytes
*
* @note If data is not found, function does not fail. Instead, 0 length is returned.
*
* @param[in] data_type Type of the variable data to read.
* @param[out] buf Buffer to store the variable data.
* @param[in,out] buf_len On input, the size of the buffer. On output, the length of the data.
*
* @retval 0 Variable data read successfully, or not found.
* @retval -EINVAL No buffer provided.
* @retval -ENOMEM Buffer too small.
*/
int read_variable_data(enum variable_data_type data_type, uint8_t *buf, uint32_t *buf_len);

#endif

/** @} */
Expand Down
16 changes: 14 additions & 2 deletions modules/trusted-firmware-m/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ set_property(TARGET zephyr_property_target
-DCRYPTO_HW_ACCELERATOR=True
)

set_property(TARGET zephyr_property_target
APPEND PROPERTY TFM_CMAKE_OPTIONS -DPLATFORM_DEFAULT_NV_SEED=OFF
)

if(CONFIG_TFM_ALLOW_NON_SECURE_FAULT_HANDLING)
set_property(TARGET zephyr_property_target
APPEND PROPERTY TFM_CMAKE_OPTIONS
Expand All @@ -187,13 +191,21 @@ if(CONFIG_TFM_PROFILE_TYPE_MINIMAL)
APPEND PROPERTY TFM_CMAKE_OPTIONS
-DPLATFORM_DEFAULT_ROTPK=OFF
-DPLATFORM_DEFAULT_IAK=OFF
-DPLATFORM_DEFAULT_NV_SEED=OFF
-DPLATFORM_DEFAULT_OTP=OFF
-DPLATFORM_DEFAULT_OTP_WRITEABLE=OFF
-DPLATFORM_DEFAULT_NV_COUNTERS=OFF
)
endif()

if(CONFIG_TFM_PLATFORM_NV_COUNTER_MODULE_DISABLED)
set_property(TARGET zephyr_property_target
APPEND PROPERTY TFM_CMAKE_OPTIONS
-DPLATFORM_DEFAULT_OTP=OFF
-DPLATFORM_DEFAULT_OTP_WRITEABLE=OFF
-DPLATFORM_DEFAULT_NV_COUNTERS=OFF
)
endif()

if(NOT CONFIG_MBEDTLS_PSA_CRYPTO_STORAGE_C)
# Workaround: NCSDK-13530
# Allow TF-M crypto to not depend on ITS when PSA crypto storage is disabled.
Expand Down Expand Up @@ -314,7 +326,7 @@ set(CRYPTO_ASYM_ENCRYPT_MODULE_ENABLED ${CONFIG_TFM_CRYPTO_ASYM_ENCRYPT_MODU
set(CRYPTO_KEY_DERIVATION_MODULE_ENABLED ${CONFIG_TFM_CRYPTO_KEY_DERIVATION_MODULE_ENABLED})
set(CRYPTO_PAKE_MODULE_ENABLED ${CONFIG_TFM_CRYPTO_PAKE_MODULE_ENABLED})
set(CRYPTO_IOVEC_BUFFER_SIZE ${CONFIG_TFM_CRYPTO_IOVEC_BUFFER_SIZE})
set(CRYPTO_NV_SEED ${CONFIG_TFM_CRYPTO_NV_SEED})
set(CRYPTO_NV_SEED 0)
set(CRYPTO_SINGLE_PART_FUNCS_DISABLED ${CONFIG_TFM_CRYPTO_SINGLE_PART_FUNCS_DISABLED})
set(CRYPTO_STACK_SIZE ${CONFIG_TFM_CRYPTO_PARTITION_STACK_SIZE})
set(CRYPTO_LIBRARY_ABI_COMPAT ON)
Expand Down
11 changes: 9 additions & 2 deletions modules/trusted-firmware-m/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ config TFM_PLATFORM_SP_STACK_SIZE

config TFM_PLATFORM_NV_COUNTER_MODULE_DISABLED
bool "Disable Non-volatile counter module"
default y if TFM_PROFILE_TYPE_MINIMAL

default n if TFM_PARTITION_PROTECTED_STORAGE && TFM_PS_ROLLBACK_PROTECTION
default y
endmenu

# Copied from secure_fw/partitions/crypto/Kconfig, appended the TFM prefix
Expand Down Expand Up @@ -130,6 +130,13 @@ config TFM_ATTEST_INCLUDE_OPTIONAL_CLAIMS
bool "Include optional claims in initial attestation token"
default y

config TFM_ATTEST_VERIFICATION_SERVICE_URL
string "TF-M attestation verification service URL"
default ""
depends on TFM_ATTEST_INCLUDE_OPTIONAL_CLAIMS
help
Optional claim of URL of the verification service in the TF-M attestation token.

config TFM_ATTEST_INCLUDE_COSE_KEY_ID
bool "Include COSE key-id in initial attestation token"
default n
Expand Down
3 changes: 2 additions & 1 deletion modules/trusted-firmware-m/Kconfig.tfm.pm
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ config PM_PARTITION_SIZE_TFM_INTERNAL_TRUSTED_STORAGE

config PM_PARTITION_SIZE_TFM_OTP_NV_COUNTERS
hex "Memory reserved for TFM OTP / Non-Volatile Counters"
default 0x2000 if !TFM_PROFILE_TYPE_MINIMAL
default 0x2000 if !TFM_PROFILE_TYPE_MINIMAL && \
!TFM_PLATFORM_NV_COUNTER_MODULE_DISABLED
default 0
help
Memory set aside for the OTP / Non-Volatile (NV) Counters partition
Expand Down
86 changes: 34 additions & 52 deletions modules/trusted-firmware-m/tfm_boards/common/attest_hal.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#include "tfm_attest_hal.h"
#include "tfm_plat_boot_seed.h"
#include "tfm_plat_device_id.h"
#include "tfm_plat_otp.h"
#include <nrf_cc3xx_platform.h>
#include "tfm_strnlen.h"
#include "nrf_provisioning.h"
Expand Down Expand Up @@ -72,50 +71,30 @@ int tfm_attest_update_security_lifecycle_otp(enum tfm_security_lifecycle_t slc)
return update_life_cycle_state(next_lcs);
}

enum tfm_plat_err_t tfm_attest_hal_get_verification_service(uint32_t *size, uint8_t *buf)
static const char *get_attestation_profile(void)
{
enum tfm_plat_err_t err;
size_t otp_size;
size_t copy_size;

err = tfm_plat_otp_read(PLAT_OTP_ID_VERIFICATION_SERVICE_URL, *size, buf);
if (err != TFM_PLAT_ERR_SUCCESS) {
return err;
}

err = tfm_plat_otp_get_size(PLAT_OTP_ID_VERIFICATION_SERVICE_URL, &otp_size);
if (err != TFM_PLAT_ERR_SUCCESS) {
return err;
}

/* Actually copied data is always the smaller */
copy_size = *size < otp_size ? *size : otp_size;
/* String content */
*size = tfm_strnlen((char *)buf, copy_size);

return TFM_PLAT_ERR_SUCCESS;
#if defined(CONFIG_TFM_ATTEST_TOKEN_PROFILE_PSA_IOT_1)
return "PSA_IOT_PROFILE_1";
#elif defined(CONFIG_TFM_ATTEST_TOKEN_PROFILE_PSA_2_0_0)
return "http://arm.com/psa/2.0.0";
#elif defined(CONFIG_TFM_ATTEST_TOKEN_PROFILE_ARM_CCA)
return "http://arm.com/CCA-SSD/1.0.0";
#else
#error "Attestation token profile not defined"
#endif
}

enum tfm_plat_err_t tfm_attest_hal_get_profile_definition(uint32_t *size, uint8_t *buf)
{
enum tfm_plat_err_t err;
size_t otp_size;
size_t copy_size;

err = tfm_plat_otp_read(PLAT_OTP_ID_PROFILE_DEFINITION, *size, buf);
if (err != TFM_PLAT_ERR_SUCCESS) {
return err;
}
const char *profile = get_attestation_profile();
uint32_t profile_len = strlen(profile);

err = tfm_plat_otp_get_size(PLAT_OTP_ID_PROFILE_DEFINITION, &otp_size);
if (err != TFM_PLAT_ERR_SUCCESS) {
return err;
if (*size < profile_len) {
return TFM_PLAT_ERR_SYSTEM_ERR;
}

/* Actually copied data is always the smaller */
copy_size = *size < otp_size ? *size : otp_size;
/* String content */
*size = tfm_strnlen((char *)buf, copy_size);
memcpy(buf, profile, profile_len);
*size = profile_len;

return TFM_PLAT_ERR_SUCCESS;
}
Expand Down Expand Up @@ -144,27 +123,30 @@ enum tfm_plat_err_t tfm_plat_get_implementation_id(uint32_t *size, uint8_t *buf)
return TFM_PLAT_ERR_SUCCESS;
}

enum tfm_plat_err_t tfm_plat_get_cert_ref(uint32_t *size, uint8_t *buf)
#if CONFIG_TFM_ATTEST_INCLUDE_OPTIONAL_CLAIMS

enum tfm_plat_err_t tfm_plat_get_cert_ref(uint32_t *size, uint8_t *buf)
{
enum tfm_plat_err_t err;
size_t otp_size;
size_t copy_size;

err = tfm_plat_otp_read(PLAT_OTP_ID_CERT_REF, *size, buf);
if (err != TFM_PLAT_ERR_SUCCESS) {
return err;
if (read_variable_data(BL_VARIABLE_DATA_TYPE_PSA_CERTIFICATION_REFERENCE, buf, size)) {
return TFM_PLAT_ERR_SYSTEM_ERR;
}

err = tfm_plat_otp_get_size(PLAT_OTP_ID_CERT_REF, &otp_size);
if (err != TFM_PLAT_ERR_SUCCESS) {
return err;
return TFM_PLAT_ERR_SUCCESS;
}

enum tfm_plat_err_t tfm_attest_hal_get_verification_service(uint32_t *size, uint8_t *buf)
{
const char *url = CONFIG_TFM_ATTEST_VERIFICATION_SERVICE_URL;
uint32_t url_len = strlen(url);

if (*size < url_len) {
return TFM_PLAT_ERR_SYSTEM_ERR;
}

/* Actually copied data is always the smaller */
copy_size = *size < otp_size ? *size : otp_size;
/* String content */
*size = tfm_strnlen((char *)buf, copy_size);
memcpy(buf, url, url_len);
*size = url_len;

return TFM_PLAT_ERR_SUCCESS;
}

#endif /* CONFIG_TFM_ATTEST_INCLUDE_OPTIONAL_CLAIMS */
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,13 @@
#define TFM_HAL_ITS_PROGRAM_UNIT (0x4)

/* OTP / NV counter definitions */
#ifdef PM_TFM_OTP_NV_COUNTERS_ADDRESS
#define TFM_OTP_NV_COUNTERS_AREA_SIZE (FLASH_OTP_NV_COUNTERS_AREA_SIZE / 2)
#define TFM_OTP_NV_COUNTERS_AREA_ADDR FLASH_OTP_NV_COUNTERS_AREA_OFFSET
#define TFM_OTP_NV_COUNTERS_SECTOR_SIZE FLASH_OTP_NV_COUNTERS_SECTOR_SIZE
#define TFM_OTP_NV_COUNTERS_BACKUP_AREA_ADDR \
(TFM_OTP_NV_COUNTERS_AREA_ADDR + TFM_OTP_NV_COUNTERS_AREA_SIZE)
#endif

/* Use Flash memory to store Code data */
#define FLASH_BASE_ADDRESS (0x00000000)
Expand Down
Loading

0 comments on commit 2cf35a2

Please sign in to comment.