blob: 643977f98ca8ed4e547356c3efbac5efd0c4c2ad [file] [log] [blame]
Dominik Ermel8101c0c2020-05-19 13:01:16 +00001/*
2 * SPDX-License-Identifier: Apache-2.0
3 *
4 * Copyright (c) 2020 Nordic Semiconductor ASA
Tamas Banee6615d2020-09-30 07:58:48 +01005 * Copyright (c) 2020 Arm Limited
Dominik Ermel8101c0c2020-05-19 13:01:16 +00006 */
7
8#include <assert.h>
9#include "bootutil/image.h"
10#include "bootutil_priv.h"
11#include "bootutil/bootutil_log.h"
Tamas Banee6615d2020-09-30 07:58:48 +010012#include "bootutil/fault_injection_hardening.h"
Dominik Ermel8101c0c2020-05-19 13:01:16 +000013
14#include "mcuboot_config/mcuboot_config.h"
15
Carlos Falgueras GarcĂ­aa4b4b0f2021-06-22 10:00:22 +020016BOOT_LOG_MODULE_DECLARE(mcuboot);
Dominik Ermel8101c0c2020-05-19 13:01:16 +000017
18/* Variables passed outside of unit via poiters. */
19static const struct flash_area *_fa_p;
20static struct image_header _hdr = { 0 };
21
Wouter Cappelle953a7612021-05-03 16:53:05 +020022#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) || defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE)
Dominik Ermel8101c0c2020-05-19 13:01:16 +000023/**
24 * Validate hash of a primary boot image.
25 *
26 * @param[in] fa_p flash area pointer
27 * @param[in] hdr boot image header pointer
28 *
Tamas Banee6615d2020-09-30 07:58:48 +010029 * @return FIH_SUCCESS on success, error code otherwise
Dominik Ermel8101c0c2020-05-19 13:01:16 +000030 */
Wouter Cappelle953a7612021-05-03 16:53:05 +020031fih_int
Dominik Ermel8101c0c2020-05-19 13:01:16 +000032boot_image_validate(const struct flash_area *fa_p,
33 struct image_header *hdr)
34{
35 static uint8_t tmpbuf[BOOT_TMPBUF_SZ];
Tamas Banee6615d2020-09-30 07:58:48 +010036 fih_int fih_rc = FIH_FAILURE;
Dominik Ermel8101c0c2020-05-19 13:01:16 +000037
Dominik Ermeld8db0252020-10-07 11:22:45 +000038 /* NOTE: The first argument to boot_image_validate, for enc_state pointer,
39 * is allowed to be NULL only because the single image loader compiles
40 * with BOOT_IMAGE_NUMBER == 1, which excludes the code that uses
41 * the pointer from compilation.
Dominik Ermel8101c0c2020-05-19 13:01:16 +000042 */
43 /* Validate hash */
Wouter Cappelle76792152022-01-19 17:28:55 +010044 if (IS_ENCRYPTED(hdr))
Wouter Cappelle953a7612021-05-03 16:53:05 +020045 {
46 /* Clear the encrypted flag we didn't supply a key
47 * This flag could be set if there was a decryption in place
48 * was performed. We will try to validate the image, and if still
49 * encrypted the validation will fail, and go in panic mode
50 */
Wouter Cappelle76792152022-01-19 17:28:55 +010051 hdr->ih_flags &= ~(ENCRYPTIONFLAGS);
Wouter Cappelle953a7612021-05-03 16:53:05 +020052 }
Tamas Banee6615d2020-09-30 07:58:48 +010053 FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, hdr, fa_p, tmpbuf,
54 BOOT_TMPBUF_SZ, NULL, 0, NULL);
Dominik Ermel8101c0c2020-05-19 13:01:16 +000055
Tamas Banee6615d2020-09-30 07:58:48 +010056 FIH_RET(fih_rc);
Dominik Ermel8101c0c2020-05-19 13:01:16 +000057}
Wouter Cappelle953a7612021-05-03 16:53:05 +020058#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT || MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE*/
Dominik Ermel8101c0c2020-05-19 13:01:16 +000059
60
61/**
62 * Attempts to load image header from flash; verifies flash header fields.
63 *
64 * @param[in] fa_p flash area pointer
65 * @param[out] hdr buffer for image header
66 *
67 * @return 0 on success, error code otherwise
68 */
69static int
70boot_image_load_header(const struct flash_area *fa_p,
71 struct image_header *hdr)
72{
73 uint32_t size;
74 int rc = flash_area_read(fa_p, 0, hdr, sizeof *hdr);
75
76 if (rc != 0) {
77 rc = BOOT_EFLASH;
78 BOOT_LOG_ERR("Failed reading image header");
79 return BOOT_EFLASH;
80 }
81
82 if (hdr->ih_magic != IMAGE_MAGIC) {
83 BOOT_LOG_ERR("Bad image magic 0x%lx", (unsigned long)hdr->ih_magic);
84
85 return BOOT_EBADIMAGE;
86 }
87
88 if (hdr->ih_flags & IMAGE_F_NON_BOOTABLE) {
89 BOOT_LOG_ERR("Image not bootable");
90
91 return BOOT_EBADIMAGE;
92 }
93
94 if (!boot_u32_safe_add(&size, hdr->ih_img_size, hdr->ih_hdr_size) ||
Dominik Ermel036d5212021-07-01 11:07:41 +000095 size >= flash_area_get_size(fa_p)) {
Dominik Ermel8101c0c2020-05-19 13:01:16 +000096 return BOOT_EBADIMAGE;
97 }
98
99 return 0;
100}
101
Wouter Cappelle953a7612021-05-03 16:53:05 +0200102#ifdef MCUBOOT_ENC_IMAGES
103
104/**
105 * Validate hash of a primary boot image doing on the fly decryption as well
106 *
107 * @param[in] fa_p flash area pointer
108 * @param[in] hdr boot image header pointer
109 *
110 * @return FIH_SUCCESS on success, error code otherwise
111 */
112inline static fih_int
113boot_image_validate_encrypted(const struct flash_area *fa_p,
114 struct image_header *hdr)
115{
116 static uint8_t tmpbuf[BOOT_TMPBUF_SZ];
117 fih_int fih_rc = FIH_FAILURE;
118
119 struct boot_loader_state boot_data;
120 struct boot_loader_state *state = &boot_data;
121 struct boot_status _bs;
122 struct boot_status *bs = &_bs;
123 uint8_t image_index;
124 int rc;
125
126 memset(&boot_data, 0, sizeof(struct boot_loader_state));
127 image_index = BOOT_CURR_IMG(state);
128 if (MUST_DECRYPT(fa_p, image_index, hdr)) {
129 rc = boot_enc_load(BOOT_CURR_ENC(state), image_index, hdr, fa_p, bs);
130 if (rc < 0) {
131 FIH_RET(fih_rc);
132 }
133 if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 0, bs)) {
134 FIH_RET(fih_rc);
135 }
136 }
137 FIH_CALL(bootutil_img_validate, fih_rc, BOOT_CURR_ENC(state), image_index,
138 hdr, fa_p, tmpbuf, BOOT_TMPBUF_SZ, NULL, 0, NULL);
139
140 FIH_RET(fih_rc);
141}
142
143/*
144 * Compute the total size of the given image. Includes the size of
145 * the TLVs.
146 */
147static int
148read_image_size(const struct flash_area *fa_p,
149 struct image_header *hdr,
150 uint32_t *size)
151{
152 struct image_tlv_info info;
153 uint32_t off;
154 uint32_t protect_tlv_size;
155 int rc;
156
157 off = BOOT_TLV_OFF(hdr);
158
159 if (flash_area_read(fa_p, off, &info, sizeof(info))) {
160 rc = BOOT_EFLASH;
161 goto done;
162 }
163
164 protect_tlv_size = hdr->ih_protect_tlv_size;
165 if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) {
166 if (protect_tlv_size != info.it_tlv_tot) {
167 rc = BOOT_EBADIMAGE;
168 goto done;
169 }
170
171 if (flash_area_read(fa_p, off + info.it_tlv_tot, &info, sizeof(info))) {
172 rc = BOOT_EFLASH;
173 goto done;
174 }
175 } else if (protect_tlv_size != 0) {
176 rc = BOOT_EBADIMAGE;
177 goto done;
178 }
179
180 if (info.it_magic != IMAGE_TLV_INFO_MAGIC) {
181 rc = BOOT_EBADIMAGE;
182 goto done;
183 }
184
185 *size = off + protect_tlv_size + info.it_tlv_tot;
186 rc = 0;
187
188done:
189 return rc;
190}
191
192
193/**
194 * reads, decrypts in memory & write back the decrypted image in the same region
195 * This function is NOT power failsafe since the image is decrypted in ram (stack)
196 *
197 * @param flash_area The ID of the source flash area.
198 * @param off_src The offset within the flash area to
199 * copy from.
200 * @param sz The number of bytes to copy. should match erase sector
201 *
202 * @return 0 on success; nonzero on failure.
203 */
204int
205decrypt_region_inplace(struct boot_loader_state *state,
206 const struct flash_area *fap,
207 struct image_header *hdr,
208 uint32_t off, uint32_t sz)
209{
210 uint32_t bytes_copied;
211 int chunk_sz;
212 int rc;
213 uint32_t tlv_off;
214 size_t blk_off;
215 uint16_t idx;
216 uint32_t blk_sz;
217 uint8_t image_index;
218
219 static uint8_t buf[1024] __attribute__((aligned));
220 assert(sz <= sizeof buf);
221
222 bytes_copied = 0;
223 while (bytes_copied < sz) {
224 if (sz - bytes_copied > sizeof buf) {
225 chunk_sz = sizeof buf;
226 } else {
227 chunk_sz = sz - bytes_copied;
228 }
229
230 rc = flash_area_read(fap, off + bytes_copied, buf, chunk_sz);
231 if (rc != 0) {
232 return BOOT_EFLASH;
233 }
234
235 image_index = BOOT_CURR_IMG(state);
236 if (IS_ENCRYPTED(hdr)) {
237 blk_sz = chunk_sz;
238 idx = 0;
239 if (off + bytes_copied < hdr->ih_hdr_size) {
240 /* do not decrypt header */
241 if (hdr->ih_hdr_size > (off + bytes_copied + chunk_sz)) {
242 /* all bytes in header, skip decryption */
243 blk_sz = 0;
244 }
245 else {
246 blk_sz = off + bytes_copied + chunk_sz - hdr->ih_hdr_size;
247 }
248
249 blk_off = 0;
250 idx = hdr->ih_hdr_size;
251 } else {
252 blk_off = ((off + bytes_copied) - hdr->ih_hdr_size) & 0xf;
253 }
254 tlv_off = BOOT_TLV_OFF(hdr);
255 if (off + bytes_copied + chunk_sz > tlv_off) {
256 /* do not decrypt TLVs */
257 if (off + bytes_copied >= tlv_off) {
258 blk_sz = 0;
259 } else {
260 blk_sz = tlv_off - (off + bytes_copied);
261 }
262 }
263 boot_encrypt(BOOT_CURR_ENC(state), image_index, fap,
264 (off + bytes_copied + idx) - hdr->ih_hdr_size, blk_sz,
265 blk_off, &buf[idx]);
266 }
267 rc = flash_area_erase(fap, off + bytes_copied, chunk_sz);
268 if (rc != 0) {
269 return BOOT_EFLASH;
270 }
271 rc = flash_area_write(fap, off + bytes_copied, buf, chunk_sz);
272 if (rc != 0) {
273 return BOOT_EFLASH;
274 }
275
276 bytes_copied += chunk_sz;
277
278 MCUBOOT_WATCHDOG_FEED();
279 }
280
281 return 0;
282}
283
284/**
285 * Check if a image was encrypted into the first slot, and decrypt it
286 * in place. this operation is not power failsafe.
287 *
288 * The operation is done by checking the last flash sector, and using it as a
289 * temporarely scratch partition. The
290 *
291 * @param[in] fa_p flash area pointer
292 * @param[in] hdr boot image header pointer
293 *
294 * @return FIH_SUCCESS on success, error code otherwise
295 */
296inline static fih_int
297decrypt_image_inplace(const struct flash_area *fa_p,
298 struct image_header *hdr)
299{
300 fih_int fih_rc = FIH_FAILURE;
301 int rc;
302 struct boot_loader_state boot_data;
303 struct boot_loader_state *state = &boot_data;
304 struct boot_status _bs;
305 struct boot_status *bs = &_bs;
306 size_t size;
307 size_t sect_size;
308 size_t sect_count;
309 size_t sect;
310 uint8_t image_index;
311 struct flash_sector sector;
312
313 memset(&boot_data, 0, sizeof(struct boot_loader_state));
314 memset(&_bs, 0, sizeof(struct boot_status));
315
316 /* Get size from last sector to know page/sector erase size */
317 rc = flash_area_sector_from_off(boot_status_off(fa_p), &sector);
318
319
320 image_index = BOOT_CURR_IMG(state);
321
322 if (MUST_DECRYPT(fa_p, image_index, hdr)) {
323#if 0 //Skip this step?, the image will just not boot if it's not decrypted properly
324 /* First check if the encrypted image is a good image before decrypting */
325 FIH_CALL(boot_image_validate_encrypted,fih_rc,_fa_p,&_hdr);
326 if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
327 FIH_RET(fih_rc);
328 }
329#endif
330 memset(&boot_data, 0, sizeof(struct boot_loader_state));
331 /* Load the encryption keys into cache */
332 rc = boot_enc_load(BOOT_CURR_ENC(state), image_index, hdr, fa_p, bs);
333 if (rc < 0) {
334 FIH_RET(fih_rc);
335 }
336 if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 0, bs)) {
337 FIH_RET(fih_rc);
338 }
339 }
340 else
341 {
342 /* Expected encrypted image! */
343 FIH_RET(fih_rc);
344 }
345
346 uint32_t src_size = 0;
347 rc = read_image_size(fa_p,hdr, &src_size);
348 if (rc != 0) {
349 FIH_RET(fih_rc);
350 }
351
352 sect_size = sector.fs_size;
353 sect_count = fa_p->fa_size / sect_size;
354 for (sect = 0, size = 0; size < src_size && sect < sect_count; sect++) {
355 rc = decrypt_region_inplace(state, fa_p,hdr, size, sect_size);
356 if (rc != 0) {
357 FIH_RET(fih_rc);
358 }
359 size += sect_size;
360 }
361
362 fih_rc = FIH_SUCCESS;
363 FIH_RET(fih_rc);
364}
365
366int
367boot_handle_enc_fw()
368{
369 int rc = -1;
370 fih_int fih_rc = FIH_FAILURE;
371
372 rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(0), &_fa_p);
373 assert(rc == 0);
374
375 rc = boot_image_load_header(_fa_p, &_hdr);
376 if (rc != 0) {
377 goto out;
378 }
379
380 if (IS_ENCRYPTED(&_hdr)) {
381 //encrypted, we need to decrypt in place
382 FIH_CALL(decrypt_image_inplace,fih_rc,_fa_p,&_hdr);
383 if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
384 rc = -1;
385 goto out;
386 }
387 }
388 else
389 {
390 rc = 0;
391 }
392
393out:
394 flash_area_close(_fa_p);
395 return rc;
396}
397#endif
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000398
399/**
400 * Gather information on image and prepare for booting.
401 *
402 * @parami[out] rsp Parameters for booting image, on success
403 *
Tamas Banee6615d2020-09-30 07:58:48 +0100404 * @return FIH_SUCCESS on success; nonzero on failure.
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000405 */
Tamas Banee6615d2020-09-30 07:58:48 +0100406fih_int
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000407boot_go(struct boot_rsp *rsp)
408{
409 int rc = -1;
Tamas Banee6615d2020-09-30 07:58:48 +0100410 fih_int fih_rc = FIH_FAILURE;
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000411
412 rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(0), &_fa_p);
413 assert(rc == 0);
414
415 rc = boot_image_load_header(_fa_p, &_hdr);
416 if (rc != 0)
417 goto out;
418
419#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT
Tamas Banee6615d2020-09-30 07:58:48 +0100420 FIH_CALL(boot_image_validate, fih_rc, _fa_p, &_hdr);
421 if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000422 goto out;
423 }
Tamas Banee6615d2020-09-30 07:58:48 +0100424#else
425 fih_rc = FIH_SUCCESS;
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000426#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */
427
Dominik Ermel036d5212021-07-01 11:07:41 +0000428 rsp->br_flash_dev_id = flash_area_get_device_id(_fa_p);
429 rsp->br_image_off = flash_area_get_off(_fa_p);
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000430 rsp->br_hdr = &_hdr;
431
432out:
433 flash_area_close(_fa_p);
Tamas Banee6615d2020-09-30 07:58:48 +0100434
435 FIH_RET(fih_rc);
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000436}