blob: 260f9cc8e9b1c43a877d833b67e8bb0d96ebd389 [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
Wouter Cappellebb7a39d2021-05-03 16:44:44 +020061inline static fih_int
62boot_image_validate_once(const struct flash_area *fa_p,
63 struct image_header *hdr)
64{
65 static struct boot_swap_state state;
66 int rc;
67 fih_int fih_rc = FIH_FAILURE;
68
69 memset(&state, 0, sizeof(struct boot_swap_state));
70 rc = boot_read_swap_state(fa_p, &state);
71 if (rc != 0)
72 FIH_RET(FIH_FAILURE);
73 if (state.magic != BOOT_MAGIC_GOOD
74 || state.image_ok != BOOT_FLAG_SET) {
75 /* At least validate the image once */
76 FIH_CALL(boot_image_validate, fih_rc, fa_p, hdr);
77 if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
78 FIH_RET(FIH_FAILURE);
79 }
80 if (state.magic != BOOT_MAGIC_GOOD) {
81 rc = boot_write_magic(fa_p);
82 if (rc != 0)
83 FIH_RET(FIH_FAILURE);
84 }
85 rc = boot_write_image_ok(fa_p);
86 if (rc != 0)
87 FIH_RET(FIH_FAILURE);
88 }
89 FIH_RET(FIH_SUCCESS);
90}
91
Dominik Ermel8101c0c2020-05-19 13:01:16 +000092/**
93 * Attempts to load image header from flash; verifies flash header fields.
94 *
95 * @param[in] fa_p flash area pointer
96 * @param[out] hdr buffer for image header
97 *
98 * @return 0 on success, error code otherwise
99 */
100static int
101boot_image_load_header(const struct flash_area *fa_p,
102 struct image_header *hdr)
103{
104 uint32_t size;
105 int rc = flash_area_read(fa_p, 0, hdr, sizeof *hdr);
106
107 if (rc != 0) {
108 rc = BOOT_EFLASH;
109 BOOT_LOG_ERR("Failed reading image header");
110 return BOOT_EFLASH;
111 }
112
113 if (hdr->ih_magic != IMAGE_MAGIC) {
114 BOOT_LOG_ERR("Bad image magic 0x%lx", (unsigned long)hdr->ih_magic);
115
116 return BOOT_EBADIMAGE;
117 }
118
119 if (hdr->ih_flags & IMAGE_F_NON_BOOTABLE) {
120 BOOT_LOG_ERR("Image not bootable");
121
122 return BOOT_EBADIMAGE;
123 }
124
125 if (!boot_u32_safe_add(&size, hdr->ih_img_size, hdr->ih_hdr_size) ||
Dominik Ermel036d5212021-07-01 11:07:41 +0000126 size >= flash_area_get_size(fa_p)) {
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000127 return BOOT_EBADIMAGE;
128 }
129
130 return 0;
131}
132
Wouter Cappelle953a7612021-05-03 16:53:05 +0200133#ifdef MCUBOOT_ENC_IMAGES
134
135/**
136 * Validate hash of a primary boot image doing on the fly decryption as well
137 *
138 * @param[in] fa_p flash area pointer
139 * @param[in] hdr boot image header pointer
140 *
141 * @return FIH_SUCCESS on success, error code otherwise
142 */
143inline static fih_int
144boot_image_validate_encrypted(const struct flash_area *fa_p,
145 struct image_header *hdr)
146{
147 static uint8_t tmpbuf[BOOT_TMPBUF_SZ];
148 fih_int fih_rc = FIH_FAILURE;
149
150 struct boot_loader_state boot_data;
151 struct boot_loader_state *state = &boot_data;
152 struct boot_status _bs;
153 struct boot_status *bs = &_bs;
154 uint8_t image_index;
155 int rc;
156
157 memset(&boot_data, 0, sizeof(struct boot_loader_state));
158 image_index = BOOT_CURR_IMG(state);
159 if (MUST_DECRYPT(fa_p, image_index, hdr)) {
160 rc = boot_enc_load(BOOT_CURR_ENC(state), image_index, hdr, fa_p, bs);
161 if (rc < 0) {
162 FIH_RET(fih_rc);
163 }
164 if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 0, bs)) {
165 FIH_RET(fih_rc);
166 }
167 }
168 FIH_CALL(bootutil_img_validate, fih_rc, BOOT_CURR_ENC(state), image_index,
169 hdr, fa_p, tmpbuf, BOOT_TMPBUF_SZ, NULL, 0, NULL);
170
171 FIH_RET(fih_rc);
172}
173
174/*
175 * Compute the total size of the given image. Includes the size of
176 * the TLVs.
177 */
178static int
179read_image_size(const struct flash_area *fa_p,
180 struct image_header *hdr,
181 uint32_t *size)
182{
183 struct image_tlv_info info;
184 uint32_t off;
185 uint32_t protect_tlv_size;
186 int rc;
187
188 off = BOOT_TLV_OFF(hdr);
189
190 if (flash_area_read(fa_p, off, &info, sizeof(info))) {
191 rc = BOOT_EFLASH;
192 goto done;
193 }
194
195 protect_tlv_size = hdr->ih_protect_tlv_size;
196 if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) {
197 if (protect_tlv_size != info.it_tlv_tot) {
198 rc = BOOT_EBADIMAGE;
199 goto done;
200 }
201
202 if (flash_area_read(fa_p, off + info.it_tlv_tot, &info, sizeof(info))) {
203 rc = BOOT_EFLASH;
204 goto done;
205 }
206 } else if (protect_tlv_size != 0) {
207 rc = BOOT_EBADIMAGE;
208 goto done;
209 }
210
211 if (info.it_magic != IMAGE_TLV_INFO_MAGIC) {
212 rc = BOOT_EBADIMAGE;
213 goto done;
214 }
215
216 *size = off + protect_tlv_size + info.it_tlv_tot;
217 rc = 0;
218
219done:
220 return rc;
221}
222
223
224/**
225 * reads, decrypts in memory & write back the decrypted image in the same region
226 * This function is NOT power failsafe since the image is decrypted in ram (stack)
227 *
228 * @param flash_area The ID of the source flash area.
229 * @param off_src The offset within the flash area to
230 * copy from.
231 * @param sz The number of bytes to copy. should match erase sector
232 *
233 * @return 0 on success; nonzero on failure.
234 */
235int
236decrypt_region_inplace(struct boot_loader_state *state,
237 const struct flash_area *fap,
238 struct image_header *hdr,
239 uint32_t off, uint32_t sz)
240{
241 uint32_t bytes_copied;
242 int chunk_sz;
243 int rc;
244 uint32_t tlv_off;
245 size_t blk_off;
246 uint16_t idx;
247 uint32_t blk_sz;
248 uint8_t image_index;
249
250 static uint8_t buf[1024] __attribute__((aligned));
251 assert(sz <= sizeof buf);
252
253 bytes_copied = 0;
254 while (bytes_copied < sz) {
255 if (sz - bytes_copied > sizeof buf) {
256 chunk_sz = sizeof buf;
257 } else {
258 chunk_sz = sz - bytes_copied;
259 }
260
261 rc = flash_area_read(fap, off + bytes_copied, buf, chunk_sz);
262 if (rc != 0) {
263 return BOOT_EFLASH;
264 }
265
266 image_index = BOOT_CURR_IMG(state);
267 if (IS_ENCRYPTED(hdr)) {
268 blk_sz = chunk_sz;
269 idx = 0;
270 if (off + bytes_copied < hdr->ih_hdr_size) {
271 /* do not decrypt header */
272 if (hdr->ih_hdr_size > (off + bytes_copied + chunk_sz)) {
273 /* all bytes in header, skip decryption */
274 blk_sz = 0;
275 }
276 else {
277 blk_sz = off + bytes_copied + chunk_sz - hdr->ih_hdr_size;
278 }
279
280 blk_off = 0;
281 idx = hdr->ih_hdr_size;
282 } else {
283 blk_off = ((off + bytes_copied) - hdr->ih_hdr_size) & 0xf;
284 }
285 tlv_off = BOOT_TLV_OFF(hdr);
286 if (off + bytes_copied + chunk_sz > tlv_off) {
287 /* do not decrypt TLVs */
288 if (off + bytes_copied >= tlv_off) {
289 blk_sz = 0;
290 } else {
291 blk_sz = tlv_off - (off + bytes_copied);
292 }
293 }
294 boot_encrypt(BOOT_CURR_ENC(state), image_index, fap,
295 (off + bytes_copied + idx) - hdr->ih_hdr_size, blk_sz,
296 blk_off, &buf[idx]);
297 }
298 rc = flash_area_erase(fap, off + bytes_copied, chunk_sz);
299 if (rc != 0) {
300 return BOOT_EFLASH;
301 }
302 rc = flash_area_write(fap, off + bytes_copied, buf, chunk_sz);
303 if (rc != 0) {
304 return BOOT_EFLASH;
305 }
306
307 bytes_copied += chunk_sz;
308
309 MCUBOOT_WATCHDOG_FEED();
310 }
311
312 return 0;
313}
314
315/**
316 * Check if a image was encrypted into the first slot, and decrypt it
317 * in place. this operation is not power failsafe.
318 *
319 * The operation is done by checking the last flash sector, and using it as a
320 * temporarely scratch partition. The
321 *
322 * @param[in] fa_p flash area pointer
323 * @param[in] hdr boot image header pointer
324 *
325 * @return FIH_SUCCESS on success, error code otherwise
326 */
327inline static fih_int
328decrypt_image_inplace(const struct flash_area *fa_p,
329 struct image_header *hdr)
330{
331 fih_int fih_rc = FIH_FAILURE;
332 int rc;
333 struct boot_loader_state boot_data;
334 struct boot_loader_state *state = &boot_data;
335 struct boot_status _bs;
336 struct boot_status *bs = &_bs;
337 size_t size;
338 size_t sect_size;
339 size_t sect_count;
340 size_t sect;
341 uint8_t image_index;
342 struct flash_sector sector;
343
344 memset(&boot_data, 0, sizeof(struct boot_loader_state));
345 memset(&_bs, 0, sizeof(struct boot_status));
346
347 /* Get size from last sector to know page/sector erase size */
348 rc = flash_area_sector_from_off(boot_status_off(fa_p), &sector);
349
350
351 image_index = BOOT_CURR_IMG(state);
352
353 if (MUST_DECRYPT(fa_p, image_index, hdr)) {
354#if 0 //Skip this step?, the image will just not boot if it's not decrypted properly
355 /* First check if the encrypted image is a good image before decrypting */
356 FIH_CALL(boot_image_validate_encrypted,fih_rc,_fa_p,&_hdr);
357 if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
358 FIH_RET(fih_rc);
359 }
360#endif
361 memset(&boot_data, 0, sizeof(struct boot_loader_state));
362 /* Load the encryption keys into cache */
363 rc = boot_enc_load(BOOT_CURR_ENC(state), image_index, hdr, fa_p, bs);
364 if (rc < 0) {
365 FIH_RET(fih_rc);
366 }
367 if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 0, bs)) {
368 FIH_RET(fih_rc);
369 }
370 }
371 else
372 {
373 /* Expected encrypted image! */
374 FIH_RET(fih_rc);
375 }
376
377 uint32_t src_size = 0;
378 rc = read_image_size(fa_p,hdr, &src_size);
379 if (rc != 0) {
380 FIH_RET(fih_rc);
381 }
382
383 sect_size = sector.fs_size;
384 sect_count = fa_p->fa_size / sect_size;
385 for (sect = 0, size = 0; size < src_size && sect < sect_count; sect++) {
386 rc = decrypt_region_inplace(state, fa_p,hdr, size, sect_size);
387 if (rc != 0) {
388 FIH_RET(fih_rc);
389 }
390 size += sect_size;
391 }
392
393 fih_rc = FIH_SUCCESS;
394 FIH_RET(fih_rc);
395}
396
397int
398boot_handle_enc_fw()
399{
400 int rc = -1;
401 fih_int fih_rc = FIH_FAILURE;
402
403 rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(0), &_fa_p);
404 assert(rc == 0);
405
406 rc = boot_image_load_header(_fa_p, &_hdr);
407 if (rc != 0) {
408 goto out;
409 }
410
411 if (IS_ENCRYPTED(&_hdr)) {
412 //encrypted, we need to decrypt in place
413 FIH_CALL(decrypt_image_inplace,fih_rc,_fa_p,&_hdr);
414 if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
415 rc = -1;
416 goto out;
417 }
418 }
419 else
420 {
421 rc = 0;
422 }
423
424out:
425 flash_area_close(_fa_p);
426 return rc;
427}
428#endif
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000429
430/**
431 * Gather information on image and prepare for booting.
432 *
433 * @parami[out] rsp Parameters for booting image, on success
434 *
Tamas Banee6615d2020-09-30 07:58:48 +0100435 * @return FIH_SUCCESS on success; nonzero on failure.
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000436 */
Tamas Banee6615d2020-09-30 07:58:48 +0100437fih_int
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000438boot_go(struct boot_rsp *rsp)
439{
440 int rc = -1;
Tamas Banee6615d2020-09-30 07:58:48 +0100441 fih_int fih_rc = FIH_FAILURE;
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000442
443 rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(0), &_fa_p);
444 assert(rc == 0);
445
446 rc = boot_image_load_header(_fa_p, &_hdr);
447 if (rc != 0)
448 goto out;
449
450#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT
Tamas Banee6615d2020-09-30 07:58:48 +0100451 FIH_CALL(boot_image_validate, fih_rc, _fa_p, &_hdr);
452 if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000453 goto out;
454 }
Wouter Cappellebb7a39d2021-05-03 16:44:44 +0200455#elif defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE)
456 FIH_CALL(boot_image_validate_once, fih_rc, _fa_p, &_hdr);
457 if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
458 goto out;
459 }
Tamas Banee6615d2020-09-30 07:58:48 +0100460#else
461 fih_rc = FIH_SUCCESS;
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000462#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */
463
Dominik Ermel036d5212021-07-01 11:07:41 +0000464 rsp->br_flash_dev_id = flash_area_get_device_id(_fa_p);
465 rsp->br_image_off = flash_area_get_off(_fa_p);
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000466 rsp->br_hdr = &_hdr;
467
468out:
469 flash_area_close(_fa_p);
Tamas Banee6615d2020-09-30 07:58:48 +0100470
471 FIH_RET(fih_rc);
Dominik Ermel8101c0c2020-05-19 13:01:16 +0000472}