Skip to content

Commit 7815f14

Browse files
MarkusLassilarlubos
authored andcommitted
tfm: Move TF-M attestation data to provisioned OTP region
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 <markus.lassila@nordicsemi.no>
1 parent cdbb369 commit 7815f14

File tree

16 files changed

+480
-145
lines changed

16 files changed

+480
-145
lines changed

cmake/sysbuild/provision_hex.cmake

+10-5
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,10 @@ function(provision application prefix_name)
1818
set(PROVISION_HEX_NAME ${prefix_name}provision.hex)
1919
set(PROVISION_HEX ${CMAKE_BINARY_DIR}/${PROVISION_HEX_NAME})
2020

21-
# This calculation is based on the LCS cycle state datacycle state data struct in bl_storage.h
2221
if(CONFIG_SOC_SERIES_NRF54LX)
23-
set(lcs_state_struct_size 0x10) # 4 * 4 bytes
22+
set(otp_write_width 4) # OTP writes are in words (4 bytes)
2423
else()
25-
set(lcs_state_struct_size 0x8) # 4 * 2 bytes
24+
set(otp_write_width 2) # OTP writes are in half-words (2 bytes)
2625
endif()
2726

2827
if(CONFIG_SECURE_BOOT)
@@ -87,6 +86,10 @@ function(provision application prefix_name)
8786
set(mcuboot_counters_slots --mcuboot-counters-slots ${SB_CONFIG_MCUBOOT_HW_DOWNGRADE_PREVENTION_COUNTER_SLOTS})
8887
endif()
8988

