aboutsummaryrefslogtreecommitdiff
path: root/bl2/src/boot_record.c
blob: b20cf936be5f49ebf5dca0b4ee24c4bfcedbbc26 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
/*
 * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */

#include "mcuboot_config/mcuboot_config.h"
#include "boot_record.h"
#include "region_defs.h"
#include "tfm_boot_status.h"
#include "target.h"
#include "../ext/mcuboot/bootutil/src/bootutil_priv.h"
#include "bootutil/image.h"
#include "bootutil/sha256.h"
#include "flash_map/flash_map.h"
#include <stdint.h>
#include <string.h>
#include <stdio.h>

#define SHA256_HASH_SIZE    (32u)
#if defined(MCUBOOT_SIGN_RSA) && defined(MCUBOOT_HW_KEY)
#   define SIG_BUF_SIZE     (MCUBOOT_SIGN_RSA_LEN / 8)
#endif

/*!
 * \def MAX_BOOT_RECORD_SZ
 *
 * \brief Maximum size of the measured boot record.
 *
 * Its size can be calculated based on the following aspects:
 *   - There are 5 allowed software component claims,
 *   - SHA256 is used as the measurement method for the other claims.
 * Considering these aspects, the only claim which size can vary is the
 * type of the software component. In case of single image boot it is
 * "NSPE_SPE" which results the maximum boot record size of 96.
 */
#define MAX_BOOT_RECORD_SZ  (96u)

/*!
 * \var shared_memory_init_done
 *
 * \brief Indicates whether shared memory area was already initialized.
 *
 */
static uint32_t shared_memory_init_done;

/*!
 * \def SHARED_MEMORY_UNINITIALZED
 *
 * \brief Indicates that shared memory is uninitialized.
 */
#define SHARED_MEMORY_UNINITIALZED (0u)

/*!
 * \def SHARED_MEMORY_INITIALZED
 *
 * \brief Indicates that shared memory was already initialized.
 */
#define SHARED_MEMORY_INITIALZED   (1u)

/* Compile time check to verify that shared data region is not overlapping with
 * non-secure data area.
 */
#if ((BOOT_TFM_SHARED_DATA_BASE  >= NS_DATA_START && \
      BOOT_TFM_SHARED_DATA_BASE  <= NS_DATA_LIMIT) || \
     (BOOT_TFM_SHARED_DATA_LIMIT >= NS_DATA_START && \
      BOOT_TFM_SHARED_DATA_LIMIT <= NS_DATA_LIMIT))
#error "Shared data area and non-secure data area is overlapping"
#endif

#ifdef MCUBOOT_INDIVIDUAL_CLAIMS
/*!
 * \brief Add the measurement data of SW component to the shared memory area
 *
 * Measurements data are:
 *  - measurement value:  Hash of the image, read out from the image's manifest
 *                        section.
 *  - measurement type:   Short test description: SHA256, etc.
 *  - signer ID:          Hash of the image public key, read out from the
 *                        image's manifest section.
 *
 * \param[in]  sw_module  Identifier of the SW component
 * \param[in]  hdr        Pointer to the image header stored in RAM
 * \param[in]  fap        Pointer to the flash area where image is stored
 *
 * \return Returns error code as specified in \ref boot_status_err_t
 */
