1 /*
2  * Copyright (c) 2022, Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 
8 #include <string.h>
9 #include <stddef.h>
10 #include <stdbool.h>
11 #include "attest_boot_data.h"
12 #include "tfm_boot_status.h"
13 #include "tfm_attest_iat_defs.h"
14 #include "q_useful_buf.h"
15 #ifdef TFM_PARTITION_MEASURED_BOOT
16 #include "measured_boot_api.h"
17 #include "boot_measurement.h"
18 #include "psa/crypto.h"
19 #endif /* TFM_PARTITION_MEASURED_BOOT */
20 
21 #ifndef TFM_PARTITION_MEASURED_BOOT
22 #define MAX_BOOT_STATUS 512
23 
24 /*!
25  * \struct attest_boot_data
26  *
27  * \brief Contains the received boot status information from bootloader
28  *
29  * \details This is a redefinition of \ref tfm_boot_data to allocate the
30  *          appropriate, service dependent size of \ref boot_data.
31  */
32 struct attest_boot_data {
33     struct shared_data_tlv_header header;
34     uint8_t data[MAX_BOOT_STATUS];
35 };
36 
37 /*!
38  * \var boot_data
39  *
40  * \brief Store the boot status in service's memory.
41  *
42  * \details Boot status comes from the secure bootloader and primarily stored
43  *          on a memory area which is shared between bootloader and SPM.
44  *          SPM provides the \ref tfm_core_get_boot_data() API to retrieve
45  *          the service related data from shared area.
46  */
47 __attribute__ ((aligned(4)))
48 static struct attest_boot_data boot_data;
49 #endif
50 
51 #ifdef TFM_PARTITION_MEASURED_BOOT
52 /*!
53  * \brief Static function to get a string representing the measurement algorithm
54  *
55  * \param[in]   algorithm        Algorithm identifier
56  * \param[out]  measurement_desc Structure to carry the output string:
57  *                               pointer + string length, not including
58  *                               the null-terminator
59  *
60  * \return Returns error code as specified in \ref psa_attest_err_t
61  */
62 static inline enum psa_attest_err_t
get_measurement_description(psa_algorithm_t algorithm,struct q_useful_buf_c * measurement_desc)63 get_measurement_description(psa_algorithm_t algorithm,
64                             struct q_useful_buf_c *measurement_desc)
65 {
66     switch (algorithm) {
67     case PSA_ALG_SHA_256:
68         measurement_desc->ptr = "sha-256";
69         measurement_desc->len = 7; /* Not including the null-terminator. */
70         break;
71     case PSA_ALG_SHA_384:
72         measurement_desc->ptr = "sha-384";
73         measurement_desc->len = 7; /* Not including the null-terminator. */
74     case PSA_ALG_SHA_512:
75         measurement_desc->ptr = "sha-512";
76         measurement_desc->len = 7; /* Not including the null-terminator. */
77         break;
78     default:
79         /* Algorithm not supported. */
80         return PSA_ATTEST_ERR_INVALID_INPUT;
81     }
82 
83     return PSA_ATTEST_ERR_SUCCESS;
84 }
85 
86 /*!
87  * \brief Static function to encode the properties of a SW component as a map
88  *
89  * \param[in]  encode_ctx        The encoding context to open the map in
90  * \param[in]  signer_id         Pointer to buffer which stores the signer ID
91  * \param[in]  sw_version        Pointer to buffer which stores the SW version
92  * \param[in]  sw_type           Pointer to buffer which stores the SW type
93  * \param[in]  measurement_desc  Pointer to buffer which stores the
94  *                               measurement description
95  * \param[in]  measurement       Pointer to buffer which stores the
96  *                               measurement value
97  *
98  * \return Returns error code as specified in \ref psa_attest_err_t
99  */
100 static enum psa_attest_err_t
attest_encode_sw_component(QCBOREncodeContext * encode_ctx,const struct q_useful_buf_c * signer_id,const struct q_useful_buf_c * sw_version,const struct q_useful_buf_c * sw_type,const struct q_useful_buf_c * measurement_desc,const struct q_useful_buf_c * measurement)101 attest_encode_sw_component(QCBOREncodeContext *encode_ctx,
102                            const struct q_useful_buf_c *signer_id,
103                            const struct q_useful_buf_c *sw_version,
104                            const struct q_useful_buf_c *sw_type,
105                            const struct q_useful_buf_c *measurement_desc,
106                            const struct q_useful_buf_c *measurement)
107 {
108     if (encode_ctx == NULL || signer_id == NULL || sw_version == NULL ||
109         sw_type == NULL || measurement_desc == NULL || measurement == NULL) {
110         return PSA_ATTEST_ERR_INVALID_INPUT;
111     }
112 
113     QCBOREncode_OpenMap(encode_ctx);
114 
115     /* Encode signer ID as byte string. */
116     QCBOREncode_AddBytesToMapN(encode_ctx,
117                                IAT_SW_COMPONENT_SIGNER_ID,
118                                *signer_id);
119 
120     /* Encode component version as text string. */
121     QCBOREncode_AddTextToMapN(encode_ctx,
122                               IAT_SW_COMPONENT_VERSION,
123                               *sw_version);
124 
125     /* Encode software component type as text string. */
126     QCBOREncode_AddTextToMapN(encode_ctx,
127                               IAT_SW_COMPONENT_MEASUREMENT_TYPE,
128                               *sw_type);
129 
130     /* Encode measurement description as text string. */
131     QCBOREncode_AddTextToMapN(encode_ctx,
132                               IAT_SW_COMPONENT_MEASUREMENT_DESC,
133                               *measurement_desc);
134 
135     /* Encode measurement value as byte string. */
136     QCBOREncode_AddBytesToMapN(encode_ctx,
137                                IAT_SW_COMPONENT_MEASUREMENT_VALUE,
138                                *measurement);
139 
140     QCBOREncode_CloseMap(encode_ctx);
141 
142     return PSA_ATTEST_ERR_SUCCESS;
143 }
144 #else
145 /*!
146  * \brief Static function to look up all entries in the shared data area
147  *       (boot status) which belong to a specific module.
148  *
149  * \param[in]     module  The identifier of SW module to look up based on this
150  * \param[out]    claim   The type of SW module's attribute
151  * \param[out]    tlv_len Length of the shared data entry
152  * \param[in/out] tlv_ptr Pointer to the shared data entry. If its value NULL as
153  *                        input then it will starts the look up from the
154  *                        beginning of the shared data section. If not NULL then
155  *                        it continue look up from the next entry. It returns
156  *                        the address of next found entry which belongs to
157  *                        module.
158  *
159  * \retval    -1          Error, boot status is malformed
160  * \retval     0          Entry not found
161  * \retval     1          Entry found
162  */
attest_get_tlv_by_module(uint8_t module,uint8_t * claim,uint16_t * tlv_len,uint8_t ** tlv_ptr)163 static int32_t attest_get_tlv_by_module(uint8_t    module,
164                                         uint8_t   *claim,
165                                         uint16_t  *tlv_len,
166                                         uint8_t  **tlv_ptr)
167 {
168     struct shared_data_tlv_entry tlv_entry;
169     uint8_t *tlv_end;
170     uint8_t *tlv_curr;
171 
172     if (boot_data.header.tlv_magic != SHARED_DATA_TLV_INFO_MAGIC) {
173         return -1;
174     }
175 
176     /* Get the boundaries of TLV section where to lookup*/
177     tlv_end = (uint8_t *)&boot_data + boot_data.header.tlv_tot_len;
178     if (*tlv_ptr == NULL) {
179         /* At first call set to the beginning of the TLV section */
180         tlv_curr = boot_data.data;
181     } else {
182         /* Any subsequent call set to the next TLV entry */
183         (void)memcpy(&tlv_entry, *tlv_ptr, SHARED_DATA_ENTRY_HEADER_SIZE);
184 
185         tlv_curr  = (*tlv_ptr) + SHARED_DATA_ENTRY_HEADER_SIZE
186                     + tlv_entry.tlv_len;
187     }
188 
189     /* Iterates over the TLV section and returns the address and size of TLVs
190      * with requested module identifier
191      */
192     while (tlv_curr < tlv_end) {
193         /* Create local copy to avoid unaligned access */
194         (void)memcpy(&tlv_entry, tlv_curr, SHARED_DATA_ENTRY_HEADER_SIZE);
195         if (GET_IAS_MODULE(tlv_entry.tlv_type) == module) {
196             *claim   = GET_IAS_CLAIM(tlv_entry.tlv_type);
197             *tlv_ptr = tlv_curr;
198             *tlv_len = tlv_entry.tlv_len;
199             return 1;
200         }
201 
202         tlv_curr += (SHARED_DATA_ENTRY_HEADER_SIZE + tlv_entry.tlv_len);
203     }
204 
205     return 0;
206 }
207 #endif /* TFM_PARTITION_MEASURED_BOOT */
208 
209 enum psa_attest_err_t
attest_encode_sw_components_array(QCBOREncodeContext * encode_ctx,const int32_t * map_label,uint32_t * cnt)210 attest_encode_sw_components_array(QCBOREncodeContext *encode_ctx,
211                                   const int32_t *map_label,
212                                   uint32_t *cnt)
213 {
214 #ifdef TFM_PARTITION_MEASURED_BOOT
215     uint8_t slot_index;
216 
217     Q_USEFUL_BUF_MAKE_STACK_UB(measurement_buf, MEASUREMENT_VALUE_MAX_SIZE);
218     Q_USEFUL_BUF_MAKE_STACK_UB(signer_id_buf, SIGNER_ID_MAX_SIZE);
219     Q_USEFUL_BUF_MAKE_STACK_UB(sw_version_buf, VERSION_MAX_SIZE);
220     Q_USEFUL_BUF_MAKE_STACK_UB(sw_type_buf, SW_TYPE_MAX_SIZE);
221     struct q_useful_buf_c measurement_desc = NULL_Q_USEFUL_BUF_C;
222     uint32_t measurement_algo;
223     bool is_locked;
224     enum psa_attest_err_t err;
225     psa_status_t status;
226 
227     if ((encode_ctx == NULL) || (cnt == NULL)) {
228         return PSA_ATTEST_ERR_INVALID_INPUT;
229     }
230 
231     *cnt = 0;
232 
233     /* Retrieve all the measurements from the Measured Boot partition
234      * which are accessible to the Attestation partition.
235      */
236     for (slot_index = 0; slot_index < NUM_OF_MEASUREMENT_SLOTS; slot_index++) {
237         measurement_buf.len = MEASUREMENT_VALUE_MAX_SIZE;
238         signer_id_buf.len   = SIGNER_ID_MAX_SIZE;
239         sw_version_buf.len  = VERSION_MAX_SIZE;
240         sw_type_buf.len     = SW_TYPE_MAX_SIZE;
241 
242         status = tfm_measured_boot_read_measurement(slot_index,
243                                                     signer_id_buf.ptr,
244                                                     signer_id_buf.len,
245                                                     &signer_id_buf.len,
246                                                     sw_version_buf.ptr,
247                                                     sw_version_buf.len,
248                                                     &sw_version_buf.len,
249                                                     &measurement_algo,
250                                                     sw_type_buf.ptr,
251                                                     sw_type_buf.len,
252                                                     &sw_type_buf.len,
253                                                     measurement_buf.ptr,
254                                                     measurement_buf.len,
255                                                     &measurement_buf.len,
256                                                     &is_locked);
257         if (status != PSA_SUCCESS) {
258             continue;
259         }
260 
261         (*cnt)++;
262         if (*cnt == 1) {
263             /* Open array which stores SW components claims. */
264             if (map_label != NULL) {
265                 QCBOREncode_OpenArrayInMapN(encode_ctx, *map_label);
266             } else {
267                 QCBOREncode_OpenArray(encode_ctx);
268             }
269         }
270 
271         err = get_measurement_description(measurement_algo, &measurement_desc);
272         if (err != PSA_ATTEST_ERR_SUCCESS) {
273             return PSA_ATTEST_ERR_GENERAL;
274         }
275 
276         err = attest_encode_sw_component(
277                             encode_ctx,
278                             (struct q_useful_buf_c *)&signer_id_buf,
279                             (struct q_useful_buf_c *)&sw_version_buf,
280                             (struct q_useful_buf_c *)&sw_type_buf,
281                             &measurement_desc,
282                             (struct q_useful_buf_c *)&measurement_buf);
283         if (err != PSA_ATTEST_ERR_SUCCESS) {
284             return PSA_ATTEST_ERR_GENERAL;
285         }
286     }
287 
288 #else /* TFM_PARTITION_MEASURED_BOOT */
289     struct q_useful_buf_c encoded_const = NULL_Q_USEFUL_BUF_C;
290     uint16_t tlv_len;
291     uint8_t *tlv_ptr;
292     uint8_t  tlv_id;
293     uint8_t module = 0;
294     int32_t found;
295 
296     if ((encode_ctx == NULL) || (cnt == NULL)) {
297         return PSA_ATTEST_ERR_INVALID_INPUT;
298     }
299 
300     *cnt = 0;
301 
302     /* Extract all boot records (measurements) from the boot status information
303      * that was received from the secure bootloader.
304      */
305     for (module = 0; module < SW_MAX; ++module) {
306         /* Indicates to restart the look up from the beginning of the shared
307          * data section.
308          */
309         tlv_ptr = NULL;
310 
311         /* Look up the first TLV entry which belongs to the SW module */
312         found = attest_get_tlv_by_module(module, &tlv_id,
313                                          &tlv_len, &tlv_ptr);
314         if (found == -1) {
315             /* Boot status area is malformed. */
316             return PSA_ATTEST_ERR_CLAIM_UNAVAILABLE;
317         } else if ((found == 1) && (tlv_id == SW_BOOT_RECORD)) {
318             (*cnt)++;
319             if (*cnt == 1) {
320                 /* Open array which stores SW components claims. */
321                 if (map_label != NULL) {
322                     QCBOREncode_OpenArrayInMapN(encode_ctx, *map_label);
323                 } else {
324                     QCBOREncode_OpenArray(encode_ctx);
325                 }
326             }
327 
328             encoded_const.ptr = tlv_ptr + SHARED_DATA_ENTRY_HEADER_SIZE;
329             encoded_const.len = tlv_len;
330             QCBOREncode_AddEncoded(encode_ctx, encoded_const);
331         }
332     }
333 #endif /* TFM_PARTITION_MEASURED_BOOT */
334 
335     if (*cnt != 0) {
336         /* Close array which stores SW components claims. */
337         QCBOREncode_CloseArray(encode_ctx);
338     }
339 
340     return PSA_ATTEST_ERR_SUCCESS;
341 }
342 
attest_boot_data_init(void)343 enum psa_attest_err_t attest_boot_data_init(void)
344 {
345 #ifdef TFM_PARTITION_MEASURED_BOOT
346     /* When the TFM_PARTITION_MEASURED_BOOT is enabled boot data is retrieved by
347      * that partition from the SPM. In this case, the initial attestation partition
348      * requests through the measured boot partition's API for boot data to create
349      * the token.
350      */
351     return PSA_ATTEST_ERR_SUCCESS;
352 #else
353     return attest_get_boot_data(TLV_MAJOR_IAS,
354                                 (struct tfm_boot_data *)&boot_data,
355                                 MAX_BOOT_STATUS);
356 #endif
357 }
358