89+
if(SB_CONFIG_TFM_OTP_PSA_CERTIFICATE_REFERENCE AND SB_CONFIG_TFM_PSA_CERTIFICATE_REFERENCE_VALUE)
90+
set(psa_certificate_reference --psa-certificate-reference ${SB_CONFIG_TFM_PSA_CERTIFICATE_REFERENCE_VALUE})
91+
endif()
92+
9093
if(CONFIG_SECURE_BOOT)
9194
add_custom_command(
9295
OUTPUT
@@ -103,7 +106,8 @@ function(provision application prefix_name)
103106
${monotonic_counter_arg}
104107
${no_verify_hashes_arg}
105108
${mcuboot_counters_slots}
106-
--lcs-state-size ${lcs_state_struct_size}
109+
--otp-write-width ${otp_write_width}
110+
${psa_certificate_reference}
107111
DEPENDS
108112
${PROVISION_KEY_DEPENDS}
109113
${PROVISION_DEPENDS}
@@ -126,7 +130,8 @@ function(provision application prefix_name)
126130
--max-size ${CONFIG_PM_PARTITION_SIZE_PROVISION}
127131
${mcuboot_counters_num}
128132
${mcuboot_counters_slots}
129-
--lcs-state-size ${lcs_state_struct_size}
133+
--otp-write-width ${otp_write_width}
134+
${psa_certificate_reference}
130135
DEPENDS
131136
${PROVISION_KEY_DEPENDS}
132137
WORKING_DIRECTORY

doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst

+6-1
Original file line numberDiff line numberDiff line change
@@ -431,8 +431,13 @@ SUIT samples
431431
Trusted Firmware-M (TF-M) samples
432432
---------------------------------
433433

434-
|no_changes_yet_note|
434+
* :ref:`tfm_psa_template` sample:
435+
436+
* Added support for the following attestation token fields:
435437

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

437442
Thread samples
438443
--------------

include/bl_storage.h

+83-9
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ typedef uint32_t lcs_reserved_t;
4444
/* We truncate the 32 byte sha256 down to 16 bytes before storing it */
4545
#define SB_PUBLIC_KEY_HASH_LEN 16
4646

47+
/* Supported collection types. */
48+
enum collection_type {
49+
BL_COLLECTION_TYPE_MONOTONIC_COUNTERS = 1,
50+
BL_COLLECTION_TYPE_VARIABLE_DATA = 0x9312,
51+
};
52+
4753
/* Counter used by NSIB to check the firmware version */
4854
#define BL_MONOTONIC_COUNTERS_DESC_NSIB 0x1
4955

@@ -88,23 +94,50 @@ struct monotonic_counter {
8894
uint16_t description;
8995
/* Number of entries in 'counter_slots' list. */
9096
uint16_t num_counter_slots;
91-
counter_t counter_slots[1];
97+
counter_t counter_slots[];
98+
};
99+
100+
/** Common part for all collections. */
101+
struct collection {
102+
uint16_t type;
103+
uint16_t count;
92104
};
93105

94106
/** The second data structure in the provision page. It has unknown length since
95107
* 'counters' is repeated. Note that each entry in counters also has unknown
96108
* length, and each entry can have different length from the others, so the
97-
* entries beyond the first cannot be accessed via array indices.
109+
* entries beyond the first cannot be accessed through array indices.
98110
*/
99111
struct counter_collection {
100-
uint16_t type; /* Must be "monotonic counter". */
101-
uint16_t num_counters; /* Number of entries in 'counters' list. */
102-
struct monotonic_counter counters[1];
112+
struct collection collection; /* Type must be BL_COLLECTION_TYPE_MONOTONIC_COUNTERS */
113+
struct monotonic_counter counters[];
114+
};
115+
116+
/* Variable data types. */
117+
enum variable_data_type {
118+
BL_VARIABLE_DATA_TYPE_PSA_CERTIFICATION_REFERENCE = 0x1
119+
};
120+
struct variable_data {
121+
uint8_t type;
122+
uint8_t length;
123+
uint8_t data[];
124+
};
125+
126+
/* The third data structure in the provision page. It has unknown length since
127+
* 'variable_data' is repeated. The collection starts immediately after the
128+
* counter collection. As the counter collection has unknown length, the start
129+
* of the variable data collection must be calculated dynamically. Similarly,
130+
* the entries in the variable data collection have unknown length, so they
131+
* cannot be accessed through array indices.
132+
*/
133+
struct variable_data_collection {
134+
struct collection collection; /* Type must be BL_COLLECTION_TYPE_VARIABLE_DATA */
135+
struct variable_data variable_data[];
103136
};
104137

105138
/** The first data structure in the bootloader storage. It has unknown length
106139
* since 'key_data' is repeated. This data structure is immediately followed by
107-
* struct counter_collection.
140+
* struct counter_collection, which is then followed by struct variable_data_collection.
108141
*/
109142
struct bl_storage_data {
110143
/* NB: When placed in OTP, reads must be 4 bytes and 4 byte aligned */
@@ -116,7 +149,28 @@ struct bl_storage_data {
116149
struct {
117150
uint32_t valid;
118151
uint8_t hash[SB_PUBLIC_KEY_HASH_LEN];
119-
} key_data[1];
152+
} key_data[];
153+
154+
/* Monotonic counter collection:
155+
* uint16_t type;
156+
* uint16_t count;
157+
* struct {
158+
* uint16_t description;
159+
* uint16_t num_counter_slots;
160+
* counter_t counter_slots[];
161+
* } counters[];
162+
*/
163+
164+
/* Variable data collection:
165+
* uint16_t type;
166+
* uint16_t count;
167+
* struct {
168+
* uint8_t type;
169+
* uint8_t length;
170+
* uint8_t data[];
171+
* } variable_data[];
172+
* uint8_t padding[]; // Padding to align to 4 bytes
173+
*/
120174
};
121175

122176
#define BL_STORAGE ((const volatile struct bl_storage_data *)(PM_PROVISION_ADDRESS))
@@ -150,7 +204,7 @@ uint32_t s1_address_read(void);
150204
uint32_t num_public_keys_read(void);
151205

152206
/**
153-
* @brief Function for reading number of public key data slots.
207+
* @brief Function for verifying public keys.
154208
*
155209
* @retval 0 if all keys are ok.
156210
* @retval -EHASHFF if one or more keys contains an aligned 0xFFFF.
@@ -257,12 +311,32 @@ int read_life_cycle_state(enum lcs *lcs);
257311
int update_life_cycle_state(enum lcs next_lcs);
258312

259313
/**
260-
* Read the implementation id from OTP and copy it into a given buffer.
314+
* Read the implementation ID from OTP and copy it into a given buffer.
261315
*
262316
* @param[out] buf Buffer that has at least BL_STORAGE_IMPLEMENTATION_ID_SIZE bytes
263317
*/
264318
void read_implementation_id_from_otp(uint8_t *buf);
265319

320+
/**
321+
* @brief Read variable data from OTP.
322+
*
323+
* Variable data starts with variable data collection ID, followed by amount of variable data
324+
* entries and the variable data entries themselves.
325+
* [Collection ID][Variable count][Type][Variable data length][Variable data][Type]...
326+
* 2 bytes 2 bytes 1 byte 1 byte 0-255 bytes
327+
*
328+
* @note If data is not found, function does not fail. Instead, 0 length is returned.
329+
*
330+
* @param[in] data_type Type of the variable data to read.
331+
* @param[out] buf Buffer to store the variable data.
332+
* @param[in,out] buf_len On input, the size of the buffer. On output, the length of the data.
333+
*
334+
* @retval 0 Variable data read successfully, or not found.
335+
* @retval -EINVAL No buffer provided.
336+
* @retval -ENOMEM Buffer too small.
337+
*/
338+
int read_variable_data(enum variable_data_type data_type, uint8_t *buf, uint32_t *buf_len);
339+
266340
/** @} */
267341

268342
#ifdef __cplusplus

modules/trusted-firmware-m/CMakeLists.txt

+14-2
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ set_property(TARGET zephyr_property_target
161161
-DCRYPTO_HW_ACCELERATOR=True
162162
)
163163

164+
set_property(TARGET zephyr_property_target
165+
APPEND PROPERTY TFM_CMAKE_OPTIONS -DPLATFORM_DEFAULT_NV_SEED=OFF
166+
)
167+
164168
if(CONFIG_TFM_ALLOW_NON_SECURE_FAULT_HANDLING)
165169
set_property(TARGET zephyr_property_target
166170
APPEND PROPERTY TFM_CMAKE_OPTIONS
@@ -180,7 +184,6 @@ if(CONFIG_TFM_PROFILE_TYPE_MINIMAL)
180184
APPEND PROPERTY TFM_CMAKE_OPTIONS
181185
-DPLATFORM_DEFAULT_ROTPK=OFF
182186
-DPLATFORM_DEFAULT_IAK=OFF
183-
-DPLATFORM_DEFAULT_NV_SEED=OFF
184187
-DPLATFORM_DEFAULT_OTP=OFF
185188
-DPLATFORM_DEFAULT_OTP_WRITEABLE=OFF
186189
-DPLATFORM_DEFAULT_NV_COUNTERS=OFF
@@ -195,6 +198,15 @@ elseif(NOT CONFIG_TFM_PARTITION_PROTECTED_STORAGE)
195198
)
196199
endif()
197200

201+
if(CONFIG_TFM_PLATFORM_NV_COUNTER_MODULE_DISABLED)
202+
set_property(TARGET zephyr_property_target
203+
APPEND PROPERTY TFM_CMAKE_OPTIONS
204+
-DPLATFORM_DEFAULT_OTP=OFF
205+
-DPLATFORM_DEFAULT_OTP_WRITEABLE=OFF
206+
-DPLATFORM_DEFAULT_NV_COUNTERS=OFF
207+
)
208+
endif()
209+
198210
if(NOT CONFIG_MBEDTLS_PSA_CRYPTO_STORAGE_C)
199211
# Workaround: NCSDK-13530
200212
# Allow TF-M crypto to not depend on ITS when PSA crypto storage is disabled.
@@ -315,7 +327,7 @@ set(CRYPTO_ASYM_ENCRYPT_MODULE_ENABLED ${CONFIG_TFM_CRYPTO_ASYM_ENCRYPT_MODU
315327
set(CRYPTO_KEY_DERIVATION_MODULE_ENABLED ${CONFIG_TFM_CRYPTO_KEY_DERIVATION_MODULE_ENABLED})
316328
set(CRYPTO_PAKE_MODULE_ENABLED ${CONFIG_TFM_CRYPTO_PAKE_MODULE_ENABLED})
317329
set(CRYPTO_IOVEC_BUFFER_SIZE ${CONFIG_TFM_CRYPTO_IOVEC_BUFFER_SIZE})
318-
set(CRYPTO_NV_SEED ${CONFIG_TFM_CRYPTO_NV_SEED})
330+
set(CRYPTO_NV_SEED 0)
319331
set(CRYPTO_SINGLE_PART_FUNCS_DISABLED ${CONFIG_TFM_CRYPTO_SINGLE_PART_FUNCS_DISABLED})
320332
set(CRYPTO_STACK_SIZE ${CONFIG_TFM_CRYPTO_PARTITION_STACK_SIZE})
321333
set(CRYPTO_LIBRARY_ABI_COMPAT ON)

modules/trusted-firmware-m/Kconfig

+7
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,13 @@ config TFM_ATTEST_INCLUDE_OPTIONAL_CLAIMS
132132
bool "Include optional claims in initial attestation token"
133133
default y
134134

135+
config TFM_ATTEST_VERIFICATION_SERVICE_URL
136+
string "TF-M attestation verification service URL"
137+
default ""
138+
depends on TFM_ATTEST_INCLUDE_OPTIONAL_CLAIMS
139+
help
140+
Optional claim of URL of the verification service in the TF-M attestation token.
141+
135142
config TFM_ATTEST_INCLUDE_COSE_KEY_ID
136143
bool "Include COSE key-id in initial attestation token"
137144
default n

modules/trusted-firmware-m/Kconfig.tfm.pm

+2-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ config PM_PARTITION_SIZE_TFM_INTERNAL_TRUSTED_STORAGE
5959

6060
config PM_PARTITION_SIZE_TFM_OTP_NV_COUNTERS
6161
hex "Memory reserved for TFM OTP / Non-Volatile Counters"
62-
default 0x2000 if !TFM_PROFILE_TYPE_MINIMAL
62+
default 0x2000 if !TFM_PROFILE_TYPE_MINIMAL && \
63+
!TFM_PLATFORM_NV_COUNTER_MODULE_DISABLED
6364
default 0
6465
help
6566
Memory set aside for the OTP / Non-Volatile (NV) Counters partition

modules/trusted-firmware-m/tfm_boards/common/attest_hal.c

+34-52
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
#include "tfm_attest_hal.h"
1212
#include "tfm_plat_boot_seed.h"
1313
#include "tfm_plat_device_id.h"
14-
#include "tfm_plat_otp.h"
1514
#include <nrf_cc3xx_platform.h>
1615
#include "tfm_strnlen.h"
1716
#include "nrf_provisioning.h"
@@ -72,50 +71,30 @@ int tfm_attest_update_security_lifecycle_otp(enum tfm_security_lifecycle_t slc)
7271
return update_life_cycle_state(next_lcs);
7372
}
7473

75-
enum tfm_plat_err_t tfm_attest_hal_get_verification_service(uint32_t *size, uint8_t *buf)
74+
static const char *get_attestation_profile(void)
7675
{
77-
enum tfm_plat_err_t err;
78-
size_t otp_size;
79-
size_t copy_size;
80-
81-
err = tfm_plat_otp_read(PLAT_OTP_ID_VERIFICATION_SERVICE_URL, *size, buf);
82-
if (err != TFM_PLAT_ERR_SUCCESS) {
83-
return err;
84-
}
85-
86-
err = tfm_plat_otp_get_size(PLAT_OTP_ID_VERIFICATION_SERVICE_URL, &otp_size);
87-
if (err != TFM_PLAT_ERR_SUCCESS) {
88-
return err;
89-
}
90-
91-
/* Actually copied data is always the smaller */
92-
copy_size = *size < otp_size ? *size : otp_size;
93-
/* String content */
94-
*size = tfm_strnlen((char *)buf, copy_size);
95-
96-
return TFM_PLAT_ERR_SUCCESS;
76+
#if defined(CONFIG_TFM_ATTEST_TOKEN_PROFILE_PSA_IOT_1)
77+
return "PSA_IOT_PROFILE_1";
78+
#elif defined(CONFIG_TFM_ATTEST_TOKEN_PROFILE_PSA_2_0_0)
79+
return "http://arm.com/psa/2.0.0";
80+
#elif defined(CONFIG_TFM_ATTEST_TOKEN_PROFILE_ARM_CCA)
81+
return "http://arm.com/CCA-SSD/1.0.0";
82+
#else
83+
#error "Attestation token profile not defined"
84+
#endif
9785
}
9886

9987
enum tfm_plat_err_t tfm_attest_hal_get_profile_definition(uint32_t *size, uint8_t *buf)
10088
{
101-
enum tfm_plat_err_t err;
102-
size_t otp_size;
103-
size_t copy_size;
104-
105-
err = tfm_plat_otp_read(PLAT_OTP_ID_PROFILE_DEFINITION, *size, buf);
106-
if (err != TFM_PLAT_ERR_SUCCESS) {
107-
return err;
108-
}
89+
const char *profile = get_attestation_profile();
90+
uint32_t profile_len = strlen(profile);
10991

110-
err = tfm_plat_otp_get_size(PLAT_OTP_ID_PROFILE_DEFINITION, &otp_size);
111-
if (err != TFM_PLAT_ERR_SUCCESS) {
112-
return err;
92+
if (*size < profile_len) {
93+
return TFM_PLAT_ERR_SYSTEM_ERR;
11394
}
11495

115-
/* Actually copied data is always the smaller */
116-
copy_size = *size < otp_size ? *size : otp_size;
117-
/* String content */
118-
*size = tfm_strnlen((char *)buf, copy_size);
96+
memcpy(buf, profile, profile_len);
97+
*size = profile_len;
11998

12099
return TFM_PLAT_ERR_SUCCESS;
121100
}
@@ -144,27 +123,30 @@ enum tfm_plat_err_t tfm_plat_get_implementation_id(uint32_t *size, uint8_t *buf)
144123
return TFM_PLAT_ERR_SUCCESS;
145124
}
146125

147-
enum tfm_plat_err_t tfm_plat_get_cert_ref(uint32_t *size, uint8_t *buf)
126+
#if CONFIG_TFM_ATTEST_INCLUDE_OPTIONAL_CLAIMS
148127

128+
enum tfm_plat_err_t tfm_plat_get_cert_ref(uint32_t *size, uint8_t *buf)
149129
{
150-
enum tfm_plat_err_t err;
151-
size_t otp_size;
152-
size_t copy_size;
153-
154-
err = tfm_plat_otp_read(PLAT_OTP_ID_CERT_REF, *size, buf);
155-
if (err != TFM_PLAT_ERR_SUCCESS) {
156-
return err;
130+
if (read_variable_data(BL_VARIABLE_DATA_TYPE_PSA_CERTIFICATION_REFERENCE, buf, size)) {
131+
return TFM_PLAT_ERR_SYSTEM_ERR;
157132
}
158133

159-
err = tfm_plat_otp_get_size(PLAT_OTP_ID_CERT_REF, &otp_size);
160-
if (err != TFM_PLAT_ERR_SUCCESS) {
161-
return err;
134+
return TFM_PLAT_ERR_SUCCESS;
135+
}
136+
137+
enum tfm_plat_err_t tfm_attest_hal_get_verification_service(uint32_t *size, uint8_t *buf)
138+
{
139+
const char *url = CONFIG_TFM_ATTEST_VERIFICATION_SERVICE_URL;
140+
uint32_t url_len = strlen(url);
141+
142+
if (*size < url_len) {
143+
return TFM_PLAT_ERR_SYSTEM_ERR;
162144
}
163145

164-
/* Actually copied data is always the smaller */
165-
copy_size = *size < otp_size ? *size : otp_size;
166-
/* String content */
167-
*size = tfm_strnlen((char *)buf, copy_size);
146+
memcpy(buf, url, url_len);
147+
*size = url_len;
168148

169149
return TFM_PLAT_ERR_SUCCESS;
170150
}
151+
152+
#endif /* CONFIG_TFM_ATTEST_INCLUDE_OPTIONAL_CLAIMS */

0 commit comments

Comments
 (0)