static enum boot_status_err_t
boot_save_sw_measurements(uint8_t sw_module,
                          const struct image_header *hdr,
                          const struct flash_area *fap)
{
    struct image_tlv_iter it;
    uint32_t offset;
    uint16_t len;
    uint8_t type;
    uint8_t buf[32];
    int32_t res;
    uint16_t ias_minor;
    enum shared_memory_err_t res2;
    char measure_type[] = "SHA256";
#if defined(MCUBOOT_SIGN_RSA) && defined(MCUBOOT_HW_KEY)
    /* Few extra bytes for encoding and for public exponent */
    uint8_t key_buf[SIG_BUF_SIZE + 24];
    bootutil_sha256_context sha256_ctx;
#endif

    /* Manifest data is concatenated to the end of the image. It is encoded in
     * TLV format.
     */

    res = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, false);
    if (res) {
        return BOOT_STATUS_ERROR;
    }

    /* Iterates over the manifest data and copy the relevant attributes to the
     * shared data area:
     *  - image hash:      SW component measurement value
     *  - public key hash: Signer ID
     */
    while (true) {
        res = bootutil_tlv_iter_next(&it, &offset, &len, &type);
        if (res < 0) {
            return BOOT_STATUS_ERROR;
        } else if (res > 0) {
            break;
        }

        if (type == IMAGE_TLV_SHA256) {
            /* Get the image's hash value from the manifest section */
            if (len != sizeof(buf)) { /* SHA256 - 32 bytes */
                return BOOT_STATUS_ERROR;
            }

            res = LOAD_IMAGE_DATA(hdr, fap, offset, buf, len);
            if (res) {
                return BOOT_STATUS_ERROR;
            }

            /* Add the image's hash value to the shared data area */
            ias_minor = SET_IAS_MINOR(sw_module, SW_MEASURE_VALUE);
            res2 = boot_add_data_to_shared_area(TLV_MAJOR_IAS,
                                                ias_minor,
                                                len,
                                                buf);
            if (res2) {
                return BOOT_STATUS_ERROR;
            }

            /* Add the measurement type to the shared data area */
            ias_minor = SET_IAS_MINOR(sw_module, SW_MEASURE_TYPE);
            res2 = boot_add_data_to_shared_area(TLV_MAJOR_IAS,
                                                ias_minor,
                                                sizeof(measure_type) - 1,
                                                (const uint8_t *)measure_type);
            if (res2) {
                return BOOT_STATUS_ERROR;
            }

#ifdef MCUBOOT_SIGN_RSA
#ifndef MCUBOOT_HW_KEY
        } else if (type == IMAGE_TLV_KEYHASH) {
            /* Get the hash of the public key from the manifest section */
            if (len != sizeof(buf)) { /* SHA256 - 32 bytes */
                return BOOT_STATUS_ERROR;
            }

            res = LOAD_IMAGE_DATA(hdr, fap, offset, buf, len);
            if (res) {
                return BOOT_STATUS_ERROR;
            }
#else /* MCUBOOT_HW_KEY */
        } else if (type == IMAGE_TLV_KEY) {
            /* Get the public key from the manifest section. */
            if (len > sizeof(key_buf)) {
                return BOOT_STATUS_ERROR;
            }
            res = LOAD_IMAGE_DATA(hdr, fap, offset, key_buf, len);
            if (res) {
                return BOOT_STATUS_ERROR;
            }

            /* Calculate the hash of the public key. */
            bootutil_sha256_init(&sha256_ctx);
            bootutil_sha256_update(&sha256_ctx, key_buf, len);
            bootutil_sha256_finish(&sha256_ctx, buf);
#endif /* MCUBOOT_HW_KEY */

            /* Add the hash of the public key to the shared data area */
            ias_minor = SET_IAS_MINOR(sw_module, SW_SIGNER_ID);
            res2 = boot_add_data_to_shared_area(TLV_MAJOR_IAS,
                                                ias_minor,
                                                SHA256_HASH_SIZE,
                                                buf);
            if (res2) {
                return BOOT_STATUS_ERROR;
            }
#endif
        }
    }

    return BOOT_STATUS_OK;
}

/*!
 * \brief Add a type identifier(short test name) of SW component to the shared
 *        memory area
 *
 * \param[in]  sw_module  Identifier of the SW component
 *
 * \return Returns error code as specified in \ref boot_status_err_t
 */
static enum boot_status_err_t
boot_save_sw_type(uint8_t sw_module)
{
    uint16_t ias_minor;
    enum shared_memory_err_t res;
    const char *sw_type;
    static const char sw_comp_s[] = "SPE";
    static const char sw_comp_ns[] = "NSPE";
    static const char sw_comp_ns_s[] = "NSPE_SPE";

    switch (sw_module) {
    case SW_SPE:
        sw_type = sw_comp_s;
        break;
    case SW_NSPE:
        sw_type = sw_comp_ns;
        break;
    case SW_S_NS:
        sw_type = sw_comp_ns_s;
        break;
    default:
        return BOOT_STATUS_ERROR;
    }

    /* Add the type identifier of the SW component to the shared data area */
    ias_minor = SET_IAS_MINOR(sw_module, SW_TYPE);
    res = boot_add_data_to_shared_area(TLV_MAJOR_IAS,
                                       ias_minor,
                                       strlen(sw_type),
                                       (const uint8_t *)sw_type);
    if (res) {
        return BOOT_STATUS_ERROR;
    }

    return BOOT_STATUS_OK;
}

/*!
 * \brief Add the version of SW component to the shared memory area
 *
 * \param[in]  sw_module  Identifier of the SW component
 * \param[in]  hdr        Pointer to the image header stored in RAM
 *
 * \return Returns error code as specified in \ref boot_status_err_t
 */
static enum boot_status_err_t
boot_save_sw_version(uint8_t sw_module,
                     const struct image_header *hdr)
{
    int32_t cnt;
    enum shared_memory_err_t res;
    char sw_version[14]; /* 8bit.8bit.16bit: 3 + 1 + 3 + 1 + 5 + 1 */
    uint16_t ias_minor;

    /* FixMe: snprintf can be replaced with a custom implementation */
    cnt = snprintf(sw_version, sizeof(sw_version), "%u.%u.%u",
                   hdr->ih_ver.iv_major,
                   hdr->ih_ver.iv_minor,
                   hdr->ih_ver.iv_revision);
    if (cnt < 0 || cnt >= sizeof(sw_version)) {
        return BOOT_STATUS_ERROR;
    }

    /* Add the version of the SW component to the shared data area */
    ias_minor = SET_IAS_MINOR(sw_module, SW_VERSION);
    res = boot_add_data_to_shared_area(TLV_MAJOR_IAS,
                                       ias_minor,
                                       cnt,
                                       (const uint8_t *)sw_version);
    if (res) {
        return BOOT_STATUS_ERROR;
    }

    return BOOT_STATUS_OK;
}
#endif /* MCUBOOT_INDIVIDUAL_CLAIMS */

/* See in boot_record.h */
enum shared_memory_err_t
boot_add_data_to_shared_area(uint8_t        major_type,
                             uint16_t       minor_type,
                             size_t         size,
                             const uint8_t *data)
{
    struct shared_data_tlv_entry tlv_entry = {0};
    struct tfm_boot_data *boot_data;
    uint8_t *next_tlv;
    uint16_t boot_data_size;
    uintptr_t tlv_end, offset;

    boot_data = (struct tfm_boot_data *)BOOT_TFM_SHARED_DATA_BASE;

    /* Check whether first time to call this function. If does then initialise
     * shared data area.
     */
    if (shared_memory_init_done == SHARED_MEMORY_UNINITIALZED) {
        memset((void *)BOOT_TFM_SHARED_DATA_BASE, 0, BOOT_TFM_SHARED_DATA_SIZE);
        boot_data->header.tlv_magic   = SHARED_DATA_TLV_INFO_MAGIC;
        boot_data->header.tlv_tot_len = SHARED_DATA_HEADER_SIZE;
        shared_memory_init_done = SHARED_MEMORY_INITIALZED;
    }

    /* Check whether TLV entry is already added.
     * Get the boundaries of TLV section
     */
    tlv_end = BOOT_TFM_SHARED_DATA_BASE + boot_data->header.tlv_tot_len;
    offset  = BOOT_TFM_SHARED_DATA_BASE + SHARED_DATA_HEADER_SIZE;

    /* Iterates over the TLV section looks for the same entry if found then
     * returns with error: SHARED_MEMORY_OVERWRITE
     */
    for (; offset < tlv_end; offset += tlv_entry.tlv_len) {
        /* Create local copy to avoid unaligned access */
        memcpy(&tlv_entry, (const void *)offset, SHARED_DATA_ENTRY_HEADER_SIZE);
        if (GET_MAJOR(tlv_entry.tlv_type) == major_type &&
            GET_MINOR(tlv_entry.tlv_type) == minor_type) {
            return SHARED_MEMORY_OVERWRITE;
        }
    }

    /* Add TLV entry */
    tlv_entry.tlv_type = SET_TLV_TYPE(major_type, minor_type);
    tlv_entry.tlv_len  = SHARED_DATA_ENTRY_SIZE(size);

    if (!boot_u16_safe_add(&boot_data_size, boot_data->header.tlv_tot_len,
                           tlv_entry.tlv_len)) {
        return SHARED_MEMORY_GEN_ERROR;
    }

    /* Verify overflow of shared area */
    if (boot_data_size > BOOT_TFM_SHARED_DATA_SIZE) {
        return SHARED_MEMORY_OVERFLOW;
    }

    next_tlv = (uint8_t *)boot_data + boot_data->header.tlv_tot_len;
    memcpy(next_tlv, &tlv_entry, SHARED_DATA_ENTRY_HEADER_SIZE);

    next_tlv += SHARED_DATA_ENTRY_HEADER_SIZE;
    memcpy(next_tlv, data, size);

    boot_data->header.tlv_tot_len += tlv_entry.tlv_len;

    return SHARED_MEMORY_OK;
}

/* See in boot_record.h */
enum boot_status_err_t
boot_save_boot_status(uint8_t sw_module,
                      const struct image_header *hdr,
                      const struct flash_area *fap)
{
#ifdef MCUBOOT_INDIVIDUAL_CLAIMS
    /* This implementation is deprecated and will probably
     * be removed in the future.
     */

    enum boot_status_err_t res;

    res = boot_save_sw_type(sw_module);
    if (res) {
        return res;
    }

    res = boot_save_sw_version(sw_module, hdr);
    if (res) {
        return res;
    }

    res = boot_save_sw_measurements(sw_module, hdr, fap);
    if (res) {
        return res;
    }

    return BOOT_STATUS_OK;

#else /* MCUBOOT_INDIVIDUAL_CLAIMS */

    struct image_tlv_iter it;
    uint32_t offset;
    uint16_t len;
    uint8_t type;
    size_t record_len = 0;
    uint8_t image_hash[32]; /* SHA256 - 32 Bytes */
    uint8_t buf[MAX_BOOT_RECORD_SZ];
    uint32_t boot_record_found = 0;
    uint32_t hash_found = 0;
    uint16_t ias_minor;
    int32_t res;
    enum shared_memory_err_t res2;

    /* Manifest data is concatenated to the end of the image.
     * It is encoded in TLV format.
     */

    res = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, false);
    if (res) {
        return BOOT_STATUS_ERROR;
    }

    /* Traverse through the TLV area to find the boot record
     * and image hash TLVs.
     */
    while (true) {
        res = bootutil_tlv_iter_next(&it, &offset, &len, &type);
        if (res < 0) {
            return BOOT_STATUS_ERROR;
        } else if (res > 0) {
            break;
        }

        if (type == IMAGE_TLV_BOOT_RECORD) {
            if (len > sizeof(buf)) {
                return BOOT_STATUS_ERROR;
            }
            res = LOAD_IMAGE_DATA(hdr, fap, offset, buf, len);
            if (res) {
                return BOOT_STATUS_ERROR;
            }

            record_len = len;
            boot_record_found = 1;

        } else if (type == IMAGE_TLV_SHA256) {
            /* Get the image's hash value from the manifest section. */
            if (len > sizeof(image_hash)) {
                return BOOT_STATUS_ERROR;
            }
            res = LOAD_IMAGE_DATA(hdr, fap, offset, image_hash, len);
            if (res) {
                return BOOT_STATUS_ERROR;
            }

            hash_found = 1;

            /* The boot record TLV is part of the protected TLV area which is
             * located before the other parts of the TLV area (including the
             * image hash) so at this point it is okay to break the loop
             * as the boot record TLV should have already been found.
             */
            break;
        }
    }


    if (!boot_record_found || !hash_found) {
        return BOOT_STATUS_ERROR;
    }

    /* Update the measurement value (hash of the image) data item in the
     * boot record. It is always the last item in the structure to make
     * it easy to calculate its position.
     * The image hash is computed over the image header, the image itself and
     * the protected TLV area (which should already include the image hash as
     * part of the boot record TLV). For this reason this field has been
     * filled with zeros during the image signing process.
     */
    offset = record_len - sizeof(image_hash);
    /* Avoid buffer overflow. */
    if ((offset + sizeof(image_hash)) > sizeof(buf)) {
        return BOOT_STATUS_ERROR;
    }
    memcpy(buf + offset, image_hash, sizeof(image_hash));

    /* Add the CBOR encoded boot record to the shared data area. */
    ias_minor = SET_IAS_MINOR(sw_module, SW_BOOT_RECORD);
    res2 = boot_add_data_to_shared_area(TLV_MAJOR_IAS,
                                        ias_minor,
                                        record_len,
                                        buf);
    if (res2) {
        return BOOT_STATUS_ERROR;
    }

    return BOOT_STATUS_OK;

#endif /* MCUBOOT_INDIVIDUAL_CLAIMS */
}