init:初始框架
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2019 Linaro Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef H_BOOTUTIL_BENCH_H__
|
||||
#define H_BOOTUTIL_BENCH_H__
|
||||
|
||||
#include "ignore.h"
|
||||
|
||||
#ifdef MCUBOOT_USE_BENCH
|
||||
|
||||
/* The platform-specific benchmark code should define a
|
||||
* `bench_state_t` type that holds the information needed for the
|
||||
* benchmark. This is generally something small, such as an integer
|
||||
* holding the state. This should also define plat_bench_start and
|
||||
* plat_bench_end, which likely have to be macros so that log messages
|
||||
* come from the right place in the code. */
|
||||
#include <platform-bench.h>
|
||||
|
||||
/*
|
||||
* These are simple barrier-type benchmarks. If a platform has
|
||||
* benchmarks that are enabled, calling `boot_bench_start()` before a
|
||||
* block of code and `boot_bench_stop()` after that block of code will
|
||||
* present this information in some manner (usually through logging).
|
||||
* The details of what is measured and how it is printed are specific
|
||||
* to the platform and the implementation. A pointer to the
|
||||
* platform-specific state should be passed in.
|
||||
*/
|
||||
#define boot_bench_start(_state) do { \
|
||||
plat_bench_start(_state); \
|
||||
} while (0)
|
||||
|
||||
#define boot_bench_stop(_state) do { \
|
||||
plat_bench_stop(_state); \
|
||||
} while (0)
|
||||
|
||||
#else /* not MCUBOOT_USE_BENCH */
|
||||
|
||||
/* The type needs to take space. As long as it remains unused, the C
|
||||
* compiler should eliminate this value entirely. */
|
||||
typedef int bench_state_t;
|
||||
|
||||
/* Without benchmarking enabled, these are just empty. */
|
||||
#define boot_bench_start(_state) do { \
|
||||
IGNORE(_state); \
|
||||
} while(0)
|
||||
|
||||
#define boot_bench_stop(_state) do { \
|
||||
IGNORE(_state); \
|
||||
} while(0)
|
||||
|
||||
#endif /* not MCUBOOT_USE_BENCH */
|
||||
|
||||
#endif /* not H_BOOTUTIL_BENCH_H__ */
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2021 Arm Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __BOOT_RECORD_H__
|
||||
#define __BOOT_RECORD_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "bootutil/image.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Add a data item to the shared data area between bootloader and
|
||||
* runtime SW
|
||||
*
|
||||
* @param[in] major_type TLV major type, identify consumer
|
||||
* @param[in] minor_type TLV minor type, identify TLV type
|
||||
* @param[in] size length of added data
|
||||
* @param[in] data pointer to data
|
||||
*
|
||||
* @return 0 on success; nonzero on failure.
|
||||
*/
|
||||
int boot_add_data_to_shared_area(uint8_t major_type,
|
||||
uint16_t minor_type,
|
||||
size_t size,
|
||||
const uint8_t *data);
|
||||
|
||||
/**
|
||||
* Add an image's all boot status information to the shared memory area
|
||||
* between the bootloader and runtime SW.
|
||||
*
|
||||
* @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 0 on success; nonzero on failure.
|
||||
*/
|
||||
int boot_save_boot_status(uint8_t sw_module,
|
||||
const struct image_header *hdr,
|
||||
const struct flash_area *fap);
|
||||
|
||||
/**
|
||||
* Add application specific data to the shared memory area between the
|
||||
* bootloader and runtime SW.
|
||||
*
|
||||
* @param[in] hdr Pointer to the image header stored in RAM.
|
||||
* @param[in] fap Pointer to the flash area where image is stored.
|
||||
*
|
||||
* @return 0 on success; nonzero on failure.
|
||||
*/
|
||||
int boot_save_shared_data(const struct image_header *hdr,
|
||||
const struct flash_area *fap);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BOOT_RECORD_H__ */
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Arm Limited
|
||||
* Copyright (c) 2020 Linaro Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __BOOT_STATUS_H__
|
||||
#define __BOOT_STATUS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The shared data between boot loader and runtime SW is TLV encoded. The
|
||||
* shared data is stored in a well known location in memory and this is a
|
||||
* contract between boot loader and runtime SW.
|
||||
*
|
||||
* The structure of shared data must be the following:
|
||||
* - At the beginning there must be a header: struct shared_data_tlv_header
|
||||
* This contains a magic number and a size field which covers the entire
|
||||
* size of the shared data area including this header.
|
||||
* - After the header there come the entries which are composed from an entry
|
||||
* header structure: struct shared_data_tlv_entry and the data. In the entry
|
||||
* header is a type field (tly_type) which identify the consumer of the
|
||||
* entry in the runtime SW and specify the subtype of that data item. There
|
||||
* is a size field (tlv_len) which covers the size of the the data. After
|
||||
* this structure comes the actual data.
|
||||
*
|
||||
* - Arbitrary number and size of data entry can be in the shared memory area.
|
||||
*
|
||||
* This table gives of overview about the tlv_type field in the entry header.
|
||||
* The tlv_type always composed from a major and minor number. Major number
|
||||
* identifies the addressee in runtime SW, who should process the data entry.
|
||||
* Minor number used to encode more info about the data entry. The actual
|
||||
* definition of minor number could change per major number.
|
||||
*
|
||||
* In case of boot status data, which can be processed by an attestation
|
||||
* service the minor number is split further to two part: sw_module and claim.
|
||||
* The sw_module identifies the SW component in the system which the data item
|
||||
* belongs to and the claim part identifies the exact type of the data.
|
||||
*
|
||||
* |---------------------------------------|
|
||||
* | tlv_type (16) |
|
||||
* |---------------------------------------|
|
||||
* | tlv_major(4)| tlv_minor(12) |
|
||||
* |---------------------------------------|
|
||||
* | MAJOR_IAS | sw_module(6) | claim(6) |
|
||||
* |---------------------------------------|
|
||||
*/
|
||||
|
||||
/* General macros to handle TLV type */
|
||||
#define MAJOR_MASK 0xF /* 4 bit */
|
||||
#define MAJOR_POS 12 /* 12 bit */
|
||||
#define MINOR_MASK 0xFFF /* 12 bit */
|
||||
|
||||
#define SET_TLV_TYPE(major, minor) \
|
||||
(((uint16_t)((major) & MAJOR_MASK) << MAJOR_POS) \
|
||||
| ((minor) & MINOR_MASK))
|
||||
#define GET_MAJOR(tlv_type) ((uint16_t)(tlv_type) >> MAJOR_POS)
|
||||
#define GET_MINOR(tlv_type) ((tlv_type) & MINOR_MASK)
|
||||
|
||||
/* Magic value which marks the beginning of shared data area in memory */
|
||||
#define SHARED_DATA_TLV_INFO_MAGIC 0x2016
|
||||
|
||||
/* Initial attestation specific macros */
|
||||
|
||||
/**
|
||||
* Major numbers (4 bit) to identify the
|
||||
* consumer of shared data in runtime SW.
|
||||
*/
|
||||
#define TLV_MAJOR_IAS 0x1
|
||||
|
||||
/* Initial attestation: Claim per SW components / SW modules */
|
||||
/* Bits: 0-2 */
|
||||
#define SW_VERSION 0x00
|
||||
#define SW_SIGNER_ID 0x01
|
||||
/* Reserved 0x02 */
|
||||
#define SW_TYPE 0x03
|
||||
/* Bits: 3-5 */
|
||||
#define SW_MEASURE_VALUE 0x08
|
||||
#define SW_MEASURE_TYPE 0x09
|
||||
#define SW_BOOT_RECORD 0x3F
|
||||
|
||||
#define MODULE_POS 6 /* 6 bit */
|
||||
#define CLAIM_MASK 0x3F /* 6 bit */
|
||||
#define MEASUREMENT_CLAIM_POS 3 /* 3 bit */
|
||||
|
||||
#define GET_IAS_MODULE(tlv_type) ((uint16_t)GET_MINOR(tlv_type) >> MODULE_POS)
|
||||
#define GET_IAS_CLAIM(tlv_type) (GET_MINOR(tlv_type) & CLAIM_MASK)
|
||||
#define SET_IAS_MINOR(sw_module, claim) \
|
||||
(((uint16_t)(sw_module) << MODULE_POS) | (claim))
|
||||
|
||||
/**
|
||||
* Shared data TLV header. All fields in little endian.
|
||||
*
|
||||
* -----------------------------------
|
||||
* | tlv_magic(16) | tlv_tot_len(16) |
|
||||
* -----------------------------------
|
||||
*/
|
||||
struct shared_data_tlv_header {
|
||||
uint16_t tlv_magic;
|
||||
uint16_t tlv_tot_len; /* size of whole TLV area (including this header) */
|
||||
};
|
||||
|
||||
#define SHARED_DATA_HEADER_SIZE sizeof(struct shared_data_tlv_header)
|
||||
|
||||
/**
|
||||
* Shared data TLV entry header format. All fields in little endian.
|
||||
*
|
||||
* -------------------------------
|
||||
* | tlv_type(16) | tlv_len(16) |
|
||||
* -------------------------------
|
||||
* | Raw data |
|
||||
* -------------------------------
|
||||
*/
|
||||
struct shared_data_tlv_entry {
|
||||
uint16_t tlv_type;
|
||||
uint16_t tlv_len; /* TLV data length (not including this header). */
|
||||
};
|
||||
|
||||
#define SHARED_DATA_ENTRY_HEADER_SIZE sizeof(struct shared_data_tlv_entry)
|
||||
#define SHARED_DATA_ENTRY_SIZE(size) (size + SHARED_DATA_ENTRY_HEADER_SIZE)
|
||||
|
||||
/* Structure to store the boot data for the runtime SW. */
|
||||
struct shared_boot_data {
|
||||
struct shared_data_tlv_header header;
|
||||
uint8_t data[];
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BOOT_STATUS_H__ */
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2017-2019 Linaro LTD
|
||||
* Copyright (c) 2016-2019 JUUL Labs
|
||||
* Copyright (c) 2019-2020 Arm Limited
|
||||
*
|
||||
* Original license:
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef H_BOOTUTIL_
|
||||
#define H_BOOTUTIL_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "bootutil/fault_injection_hardening.h"
|
||||
#include "bootutil/bootutil_public.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct image_header;
|
||||
/**
|
||||
* A response object provided by the boot loader code; indicates where to jump
|
||||
* to execute the main image.
|
||||
*/
|
||||
struct boot_rsp {
|
||||
/** A pointer to the header of the image to be executed. */
|
||||
const struct image_header *br_hdr;
|
||||
|
||||
/**
|
||||
* The flash offset of the image to execute. Indicates the position of
|
||||
* the image header within its flash device.
|
||||
*/
|
||||
uint8_t br_flash_dev_id;
|
||||
uint32_t br_image_off;
|
||||
};
|
||||
|
||||
/* This is not actually used by mcuboot's code but can be used by apps
|
||||
* when attempting to read/write a trailer.
|
||||
*/
|
||||
struct image_trailer {
|
||||
uint8_t swap_type;
|
||||
uint8_t pad1[BOOT_MAX_ALIGN - 1];
|
||||
uint8_t copy_done;
|
||||
uint8_t pad2[BOOT_MAX_ALIGN - 1];
|
||||
uint8_t image_ok;
|
||||
uint8_t pad3[BOOT_MAX_ALIGN - 1];
|
||||
uint8_t magic[16];
|
||||
};
|
||||
|
||||
/* you must have pre-allocated all the entries within this structure */
|
||||
fih_int boot_go(struct boot_rsp *rsp);
|
||||
|
||||
struct boot_loader_state;
|
||||
fih_int context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp);
|
||||
|
||||
#define SPLIT_GO_OK (0)
|
||||
#define SPLIT_GO_NON_MATCHING (-1)
|
||||
#define SPLIT_GO_ERR (-2)
|
||||
|
||||
fih_int split_go(int loader_slot, int split_slot, void **entry);
|
||||
|
||||
/* The following was added by TI */
|
||||
fih_int boot_return_highest_version(struct boot_rsp *rsp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2017 Linaro Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef H_BOOTUTIL_LOG_H_
|
||||
#define H_BOOTUTIL_LOG_H_
|
||||
|
||||
#include "ignore.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <mcuboot_config/mcuboot_config.h>
|
||||
#include <mcuboot_config/mcuboot_logging.h>
|
||||
|
||||
#ifdef MCUBOOT_HAVE_LOGGING
|
||||
|
||||
#define BOOT_LOG_ERR(...) MCUBOOT_LOG_ERR(__VA_ARGS__)
|
||||
#define BOOT_LOG_WRN(...) MCUBOOT_LOG_WRN(__VA_ARGS__)
|
||||
#define BOOT_LOG_INF(...) MCUBOOT_LOG_INF(__VA_ARGS__)
|
||||
#define BOOT_LOG_DBG(...) MCUBOOT_LOG_DBG(__VA_ARGS__)
|
||||
#define BOOT_LOG_SIM(...) MCUBOOT_LOG_SIM(__VA_ARGS__)
|
||||
|
||||
#else
|
||||
|
||||
#define BOOT_LOG_ERR(...) IGNORE(__VA_ARGS__)
|
||||
#define BOOT_LOG_WRN(...) IGNORE(__VA_ARGS__)
|
||||
#define BOOT_LOG_INF(...) IGNORE(__VA_ARGS__)
|
||||
#define BOOT_LOG_DBG(...) IGNORE(__VA_ARGS__)
|
||||
#define BOOT_LOG_SIM(...) IGNORE(__VA_ARGS__)
|
||||
|
||||
#endif /* MCUBOOT_HAVE_LOGGING */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2017-2019 Linaro LTD
|
||||
* Copyright (c) 2016-2019 JUUL Labs
|
||||
* Copyright (c) 2019-2020 Arm Limited
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* Original license:
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Public MCUBoot interface API
|
||||
*
|
||||
* This file contains API which can be combined with the application in order
|
||||
* to interact with the MCUBoot bootloader. This API are shared code-base betwen
|
||||
* MCUBoot and the application which controls DFU process.
|
||||
*/
|
||||
|
||||
#ifndef H_BOOTUTIL_PUBLIC
|
||||
#define H_BOOTUTIL_PUBLIC
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <flash_map_backend/flash_map_backend.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Attempt to boot the contents of the primary slot. */
|
||||
#define BOOT_SWAP_TYPE_NONE 1
|
||||
|
||||
/**
|
||||
* Swap to the secondary slot.
|
||||
* Absent a confirm command, revert back on next boot.
|
||||
*/
|
||||
#define BOOT_SWAP_TYPE_TEST 2
|
||||
|
||||
/**
|
||||
* Swap to the secondary slot,
|
||||
* and permanently switch to booting its contents.
|
||||
*/
|
||||
#define BOOT_SWAP_TYPE_PERM 3
|
||||
|
||||
/** Swap back to alternate slot. A confirm changes this state to NONE. */
|
||||
#define BOOT_SWAP_TYPE_REVERT 4
|
||||
|
||||
/** Swap failed because image to be run is not valid */
|
||||
#define BOOT_SWAP_TYPE_FAIL 5
|
||||
|
||||
/** Swapping encountered an unrecoverable error */
|
||||
#define BOOT_SWAP_TYPE_PANIC 0xff
|
||||
|
||||
#define BOOT_MAX_ALIGN 8
|
||||
|
||||
#define BOOT_MAGIC_GOOD 1
|
||||
#define BOOT_MAGIC_BAD 2
|
||||
#define BOOT_MAGIC_UNSET 3
|
||||
#define BOOT_MAGIC_ANY 4 /* NOTE: control only, not dependent on sector */
|
||||
#define BOOT_MAGIC_NOTGOOD 5 /* NOTE: control only, not dependent on sector */
|
||||
|
||||
/*
|
||||
* NOTE: leave BOOT_FLAG_SET equal to one, this is written to flash!
|
||||
*/
|
||||
#define BOOT_FLAG_SET 1
|
||||
#define BOOT_FLAG_BAD 2
|
||||
#define BOOT_FLAG_UNSET 3
|
||||
#define BOOT_FLAG_ANY 4 /* NOTE: control only, not dependent on sector */
|
||||
|
||||
#define BOOT_MAGIC_SZ (sizeof boot_img_magic)
|
||||
|
||||
struct boot_swap_state {
|
||||
uint8_t magic; /* One of the BOOT_MAGIC_[...] values. */
|
||||
uint8_t swap_type; /* One of the BOOT_SWAP_TYPE_[...] values. */
|
||||
uint8_t copy_done; /* One of the BOOT_FLAG_[...] values. */
|
||||
uint8_t image_ok; /* One of the BOOT_FLAG_[...] values. */
|
||||
uint8_t image_num; /* Boot status belongs to this image */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Determines the action, if any, that mcuboot will take on a image pair.
|
||||
*
|
||||
* @param image_index Image pair index.
|
||||
*
|
||||
* @return a BOOT_SWAP_TYPE_[...] constant on success, negative errno code on
|
||||
* fail.
|
||||
*/
|
||||
int boot_swap_type_multi(int image_index);
|
||||
|
||||
/**
|
||||
* @brief Determines the action, if any, that mcuboot will take.
|
||||
*
|
||||
* Works the same as a boot_swap_type_multi(0) call;
|
||||
*
|
||||
* @return a BOOT_SWAP_TYPE_[...] constant on success, negative errno code on
|
||||
* fail.
|
||||
*/
|
||||
int boot_swap_type(void);
|
||||
|
||||
/**
|
||||
* Marks the image in the secondary slot as pending. On the next reboot,
|
||||
* the system will perform a one-time boot of the the secondary slot image.
|
||||
*
|
||||
* @param permanent Whether the image should be used permanently or
|
||||
* only tested once:
|
||||
* 0=run image once, then confirm or revert.
|
||||
* 1=run image forever.
|
||||
*
|
||||
* @return 0 on success; nonzero on failure.
|
||||
*/
|
||||
int boot_set_pending(int permanent);
|
||||
|
||||
/**
|
||||
* @brief Marks the image in the primary slot as confirmed.
|
||||
*
|
||||
* The system will continue booting into the image in the primary slot until
|
||||
* told to boot from a different slot.
|
||||
*
|
||||
* @return 0 on success; nonzero on failure.
|
||||
*/
|
||||
int boot_set_confirmed(void);
|
||||
|
||||
/**
|
||||
* @brief Get offset of the swap info field in the image trailer.
|
||||
*
|
||||
* @param fap Flash are for which offset is determined.
|
||||
*
|
||||
* @retval offset of the swap info field.
|
||||
*/
|
||||
uint32_t boot_swap_info_off(const struct flash_area *fap);
|
||||
|
||||
/**
|
||||
* @brief Get value of image-ok flag of the image.
|
||||
*
|
||||
* If called from chin-loaded image the image-ok flag value can be used to check
|
||||
* whether application itself is already confirmed.
|
||||
*
|
||||
* @param fap Flash area of the image.
|
||||
* @param image_ok[out] image-ok value.
|
||||
*
|
||||
* @return 0 on success; nonzero on failure.
|
||||
*/
|
||||
int boot_read_image_ok(const struct flash_area *fap, uint8_t *image_ok);
|
||||
|
||||
/**
|
||||
* @brief Read the image swap state
|
||||
*
|
||||
* @param flash_area_id Id of flash parttition from which trailer will be read.
|
||||
* @param state Struture for holding swap state.
|
||||
*
|
||||
* @return 0 on success, nonzero errno code on fail.
|
||||
*/
|
||||
int
|
||||
boot_read_swap_state_by_id(int flash_area_id, struct boot_swap_state *state);
|
||||
|
||||
#define BOOT_MAGIC_ARR_SZ \
|
||||
(sizeof boot_img_magic / sizeof boot_img_magic[0])
|
||||
|
||||
extern const uint32_t boot_img_magic[4];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef H_BOOTUTIL_TEST_
|
||||
#define H_BOOTUTIL_TEST_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int boot_test_all(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Linaro Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef H_BOOTUTIL_CAPS_H_
|
||||
#define H_BOOTUTIL_CAPS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The bootloader can be compile with different capabilities selected
|
||||
* at compile time. This function provides runtime access to these
|
||||
* capabilities. This is intended primarily for testing, although
|
||||
* these will possibly be available at runtime to the application
|
||||
* running within the bootloader.
|
||||
*/
|
||||
uint32_t bootutil_get_caps(void);
|
||||
|
||||
#define BOOTUTIL_CAP_RSA2048 (1<<0)
|
||||
#define BOOTUTIL_CAP_ECDSA_P224 (1<<1)
|
||||
#define BOOTUTIL_CAP_ECDSA_P256 (1<<2)
|
||||
#define BOOTUTIL_CAP_SWAP_USING_SCRATCH (1<<3)
|
||||
#define BOOTUTIL_CAP_OVERWRITE_UPGRADE (1<<4)
|
||||
#define BOOTUTIL_CAP_ENC_RSA (1<<5)
|
||||
#define BOOTUTIL_CAP_ENC_KW (1<<6)
|
||||
#define BOOTUTIL_CAP_VALIDATE_PRIMARY_SLOT (1<<7)
|
||||
#define BOOTUTIL_CAP_RSA3072 (1<<8)
|
||||
#define BOOTUTIL_CAP_ED25519 (1<<9)
|
||||
#define BOOTUTIL_CAP_ENC_EC256 (1<<10)
|
||||
#define BOOTUTIL_CAP_SWAP_USING_MOVE (1<<11)
|
||||
#define BOOTUTIL_CAP_DOWNGRADE_PREVENTION (1<<12)
|
||||
#define BOOTUTIL_CAP_ENC_X25519 (1<<13)
|
||||
#define BOOTUTIL_CAP_BOOTSTRAP (1<<14)
|
||||
|
||||
/*
|
||||
* Query the number of images this bootloader is configured for. This
|
||||
* is also primarily used for testing.
|
||||
*/
|
||||
uint32_t bootutil_get_num_images(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* This module provides a thin abstraction over some of the crypto
|
||||
* primitives to make it easier to swap out the used crypto library.
|
||||
*
|
||||
* At this point, there are two choices: MCUBOOT_USE_MBED_TLS, or
|
||||
* MCUBOOT_USE_TINYCRYPT. It is a compile error there is not exactly
|
||||
* one of these defined.
|
||||
*/
|
||||
|
||||
#ifndef __BOOTUTIL_CRYPTO_AES_CTR_H_
|
||||
#define __BOOTUTIL_CRYPTO_AES_CTR_H_
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
#if (defined(MCUBOOT_USE_MBED_TLS) + \
|
||||
defined(MCUBOOT_USE_TINYCRYPT)) != 1
|
||||
#error "One crypto backend must be defined: either MBED_TLS or TINYCRYPT"
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_USE_MBED_TLS)
|
||||
#include <mbedtls/aes.h>
|
||||
#define BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE (16)
|
||||
#define BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE (16)
|
||||
#endif /* MCUBOOT_USE_MBED_TLS */
|
||||
|
||||
#if defined(MCUBOOT_USE_TINYCRYPT)
|
||||
#include <string.h>
|
||||
#include <tinycrypt/aes.h>
|
||||
#include <tinycrypt/ctr_mode.h>
|
||||
#include <tinycrypt/constants.h>
|
||||
#define BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE TC_AES_KEY_SIZE
|
||||
#define BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE TC_AES_BLOCK_SIZE
|
||||
#endif /* MCUBOOT_USE_TINYCRYPT */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_USE_MBED_TLS)
|
||||
typedef mbedtls_aes_context bootutil_aes_ctr_context;
|
||||
static inline void bootutil_aes_ctr_init(bootutil_aes_ctr_context *ctx)
|
||||
{
|
||||
(void)mbedtls_aes_init(ctx);
|
||||
}
|
||||
|
||||
static inline void bootutil_aes_ctr_drop(bootutil_aes_ctr_context *ctx)
|
||||
{
|
||||
/* XXX: config defines MBEDTLS_PLATFORM_NO_STD_FUNCTIONS so no need to free */
|
||||
/* (void)mbedtls_aes_free(ctx); */
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline int bootutil_aes_ctr_set_key(bootutil_aes_ctr_context *ctx, const uint8_t *k)
|
||||
{
|
||||
return mbedtls_aes_setkey_enc(ctx, k, BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE * 8);
|
||||
}
|
||||
|
||||
static inline int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *m, uint32_t mlen, size_t blk_off, uint8_t *c)
|
||||
{
|
||||
uint8_t stream_block[BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE];
|
||||
return mbedtls_aes_crypt_ctr(ctx, mlen, &blk_off, counter, stream_block, m, c);
|
||||
}
|
||||
|
||||
static inline int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *c, uint32_t clen, size_t blk_off, uint8_t *m)
|
||||
{
|
||||
uint8_t stream_block[BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE];
|
||||
return mbedtls_aes_crypt_ctr(ctx, clen, &blk_off, counter, stream_block, c, m);
|
||||
}
|
||||
#endif /* MCUBOOT_USE_MBED_TLS */
|
||||
|
||||
#if defined(MCUBOOT_USE_TINYCRYPT)
|
||||
typedef struct tc_aes_key_sched_struct bootutil_aes_ctr_context;
|
||||
static inline void bootutil_aes_ctr_init(bootutil_aes_ctr_context *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline void bootutil_aes_ctr_drop(bootutil_aes_ctr_context *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline int bootutil_aes_ctr_set_key(bootutil_aes_ctr_context *ctx, const uint8_t *k)
|
||||
{
|
||||
int rc;
|
||||
rc = tc_aes128_set_encrypt_key(ctx, k);
|
||||
if (rc != TC_CRYPTO_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _bootutil_aes_ctr_crypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *in, uint32_t inlen, uint32_t blk_off, uint8_t *out)
|
||||
{
|
||||
int rc;
|
||||
rc = tc_ctr_mode(out, inlen, in, inlen, counter, &blk_off, ctx);
|
||||
if (rc != TC_CRYPTO_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *m, uint32_t mlen, uint32_t blk_off, uint8_t *c)
|
||||
{
|
||||
return _bootutil_aes_ctr_crypt(ctx, counter, m, mlen, blk_off, c);
|
||||
}
|
||||
|
||||
static inline int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *c, uint32_t clen, uint32_t blk_off, uint8_t *m)
|
||||
{
|
||||
return _bootutil_aes_ctr_crypt(ctx, counter, c, clen, blk_off, m);
|
||||
}
|
||||
#endif /* MCUBOOT_USE_TINYCRYPT */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BOOTUTIL_CRYPTO_AES_CTR_H_ */
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* This module provides a thin abstraction over some of the crypto
|
||||
* primitives to make it easier to swap out the used crypto library.
|
||||
*
|
||||
* At this point, there are two choices: MCUBOOT_USE_MBED_TLS, or
|
||||
* MCUBOOT_USE_TINYCRYPT. It is a compile error there is not exactly
|
||||
* one of these defined.
|
||||
*/
|
||||
|
||||
#ifndef __BOOTUTIL_CRYPTO_AES_KW_H_
|
||||
#define __BOOTUTIL_CRYPTO_AES_KW_H_
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
#if (defined(MCUBOOT_USE_MBED_TLS) + \
|
||||
defined(MCUBOOT_USE_TINYCRYPT)) != 1
|
||||
#error "One crypto backend must be defined: either MBED_TLS or TINYCRYPT"
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_USE_MBED_TLS)
|
||||
#include <mbedtls/aes.h>
|
||||
#include <mbedtls/nist_kw.h>
|
||||
#endif /* MCUBOOT_USE_MBED_TLS */
|
||||
|
||||
#if defined(MCUBOOT_USE_TINYCRYPT)
|
||||
#include <tinycrypt/aes.h>
|
||||
#include <tinycrypt/constants.h>
|
||||
#endif /* MCUBOOT_USE_TINYCRYPT */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_USE_MBED_TLS)
|
||||
typedef mbedtls_nist_kw_context bootutil_aes_kw_context;
|
||||
static inline void bootutil_aes_kw_init(bootutil_aes_kw_context *ctx)
|
||||
{
|
||||
(void)mbedtls_nist_kw_init(ctx);
|
||||
}
|
||||
|
||||
static inline void bootutil_aes_kw_drop(bootutil_aes_kw_context *ctx)
|
||||
{
|
||||
/* XXX: config defines MBEDTLS_PLATFORM_NO_STD_FUNCTIONS so no need to free */
|
||||
/* (void)mbedtls_aes_free(ctx); */
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline int bootutil_aes_kw_set_unwrap_key(bootutil_aes_kw_context *ctx, const uint8_t *k, uint32_t klen)
|
||||
{
|
||||
return mbedtls_nist_kw_setkey(ctx, MBEDTLS_CIPHER_ID_AES, k, klen * 8, 0);
|
||||
}
|
||||
|
||||
static inline int bootutil_aes_kw_unwrap(bootutil_aes_kw_context *ctx, const uint8_t *wrapped_key, uint32_t wrapped_key_len, uint8_t *key, uint32_t key_len)
|
||||
{
|
||||
size_t olen;
|
||||
return mbedtls_nist_kw_unwrap(ctx, MBEDTLS_KW_MODE_KW, wrapped_key, wrapped_key_len, key, &olen, key_len);
|
||||
}
|
||||
#endif /* MCUBOOT_USE_MBED_TLS */
|
||||
|
||||
#if defined(MCUBOOT_USE_TINYCRYPT)
|
||||
typedef struct tc_aes_key_sched_struct bootutil_aes_kw_context;
|
||||
static inline void bootutil_aes_kw_init(bootutil_aes_kw_context *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline void bootutil_aes_kw_drop(bootutil_aes_kw_context *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline int bootutil_aes_kw_set_unwrap_key(bootutil_aes_kw_context *ctx, const uint8_t *k, uint32_t klen)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (klen != 16) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = tc_aes128_set_decrypt_key(ctx, k);
|
||||
if (rc != TC_CRYPTO_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements AES key unwrapping following RFC-3394 section 2.2.2, using
|
||||
* tinycrypt for AES-128 decryption.
|
||||
*/
|
||||
static int bootutil_aes_kw_unwrap(bootutil_aes_kw_context *ctx, const uint8_t *wrapped_key, uint32_t wrapped_key_len, uint8_t *key, uint32_t key_len)
|
||||
{
|
||||
uint8_t A[8];
|
||||
uint8_t B[16];
|
||||
int8_t i, j, k;
|
||||
|
||||
if (wrapped_key_len != 24 || key_len != 16) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (k = 0; k < 8; k++) {
|
||||
A[k] = wrapped_key[k];
|
||||
key[k] = wrapped_key[8 + k];
|
||||
key[8 + k] = wrapped_key[16 + k];
|
||||
}
|
||||
|
||||
for (j = 5; j >= 0; j--) {
|
||||
for (i = 2; i > 0; i--) {
|
||||
for (k = 0; k < 8; k++) {
|
||||
B[k] = A[k];
|
||||
B[8 + k] = key[((i-1) * 8) + k];
|
||||
}
|
||||
B[7] ^= 2 * j + i;
|
||||
if (tc_aes_decrypt((uint8_t *)&B, (uint8_t *)&B, ctx) != TC_CRYPTO_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
for (k = 0; k < 8; k++) {
|
||||
A[k] = B[k];
|
||||
key[((i-1) * 8) + k] = B[8 + k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0, k = 0; i < 8; i++) {
|
||||
k |= A[i] ^ 0xa6;
|
||||
}
|
||||
if (k) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* MCUBOOT_USE_TINYCRYPT */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BOOTUTIL_CRYPTO_AES_KW_H_ */
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This module provides a thin abstraction over some of the crypto
|
||||
* primitives to make it easier to swap out the used crypto library.
|
||||
*
|
||||
* At this point, there are two choices: MCUBOOT_USE_MBED_TLS, or
|
||||
* MCUBOOT_USE_TINYCRYPT. It is a compile error there is not exactly
|
||||
* one of these defined.
|
||||
*/
|
||||
|
||||
#ifndef __BOOTUTIL_CRYPTO_ECDH_P256_H_
|
||||
#define __BOOTUTIL_CRYPTO_ECDH_P256_H_
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
#if (defined(MCUBOOT_USE_TINYCRYPT)) != 1
|
||||
#error "One crypto backend must be defined: TINYCRYPT"
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_USE_TINYCRYPT)
|
||||
#include <tinycrypt/ecc_dh.h>
|
||||
#include <tinycrypt/constants.h>
|
||||
#define BOOTUTIL_CRYPTO_ECDH_P256_HASH_SIZE (4 * 8)
|
||||
#endif /* MCUBOOT_USE_TINYCRYPT */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_USE_TINYCRYPT)
|
||||
typedef uintptr_t bootutil_ecdh_p256_context;
|
||||
static inline void bootutil_ecdh_p256_init(bootutil_ecdh_p256_context *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline void bootutil_ecdh_p256_drop(bootutil_ecdh_p256_context *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline int bootutil_ecdh_p256_shared_secret(bootutil_ecdh_p256_context *ctx, const uint8_t *pk, const uint8_t *sk, uint8_t *z)
|
||||
{
|
||||
int rc;
|
||||
(void)ctx;
|
||||
|
||||
rc = uECC_valid_public_key(pk, uECC_secp256r1());
|
||||
if (rc != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = uECC_shared_secret(pk, sk, z, uECC_secp256r1());
|
||||
if (rc != TC_CRYPTO_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* MCUBOOT_USE_TINYCRYPT */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BOOTUTIL_CRYPTO_ECDH_P256_H_ */
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* This module provides a thin abstraction over some of the crypto
|
||||
* primitives to make it easier to swap out the used crypto library.
|
||||
*
|
||||
* At this point, there are two choices: MCUBOOT_USE_MBED_TLS, or
|
||||
* MCUBOOT_USE_TINYCRYPT. It is a compile error there is not exactly
|
||||
* one of these defined.
|
||||
*/
|
||||
|
||||
#ifndef __BOOTUTIL_CRYPTO_ECDH_X25519_H_
|
||||
#define __BOOTUTIL_CRYPTO_ECDH_X25519_H_
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
#if (defined(MCUBOOT_USE_MBED_TLS) + \
|
||||
defined(MCUBOOT_USE_TINYCRYPT)) != 1
|
||||
#error "One crypto backend must be defined: either MBED_TLS or TINYCRYPT"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_USE_TINYCRYPT) || defined(MCUBOOT_USE_MBED_TLS)
|
||||
extern int X25519(uint8_t out_shared_key[32], const uint8_t private_key[32],
|
||||
const uint8_t peer_public_value[32]);
|
||||
|
||||
typedef uintptr_t bootutil_ecdh_x25519_context;
|
||||
static inline void bootutil_ecdh_x25519_init(bootutil_ecdh_x25519_context *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline void bootutil_ecdh_x25519_drop(bootutil_ecdh_x25519_context *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline int bootutil_ecdh_x25519_shared_secret(bootutil_ecdh_x25519_context *ctx, const uint8_t *pk, const uint8_t *sk, uint8_t *z)
|
||||
{
|
||||
int rc;
|
||||
(void)ctx;
|
||||
|
||||
rc = X25519(z, sk, pk);
|
||||
if (rc != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* MCUBOOT_USE_TINYCRYPT */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BOOTUTIL_CRYPTO_ECDH_X25519_H_ */
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* This module provides a thin abstraction over some of the crypto
|
||||
* primitives to make it easier to swap out the used crypto library.
|
||||
*
|
||||
* At this point, there are two choices: MCUBOOT_USE_MBED_TLS, or
|
||||
* MCUBOOT_USE_TINYCRYPT. It is a compile error there is not exactly
|
||||
* one of these defined.
|
||||
*/
|
||||
|
||||
#ifndef __BOOTUTIL_CRYPTO_ECDSA_P256_H_
|
||||
#define __BOOTUTIL_CRYPTO_ECDSA_P256_H_
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
#if (defined(MCUBOOT_USE_TINYCRYPT) + \
|
||||
defined(MCUBOOT_USE_CC310) + \
|
||||
defined(MCUBOOT_USE_TI_CRYPTO) + \
|
||||
defined(MCUBOOT_USE_MSP_CRYPTO) + \
|
||||
defined(MCUBOOT_USE_MBED_TLS)) != 1
|
||||
#error "One crypto backend must be defined: either CC310 or TINYCRYPT"
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_USE_TINYCRYPT)
|
||||
#include <tinycrypt/ecc_dsa.h>
|
||||
#include <tinycrypt/constants.h>
|
||||
#define BOOTUTIL_CRYPTO_ECDSA_P256_HASH_SIZE (4 * 8)
|
||||
#endif /* MCUBOOT_USE_TINYCRYPT */
|
||||
|
||||
#if defined(MCUBOOT_USE_CC310)
|
||||
#include <cc310_glue.h>
|
||||
#define BOOTUTIL_CRYPTO_ECDSA_P256_HASH_SIZE (4 * 8)
|
||||
#endif /* MCUBOOT_USE_CC310 */
|
||||
|
||||
#if defined(MCUBOOT_USE_TI_CRYPTO)
|
||||
#define BOOTUTIL_CRYPTO_ECDSA_P256_HASH_SIZE (4 * 8)
|
||||
#endif /* MCUBOOT_USE_TI_CRYPTO */
|
||||
|
||||
#if defined(MCUBOOT_USE_MSP_CRYPTO)
|
||||
#define BOOTUTIL_CRYPTO_ECDSA_P256_HASH_SIZE (4 * 8)
|
||||
#endif /* MCUBOOT_USE_TI_CRYPTO */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_USE_TINYCRYPT)
|
||||
typedef uintptr_t bootutil_ecdsa_p256_context;
|
||||
static inline void bootutil_ecdsa_p256_init(bootutil_ecdsa_p256_context *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline void bootutil_ecdsa_p256_drop(bootutil_ecdsa_p256_context *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline int bootutil_ecdsa_p256_verify(bootutil_ecdsa_p256_context *ctx, const uint8_t *pk, const uint8_t *hash, const uint8_t *sig)
|
||||
{
|
||||
int rc;
|
||||
(void)ctx;
|
||||
rc = uECC_verify(pk, hash, BOOTUTIL_CRYPTO_ECDSA_P256_HASH_SIZE, sig, uECC_secp256r1());
|
||||
if (rc != TC_CRYPTO_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* MCUBOOT_USE_TINYCRYPT */
|
||||
|
||||
#if defined(MCUBOOT_USE_CC310)
|
||||
typedef uintptr_t bootutil_ecdsa_p256_context;
|
||||
static inline void bootutil_ecdsa_p256_init(bootutil_ecdsa_p256_context *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline void bootutil_ecdsa_p256_drop(bootutil_ecdsa_p256_context *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline int bootutil_ecdsa_p256_verify(bootutil_ecdsa_p256_context *ctx, uint8_t *pk, uint8_t *hash, uint8_t *sig)
|
||||
{
|
||||
(void)ctx;
|
||||
return cc310_ecdsa_verify_secp256r1(hash, pk, sig, BOOTUTIL_CRYPTO_ECDSA_P256_HASH_SIZE);
|
||||
}
|
||||
#endif /* MCUBOOT_USE_CC310 */
|
||||
|
||||
#if defined(MCUBOOT_USE_TI_CRYPTO)
|
||||
#include "ti-crypto/sl_crypto.h"
|
||||
typedef void* bootutil_ecdsa_p256_context;
|
||||
static inline void bootutil_ecdsa_p256_init(bootutil_ecdsa_p256_context *ctx)
|
||||
{
|
||||
SlCrypto_ecdsa_p256_init();
|
||||
}
|
||||
|
||||
static inline void bootutil_ecdsa_p256_drop(bootutil_ecdsa_p256_context *ctx)
|
||||
{
|
||||
SlCrypto_ecdsa_p256_drop();
|
||||
}
|
||||
|
||||
static inline int bootutil_ecdsa_p256_verify(bootutil_ecdsa_p256_context *ctx, const uint8_t *pk, const uint8_t *hash, const uint8_t *sig)
|
||||
{
|
||||
return SlCrypto_ecdsa_p256_verify(pk, hash, sig);
|
||||
}
|
||||
#endif /* MCUBOOT_USE_TI_CRYPTO */
|
||||
|
||||
#if defined(MCUBOOT_USE_MSP_CRYPTO)
|
||||
#include "msp-crypto/msp_crypto.h"
|
||||
typedef void* bootutil_ecdsa_p256_context;
|
||||
static inline void bootutil_ecdsa_p256_init(bootutil_ecdsa_p256_context *ctx)
|
||||
{
|
||||
MSPCrypto_ecdsa_p256_init();
|
||||
}
|
||||
|
||||
static inline void bootutil_ecdsa_p256_drop(bootutil_ecdsa_p256_context *ctx)
|
||||
{
|
||||
MSPCrypto_ecdsa_p256_drop();
|
||||
}
|
||||
|
||||
static inline int bootutil_ecdsa_p256_verify(bootutil_ecdsa_p256_context *ctx, const uint8_t *pk, const uint8_t *hash, const uint8_t *sig)
|
||||
{
|
||||
return MSPCrypto_ecdsa_p256_verify(pk, hash, sig);
|
||||
}
|
||||
#endif /* MCUBOOT_USE_MSP_CRYPTO */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BOOTUTIL_CRYPTO_ECDSA_P256_H_ */
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* This module provides a thin abstraction over some of the crypto
|
||||
* primitives to make it easier to swap out the used crypto library.
|
||||
*
|
||||
* At this point, there are two choices: MCUBOOT_USE_MBED_TLS, or
|
||||
* MCUBOOT_USE_TINYCRYPT. It is a compile error there is not exactly
|
||||
* one of these defined.
|
||||
*/
|
||||
|
||||
#ifndef __BOOTUTIL_CRYPTO_HMAC_SHA256_H_
|
||||
#define __BOOTUTIL_CRYPTO_HMAC_SHA256_H_
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
#if (defined(MCUBOOT_USE_TINYCRYPT)) != 1
|
||||
#error "One crypto backend must be defined: TINYCRYPT"
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_USE_TINYCRYPT)
|
||||
#include <tinycrypt/sha256.h>
|
||||
#include <tinycrypt/utils.h>
|
||||
#include <tinycrypt/constants.h>
|
||||
#include <tinycrypt/hmac.h>
|
||||
#endif /* MCUBOOT_USE_TINYCRYPT */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_USE_TINYCRYPT)
|
||||
typedef struct tc_hmac_state_struct bootutil_hmac_sha256_context;
|
||||
static inline void bootutil_hmac_sha256_init(bootutil_hmac_sha256_context *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline void bootutil_hmac_sha256_drop(bootutil_hmac_sha256_context *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline int bootutil_hmac_sha256_set_key(bootutil_hmac_sha256_context *ctx, const uint8_t *key, unsigned int key_size)
|
||||
{
|
||||
int rc;
|
||||
rc = tc_hmac_set_key(ctx, key, key_size);
|
||||
if (rc != TC_CRYPTO_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
rc = tc_hmac_init(ctx);
|
||||
if (rc != TC_CRYPTO_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int bootutil_hmac_sha256_update(bootutil_hmac_sha256_context *ctx, const void *data, unsigned int data_length)
|
||||
{
|
||||
int rc;
|
||||
rc = tc_hmac_update(ctx, data, data_length);
|
||||
if (rc != TC_CRYPTO_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int bootutil_hmac_sha256_finish(bootutil_hmac_sha256_context *ctx, uint8_t *tag, unsigned int taglen)
|
||||
{
|
||||
int rc;
|
||||
rc = tc_hmac_final(tag, taglen, ctx);
|
||||
if (rc != TC_CRYPTO_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* MCUBOOT_USE_TINYCRYPT */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BOOTUTIL_CRYPTO_HMAC_SHA256_H_ */
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2017-2019 Linaro LTD
|
||||
* Copyright (c) 2017-2019 JUUL Labs
|
||||
*/
|
||||
|
||||
/*
|
||||
* This module provides a thin abstraction over some of the crypto
|
||||
* primitives to make it easier to swap out the used crypto library.
|
||||
*
|
||||
* At this point, there are two choices: MCUBOOT_USE_MBED_TLS, or
|
||||
* MCUBOOT_USE_TINYCRYPT. It is a compile error there is not exactly
|
||||
* one of these defined.
|
||||
*/
|
||||
|
||||
#ifndef __BOOTUTIL_CRYPTO_SHA256_H_
|
||||
#define __BOOTUTIL_CRYPTO_SHA256_H_
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
#if (defined(MCUBOOT_USE_MBED_TLS) + \
|
||||
defined(MCUBOOT_USE_TINYCRYPT) + \
|
||||
defined(MCUBOOT_USE_TI_CRYPTO) + \
|
||||
defined(MCUBOOT_USE_MSP_CRYPTO) + \
|
||||
defined(MCUBOOT_USE_CC310)) != 1
|
||||
#error "One crypto backend must be defined: either CC310, MBED_TLS or TINYCRYPT"
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_USE_MBED_TLS)
|
||||
#include <mbedtls/sha256.h>
|
||||
#define BOOTUTIL_CRYPTO_SHA256_BLOCK_SIZE (64)
|
||||
#define BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE (32)
|
||||
#endif /* MCUBOOT_USE_MBED_TLS */
|
||||
|
||||
#if defined(MCUBOOT_USE_TINYCRYPT)
|
||||
#include <tinycrypt/sha256.h>
|
||||
#include <tinycrypt/constants.h>
|
||||
#define BOOTUTIL_CRYPTO_SHA256_BLOCK_SIZE TC_SHA256_BLOCK_SIZE
|
||||
#define BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE TC_SHA256_DIGEST_SIZE
|
||||
#endif /* MCUBOOT_USE_TINYCRYPT */
|
||||
|
||||
#if defined(MCUBOOT_USE_CC310)
|
||||
#include <cc310_glue.h>
|
||||
#define BOOTUTIL_CRYPTO_SHA256_BLOCK_SIZE (64)
|
||||
#define BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE (32)
|
||||
#endif /* MCUBOOT_USE_CC310 */
|
||||
|
||||
#if defined(MCUBOOT_USE_TI_CRYPTO) || defined(MCUBOOT_USE_MSP_CRYPTO)
|
||||
#define BOOTUTIL_CRYPTO_SHA256_BLOCK_SIZE (64)
|
||||
#define BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE (32)
|
||||
#endif /* MCUBOOT_USE_TI_CRYPTO */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_USE_MBED_TLS)
|
||||
typedef mbedtls_sha256_context bootutil_sha256_context;
|
||||
|
||||
static inline void bootutil_sha256_init(bootutil_sha256_context *ctx)
|
||||
{
|
||||
mbedtls_sha256_init(ctx);
|
||||
(void)mbedtls_sha256_starts_ret(ctx, 0);
|
||||
}
|
||||
|
||||
static inline void bootutil_sha256_drop(bootutil_sha256_context *ctx)
|
||||
{
|
||||
/* XXX: config defines MBEDTLS_PLATFORM_NO_STD_FUNCTIONS so no need to free */
|
||||
/* (void)mbedtls_sha256_free(ctx); */
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline int bootutil_sha256_update(bootutil_sha256_context *ctx,
|
||||
const void *data,
|
||||
uint32_t data_len)
|
||||
{
|
||||
return mbedtls_sha256_update_ret(ctx, data, data_len);
|
||||
}
|
||||
|
||||
static inline int bootutil_sha256_finish(bootutil_sha256_context *ctx,
|
||||
uint8_t *output)
|
||||
{
|
||||
return mbedtls_sha256_finish_ret(ctx, output);
|
||||
}
|
||||
#endif /* MCUBOOT_USE_MBED_TLS */
|
||||
|
||||
#if defined(MCUBOOT_USE_TINYCRYPT)
|
||||
typedef struct tc_sha256_state_struct bootutil_sha256_context;
|
||||
static inline void bootutil_sha256_init(bootutil_sha256_context *ctx)
|
||||
{
|
||||
tc_sha256_init(ctx);
|
||||
}
|
||||
|
||||
static inline void bootutil_sha256_drop(bootutil_sha256_context *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
}
|
||||
|
||||
static inline int bootutil_sha256_update(bootutil_sha256_context *ctx,
|
||||
const void *data,
|
||||
uint32_t data_len)
|
||||
{
|
||||
return tc_sha256_update(ctx, data, data_len);
|
||||
}
|
||||
|
||||
static inline int bootutil_sha256_finish(bootutil_sha256_context *ctx,
|
||||
uint8_t *output)
|
||||
{
|
||||
return tc_sha256_final(output, ctx);
|
||||
}
|
||||
#endif /* MCUBOOT_USE_TINYCRYPT */
|
||||
|
||||
#if defined(MCUBOOT_USE_CC310)
|
||||
static inline void bootutil_sha256_init(bootutil_sha256_context *ctx)
|
||||
{
|
||||
cc310_sha256_init(ctx);
|
||||
}
|
||||
|
||||
static inline void bootutil_sha256_drop(bootutil_sha256_context *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
nrf_cc310_disable();
|
||||
}
|
||||
|
||||
static inline int bootutil_sha256_update(bootutil_sha256_context *ctx,
|
||||
const void *data,
|
||||
uint32_t data_len)
|
||||
{
|
||||
cc310_sha256_update(ctx, data, data_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int bootutil_sha256_finish(bootutil_sha256_context *ctx,
|
||||
uint8_t *output)
|
||||
{
|
||||
cc310_sha256_finalize(ctx, output);
|
||||
return 0;
|
||||
}
|
||||
#endif /* MCUBOOT_USE_CC310 */
|
||||
|
||||
#if defined(MCUBOOT_USE_TI_CRYPTO)
|
||||
#include "ti-crypto/sl_crypto.h"
|
||||
typedef void* bootutil_sha256_context;
|
||||
static inline void bootutil_sha256_init(bootutil_sha256_context *ctx)
|
||||
{
|
||||
SlCrypto_sha256_init();
|
||||
}
|
||||
|
||||
static inline void bootutil_sha256_drop(bootutil_sha256_context *ctx)
|
||||
{
|
||||
SlCrypto_sha256_drop();
|
||||
}
|
||||
|
||||
static inline int bootutil_sha256_update(bootutil_sha256_context *ctx,
|
||||
const void *data,
|
||||
uint32_t data_len)
|
||||
{
|
||||
return SlCrypto_sha256_update(data, data_len);
|
||||
}
|
||||
|
||||
static inline int bootutil_sha256_finish(bootutil_sha256_context *ctx,
|
||||
uint8_t *output)
|
||||
{
|
||||
return SlCrypto_sha256_final(output);
|
||||
}
|
||||
#endif /* MCUBOOT_USE_TI_CRYPTO */
|
||||
|
||||
#if defined(MCUBOOT_USE_MSP_CRYPTO)
|
||||
|
||||
#include "msp-crypto/msp_crypto.h"
|
||||
typedef uint32_t bootutil_sha256_context;
|
||||
|
||||
static inline void bootutil_sha256_init(bootutil_sha256_context *ctx)
|
||||
{
|
||||
MSPCrypto_sha256_init();
|
||||
}
|
||||
|
||||
static inline void bootutil_sha256_drop(bootutil_sha256_context *ctx)
|
||||
{
|
||||
MSPCrypto_sha256_drop();
|
||||
}
|
||||
|
||||
static inline int bootutil_sha256_update(bootutil_sha256_context *ctx,
|
||||
const void *data,
|
||||
uint32_t data_len)
|
||||
{
|
||||
return MSPCrypto_sha256_update(data, data_len);
|
||||
}
|
||||
|
||||
static inline int bootutil_sha256_finish(bootutil_sha256_context *ctx,
|
||||
uint8_t *output)
|
||||
{
|
||||
return MSPCrypto_sha256_finish(output);
|
||||
}
|
||||
|
||||
#endif /* MCUBOOT_USE_MSP_CRYPTO */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BOOTUTIL_CRYPTO_SHA256_H_ */
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2018-2019 JUUL Labs
|
||||
* Copyright (c) 2019 Arm Limited
|
||||
*
|
||||
* Original license:
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef BOOTUTIL_ENC_KEY_H
|
||||
#define BOOTUTIL_ENC_KEY_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <flash_map_backend/flash_map_backend.h>
|
||||
#include "bootutil/crypto/aes_ctr.h"
|
||||
#include "bootutil/image.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define BOOT_ENC_KEY_SIZE 16
|
||||
#define BOOT_ENC_KEY_SIZE_BITS (BOOT_ENC_KEY_SIZE * 8)
|
||||
|
||||
#define TLV_ENC_RSA_SZ 256
|
||||
#define TLV_ENC_KW_SZ 24
|
||||
#define TLV_ENC_EC256_SZ (65 + 32 + 16)
|
||||
#define TLV_ENC_X25519_SZ (32 + 32 + 16)
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_RSA)
|
||||
#define BOOT_ENC_TLV_SIZE TLV_ENC_RSA_SZ
|
||||
#elif defined(MCUBOOT_ENCRYPT_EC256)
|
||||
#define BOOT_ENC_TLV_SIZE TLV_ENC_EC256_SZ
|
||||
#elif defined(MCUBOOT_ENCRYPT_X25519)
|
||||
#define BOOT_ENC_TLV_SIZE TLV_ENC_X25519_SZ
|
||||
#else
|
||||
#define BOOT_ENC_TLV_SIZE TLV_ENC_KW_SZ
|
||||
#endif
|
||||
|
||||
#define BOOT_ENC_TLV_ALIGN_SIZE \
|
||||
((((BOOT_ENC_TLV_SIZE - 1) / BOOT_MAX_ALIGN) + 1) * BOOT_MAX_ALIGN)
|
||||
|
||||
struct enc_key_data {
|
||||
uint8_t valid;
|
||||
bootutil_aes_ctr_context aes_ctr;
|
||||
};
|
||||
|
||||
extern const struct bootutil_key bootutil_enc_key;
|
||||
struct boot_status;
|
||||
|
||||
int boot_enc_init(struct enc_key_data *enc_state, uint8_t slot);
|
||||
int boot_enc_drop(struct enc_key_data *enc_state, uint8_t slot);
|
||||
int boot_enc_set_key(struct enc_key_data *enc_state, uint8_t slot,
|
||||
const struct boot_status *bs);
|
||||
int boot_enc_load(struct enc_key_data *enc_state, int image_index,
|
||||
const struct image_header *hdr, const struct flash_area *fap,
|
||||
struct boot_status *bs);
|
||||
int boot_enc_decrypt(const uint8_t *buf, uint8_t *enckey);
|
||||
bool boot_enc_valid(struct enc_key_data *enc_state, int image_index,
|
||||
const struct flash_area *fap);
|
||||
void boot_encrypt(struct enc_key_data *enc_state, int image_index,
|
||||
const struct flash_area *fap, uint32_t off, uint32_t sz,
|
||||
uint32_t blk_off, uint8_t *buf);
|
||||
void boot_enc_zeroize(struct enc_key_data *enc_state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BOOTUTIL_ENC_KEY_H */
|
||||
Vendored
+377
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2020 Arm Limited
|
||||
*/
|
||||
|
||||
#ifndef __FAULT_INJECTION_HARDENING_H__
|
||||
#define __FAULT_INJECTION_HARDENING_H__
|
||||
|
||||
/* Fault injection mitigation library.
|
||||
*
|
||||
* Has support for different measures, which can either be enabled/disabled
|
||||
* separately or by defining one of the MCUBOOT_FIH_PROFILEs.
|
||||
*
|
||||
* NOTE: These constructs against fault injection attacks are not guaranteed to
|
||||
* be secure for all compilers, but execution is going to be correct and
|
||||
* including them will certainly help to harden the code.
|
||||
*
|
||||
* FIH_ENABLE_DOUBLE_VARS makes critical variables into a tuple (x, x ^ msk).
|
||||
* Then the correctness of x can be checked by XORing the two tuple values
|
||||
* together. This also means that comparisons between fih_ints can be verified
|
||||
* by doing x == y && x_msk == y_msk.
|
||||
*
|
||||
* FIH_ENABLE_GLOBAL_FAIL makes all while(1) failure loops redirect to a global
|
||||
* failure loop. This loop has mitigations against loop escapes / unlooping.
|
||||
* This also means that any unlooping won't immediately continue executing the
|
||||
* function that was executing before the failure.
|
||||
*
|
||||
* FIH_ENABLE_CFI (Control Flow Integrity) creates a global counter that is
|
||||
* incremented before every FIH_CALL of vulnerable functions. On the function
|
||||
* return the counter is decremented, and after the return it is verified that
|
||||
* the counter has the same value as before this process. This can be used to
|
||||
* verify that the function has actually been called. This protection is
|
||||
* intended to discover that important functions are called in an expected
|
||||
* sequence and neither of them is missed due to an instruction skip which could
|
||||
* be a result of glitching attack. It does not provide protection against ROP
|
||||
* or JOP attacks.
|
||||
*
|
||||
* FIH_ENABLE_DELAY causes random delays. This makes it hard to cause faults
|
||||
* precisely. It requires an RNG. An mbedtls integration is provided in
|
||||
* fault_injection_hardening_delay_mbedtls.h, but any RNG that has an entropy
|
||||
* source can be used by implementing the fih_delay_random_uchar function.
|
||||
*
|
||||
* The basic call pattern is:
|
||||
*
|
||||
* fih_int fih_rc = FIH_FAILURE;
|
||||
* FIH_CALL(vulnerable_function, fih_rc, arg1, arg2);
|
||||
* if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
|
||||
* FIH_PANIC;
|
||||
* }
|
||||
*
|
||||
* Note that any function called by FIH_CALL must only return using FIH_RETURN,
|
||||
* as otherwise the CFI counter will not be decremented and the CFI check will
|
||||
* fail causing a panic.
|
||||
*/
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
#if defined(MCUBOOT_FIH_PROFILE_HIGH)
|
||||
|
||||
#define FIH_ENABLE_DELAY /* Requires an entropy source */
|
||||
#define FIH_ENABLE_DOUBLE_VARS
|
||||
#define FIH_ENABLE_GLOBAL_FAIL
|
||||
#define FIH_ENABLE_CFI
|
||||
|
||||
#elif defined(MCUBOOT_FIH_PROFILE_MEDIUM)
|
||||
|
||||
#define FIH_ENABLE_DOUBLE_VARS
|
||||
#define FIH_ENABLE_GLOBAL_FAIL
|
||||
#define FIH_ENABLE_CFI
|
||||
|
||||
#elif defined(MCUBOOT_FIH_PROFILE_LOW)
|
||||
|
||||
#define FIH_ENABLE_GLOBAL_FAIL
|
||||
#define FIH_ENABLE_CFI
|
||||
|
||||
#elif !defined(MCUBOOT_FIH_PROFILE_OFF)
|
||||
#define MCUBOOT_FIH_PROFILE_OFF
|
||||
#endif /* MCUBOOT_FIH_PROFILE */
|
||||
|
||||
#ifdef FIH_ENABLE_DELAY
|
||||
#include "fault_injection_hardening_delay_rng.h"
|
||||
#endif /* FIH_ENABLE_DELAY */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* Non-zero success value to defend against register resets. Zero is the most
|
||||
* common value for a corrupted register so complex bit-patterns are used
|
||||
*/
|
||||
#ifndef MCUBOOT_FIH_PROFILE_OFF
|
||||
#define FIH_POSITIVE_VALUE 0x1AAAAAAA
|
||||
#define FIH_NEGATIVE_VALUE 0x15555555
|
||||
#else
|
||||
#define FIH_POSITIVE_VALUE 0
|
||||
#define FIH_NEGATIVE_VALUE -1
|
||||
#endif
|
||||
|
||||
/* A volatile mask is used to prevent compiler optimization - the mask is xored
|
||||
* with the variable to create the backup and the integrity can be checked with
|
||||
* another xor. The mask value doesn't _really_ matter that much, as long as
|
||||
* it has reasonably high hamming weight.
|
||||
*/
|
||||
#define _FIH_MASK_VALUE 0xBEEF
|
||||
|
||||
#ifdef FIH_ENABLE_DOUBLE_VARS
|
||||
|
||||
/* All ints are replaced with two int - the normal one and a backup which is
|
||||
* XORed with the mask.
|
||||
*/
|
||||
extern volatile int _fih_mask;
|
||||
typedef volatile struct {
|
||||
volatile int val;
|
||||
volatile int msk;
|
||||
} fih_int;
|
||||
|
||||
#else
|
||||
|
||||
typedef int fih_int;
|
||||
|
||||
#endif /* FIH_ENABLE_DOUBLE_VARS */
|
||||
|
||||
extern fih_int FIH_SUCCESS;
|
||||
extern fih_int FIH_FAILURE;
|
||||
|
||||
#ifdef FIH_ENABLE_GLOBAL_FAIL
|
||||
/* Global failure handler - more resistant to unlooping. noinline and used are
|
||||
* used to prevent optimization
|
||||
*/
|
||||
__attribute__((noinline)) __attribute__((used))
|
||||
void fih_panic_loop(void);
|
||||
#define FIH_PANIC fih_panic_loop()
|
||||
#else
|
||||
#define FIH_PANIC while (1) {}
|
||||
#endif /* FIH_ENABLE_GLOBAL_FAIL */
|
||||
|
||||
/* NOTE: For functions to be inlined outside their compilation unit they have to
|
||||
* have the body in the header file. This is required as function calls are easy
|
||||
* to skip.
|
||||
*/
|
||||
#ifdef FIH_ENABLE_DELAY
|
||||
|
||||
/* Delaying logic, with randomness from a CSPRNG */
|
||||
__attribute__((always_inline)) inline
|
||||
int fih_delay(void)
|
||||
{
|
||||
unsigned char delay;
|
||||
int foo = 0;
|
||||
volatile int rc;
|
||||
|
||||
delay = fih_delay_random_uchar();
|
||||
|
||||
for (volatile int i = 0; i < delay; i++) {
|
||||
foo++;
|
||||
}
|
||||
|
||||
rc = 1;
|
||||
|
||||
/* rc is volatile so if it is the return value then the function cannot be
|
||||
* optimized
|
||||
*/
|
||||
return rc;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
__attribute__((always_inline)) inline
|
||||
int fih_delay_init(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline
|
||||
int fih_delay(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif /* FIH_ENABLE_DELAY */
|
||||
|
||||
#ifdef FIH_ENABLE_DOUBLE_VARS
|
||||
|
||||
__attribute__((always_inline)) inline
|
||||
void fih_int_validate(fih_int x)
|
||||
{
|
||||
if (x.val != (x.msk ^ _fih_mask)) {
|
||||
FIH_PANIC;
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert a fih_int to an int. Validate for tampering. */
|
||||
__attribute__((always_inline)) inline
|
||||
int fih_int_decode(fih_int x)
|
||||
{
|
||||
fih_int_validate(x);
|
||||
return x.val;
|
||||
}
|
||||
|
||||
/* Convert an int to a fih_int, can be used to encode specific error codes. */
|
||||
__attribute__((always_inline)) inline
|
||||
fih_int fih_int_encode(int x)
|
||||
{
|
||||
fih_int ret = {x, x ^ _fih_mask};
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Standard equality. If A == B then 1, else 0 */
|
||||
__attribute__((always_inline)) inline
|
||||
int fih_eq(fih_int x, fih_int y)
|
||||
{
|
||||
fih_int_validate(x);
|
||||
fih_int_validate(y);
|
||||
return (x.val == y.val) && fih_delay() && (x.msk == y.msk);
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline
|
||||
int fih_not_eq(fih_int x, fih_int y)
|
||||
{
|
||||
fih_int_validate(x);
|
||||
fih_int_validate(y);
|
||||
return (x.val != y.val) && fih_delay() && (x.msk != y.msk);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* NOOP */
|
||||
__attribute__((always_inline)) inline
|
||||
void fih_int_validate(fih_int x)
|
||||
{
|
||||
(void) x;
|
||||
return;
|
||||
}
|
||||
|
||||
/* NOOP */
|
||||
__attribute__((always_inline)) inline
|
||||
int fih_int_decode(fih_int x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/* NOOP */
|
||||
__attribute__((always_inline)) inline
|
||||
fih_int fih_int_encode(int x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline
|
||||
int fih_eq(fih_int x, fih_int y)
|
||||
{
|
||||
return x == y;
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) inline
|
||||
int fih_not_eq(fih_int x, fih_int y)
|
||||
{
|
||||
return x != y;
|
||||
}
|
||||
#endif /* FIH_ENABLE_DOUBLE_VARS */
|
||||
|
||||
/* C has a common return pattern where 0 is a correct value and all others are
|
||||
* errors. This function converts 0 to FIH_SUCCESS and any other number to a
|
||||
* value that is not FIH_SUCCESS
|
||||
*/
|
||||
__attribute__((always_inline)) inline
|
||||
fih_int fih_int_encode_zero_equality(int x)
|
||||
{
|
||||
if (x) {
|
||||
return FIH_FAILURE;
|
||||
} else {
|
||||
return FIH_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef FIH_ENABLE_CFI
|
||||
extern fih_int _fih_cfi_ctr;
|
||||
#endif /* FIH_ENABLE_CFI */
|
||||
|
||||
fih_int fih_cfi_get_and_increment(void);
|
||||
void fih_cfi_validate(fih_int saved);
|
||||
void fih_cfi_decrement(void);
|
||||
|
||||
/* Label for interacting with FIH testing tool. Can be parsed from the elf file
|
||||
* after compilation. Does not require debug symbols.
|
||||
*/
|
||||
#if defined(__ICCARM__)
|
||||
#define FIH_LABEL(str, lin, cnt) __asm volatile ("FIH_LABEL_" str "_" #lin "_" #cnt "::" ::);
|
||||
#else
|
||||
#define FIH_LABEL(str) __asm volatile ("FIH_LABEL_" str "_%=:" ::);
|
||||
#endif
|
||||
|
||||
/* Main FIH calling macro. return variable is second argument. Does some setup
|
||||
* before and validation afterwards. Inserts labels for use with testing script.
|
||||
*
|
||||
* First perform the precall step - this gets the current value of the CFI
|
||||
* counter and saves it to a local variable, and then increments the counter.
|
||||
*
|
||||
* Then set the return variable to FIH_FAILURE as a base case.
|
||||
*
|
||||
* Then perform the function call. As part of the funtion FIH_RET must be called
|
||||
* which will decrement the counter.
|
||||
*
|
||||
* The postcall step gets the value of the counter and compares it to the
|
||||
* previously saved value. If this is equal then the function call and all child
|
||||
* function calls were performed.
|
||||
*/
|
||||
#if defined(__ICCARM__)
|
||||
#define FIH_CALL(f, ret, ...) FIH_CALL2(f, ret, __LINE__, __COUNTER__, __VA_ARGS__)
|
||||
|
||||
#define FIH_CALL2(f, ret, l, c, ...) \
|
||||
do { \
|
||||
FIH_LABEL("FIH_CALL_START", l, c); \
|
||||
FIH_CFI_PRECALL_BLOCK; \
|
||||
ret = FIH_FAILURE; \
|
||||
if (fih_delay()) { \
|
||||
ret = f(__VA_ARGS__); \
|
||||
} \
|
||||
FIH_CFI_POSTCALL_BLOCK; \
|
||||
FIH_LABEL("FIH_CALL_END", l, c); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
|
||||
#define FIH_CALL(f, ret, ...) \
|
||||
do { \
|
||||
FIH_LABEL("FIH_CALL_START"); \
|
||||
FIH_CFI_PRECALL_BLOCK; \
|
||||
ret = FIH_FAILURE; \
|
||||
if (fih_delay()) { \
|
||||
ret = f(__VA_ARGS__); \
|
||||
} \
|
||||
FIH_CFI_POSTCALL_BLOCK; \
|
||||
FIH_LABEL("FIH_CALL_END"); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/* FIH return changes the state of the internal state machine. If you do a
|
||||
* FIH_CALL then you need to do a FIH_RET else the state machine will detect
|
||||
* tampering and panic.
|
||||
*/
|
||||
#define FIH_RET(ret) \
|
||||
do { \
|
||||
FIH_CFI_PRERET; \
|
||||
return ret; \
|
||||
} while (0)
|
||||
|
||||
|
||||
#ifdef FIH_ENABLE_CFI
|
||||
/* Macro wrappers for functions - Even when the functions have zero body this
|
||||
* saves a few bytes on noop functions as it doesn't generate the call/ret
|
||||
*
|
||||
* CFI precall function saves the CFI counter and then increments it - the
|
||||
* postcall then checks if the counter is equal to the saved value. In order for
|
||||
* this to be the case a FIH_RET must have been performed inside the called
|
||||
* function in order to decrement the counter, so the function must have been
|
||||
* called.
|
||||
*/
|
||||
#define FIH_CFI_PRECALL_BLOCK \
|
||||
fih_int _fih_cfi_saved_value = fih_cfi_get_and_increment()
|
||||
|
||||
#define FIH_CFI_POSTCALL_BLOCK \
|
||||
fih_cfi_validate(_fih_cfi_saved_value)
|
||||
|
||||
#define FIH_CFI_PRERET \
|
||||
fih_cfi_decrement()
|
||||
#else
|
||||
#define FIH_CFI_PRECALL_BLOCK
|
||||
#define FIH_CFI_POSTCALL_BLOCK
|
||||
#define FIH_CFI_PRERET
|
||||
#endif /* FIH_ENABLE_CFI */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __FAULT_INJECTION_HARDENING_H__ */
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2020 Arm Limited
|
||||
*/
|
||||
|
||||
#ifndef __FAULT_INJECTION_HARDENING_DELAY_RNG_H__
|
||||
#define __FAULT_INJECTION_HARDENING_DELAY_RNG_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/**
|
||||
* \brief Set up the RNG for use with random delays. Called once at startup.
|
||||
*/
|
||||
int fih_delay_init(void);
|
||||
|
||||
/**
|
||||
* \brief Get a random unsigned char from an RNG seeded with an entropy source.
|
||||
*
|
||||
* \return A random value that fits inside an unsigned char.
|
||||
*/
|
||||
unsigned char fih_delay_random_uchar(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __FAULT_INJECTION_HARDENING_DELAY_RNG_H__ */
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2017 Nordic Semiconductor ASA
|
||||
*
|
||||
* Original license:
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef H_IGNORE_
|
||||
#define H_IGNORE_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* These macros prevent the "set but not used" warnings for log writes below
|
||||
* the log level.
|
||||
*/
|
||||
|
||||
#define IGN_1(X) ((void)(X))
|
||||
#define IGN_2(X, ...) ((void)(X));IGN_1(__VA_ARGS__)
|
||||
#define IGN_3(X, ...) ((void)(X));IGN_2(__VA_ARGS__)
|
||||
#define IGN_4(X, ...) ((void)(X));IGN_3(__VA_ARGS__)
|
||||
#define IGN_5(X, ...) ((void)(X));IGN_4(__VA_ARGS__)
|
||||
#define IGN_6(X, ...) ((void)(X));IGN_5(__VA_ARGS__)
|
||||
#define IGN_7(X, ...) ((void)(X));IGN_6(__VA_ARGS__)
|
||||
#define IGN_8(X, ...) ((void)(X));IGN_7(__VA_ARGS__)
|
||||
#define IGN_9(X, ...) ((void)(X));IGN_8(__VA_ARGS__)
|
||||
#define IGN_10(X, ...) ((void)(X));IGN_9(__VA_ARGS__)
|
||||
#define IGN_11(X, ...) ((void)(X));IGN_10(__VA_ARGS__)
|
||||
#define IGN_12(X, ...) ((void)(X));IGN_11(__VA_ARGS__)
|
||||
#define IGN_13(X, ...) ((void)(X));IGN_12(__VA_ARGS__)
|
||||
#define IGN_14(X, ...) ((void)(X));IGN_13(__VA_ARGS__)
|
||||
#define IGN_15(X, ...) ((void)(X));IGN_14(__VA_ARGS__)
|
||||
#define IGN_16(X, ...) ((void)(X));IGN_15(__VA_ARGS__)
|
||||
#define IGN_17(X, ...) ((void)(X));IGN_16(__VA_ARGS__)
|
||||
#define IGN_18(X, ...) ((void)(X));IGN_17(__VA_ARGS__)
|
||||
#define IGN_19(X, ...) ((void)(X));IGN_18(__VA_ARGS__)
|
||||
#define IGN_20(X, ...) ((void)(X));IGN_19(__VA_ARGS__)
|
||||
|
||||
#define GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, \
|
||||
_13, _14, _15, _16, _17, _18, _19, _20, NAME, ...) NAME
|
||||
#define IGNORE(...) \
|
||||
GET_MACRO(__VA_ARGS__, IGN_20, IGN_19, IGN_18, IGN_17, IGN_16, IGN_15, \
|
||||
IGN_14, IGN_13, IGN_12, IGN_11, IGN_10, IGN_9, IGN_8, IGN_7, \
|
||||
IGN_6, IGN_5, IGN_4, IGN_3, IGN_2, IGN_1)(__VA_ARGS__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2016-2019 Linaro LTD
|
||||
* Copyright (c) 2016-2019 JUUL Labs
|
||||
* Copyright (c) 2019-2020 Arm Limited
|
||||
*
|
||||
* Original license:
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef H_IMAGE_
|
||||
#define H_IMAGE_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include "bootutil/fault_injection_hardening.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct flash_area;
|
||||
|
||||
#define IMAGE_MAGIC 0x96f3b83d
|
||||
#define IMAGE_MAGIC_V1 0x96f3b83c
|
||||
#define IMAGE_MAGIC_NONE 0xffffffff
|
||||
#define IMAGE_TLV_INFO_MAGIC 0x6907
|
||||
#define IMAGE_TLV_PROT_INFO_MAGIC 0x6908
|
||||
|
||||
#define IMAGE_HEADER_SIZE 32
|
||||
|
||||
/*
|
||||
* Image header flags.
|
||||
*/
|
||||
#define IMAGE_F_PIC 0x00000001 /* Not supported. */
|
||||
#define IMAGE_F_NON_BOOTABLE 0x00000010 /* Split image app. */
|
||||
#define IMAGE_F_ENCRYPTED 0x00000004 /* Encrypted. */
|
||||
/*
|
||||
* Indicates that this image should be loaded into RAM instead of run
|
||||
* directly from flash. The address to load should be in the
|
||||
* ih_load_addr field of the header.
|
||||
*/
|
||||
#define IMAGE_F_RAM_LOAD 0x00000020
|
||||
|
||||
/*
|
||||
* ECSDA224 is with NIST P-224
|
||||
* ECSDA256 is with NIST P-256
|
||||
*/
|
||||
|
||||
/*
|
||||
* Image trailer TLV types.
|
||||
*
|
||||
* Signature is generated by computing signature over the image hash.
|
||||
* Currently the only image hash type is SHA256.
|
||||
*
|
||||
* Signature comes in the form of 2 TLVs.
|
||||
* 1st on identifies the public key which should be used to verify it.
|
||||
* 2nd one is the actual signature.
|
||||
*/
|
||||
#define IMAGE_TLV_KEYHASH 0x01 /* hash of the public key */
|
||||
#define IMAGE_TLV_PUBKEY 0x02 /* public key */
|
||||
#define IMAGE_TLV_SHA256 0x10 /* SHA256 of image hdr and body */
|
||||
#define IMAGE_TLV_RSA2048_PSS 0x20 /* RSA2048 of hash output */
|
||||
#define IMAGE_TLV_ECDSA224 0x21 /* ECDSA of hash output */
|
||||
#define IMAGE_TLV_ECDSA256 0x22 /* ECDSA of hash output */
|
||||
#define IMAGE_TLV_RSA3072_PSS 0x23 /* RSA3072 of hash output */
|
||||
#define IMAGE_TLV_ED25519 0x24 /* ed25519 of hash output */
|
||||
#define IMAGE_TLV_ENC_RSA2048 0x30 /* Key encrypted with RSA-OAEP-2048 */
|
||||
#define IMAGE_TLV_ENC_KW128 0x31 /* Key encrypted with AES-KW-128 */
|
||||
#define IMAGE_TLV_ENC_EC256 0x32 /* Key encrypted with ECIES-EC256 */
|
||||
#define IMAGE_TLV_ENC_X25519 0x33 /* Key encrypted with ECIES-X25519 */
|
||||
#define IMAGE_TLV_DEPENDENCY 0x40 /* Image depends on other image */
|
||||
#define IMAGE_TLV_SEC_CNT 0x50 /* security counter */
|
||||
#define IMAGE_TLV_BOOT_RECORD 0x60 /* measured boot record */
|
||||
/*
|
||||
* vendor reserved TLVs at xxA0-xxFF,
|
||||
* where xx denotes the upper byte
|
||||
* range. Examples:
|
||||
* 0x00a0 - 0x00ff
|
||||
* 0x01a0 - 0x01ff
|
||||
* 0x02a0 - 0x02ff
|
||||
* ...
|
||||
* 0xffa0 - 0xfffe
|
||||
*/
|
||||
#define IMAGE_TLV_CUSTOM_TI_ADD_KEY 0xCC /* Custom TLV to add key based on hash */
|
||||
#define IMAGE_TLV_CUSTOM_TI_REVOKE_KEY 0xCD /* Custom TLV to revoke key based on hash */
|
||||
|
||||
#define IMAGE_TLV_ANY 0xffff /* Used to iterate over all TLV */
|
||||
|
||||
struct image_version {
|
||||
uint8_t iv_major;
|
||||
uint8_t iv_minor;
|
||||
uint16_t iv_revision;
|
||||
uint32_t iv_build_num;
|
||||
};
|
||||
|
||||
struct image_dependency {
|
||||
uint8_t image_id; /* Image index (from 0) */
|
||||
uint8_t _pad1;
|
||||
uint16_t _pad2;
|
||||
struct image_version image_min_version; /* Indicates at minimum which
|
||||
* version of firmware must be
|
||||
* available to satisfy compliance
|
||||
*/
|
||||
};
|
||||
|
||||
/** Image header. All fields are in little endian byte order. */
|
||||
struct image_header {
|
||||
uint32_t ih_magic;
|
||||
uint32_t ih_load_addr;
|
||||
uint16_t ih_hdr_size; /* Size of image header (bytes). */
|
||||
uint16_t ih_protect_tlv_size; /* Size of protected TLV area (bytes). */
|
||||
uint32_t ih_img_size; /* Does not include header. */
|
||||
uint32_t ih_flags; /* IMAGE_F_[...]. */
|
||||
struct image_version ih_ver;
|
||||
uint32_t _pad1;
|
||||
};
|
||||
|
||||
/** Image TLV header. All fields in little endian. */
|
||||
struct image_tlv_info {
|
||||
uint16_t it_magic;
|
||||
uint16_t it_tlv_tot; /* size of TLV area (including tlv_info header) */
|
||||
};
|
||||
|
||||
/** Image trailer TLV format. All fields in little endian. */
|
||||
struct image_tlv {
|
||||
uint16_t it_type; /* IMAGE_TLV_[...]. */
|
||||
uint16_t it_len; /* Data length (not including TLV header). */
|
||||
};
|
||||
|
||||
#define IS_ENCRYPTED(hdr) ((hdr)->ih_flags & IMAGE_F_ENCRYPTED)
|
||||
#define MUST_DECRYPT(fap, idx, hdr) \
|
||||
((fap)->fa_id == FLASH_AREA_IMAGE_SECONDARY(idx) && IS_ENCRYPTED(hdr))
|
||||
|
||||
_Static_assert(sizeof(struct image_header) == IMAGE_HEADER_SIZE,
|
||||
"struct image_header not required size");
|
||||
|
||||
struct enc_key_data;
|
||||
fih_int bootutil_img_validate(struct enc_key_data *enc_state, int image_index,
|
||||
struct image_header *hdr,
|
||||
const struct flash_area *fap,
|
||||
uint8_t *tmp_buf, uint32_t tmp_buf_sz,
|
||||
uint8_t *seed, int seed_len, uint8_t *out_hash);
|
||||
|
||||
struct image_tlv_iter {
|
||||
const struct image_header *hdr;
|
||||
const struct flash_area *fap;
|
||||
uint16_t type;
|
||||
bool prot;
|
||||
uint32_t prot_end;
|
||||
uint32_t tlv_off;
|
||||
uint32_t tlv_end;
|
||||
};
|
||||
|
||||
int bootutil_tlv_iter_begin(struct image_tlv_iter *it,
|
||||
const struct image_header *hdr,
|
||||
const struct flash_area *fap, uint16_t type,
|
||||
bool prot);
|
||||
int bootutil_tlv_iter_next(struct image_tlv_iter *it, uint32_t *off,
|
||||
uint16_t *len, uint16_t *type);
|
||||
|
||||
int32_t bootutil_get_img_security_cnt(struct image_header *hdr,
|
||||
const struct flash_area *fap,
|
||||
uint32_t *security_cnt);
|
||||
|
||||
int boot_compare_version(struct image_version *ver1,
|
||||
volatile const struct image_version *ver2);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __SECURITY_CNT_H__
|
||||
#define __SECURITY_CNT_H__
|
||||
|
||||
/**
|
||||
* @file security_cnt.h
|
||||
*
|
||||
* @note The interface must be implemented in a fail-safe way that is
|
||||
* resistant to asynchronous power failures or it can use hardware
|
||||
* counters that have this capability, if supported by the platform.
|
||||
* When a counter incrementation was interrupted it must be able to
|
||||
* continue the incrementation process or recover the previous consistent
|
||||
* status of the counters. If the counters have reached a stable status
|
||||
* (every counter incrementation operation has finished), from that point
|
||||
* their value cannot decrease due to any kind of power failure.
|
||||
*
|
||||
* @note A security counter might be implemented using non-volatile OTP memory
|
||||
* (i.e. fuses) in which case it is the responsibility of the platform
|
||||
* code to map each possible security counter values onto the fuse bits
|
||||
* as the direct usage of counter values can be costly / impractical.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "bootutil/fault_injection_hardening.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define BIM_INFO_PAGE_MAGIC 0x96fa
|
||||
#define BIM_INFO_PAGE_ERASED 0xFFFF
|
||||
#define BIM_INFO_MAGIC_SZ 2
|
||||
#define BIM_ITEM_SZ 10
|
||||
#define BIMINFO_SUCCESS 0
|
||||
#define BIMINFO_FAIL 22
|
||||
#define BIMINFO_FULL 0xFF
|
||||
|
||||
/* each item consists of status (2 bytes) + image id (4 bytes) + security counter (4 bytes) + item id (2 bytes) */
|
||||
#define SC_ITEM_ID 0x9373
|
||||
#define SC_STATUS_EMPTY 0xFFFF
|
||||
#define SC_STATUS_VALID 0xFCFC
|
||||
#define SC_STATUS_INVALID 0xF0F0
|
||||
|
||||
|
||||
typedef struct sc_page_head {
|
||||
uint16_t sc_page_id;
|
||||
}sc_page_head_t;
|
||||
|
||||
typedef struct sc_item {
|
||||
uint16_t sc_status;
|
||||
uint16_t image_id;
|
||||
uint32_t security_counter;
|
||||
uint16_t item_id;
|
||||
}sc_item_t;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Initialises the security counters.
|
||||
*
|
||||
* @return FIH_SUCCESS on success
|
||||
*/
|
||||
fih_int boot_nv_security_counter_init(void);
|
||||
|
||||
/**
|
||||
* Reads the stored value of a given image's security counter.
|
||||
*
|
||||
* @param image_id Index of the image (from 0).
|
||||
* @param security_cnt Pointer to store the security counter value.
|
||||
*
|
||||
* @return FIH_SUCCESS on success
|
||||
*/
|
||||
fih_int boot_nv_security_counter_get(uint32_t image_id, fih_int *security_cnt);
|
||||
|
||||
/**
|
||||
* Updates the stored value of a given image's security counter with a new
|
||||
* security counter value if the new one is greater.
|
||||
*
|
||||
* @param image_id Index of the image (from 0).
|
||||
* @param img_security_cnt New security counter value. The new value must be
|
||||
* between 0 and UINT32_MAX and it must be greater than
|
||||
* or equal to the current security counter value.
|
||||
*
|
||||
* @return 0 on success; nonzero on failure.
|
||||
*/
|
||||
int32_t boot_nv_security_counter_update(uint32_t image_id,
|
||||
uint32_t img_security_cnt);
|
||||
|
||||
/**
|
||||
* Lock the BIM INFO sector
|
||||
*/
|
||||
void lockBimSector(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SECURITY_CNT_H__ */
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef __BOOTUTIL_SIGN_KEY_H_
|
||||
#define __BOOTUTIL_SIGN_KEY_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef MCUBOOT_HW_KEY
|
||||
struct bootutil_key {
|
||||
const uint8_t *key;
|
||||
const unsigned int *len;
|
||||
};
|
||||
|
||||
extern const struct bootutil_key bootutil_keys[];
|
||||
#else
|
||||
struct bootutil_key {
|
||||
uint8_t *key;
|
||||
unsigned int *len;
|
||||
};
|
||||
|
||||
extern struct bootutil_key bootutil_keys[];
|
||||
|
||||
/**
|
||||
* Retrieve the hash of the corresponding public key for image authentication.
|
||||
*
|
||||
* @param[in] image_index Index of the image to be authenticated.
|
||||
* @param[out] public_key_hash Buffer to store the key-hash in.
|
||||
* @param[in,out] key_hash_size As input the size of the buffer. As output
|
||||
* the actual key-hash length.
|
||||
*
|
||||
* @return 0 on success; nonzero on failure.
|
||||
*/
|
||||
int boot_retrieve_public_key_hash(uint8_t image_index,
|
||||
uint8_t *public_key_hash,
|
||||
size_t *key_hash_size);
|
||||
#endif /* !MCUBOOT_HW_KEY */
|
||||
|
||||
extern const int bootutil_key_cnt;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BOOTUTIL_SIGN_KEY_H_ */
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2021 Arm Limited
|
||||
* Copyright (c) 2020 Linaro Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
#if defined(MCUBOOT_MEASURED_BOOT) || defined(MCUBOOT_DATA_SHARING)
|
||||
#include "bootutil/boot_record.h"
|
||||
#include "bootutil/boot_status.h"
|
||||
#include "bootutil_priv.h"
|
||||
#include "bootutil/image.h"
|
||||
#include "flash_map_backend/flash_map_backend.h"
|
||||
|
||||
/* Error codes for using the shared memory area. */
|
||||
#define SHARED_MEMORY_OK (0)
|
||||
#define SHARED_MEMORY_OVERFLOW (1)
|
||||
#define SHARED_MEMORY_OVERWRITE (2)
|
||||
#define SHARED_MEMORY_GEN_ERROR (3)
|
||||
|
||||
/**
|
||||
* @var shared_memory_init_done
|
||||
*
|
||||
* @brief Indicates whether shared memory area was already initialized.
|
||||
*
|
||||
*/
|
||||
static bool shared_memory_init_done;
|
||||
|
||||
/* See in boot_record.h */
|
||||
int
|
||||
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 shared_boot_data *boot_data;
|
||||
uint16_t boot_data_size;
|
||||
uintptr_t tlv_end, offset;
|
||||
|
||||
if (data == NULL) {
|
||||
return SHARED_MEMORY_GEN_ERROR;
|
||||
}
|
||||
|
||||
boot_data = (struct shared_boot_data *)MCUBOOT_SHARED_DATA_BASE;
|
||||
|
||||
/* Check whether first time to call this function. If does then initialise
|
||||
* shared data area.
|
||||
*/
|
||||
if (!shared_memory_init_done) {
|
||||
memset((void *)MCUBOOT_SHARED_DATA_BASE, 0, MCUBOOT_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 = true;
|
||||
}
|
||||
|
||||
/* Check whether TLV entry is already added.
|
||||
* Get the boundaries of TLV section
|
||||
*/
|
||||
tlv_end = MCUBOOT_SHARED_DATA_BASE + boot_data->header.tlv_tot_len;
|
||||
offset = MCUBOOT_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
|
||||
*/
|
||||
while (offset < tlv_end) {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
offset += SHARED_DATA_ENTRY_SIZE(tlv_entry.tlv_len);
|
||||
}
|
||||
|
||||
/* Add TLV entry */
|
||||
tlv_entry.tlv_type = SET_TLV_TYPE(major_type, minor_type);
|
||||
tlv_entry.tlv_len = size;
|
||||
|
||||
if (!boot_u16_safe_add(&boot_data_size, boot_data->header.tlv_tot_len,
|
||||
SHARED_DATA_ENTRY_SIZE(size))) {
|
||||
return SHARED_MEMORY_GEN_ERROR;
|
||||
}
|
||||
|
||||
/* Verify overflow of shared area */
|
||||
if (boot_data_size > MCUBOOT_SHARED_DATA_SIZE) {
|
||||
return SHARED_MEMORY_OVERFLOW;
|
||||
}
|
||||
|
||||
offset = tlv_end;
|
||||
memcpy((void *)offset, &tlv_entry, SHARED_DATA_ENTRY_HEADER_SIZE);
|
||||
|
||||
offset += SHARED_DATA_ENTRY_HEADER_SIZE;
|
||||
memcpy((void *)offset, data, size);
|
||||
|
||||
boot_data->header.tlv_tot_len += SHARED_DATA_ENTRY_SIZE(size);
|
||||
|
||||
return SHARED_MEMORY_OK;
|
||||
}
|
||||
#endif /* MCUBOOT_MEASURED_BOOT OR MCUBOOT_DATA_SHARING */
|
||||
|
||||
#ifdef MCUBOOT_MEASURED_BOOT
|
||||
/* See in boot_record.h */
|
||||
int
|
||||
boot_save_boot_status(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;
|
||||
uint16_t type;
|
||||
uint16_t ias_minor;
|
||||
size_t record_len = 0;
|
||||
uint8_t image_hash[32]; /* SHA256 - 32 Bytes */
|
||||
uint8_t buf[MAX_BOOT_RECORD_SZ];
|
||||
bool boot_record_found = false;
|
||||
bool hash_found = false;
|
||||
int rc;
|
||||
|
||||
/* Manifest data is concatenated to the end of the image.
|
||||
* It is encoded in TLV format.
|
||||
*/
|
||||
|
||||
rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, false);
|
||||
if (rc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Traverse through the TLV area to find the boot record
|
||||
* and image hash TLVs.
|
||||
*/
|
||||
while (true) {
|
||||
rc = bootutil_tlv_iter_next(&it, &offset, &len, &type);
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
} else if (rc > 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == IMAGE_TLV_BOOT_RECORD) {
|
||||
if (len > sizeof(buf)) {
|
||||
return -1;
|
||||
}
|
||||
rc = flash_area_read(fap, offset, buf, len);
|
||||
if (rc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
record_len = len;
|
||||
boot_record_found = true;
|
||||
|
||||
} else if (type == IMAGE_TLV_SHA256) {
|
||||
/* Get the image's hash value from the manifest section. */
|
||||
if (len > sizeof(image_hash)) {
|
||||
return -1;
|
||||
}
|
||||
rc = flash_area_read(fap, offset, image_hash, len);
|
||||
if (rc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
hash_found = true;
|
||||
|
||||
/* 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 -1;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
/* The size of 'buf' has already been checked when
|
||||
* the BOOT_RECORD TLV was read, it won't overflow.
|
||||
*/
|
||||
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);
|
||||
rc = boot_add_data_to_shared_area(TLV_MAJOR_IAS,
|
||||
ias_minor,
|
||||
record_len,
|
||||
buf);
|
||||
if (rc != SHARED_MEMORY_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* MCUBOOT_MEASURED_BOOT */
|
||||
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2017-2019 Linaro LTD
|
||||
* Copyright (c) 2016-2019 JUUL Labs
|
||||
* Copyright (c) 2019-2020 Arm Limited
|
||||
*
|
||||
* Original license:
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "sysflash/sysflash.h"
|
||||
#include "flash_map_backend/flash_map_backend.h"
|
||||
|
||||
#include "bootutil/image.h"
|
||||
#include "bootutil/bootutil.h"
|
||||
#include "bootutil_priv.h"
|
||||
#include "bootutil/bootutil_log.h"
|
||||
#include "bootutil/fault_injection_hardening.h"
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
#include "bootutil/enc_key.h"
|
||||
#endif
|
||||
|
||||
MCUBOOT_LOG_MODULE_DECLARE(mcuboot);
|
||||
|
||||
/* Currently only used by imgmgr */
|
||||
int boot_current_slot;
|
||||
|
||||
extern const uint32_t boot_img_magic[];
|
||||
|
||||
#define BOOT_MAGIC_ARR_SZ \
|
||||
(sizeof boot_img_magic / sizeof boot_img_magic[0])
|
||||
|
||||
/**
|
||||
* @brief Determine if the data at two memory addresses is equal
|
||||
*
|
||||
* @param s1 The first memory region to compare.
|
||||
* @param s2 The second memory region to compare.
|
||||
* @param n The amount of bytes to compare.
|
||||
*
|
||||
* @note This function does not comply with the specification of memcmp,
|
||||
* so should not be considered a drop-in replacement. It has no
|
||||
* constant time execution. The point is to make sure that all the
|
||||
* bytes are compared and detect if loop was abused and some cycles
|
||||
* was skipped due to fault injection.
|
||||
*
|
||||
* @return FIH_SUCCESS if memory regions are equal, otherwise FIH_FAILURE
|
||||
*/
|
||||
#ifdef MCUBOOT_FIH_PROFILE_OFF
|
||||
inline
|
||||
fih_int boot_fih_memequal(const void *s1, const void *s2, size_t n)
|
||||
{
|
||||
return memcmp(s1, s2, n);
|
||||
}
|
||||
#else
|
||||
fih_int boot_fih_memequal(const void *s1, const void *s2, size_t n)
|
||||
{
|
||||
size_t i;
|
||||
uint8_t *s1_p = (uint8_t*) s1;
|
||||
uint8_t *s2_p = (uint8_t*) s2;
|
||||
fih_int ret = FIH_FAILURE;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (s1_p[i] != s2_p[i]) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (i == n) {
|
||||
ret = FIH_SUCCESS;
|
||||
}
|
||||
|
||||
out:
|
||||
FIH_RET(ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t
|
||||
boot_status_sz(uint32_t min_write_sz)
|
||||
{
|
||||
return /* state for all sectors */
|
||||
BOOT_STATUS_MAX_ENTRIES * BOOT_STATUS_STATE_COUNT * min_write_sz;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
boot_trailer_sz(uint32_t min_write_sz)
|
||||
{
|
||||
return /* state for all sectors */
|
||||
boot_status_sz(min_write_sz) +
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
/* encryption keys */
|
||||
# if MCUBOOT_SWAP_SAVE_ENCTLV
|
||||
BOOT_ENC_TLV_ALIGN_SIZE * 2 +
|
||||
# else
|
||||
BOOT_ENC_KEY_SIZE * 2 +
|
||||
# endif
|
||||
#endif
|
||||
/* swap_type + copy_done + image_ok + swap_size */
|
||||
BOOT_MAX_ALIGN * 4 +
|
||||
BOOT_MAGIC_SZ;
|
||||
}
|
||||
|
||||
int
|
||||
boot_status_entries(int image_index, const struct flash_area *fap)
|
||||
{
|
||||
#if MCUBOOT_SWAP_USING_SCRATCH
|
||||
if (fap->fa_id == FLASH_AREA_IMAGE_SCRATCH) {
|
||||
return BOOT_STATUS_STATE_COUNT;
|
||||
} else
|
||||
#endif
|
||||
if (fap->fa_id == FLASH_AREA_IMAGE_PRIMARY(image_index) ||
|
||||
fap->fa_id == FLASH_AREA_IMAGE_SECONDARY(image_index)) {
|
||||
return BOOT_STATUS_STATE_COUNT * BOOT_STATUS_MAX_ENTRIES;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
boot_status_off(const struct flash_area *fap)
|
||||
{
|
||||
uint32_t off_from_end;
|
||||
uint8_t elem_sz;
|
||||
|
||||
elem_sz = flash_area_align(fap);
|
||||
|
||||
off_from_end = boot_trailer_sz(elem_sz);
|
||||
|
||||
assert(off_from_end <= fap->fa_size);
|
||||
return fap->fa_size - off_from_end;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
boot_magic_off(const struct flash_area *fap)
|
||||
{
|
||||
return fap->fa_size - BOOT_MAGIC_SZ;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
boot_image_ok_off(const struct flash_area *fap)
|
||||
{
|
||||
return boot_magic_off(fap) - BOOT_MAX_ALIGN;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
boot_copy_done_off(const struct flash_area *fap)
|
||||
{
|
||||
return boot_image_ok_off(fap) - BOOT_MAX_ALIGN;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
boot_swap_size_off(const struct flash_area *fap)
|
||||
{
|
||||
return boot_swap_info_off(fap) - BOOT_MAX_ALIGN;
|
||||
}
|
||||
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
static inline uint32_t
|
||||
boot_enc_key_off(const struct flash_area *fap, uint8_t slot)
|
||||
{
|
||||
#if MCUBOOT_SWAP_SAVE_ENCTLV
|
||||
return boot_swap_size_off(fap) - ((slot + 1) *
|
||||
((((BOOT_ENC_TLV_SIZE - 1) / BOOT_MAX_ALIGN) + 1) * BOOT_MAX_ALIGN));
|
||||
#else
|
||||
return boot_swap_size_off(fap) - ((slot + 1) * BOOT_ENC_KEY_SIZE);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This functions tries to locate the status area after an aborted swap,
|
||||
* by looking for the magic in the possible locations.
|
||||
*
|
||||
* If the magic is successfully found, a flash_area * is returned and it
|
||||
* is the responsibility of the called to close it.
|
||||
*
|
||||
* @returns 0 on success, -1 on errors
|
||||
*/
|
||||
static int
|
||||
boot_find_status(int image_index, const struct flash_area **fap)
|
||||
{
|
||||
uint32_t magic[BOOT_MAGIC_ARR_SZ];
|
||||
uint32_t off;
|
||||
uint8_t areas[2] = {
|
||||
#if MCUBOOT_SWAP_USING_SCRATCH
|
||||
FLASH_AREA_IMAGE_SCRATCH,
|
||||
#endif
|
||||
FLASH_AREA_IMAGE_PRIMARY(image_index),
|
||||
};
|
||||
unsigned int i;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* In the middle a swap, tries to locate the area that is currently
|
||||
* storing a valid magic, first on the primary slot, then on scratch.
|
||||
* Both "slots" can end up being temporary storage for a swap and it
|
||||
* is assumed that if magic is valid then other metadata is too,
|
||||
* because magic is always written in the last step.
|
||||
*/
|
||||
|
||||
for (i = 0; i < sizeof(areas) / sizeof(areas[0]); i++) {
|
||||
rc = flash_area_open(areas[i], fap);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
off = boot_magic_off(*fap);
|
||||
rc = flash_area_read(*fap, off, magic, BOOT_MAGIC_SZ);
|
||||
if (rc != 0) {
|
||||
flash_area_close(*fap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (memcmp(magic, boot_img_magic, BOOT_MAGIC_SZ) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
flash_area_close(*fap);
|
||||
}
|
||||
|
||||
/* If we got here, no magic was found */
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
boot_read_swap_size(int image_index, uint32_t *swap_size)
|
||||
{
|
||||
uint32_t off;
|
||||
const struct flash_area *fap;
|
||||
int rc;
|
||||
|
||||
rc = boot_find_status(image_index, &fap);
|
||||
if (rc == 0) {
|
||||
off = boot_swap_size_off(fap);
|
||||
rc = flash_area_read(fap, off, swap_size, sizeof *swap_size);
|
||||
flash_area_close(fap);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
int
|
||||
boot_read_enc_key(int image_index, uint8_t slot, struct boot_status *bs)
|
||||
{
|
||||
uint32_t off;
|
||||
const struct flash_area *fap;
|
||||
#if MCUBOOT_SWAP_SAVE_ENCTLV
|
||||
int i;
|
||||
#endif
|
||||
int rc;
|
||||
|
||||
rc = boot_find_status(image_index, &fap);
|
||||
if (rc == 0) {
|
||||
off = boot_enc_key_off(fap, slot);
|
||||
#if MCUBOOT_SWAP_SAVE_ENCTLV
|
||||
rc = flash_area_read(fap, off, bs->enctlv[slot], BOOT_ENC_TLV_ALIGN_SIZE);
|
||||
if (rc == 0) {
|
||||
for (i = 0; i < BOOT_ENC_TLV_ALIGN_SIZE; i++) {
|
||||
if (bs->enctlv[slot][i] != 0xff) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Only try to decrypt non-erased TLV metadata */
|
||||
if (i != BOOT_ENC_TLV_ALIGN_SIZE) {
|
||||
rc = boot_enc_decrypt(bs->enctlv[slot], bs->enckey[slot]);
|
||||
}
|
||||
}
|
||||
#else
|
||||
rc = flash_area_read(fap, off, bs->enckey[slot], BOOT_ENC_KEY_SIZE);
|
||||
#endif
|
||||
flash_area_close(fap);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Write trailer data; status bytes, swap_size, etc
|
||||
*
|
||||
* @returns 0 on success, != 0 on error.
|
||||
*/
|
||||
static int
|
||||
boot_write_trailer(const struct flash_area *fap, uint32_t off,
|
||||
const uint8_t *inbuf, uint8_t inlen)
|
||||
{
|
||||
uint8_t buf[BOOT_MAX_ALIGN];
|
||||
uint8_t align;
|
||||
uint8_t erased_val;
|
||||
int rc;
|
||||
|
||||
align = flash_area_align(fap);
|
||||
if (inlen > BOOT_MAX_ALIGN || align > BOOT_MAX_ALIGN) {
|
||||
return -1;
|
||||
}
|
||||
erased_val = flash_area_erased_val(fap);
|
||||
if (align < inlen) {
|
||||
align = inlen;
|
||||
}
|
||||
memcpy(buf, inbuf, inlen);
|
||||
memset(&buf[inlen], erased_val, align - inlen);
|
||||
|
||||
rc = flash_area_write(fap, off, buf, align);
|
||||
if (rc != 0) {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
boot_write_trailer_flag(const struct flash_area *fap, uint32_t off,
|
||||
uint8_t flag_val)
|
||||
{
|
||||
const uint8_t buf[1] = { flag_val };
|
||||
return boot_write_trailer(fap, off, buf, 1);
|
||||
}
|
||||
|
||||
int
|
||||
boot_write_copy_done(const struct flash_area *fap)
|
||||
{
|
||||
uint32_t off;
|
||||
|
||||
off = boot_copy_done_off(fap);
|
||||
BOOT_LOG_DBG("writing copy_done; fa_id=%d off=0x%lx (0x%lx)",
|
||||
fap->fa_id, (unsigned long)off,
|
||||
(unsigned long)(fap->fa_off + off));
|
||||
return boot_write_trailer_flag(fap, off, BOOT_FLAG_SET);
|
||||
}
|
||||
|
||||
int
|
||||
boot_write_swap_size(const struct flash_area *fap, uint32_t swap_size)
|
||||
{
|
||||
uint32_t off;
|
||||
|
||||
off = boot_swap_size_off(fap);
|
||||
BOOT_LOG_DBG("writing swap_size; fa_id=%d off=0x%lx (0x%lx)",
|
||||
fap->fa_id, (unsigned long)off,
|
||||
(unsigned long)fap->fa_off + off);
|
||||
return boot_write_trailer(fap, off, (const uint8_t *) &swap_size, 4);
|
||||
}
|
||||
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
int
|
||||
boot_write_enc_key(const struct flash_area *fap, uint8_t slot,
|
||||
const struct boot_status *bs)
|
||||
{
|
||||
uint32_t off;
|
||||
int rc;
|
||||
|
||||
off = boot_enc_key_off(fap, slot);
|
||||
BOOT_LOG_DBG("writing enc_key; fa_id=%d off=0x%lx (0x%lx)",
|
||||
fap->fa_id, (unsigned long)off,
|
||||
(unsigned long)fap->fa_off + off);
|
||||
#if MCUBOOT_SWAP_SAVE_ENCTLV
|
||||
rc = flash_area_write(fap, off, bs->enctlv[slot], BOOT_ENC_TLV_ALIGN_SIZE);
|
||||
#else
|
||||
rc = flash_area_write(fap, off, bs->enckey[slot], BOOT_ENC_KEY_SIZE);
|
||||
#endif
|
||||
if (rc != 0) {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,448 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2017-2020 Linaro LTD
|
||||
* Copyright (c) 2017-2019 JUUL Labs
|
||||
* Copyright (c) 2019-2020 Arm Limited
|
||||
*
|
||||
* Original license:
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#ifndef H_BOOTUTIL_PRIV_
|
||||
#define H_BOOTUTIL_PRIV_
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "sysflash/sysflash.h"
|
||||
|
||||
#include <flash_map_backend/flash_map_backend.h>
|
||||
|
||||
#include "bootutil/bootutil.h"
|
||||
#include "bootutil/image.h"
|
||||
#include "bootutil/fault_injection_hardening.h"
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
#include "bootutil/enc_key.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef MCUBOOT_HAVE_ASSERT_H
|
||||
#include "mcuboot_config/mcuboot_assert.h"
|
||||
#else
|
||||
#include <assert.h>
|
||||
#define ASSERT assert
|
||||
#endif
|
||||
|
||||
struct flash_area;
|
||||
|
||||
#define BOOT_EFLASH 1
|
||||
#define BOOT_EFILE 2
|
||||
#define BOOT_EBADIMAGE 3
|
||||
#define BOOT_EBADVECT 4
|
||||
#define BOOT_EBADSTATUS 5
|
||||
#define BOOT_ENOMEM 6
|
||||
#define BOOT_EBADARGS 7
|
||||
#define BOOT_EBADVERSION 8
|
||||
|
||||
#define BOOT_TMPBUF_SZ 256
|
||||
|
||||
/** Number of image slots in flash; currently limited to two. */
|
||||
#define BOOT_NUM_SLOTS 2
|
||||
|
||||
#if (defined(MCUBOOT_OVERWRITE_ONLY) + \
|
||||
defined(MCUBOOT_SWAP_USING_MOVE) + \
|
||||
defined(MCUBOOT_DIRECT_XIP) + \
|
||||
defined(MCUBOOT_RAM_LOAD)) > 1
|
||||
#error "Please enable only one of MCUBOOT_OVERWRITE_ONLY, MCUBOOT_SWAP_USING_MOVE, MCUBOOT_DIRECT_XIP or MCUBOOT_RAM_LOAD"
|
||||
#endif
|
||||
|
||||
#if !defined(MCUBOOT_OVERWRITE_ONLY) && \
|
||||
!defined(MCUBOOT_SWAP_USING_MOVE) && \
|
||||
!defined(MCUBOOT_DIRECT_XIP) && \
|
||||
!defined(MCUBOOT_RAM_LOAD)
|
||||
#define MCUBOOT_SWAP_USING_SCRATCH 1
|
||||
#endif
|
||||
|
||||
#define BOOT_STATUS_OP_MOVE 1
|
||||
#define BOOT_STATUS_OP_SWAP 2
|
||||
|
||||
/*
|
||||
* Maintain state of copy progress.
|
||||
*/
|
||||
struct boot_status {
|
||||
uint32_t idx; /* Which area we're operating on */
|
||||
uint8_t state; /* Which part of the swapping process are we at */
|
||||
uint8_t op; /* What operation are we performing? */
|
||||
uint8_t use_scratch; /* Are status bytes ever written to scratch? */
|
||||
uint8_t swap_type; /* The type of swap in effect */
|
||||
uint32_t swap_size; /* Total size of swapped image */
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
uint8_t enckey[BOOT_NUM_SLOTS][BOOT_ENC_KEY_SIZE];
|
||||
#if MCUBOOT_SWAP_SAVE_ENCTLV
|
||||
uint8_t enctlv[BOOT_NUM_SLOTS][BOOT_ENC_TLV_ALIGN_SIZE];
|
||||
#endif
|
||||
#endif
|
||||
int source; /* Which slot contains swap status metadata */
|
||||
};
|
||||
|
||||
#define BOOT_STATUS_IDX_0 1
|
||||
|
||||
#define BOOT_STATUS_STATE_0 1
|
||||
#define BOOT_STATUS_STATE_1 2
|
||||
#define BOOT_STATUS_STATE_2 3
|
||||
|
||||
/**
|
||||
* End-of-image slot structure.
|
||||
*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* ~ ~
|
||||
* ~ Swap status (BOOT_MAX_IMG_SECTORS * min-write-size * 3) ~
|
||||
* ~ ~
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Encryption key 0 (16 octets) [*] |
|
||||
* | |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Encryption key 1 (16 octets) [*] |
|
||||
* | |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Swap size (4 octets) |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Swap info | 0xff padding (7 octets) |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Copy done | 0xff padding (7 octets) |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Image OK | 0xff padding (7 octets) |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | MAGIC (16 octets) |
|
||||
* | |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*
|
||||
* [*]: Only present if the encryption option is enabled
|
||||
* (`MCUBOOT_ENC_IMAGES`).
|
||||
*/
|
||||
|
||||
extern const uint32_t boot_img_magic[4];
|
||||
|
||||
#ifdef MCUBOOT_IMAGE_NUMBER
|
||||
#define BOOT_IMAGE_NUMBER MCUBOOT_IMAGE_NUMBER
|
||||
#else
|
||||
#define BOOT_IMAGE_NUMBER 1
|
||||
#endif
|
||||
|
||||
_Static_assert(BOOT_IMAGE_NUMBER > 0, "Invalid value for BOOT_IMAGE_NUMBER");
|
||||
|
||||
#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)
|
||||
#define ARE_SLOTS_EQUIVALENT() 0
|
||||
#else
|
||||
#define ARE_SLOTS_EQUIVALENT() 1
|
||||
|
||||
#if (BOOT_IMAGE_NUMBER != 1)
|
||||
#error "The MCUBOOT_DIRECT_XIP and MCUBOOT_RAM_LOAD mode only supports single-image boot (MCUBOOT_IMAGE_NUMBER=1)."
|
||||
#endif
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
#error "Image encryption (MCUBOOT_ENC_IMAGES) is not supported when MCUBOOT_DIRECT_XIP or MCUBOOT_RAM_LOAD mode is selected."
|
||||
#endif
|
||||
#endif /* MCUBOOT_DIRECT_XIP || MCUBOOT_RAM_LOAD */
|
||||
|
||||
#define BOOT_MAX_IMG_SECTORS MCUBOOT_MAX_IMG_SECTORS
|
||||
|
||||
/*
|
||||
* Extract the swap type and image number from image trailers's swap_info
|
||||
* filed.
|
||||
*/
|
||||
#define BOOT_GET_SWAP_TYPE(swap_info) ((swap_info) & 0x0F)
|
||||
#define BOOT_GET_IMAGE_NUM(swap_info) ((swap_info) >> 4)
|
||||
|
||||
/* Construct the swap_info field from swap type and image number */
|
||||
#define BOOT_SET_SWAP_INFO(swap_info, image, type) { \
|
||||
assert((image) < 0xF); \
|
||||
assert((type) < 0xF); \
|
||||
(swap_info) = (image) << 4 \
|
||||
| (type); \
|
||||
}
|
||||
|
||||
#define BOOT_LOG_IMAGE_INFO(slot, hdr) \
|
||||
BOOT_LOG_INF("%-9s slot: version=%u.%u.%u+%u", \
|
||||
((slot) == BOOT_PRIMARY_SLOT) ? "Primary" : "Secondary", \
|
||||
(hdr)->ih_ver.iv_major, \
|
||||
(hdr)->ih_ver.iv_minor, \
|
||||
(hdr)->ih_ver.iv_revision, \
|
||||
(hdr)->ih_ver.iv_build_num)
|
||||
|
||||
/*
|
||||
* The current flashmap API does not check the amount of space allocated when
|
||||
* loading sector data from the flash device, allowing for smaller counts here
|
||||
* would most surely incur in overruns.
|
||||
*
|
||||
* TODO: make flashmap API receive the current sector array size.
|
||||
*/
|
||||
#if BOOT_MAX_IMG_SECTORS < 32
|
||||
#error "Too few sectors, please increase BOOT_MAX_IMG_SECTORS to at least 32"
|
||||
#endif
|
||||
|
||||
#if MCUBOOT_SWAP_USING_MOVE
|
||||
#define BOOT_STATUS_MOVE_STATE_COUNT 1
|
||||
#define BOOT_STATUS_SWAP_STATE_COUNT 2
|
||||
#define BOOT_STATUS_STATE_COUNT (BOOT_STATUS_MOVE_STATE_COUNT + BOOT_STATUS_SWAP_STATE_COUNT)
|
||||
#else
|
||||
#define BOOT_STATUS_STATE_COUNT 3
|
||||
#endif
|
||||
|
||||
/** Maximum number of image sectors supported by the bootloader. */
|
||||
#define BOOT_STATUS_MAX_ENTRIES BOOT_MAX_IMG_SECTORS
|
||||
|
||||
#define BOOT_PRIMARY_SLOT 0
|
||||
#define BOOT_SECONDARY_SLOT 1
|
||||
|
||||
#define BOOT_STATUS_SOURCE_NONE 0
|
||||
#define BOOT_STATUS_SOURCE_SCRATCH 1
|
||||
#define BOOT_STATUS_SOURCE_PRIMARY_SLOT 2
|
||||
|
||||
/**
|
||||
* Compatibility shim for flash sector type.
|
||||
*
|
||||
* This can be deleted when flash_area_to_sectors() is removed.
|
||||
*/
|
||||
#ifdef MCUBOOT_USE_FLASH_AREA_GET_SECTORS
|
||||
typedef struct flash_sector boot_sector_t;
|
||||
#else
|
||||
typedef struct flash_area boot_sector_t;
|
||||
#endif
|
||||
|
||||
/** Private state maintained during boot. */
|
||||
struct boot_loader_state {
|
||||
struct {
|
||||
struct image_header hdr;
|
||||
const struct flash_area *area;
|
||||
boot_sector_t *sectors;
|
||||
size_t num_sectors;
|
||||
} imgs[BOOT_IMAGE_NUMBER][BOOT_NUM_SLOTS];
|
||||
|
||||
#if MCUBOOT_SWAP_USING_SCRATCH
|
||||
struct {
|
||||
const struct flash_area *area;
|
||||
boot_sector_t *sectors;
|
||||
size_t num_sectors;
|
||||
} scratch;
|
||||
#endif
|
||||
|
||||
uint8_t swap_type[BOOT_IMAGE_NUMBER];
|
||||
uint32_t write_sz;
|
||||
|
||||
#if defined(MCUBOOT_ENC_IMAGES)
|
||||
struct enc_key_data enc[BOOT_IMAGE_NUMBER][BOOT_NUM_SLOTS];
|
||||
#endif
|
||||
|
||||
#if (BOOT_IMAGE_NUMBER > 1)
|
||||
uint8_t curr_img_idx;
|
||||
#endif
|
||||
};
|
||||
|
||||
fih_int bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig,
|
||||
size_t slen, uint8_t key_id);
|
||||
|
||||
fih_int boot_fih_memequal(const void *s1, const void *s2, size_t n);
|
||||
|
||||
int boot_magic_compatible_check(uint8_t tbl_val, uint8_t val);
|
||||
uint32_t boot_status_sz(uint32_t min_write_sz);
|
||||
uint32_t boot_trailer_sz(uint32_t min_write_sz);
|
||||
int boot_status_entries(int image_index, const struct flash_area *fap);
|
||||
uint32_t boot_status_off(const struct flash_area *fap);
|
||||
int boot_read_swap_state(const struct flash_area *fap,
|
||||
struct boot_swap_state *state);
|
||||
int boot_read_swap_state_by_id(int flash_area_id,
|
||||
struct boot_swap_state *state);
|
||||
int boot_write_magic(const struct flash_area *fap);
|
||||
int boot_write_status(const struct boot_loader_state *state, struct boot_status *bs);
|
||||
int boot_write_copy_done(const struct flash_area *fap);
|
||||
int boot_write_image_ok(const struct flash_area *fap);
|
||||
int boot_write_swap_info(const struct flash_area *fap, uint8_t swap_type,
|
||||
uint8_t image_num);
|
||||
int boot_write_swap_size(const struct flash_area *fap, uint32_t swap_size);
|
||||
int boot_read_swap_size(int image_index, uint32_t *swap_size);
|
||||
int boot_slots_compatible(struct boot_loader_state *state);
|
||||
uint32_t boot_status_internal_off(const struct boot_status *bs, int elem_sz);
|
||||
int boot_read_image_header(struct boot_loader_state *state, int slot,
|
||||
struct image_header *out_hdr, struct boot_status *bs);
|
||||
int boot_copy_region(struct boot_loader_state *state,
|
||||
const struct flash_area *fap_src,
|
||||
const struct flash_area *fap_dst,
|
||||
uint32_t off_src, uint32_t off_dst, uint32_t sz);
|
||||
int boot_erase_region(const struct flash_area *fap, uint32_t off, uint32_t sz);
|
||||
bool boot_status_is_reset(const struct boot_status *bs);
|
||||
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
int boot_write_enc_key(const struct flash_area *fap, uint8_t slot,
|
||||
const struct boot_status *bs);
|
||||
int boot_read_enc_key(int image_index, uint8_t slot, struct boot_status *bs);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Checks that a buffer is erased according to what the erase value for the
|
||||
* flash device provided in `flash_area` is.
|
||||
*
|
||||
* @returns true if the buffer is erased; false if any of the bytes is not
|
||||
* erased, or when buffer is NULL, or when len == 0.
|
||||
*/
|
||||
bool bootutil_buffer_is_erased(const struct flash_area *area,
|
||||
const void *buffer, size_t len);
|
||||
|
||||
/**
|
||||
* Safe (non-overflowing) uint32_t addition. Returns true, and stores
|
||||
* the result in *dest if it can be done without overflow. Otherwise,
|
||||
* returns false.
|
||||
*/
|
||||
static inline bool boot_u32_safe_add(uint32_t *dest, uint32_t a, uint32_t b)
|
||||
{
|
||||
/*
|
||||
* "a + b <= UINT32_MAX", subtract 'b' from both sides to avoid
|
||||
* the overflow.
|
||||
*/
|
||||
if (a > UINT32_MAX - b) {
|
||||
return false;
|
||||
} else {
|
||||
*dest = a + b;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe (non-overflowing) uint16_t addition. Returns true, and stores
|
||||
* the result in *dest if it can be done without overflow. Otherwise,
|
||||
* returns false.
|
||||
*/
|
||||
static inline bool boot_u16_safe_add(uint16_t *dest, uint16_t a, uint16_t b)
|
||||
{
|
||||
uint32_t tmp = a + b;
|
||||
if (tmp > UINT16_MAX) {
|
||||
return false;
|
||||
} else {
|
||||
*dest = tmp;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Accessors for the contents of struct boot_loader_state.
|
||||
*/
|
||||
|
||||
/* These are macros so they can be used as lvalues. */
|
||||
#if (BOOT_IMAGE_NUMBER > 1)
|
||||
#define BOOT_CURR_IMG(state) ((state)->curr_img_idx)
|
||||
#else
|
||||
#define BOOT_CURR_IMG(state) 0
|
||||
#endif
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
#define BOOT_CURR_ENC(state) ((state)->enc[BOOT_CURR_IMG(state)])
|
||||
#else
|
||||
#define BOOT_CURR_ENC(state) NULL
|
||||
#endif
|
||||
#define BOOT_IMG(state, slot) ((state)->imgs[BOOT_CURR_IMG(state)][(slot)])
|
||||
#define BOOT_IMG_AREA(state, slot) (BOOT_IMG(state, slot).area)
|
||||
#define BOOT_WRITE_SZ(state) ((state)->write_sz)
|
||||
#define BOOT_SWAP_TYPE(state) ((state)->swap_type[BOOT_CURR_IMG(state)])
|
||||
#define BOOT_TLV_OFF(hdr) ((hdr)->ih_hdr_size + (hdr)->ih_img_size)
|
||||
|
||||
#define BOOT_IS_UPGRADE(swap_type) \
|
||||
(((swap_type) == BOOT_SWAP_TYPE_TEST) || \
|
||||
((swap_type) == BOOT_SWAP_TYPE_REVERT) || \
|
||||
((swap_type) == BOOT_SWAP_TYPE_PERM))
|
||||
|
||||
static inline struct image_header*
|
||||
boot_img_hdr(struct boot_loader_state *state, size_t slot)
|
||||
{
|
||||
return &BOOT_IMG(state, slot).hdr;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
boot_img_num_sectors(const struct boot_loader_state *state, size_t slot)
|
||||
{
|
||||
return BOOT_IMG(state, slot).num_sectors;
|
||||
}
|
||||
|
||||
/*
|
||||
* Offset of the slot from the beginning of the flash device.
|
||||
*/
|
||||
static inline uint32_t
|
||||
boot_img_slot_off(struct boot_loader_state *state, size_t slot)
|
||||
{
|
||||
return BOOT_IMG(state, slot).area->fa_off;
|
||||
}
|
||||
|
||||
#ifndef MCUBOOT_USE_FLASH_AREA_GET_SECTORS
|
||||
|
||||
static inline size_t
|
||||
boot_img_sector_size(const struct boot_loader_state *state,
|
||||
size_t slot, size_t sector)
|
||||
{
|
||||
return BOOT_IMG(state, slot).sectors[sector].fa_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Offset of the sector from the beginning of the image, NOT the flash
|
||||
* device.
|
||||
*/
|
||||
static inline uint32_t
|
||||
boot_img_sector_off(const struct boot_loader_state *state, size_t slot,
|
||||
size_t sector)
|
||||
{
|
||||
return BOOT_IMG(state, slot).sectors[sector].fa_off -
|
||||
BOOT_IMG(state, slot).sectors[0].fa_off;
|
||||
}
|
||||
|
||||
#else /* defined(MCUBOOT_USE_FLASH_AREA_GET_SECTORS) */
|
||||
|
||||
static inline size_t
|
||||
boot_img_sector_size(const struct boot_loader_state *state,
|
||||
size_t slot, size_t sector)
|
||||
{
|
||||
return BOOT_IMG(state, slot).sectors[sector].fs_size;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
boot_img_sector_off(const struct boot_loader_state *state, size_t slot,
|
||||
size_t sector)
|
||||
{
|
||||
return BOOT_IMG(state, slot).sectors[sector].fs_off -
|
||||
BOOT_IMG(state, slot).sectors[0].fs_off;
|
||||
}
|
||||
|
||||
#endif /* !defined(MCUBOOT_USE_FLASH_AREA_GET_SECTORS) */
|
||||
|
||||
#ifdef MCUBOOT_RAM_LOAD
|
||||
#define LOAD_IMAGE_DATA(hdr, fap, start, output, size) \
|
||||
(memcpy((output),(void*)((hdr)->ih_load_addr + (start)), \
|
||||
(size)), 0)
|
||||
#else
|
||||
#define LOAD_IMAGE_DATA(hdr, fap, start, output, size) \
|
||||
(flash_area_read((fap), (start), (output), (size)))
|
||||
#endif /* MCUBOOT_RAM_LOAD */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,604 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2017-2019 Linaro LTD
|
||||
* Copyright (c) 2016-2019 JUUL Labs
|
||||
* Copyright (c) 2019-2020 Arm Limited
|
||||
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* Original license:
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Public MCUBoot interface API implementation
|
||||
*
|
||||
* This file contains API implementation which can be combined with
|
||||
* the application in order to interact with the MCUBoot bootloader.
|
||||
* This file contains shared code-base betwen MCUBoot and the application
|
||||
* which controls DFU process.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "sysflash/sysflash.h"
|
||||
#include "flash_map_backend/flash_map_backend.h"
|
||||
|
||||
#include "bootutil/image.h"
|
||||
#include "bootutil/bootutil_public.h"
|
||||
#include "bootutil_priv.h"
|
||||
#include "bootutil/bootutil_log.h"
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
#include "bootutil/enc_key.h"
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MCUBOOT
|
||||
MCUBOOT_LOG_MODULE_DECLARE(mcuboot);
|
||||
#else
|
||||
MCUBOOT_LOG_MODULE_REGISTER(mcuboot_util);
|
||||
#endif
|
||||
|
||||
const uint32_t boot_img_magic[] = {
|
||||
0xf395c277,
|
||||
0x7fefd260,
|
||||
0x0f505235,
|
||||
0x8079b62c,
|
||||
};
|
||||
|
||||
#define BOOT_MAGIC_ARR_SZ \
|
||||
(sizeof boot_img_magic / sizeof boot_img_magic[0])
|
||||
|
||||
struct boot_swap_table {
|
||||
uint8_t magic_primary_slot;
|
||||
uint8_t magic_secondary_slot;
|
||||
uint8_t image_ok_primary_slot;
|
||||
uint8_t image_ok_secondary_slot;
|
||||
uint8_t copy_done_primary_slot;
|
||||
|
||||
uint8_t swap_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* This set of tables maps image trailer contents to swap operation type.
|
||||
* When searching for a match, these tables must be iterated sequentially.
|
||||
*
|
||||
* NOTE: the table order is very important. The settings in the secondary
|
||||
* slot always are priority to the primary slot and should be located
|
||||
* earlier in the table.
|
||||
*
|
||||
* The table lists only states where there is action needs to be taken by
|
||||
* the bootloader, as in starting/finishing a swap operation.
|
||||
*/
|
||||
static const struct boot_swap_table boot_swap_tables[] = {
|
||||
{
|
||||
.magic_primary_slot = BOOT_MAGIC_ANY,
|
||||
.magic_secondary_slot = BOOT_MAGIC_GOOD,
|
||||
.image_ok_primary_slot = BOOT_FLAG_ANY,
|
||||
.image_ok_secondary_slot = BOOT_FLAG_UNSET,
|
||||
.copy_done_primary_slot = BOOT_FLAG_ANY,
|
||||
.swap_type = BOOT_SWAP_TYPE_TEST,
|
||||
},
|
||||
{
|
||||
.magic_primary_slot = BOOT_MAGIC_ANY,
|
||||
.magic_secondary_slot = BOOT_MAGIC_GOOD,
|
||||
.image_ok_primary_slot = BOOT_FLAG_ANY,
|
||||
.image_ok_secondary_slot = BOOT_FLAG_SET,
|
||||
.copy_done_primary_slot = BOOT_FLAG_ANY,
|
||||
.swap_type = BOOT_SWAP_TYPE_PERM,
|
||||
},
|
||||
{
|
||||
.magic_primary_slot = BOOT_MAGIC_GOOD,
|
||||
.magic_secondary_slot = BOOT_MAGIC_UNSET,
|
||||
.image_ok_primary_slot = BOOT_FLAG_UNSET,
|
||||
.image_ok_secondary_slot = BOOT_FLAG_ANY,
|
||||
.copy_done_primary_slot = BOOT_FLAG_SET,
|
||||
.swap_type = BOOT_SWAP_TYPE_REVERT,
|
||||
},
|
||||
};
|
||||
|
||||
#define BOOT_SWAP_TABLES_COUNT \
|
||||
(sizeof boot_swap_tables / sizeof boot_swap_tables[0])
|
||||
|
||||
static int
|
||||
boot_magic_decode(const uint32_t *magic)
|
||||
{
|
||||
if (memcmp(magic, boot_img_magic, BOOT_MAGIC_SZ) == 0) {
|
||||
return BOOT_MAGIC_GOOD;
|
||||
}
|
||||
return BOOT_MAGIC_BAD;
|
||||
}
|
||||
|
||||
static int
|
||||
boot_flag_decode(uint8_t flag)
|
||||
{
|
||||
if (flag != BOOT_FLAG_SET) {
|
||||
return BOOT_FLAG_BAD;
|
||||
}
|
||||
return BOOT_FLAG_SET;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
boot_magic_off(const struct flash_area *fap)
|
||||
{
|
||||
return fap->fa_size - BOOT_MAGIC_SZ;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
boot_image_ok_off(const struct flash_area *fap)
|
||||
{
|
||||
return boot_magic_off(fap) - BOOT_MAX_ALIGN;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
boot_copy_done_off(const struct flash_area *fap)
|
||||
{
|
||||
return boot_image_ok_off(fap) - BOOT_MAX_ALIGN;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
boot_swap_size_off(const struct flash_area *fap)
|
||||
{
|
||||
return boot_swap_info_off(fap) - BOOT_MAX_ALIGN;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
boot_swap_info_off(const struct flash_area *fap)
|
||||
{
|
||||
return boot_copy_done_off(fap) - BOOT_MAX_ALIGN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a status source table is satisfied by the specified magic
|
||||
* code.
|
||||
*
|
||||
* @param tbl_val A magic field from a status source table.
|
||||
* @param val The magic value in a trailer, encoded as a
|
||||
* BOOT_MAGIC_[...].
|
||||
*
|
||||
* @return 1 if the two values are compatible;
|
||||
* 0 otherwise.
|
||||
*/
|
||||
int
|
||||
boot_magic_compatible_check(uint8_t tbl_val, uint8_t val)
|
||||
{
|
||||
switch (tbl_val) {
|
||||
case BOOT_MAGIC_ANY:
|
||||
return 1;
|
||||
|
||||
case BOOT_MAGIC_NOTGOOD:
|
||||
return val != BOOT_MAGIC_GOOD;
|
||||
|
||||
default:
|
||||
return tbl_val == val;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
static inline uint32_t
|
||||
boot_enc_key_off(const struct flash_area *fap, uint8_t slot)
|
||||
{
|
||||
#if MCUBOOT_SWAP_SAVE_ENCTLV
|
||||
return boot_swap_size_off(fap) - ((slot + 1) *
|
||||
((((BOOT_ENC_TLV_SIZE - 1) / BOOT_MAX_ALIGN) + 1) * BOOT_MAX_ALIGN));
|
||||
#else
|
||||
return boot_swap_size_off(fap) - ((slot + 1) * BOOT_ENC_KEY_SIZE);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
bool bootutil_buffer_is_erased(const struct flash_area *area,
|
||||
const void *buffer, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
uint8_t *u8b;
|
||||
uint8_t erased_val;
|
||||
|
||||
if (buffer == NULL || len == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
erased_val = flash_area_erased_val(area);
|
||||
for (i = 0, u8b = (uint8_t *)buffer; i < len; i++) {
|
||||
if (u8b[i] != erased_val) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
boot_read_flag(const struct flash_area *fap, uint8_t *flag, uint32_t off)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = flash_area_read(fap, off, flag, sizeof *flag);
|
||||
if (rc < 0) {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
if (bootutil_buffer_is_erased(fap, flag, sizeof *flag)) {
|
||||
*flag = BOOT_FLAG_UNSET;
|
||||
} else {
|
||||
*flag = boot_flag_decode(*flag);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
boot_read_copy_done(const struct flash_area *fap, uint8_t *copy_done)
|
||||
{
|
||||
return boot_read_flag(fap, copy_done, boot_copy_done_off(fap));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
boot_read_swap_state(const struct flash_area *fap,
|
||||
struct boot_swap_state *state)
|
||||
{
|
||||
uint32_t magic[BOOT_MAGIC_ARR_SZ];
|
||||
uint32_t off;
|
||||
uint8_t swap_info;
|
||||
int rc;
|
||||
|
||||
off = boot_magic_off(fap);
|
||||
rc = flash_area_read(fap, off, magic, BOOT_MAGIC_SZ);
|
||||
if (rc < 0) {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
if (bootutil_buffer_is_erased(fap, magic, BOOT_MAGIC_SZ)) {
|
||||
state->magic = BOOT_MAGIC_UNSET;
|
||||
} else {
|
||||
state->magic = boot_magic_decode(magic);
|
||||
}
|
||||
|
||||
off = boot_swap_info_off(fap);
|
||||
rc = flash_area_read(fap, off, &swap_info, sizeof swap_info);
|
||||
if (rc < 0) {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
|
||||
/* Extract the swap type and image number */
|
||||
state->swap_type = BOOT_GET_SWAP_TYPE(swap_info);
|
||||
state->image_num = BOOT_GET_IMAGE_NUM(swap_info);
|
||||
|
||||
if (bootutil_buffer_is_erased(fap, &swap_info, sizeof swap_info) ||
|
||||
state->swap_type > BOOT_SWAP_TYPE_REVERT) {
|
||||
state->swap_type = BOOT_SWAP_TYPE_NONE;
|
||||
state->image_num = 0;
|
||||
}
|
||||
|
||||
rc = boot_read_copy_done(fap, &state->copy_done);
|
||||
if (rc) {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
|
||||
return boot_read_image_ok(fap, &state->image_ok);
|
||||
}
|
||||
|
||||
int
|
||||
boot_read_swap_state_by_id(int flash_area_id, struct boot_swap_state *state)
|
||||
{
|
||||
const struct flash_area *fap;
|
||||
int rc;
|
||||
|
||||
rc = flash_area_open(flash_area_id, &fap);
|
||||
if (rc != 0) {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
|
||||
rc = boot_read_swap_state(fap, state);
|
||||
flash_area_close(fap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
boot_write_magic(const struct flash_area *fap)
|
||||
{
|
||||
uint32_t off;
|
||||
int rc;
|
||||
|
||||
off = boot_magic_off(fap);
|
||||
|
||||
BOOT_LOG_DBG("writing magic; fa_id=%d off=0x%lx (0x%lx)",
|
||||
fap->fa_id, (unsigned long)off,
|
||||
(unsigned long)(fap->fa_off + off));
|
||||
rc = flash_area_write(fap, off, boot_img_magic, BOOT_MAGIC_SZ);
|
||||
if (rc != 0) {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write trailer data; status bytes, swap_size, etc
|
||||
*
|
||||
* @returns 0 on success, != 0 on error.
|
||||
*/
|
||||
static int
|
||||
boot_write_trailer(const struct flash_area *fap, uint32_t off,
|
||||
const uint8_t *inbuf, uint8_t inlen)
|
||||
{
|
||||
uint8_t buf[BOOT_MAX_ALIGN];
|
||||
uint8_t align;
|
||||
uint8_t erased_val;
|
||||
int rc;
|
||||
|
||||
align = flash_area_align(fap);
|
||||
if (inlen > BOOT_MAX_ALIGN || align > BOOT_MAX_ALIGN) {
|
||||
return -1;
|
||||
}
|
||||
erased_val = flash_area_erased_val(fap);
|
||||
if (align < inlen) {
|
||||
align = inlen;
|
||||
}
|
||||
memcpy(buf, inbuf, inlen);
|
||||
memset(&buf[inlen], erased_val, align - inlen);
|
||||
|
||||
rc = flash_area_write(fap, off, buf, align);
|
||||
if (rc != 0) {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
boot_write_trailer_flag(const struct flash_area *fap, uint32_t off,
|
||||
uint8_t flag_val)
|
||||
{
|
||||
const uint8_t buf[1] = { flag_val };
|
||||
return boot_write_trailer(fap, off, buf, 1);
|
||||
}
|
||||
|
||||
int
|
||||
boot_write_image_ok(const struct flash_area *fap)
|
||||
{
|
||||
uint32_t off;
|
||||
|
||||
off = boot_image_ok_off(fap);
|
||||
BOOT_LOG_DBG("writing image_ok; fa_id=%d off=0x%lx (0x%lx)",
|
||||
fap->fa_id, (unsigned long)off,
|
||||
(unsigned long)(fap->fa_off + off));
|
||||
return boot_write_trailer_flag(fap, off, BOOT_FLAG_SET);
|
||||
}
|
||||
|
||||
int
|
||||
boot_read_image_ok(const struct flash_area *fap, uint8_t *image_ok)
|
||||
{
|
||||
return boot_read_flag(fap, image_ok, boot_image_ok_off(fap));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified value to the `swap-type` field of an image trailer.
|
||||
* This value is persisted so that the boot loader knows what swap operation to
|
||||
* resume in case of an unexpected reset.
|
||||
*/
|
||||
int
|
||||
boot_write_swap_info(const struct flash_area *fap, uint8_t swap_type,
|
||||
uint8_t image_num)
|
||||
{
|
||||
uint32_t off;
|
||||
uint8_t swap_info;
|
||||
|
||||
BOOT_SET_SWAP_INFO(swap_info, image_num, swap_type);
|
||||
off = boot_swap_info_off(fap);
|
||||
BOOT_LOG_DBG("writing swap_info; fa_id=%d off=0x%lx (0x%lx), swap_type=0x%x"
|
||||
" image_num=0x%x",
|
||||
fap->fa_id, (unsigned long)off,
|
||||
(unsigned long)(fap->fa_off + off), swap_type, image_num);
|
||||
return boot_write_trailer(fap, off, (const uint8_t *) &swap_info, 1);
|
||||
}
|
||||
|
||||
int
|
||||
boot_swap_type_multi(int image_index)
|
||||
{
|
||||
const struct boot_swap_table *table;
|
||||
struct boot_swap_state primary_slot;
|
||||
struct boot_swap_state secondary_slot;
|
||||
int rc;
|
||||
size_t i;
|
||||
|
||||
rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY(image_index),
|
||||
&primary_slot);
|
||||
if (rc) {
|
||||
return BOOT_SWAP_TYPE_PANIC;
|
||||
}
|
||||
|
||||
rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SECONDARY(image_index),
|
||||
&secondary_slot);
|
||||
if (rc) {
|
||||
return BOOT_SWAP_TYPE_PANIC;
|
||||
}
|
||||
|
||||
for (i = 0; i < BOOT_SWAP_TABLES_COUNT; i++) {
|
||||
table = boot_swap_tables + i;
|
||||
|
||||
if (boot_magic_compatible_check(table->magic_primary_slot,
|
||||
primary_slot.magic) &&
|
||||
boot_magic_compatible_check(table->magic_secondary_slot,
|
||||
secondary_slot.magic) &&
|
||||
(table->image_ok_primary_slot == BOOT_FLAG_ANY ||
|
||||
table->image_ok_primary_slot == primary_slot.image_ok) &&
|
||||
(table->image_ok_secondary_slot == BOOT_FLAG_ANY ||
|
||||
table->image_ok_secondary_slot == secondary_slot.image_ok) &&
|
||||
(table->copy_done_primary_slot == BOOT_FLAG_ANY ||
|
||||
table->copy_done_primary_slot == primary_slot.copy_done)) {
|
||||
BOOT_LOG_INF("Swap type: %s",
|
||||
table->swap_type == BOOT_SWAP_TYPE_TEST ? "test" :
|
||||
table->swap_type == BOOT_SWAP_TYPE_PERM ? "perm" :
|
||||
table->swap_type == BOOT_SWAP_TYPE_REVERT ? "revert" :
|
||||
"BUG; can't happen");
|
||||
if (table->swap_type != BOOT_SWAP_TYPE_TEST &&
|
||||
table->swap_type != BOOT_SWAP_TYPE_PERM &&
|
||||
table->swap_type != BOOT_SWAP_TYPE_REVERT) {
|
||||
return BOOT_SWAP_TYPE_PANIC;
|
||||
}
|
||||
return table->swap_type;
|
||||
}
|
||||
}
|
||||
|
||||
BOOT_LOG_INF("Swap type: none");
|
||||
return BOOT_SWAP_TYPE_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is not used by the bootloader itself, but its required API
|
||||
* by external tooling like mcumgr.
|
||||
*/
|
||||
int
|
||||
boot_swap_type(void)
|
||||
{
|
||||
return boot_swap_type_multi(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the image in the secondary slot as pending. On the next reboot,
|
||||
* the system will perform a one-time boot of the the secondary slot image.
|
||||
*
|
||||
* @param permanent Whether the image should be used permanently or
|
||||
* only tested once:
|
||||
* 0=run image once, then confirm or revert.
|
||||
* 1=run image forever.
|
||||
*
|
||||
* @return 0 on success; nonzero on failure.
|
||||
*/
|
||||
int
|
||||
boot_set_pending(int permanent)
|
||||
{
|
||||
const struct flash_area *fap;
|
||||
struct boot_swap_state state_secondary_slot;
|
||||
uint8_t swap_type;
|
||||
int rc;
|
||||
|
||||
rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SECONDARY(0),
|
||||
&state_secondary_slot);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
switch (state_secondary_slot.magic) {
|
||||
case BOOT_MAGIC_GOOD:
|
||||
/* Swap already scheduled. */
|
||||
return 0;
|
||||
|
||||
case BOOT_MAGIC_UNSET:
|
||||
rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(0), &fap);
|
||||
if (rc != 0) {
|
||||
rc = BOOT_EFLASH;
|
||||
} else {
|
||||
rc = boot_write_magic(fap);
|
||||
}
|
||||
|
||||
if (rc == 0 && permanent) {
|
||||
rc = boot_write_image_ok(fap);
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
if (permanent) {
|
||||
swap_type = BOOT_SWAP_TYPE_PERM;
|
||||
} else {
|
||||
swap_type = BOOT_SWAP_TYPE_TEST;
|
||||
}
|
||||
rc = boot_write_swap_info(fap, swap_type, 0);
|
||||
}
|
||||
|
||||
flash_area_close(fap);
|
||||
return rc;
|
||||
|
||||
case BOOT_MAGIC_BAD:
|
||||
/* The image slot is corrupt. There is no way to recover, so erase the
|
||||
* slot to allow future upgrades.
|
||||
*/
|
||||
rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(0), &fap);
|
||||
if (rc != 0) {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
|
||||
flash_area_erase(fap, 0, fap->fa_size);
|
||||
flash_area_close(fap);
|
||||
return BOOT_EBADIMAGE;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
return BOOT_EBADIMAGE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the image in the primary slot as confirmed. The system will continue
|
||||
* booting into the image in the primary slot until told to boot from a
|
||||
* different slot.
|
||||
*
|
||||
* @return 0 on success; nonzero on failure.
|
||||
*/
|
||||
int
|
||||
boot_set_confirmed(void)
|
||||
{
|
||||
const struct flash_area *fap;
|
||||
struct boot_swap_state state_primary_slot;
|
||||
int rc;
|
||||
|
||||
rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY(0),
|
||||
&state_primary_slot);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
switch (state_primary_slot.magic) {
|
||||
case BOOT_MAGIC_GOOD:
|
||||
/* Confirm needed; proceed. */
|
||||
break;
|
||||
|
||||
case BOOT_MAGIC_UNSET:
|
||||
/* Already confirmed. */
|
||||
return 0;
|
||||
|
||||
case BOOT_MAGIC_BAD:
|
||||
/* Unexpected state. */
|
||||
return BOOT_EBADVECT;
|
||||
}
|
||||
|
||||
rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(0), &fap);
|
||||
if (rc) {
|
||||
rc = BOOT_EFLASH;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (state_primary_slot.copy_done == BOOT_FLAG_UNSET) {
|
||||
/* Swap never completed. This is unexpected. */
|
||||
rc = BOOT_EBADVECT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (state_primary_slot.image_ok != BOOT_FLAG_UNSET) {
|
||||
/* Already confirmed. */
|
||||
goto done;
|
||||
}
|
||||
|
||||
rc = boot_write_image_ok(fap);
|
||||
|
||||
done:
|
||||
flash_area_close(fap);
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2017 Linaro Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <bootutil/caps.h>
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
uint32_t bootutil_get_caps(void)
|
||||
{
|
||||
uint32_t res = 0;
|
||||
|
||||
#if defined(MCUBOOT_SIGN_RSA)
|
||||
#if MCUBOOT_SIGN_RSA_LEN == 2048
|
||||
res |= BOOTUTIL_CAP_RSA2048;
|
||||
#endif
|
||||
#if MCUBOOT_SIGN_RSA_LEN == 3072
|
||||
res |= BOOTUTIL_CAP_RSA3072;
|
||||
#endif
|
||||
#endif
|
||||
#if defined(MCUBOOT_SIGN_EC)
|
||||
res |= BOOTUTIL_CAP_ECDSA_P224;
|
||||
#endif
|
||||
#if defined(MCUBOOT_SIGN_EC256)
|
||||
res |= BOOTUTIL_CAP_ECDSA_P256;
|
||||
#endif
|
||||
#if defined(MCUBOOT_SIGN_ED25519)
|
||||
res |= BOOTUTIL_CAP_ED25519;
|
||||
#endif
|
||||
#if defined(MCUBOOT_OVERWRITE_ONLY)
|
||||
res |= BOOTUTIL_CAP_OVERWRITE_UPGRADE;
|
||||
#elif defined(MCUBOOT_SWAP_USING_MOVE)
|
||||
res |= BOOTUTIL_CAP_SWAP_USING_MOVE;
|
||||
#else
|
||||
res |= BOOTUTIL_CAP_SWAP_USING_SCRATCH;
|
||||
#endif
|
||||
#if defined(MCUBOOT_ENCRYPT_RSA)
|
||||
res |= BOOTUTIL_CAP_ENC_RSA;
|
||||
#endif
|
||||
#if defined(MCUBOOT_ENCRYPT_KW)
|
||||
res |= BOOTUTIL_CAP_ENC_KW;
|
||||
#endif
|
||||
#if defined(MCUBOOT_ENCRYPT_EC256)
|
||||
res |= BOOTUTIL_CAP_ENC_EC256;
|
||||
#endif
|
||||
#if defined(MCUBOOT_ENCRYPT_X25519)
|
||||
res |= BOOTUTIL_CAP_ENC_X25519;
|
||||
#endif
|
||||
#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
|
||||
res |= BOOTUTIL_CAP_VALIDATE_PRIMARY_SLOT;
|
||||
#endif
|
||||
#if defined(MCUBOOT_DOWNGRADE_PREVENTION)
|
||||
res |= BOOTUTIL_CAP_DOWNGRADE_PREVENTION;
|
||||
#endif
|
||||
#if defined(MCUBOOT_BOOTSTRAP)
|
||||
res |= BOOTUTIL_CAP_BOOTSTRAP;
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t bootutil_get_num_images(void)
|
||||
{
|
||||
#if defined(MCUBOOT_IMAGE_NUMBER)
|
||||
return MCUBOOT_IMAGE_NUMBER;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,774 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2018-2019 JUUL Labs
|
||||
* Copyright (c) 2019 Arm Limited
|
||||
*/
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
#if defined(MCUBOOT_ENC_IMAGES)
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_RSA)
|
||||
#include "mbedtls/rsa.h"
|
||||
#include "mbedtls/rsa_internal.h"
|
||||
#include "mbedtls/asn1.h"
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_KW)
|
||||
#include "bootutil/crypto/aes_kw.h"
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_EC256)
|
||||
#include "bootutil/crypto/ecdh_p256.h"
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_X25519)
|
||||
#include "bootutil/crypto/ecdh_x25519.h"
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519)
|
||||
#include "bootutil/crypto/sha256.h"
|
||||
#include "bootutil/crypto/hmac_sha256.h"
|
||||
#include "mbedtls/oid.h"
|
||||
#include "mbedtls/asn1.h"
|
||||
#endif
|
||||
|
||||
#include "bootutil/image.h"
|
||||
#include "bootutil/enc_key.h"
|
||||
#include "bootutil/sign_key.h"
|
||||
|
||||
#include "bootutil_priv.h"
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519)
|
||||
#if defined(_compare)
|
||||
static inline int bootutil_constant_time_compare(const uint8_t *a, const uint8_t *b, size_t size)
|
||||
{
|
||||
return _compare(a, b, size);
|
||||
}
|
||||
#else
|
||||
static int bootutil_constant_time_compare(const uint8_t *a, const uint8_t *b, size_t size)
|
||||
{
|
||||
const uint8_t *tempa = a;
|
||||
const uint8_t *tempb = b;
|
||||
uint8_t result = 0;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
result |= tempa[i] ^ tempb[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_KW)
|
||||
static int
|
||||
key_unwrap(const uint8_t *wrapped, uint8_t *enckey)
|
||||
{
|
||||
bootutil_aes_kw_context aes_kw;
|
||||
int rc;
|
||||
|
||||
bootutil_aes_kw_init(&aes_kw);
|
||||
rc = bootutil_aes_kw_set_unwrap_key(&aes_kw, bootutil_enc_key.key, *bootutil_enc_key.len);
|
||||
if (rc != 0) {
|
||||
goto done;
|
||||
}
|
||||
rc = bootutil_aes_kw_unwrap(&aes_kw, wrapped, TLV_ENC_KW_SZ, enckey, BOOT_ENC_KEY_SIZE);
|
||||
if (rc != 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
bootutil_aes_kw_drop(&aes_kw);
|
||||
return rc;
|
||||
}
|
||||
#endif /* MCUBOOT_ENCRYPT_KW */
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_RSA)
|
||||
static int
|
||||
parse_rsa_enckey(mbedtls_rsa_context *ctx, uint8_t **p, uint8_t *end)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
if (mbedtls_asn1_get_tag(p, end, &len,
|
||||
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*p + len != end) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Non-optional fields. */
|
||||
if ( /* version */
|
||||
mbedtls_asn1_get_int(p, end, &ctx->ver) != 0 ||
|
||||
/* public modulus */
|
||||
mbedtls_asn1_get_mpi(p, end, &ctx->N) != 0 ||
|
||||
/* public exponent */
|
||||
mbedtls_asn1_get_mpi(p, end, &ctx->E) != 0 ||
|
||||
/* private exponent */
|
||||
mbedtls_asn1_get_mpi(p, end, &ctx->D) != 0 ||
|
||||
/* primes */
|
||||
mbedtls_asn1_get_mpi(p, end, &ctx->P) != 0 ||
|
||||
mbedtls_asn1_get_mpi(p, end, &ctx->Q) != 0) {
|
||||
|
||||
return -3;
|
||||
}
|
||||
|
||||
#if !defined(MBEDTLS_RSA_NO_CRT)
|
||||
/*
|
||||
* DP/DQ/QP are only used inside mbedTLS if it was built with the
|
||||
* Chinese Remainder Theorem enabled (default). In case it is disabled
|
||||
* we parse, or if not available, we calculate those values.
|
||||
*/
|
||||
if (*p < end) {
|
||||
if ( /* d mod (p-1) and d mod (q-1) */
|
||||
mbedtls_asn1_get_mpi(p, end, &ctx->DP) != 0 ||
|
||||
mbedtls_asn1_get_mpi(p, end, &ctx->DQ) != 0 ||
|
||||
/* q ^ (-1) mod p */
|
||||
mbedtls_asn1_get_mpi(p, end, &ctx->QP) != 0) {
|
||||
|
||||
return -4;
|
||||
}
|
||||
} else {
|
||||
if (mbedtls_rsa_deduce_crt(&ctx->P, &ctx->Q, &ctx->D,
|
||||
&ctx->DP, &ctx->DQ, &ctx->QP) != 0) {
|
||||
return -5;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ctx->len = mbedtls_mpi_size(&ctx->N);
|
||||
|
||||
if (mbedtls_rsa_check_privkey(ctx) != 0) {
|
||||
return -6;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_EC256)
|
||||
static const uint8_t ec_pubkey_oid[] = MBEDTLS_OID_EC_ALG_UNRESTRICTED;
|
||||
static const uint8_t ec_secp256r1_oid[] = MBEDTLS_OID_EC_GRP_SECP256R1;
|
||||
|
||||
#define SHARED_KEY_LEN NUM_ECC_BYTES
|
||||
#define PRIV_KEY_LEN NUM_ECC_BYTES
|
||||
|
||||
/*
|
||||
* Parses the output of `imgtool keygen`, which produces a PKCS#8 elliptic
|
||||
* curve keypair. See RFC5208 and RFC5915.
|
||||
*/
|
||||
static int
|
||||
parse_ec256_enckey(uint8_t **p, uint8_t *end, uint8_t *private_key)
|
||||
{
|
||||
int rc;
|
||||
size_t len;
|
||||
int version;
|
||||
mbedtls_asn1_buf alg;
|
||||
mbedtls_asn1_buf param;
|
||||
|
||||
if ((rc = mbedtls_asn1_get_tag(p, end, &len,
|
||||
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*p + len != end) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
version = 0;
|
||||
if (mbedtls_asn1_get_int(p, end, &version) || version != 0) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
if ((rc = mbedtls_asn1_get_alg(p, end, &alg, ¶m)) != 0) {
|
||||
return -5;
|
||||
}
|
||||
|
||||
if (alg.len != sizeof(ec_pubkey_oid) - 1 ||
|
||||
memcmp(alg.p, ec_pubkey_oid, sizeof(ec_pubkey_oid) - 1)) {
|
||||
return -6;
|
||||
}
|
||||
if (param.len != sizeof(ec_secp256r1_oid) - 1 ||
|
||||
memcmp(param.p, ec_secp256r1_oid, sizeof(ec_secp256r1_oid) - 1)) {
|
||||
return -7;
|
||||
}
|
||||
|
||||
if ((rc = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_OCTET_STRING)) != 0) {
|
||||
return -8;
|
||||
}
|
||||
|
||||
/* RFC5915 - ECPrivateKey */
|
||||
|
||||
if ((rc = mbedtls_asn1_get_tag(p, end, &len,
|
||||
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) {
|
||||
return -9;
|
||||
}
|
||||
|
||||
version = 0;
|
||||
if (mbedtls_asn1_get_int(p, end, &version) || version != 1) {
|
||||
return -10;
|
||||
}
|
||||
|
||||
/* privateKey */
|
||||
|
||||
if ((rc = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_OCTET_STRING)) != 0) {
|
||||
return -11;
|
||||
}
|
||||
|
||||
if (len != NUM_ECC_BYTES) {
|
||||
return -12;
|
||||
}
|
||||
|
||||
memcpy(private_key, *p, len);
|
||||
|
||||
/* publicKey usually follows but is not parsed here */
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* defined(MCUBOOT_ENCRYPT_EC256) */
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_X25519)
|
||||
#define X25519_OID "\x6e"
|
||||
static const uint8_t ec_pubkey_oid[] = MBEDTLS_OID_ISO_IDENTIFIED_ORG \
|
||||
MBEDTLS_OID_ORG_GOV X25519_OID;
|
||||
|
||||
#define SHARED_KEY_LEN 32
|
||||
#define PRIV_KEY_LEN 32
|
||||
|
||||
static int
|
||||
parse_x25519_enckey(uint8_t **p, uint8_t *end, uint8_t *private_key)
|
||||
{
|
||||
size_t len;
|
||||
int version;
|
||||
mbedtls_asn1_buf alg;
|
||||
mbedtls_asn1_buf param;
|
||||
|
||||
if (mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
|
||||
MBEDTLS_ASN1_SEQUENCE) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*p + len != end) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
version = 0;
|
||||
if (mbedtls_asn1_get_int(p, end, &version) || version != 0) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (mbedtls_asn1_get_alg(p, end, &alg, ¶m) != 0) {
|
||||
return -4;
|
||||
}
|
||||
|
||||
if (alg.len != sizeof(ec_pubkey_oid) - 1 ||
|
||||
memcmp(alg.p, ec_pubkey_oid, sizeof(ec_pubkey_oid) - 1)) {
|
||||
return -5;
|
||||
}
|
||||
|
||||
if (mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_OCTET_STRING) != 0) {
|
||||
return -6;
|
||||
}
|
||||
|
||||
if (mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_OCTET_STRING) != 0) {
|
||||
return -7;
|
||||
}
|
||||
|
||||
if (len != PRIV_KEY_LEN) {
|
||||
return -8;
|
||||
}
|
||||
|
||||
memcpy(private_key, *p, PRIV_KEY_LEN);
|
||||
return 0;
|
||||
}
|
||||
#endif /* defined(MCUBOOT_ENCRYPT_X25519) */
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519)
|
||||
/*
|
||||
* HKDF as described by RFC5869.
|
||||
*
|
||||
* @param ikm The input data to be derived.
|
||||
* @param ikm_len Length of the input data.
|
||||
* @param info An information tag.
|
||||
* @param info_len Length of the information tag.
|
||||
* @param okm Output of the KDF computation.
|
||||
* @param okm_len On input the requested length; on output the generated length
|
||||
*/
|
||||
static int
|
||||
hkdf(uint8_t *ikm, uint16_t ikm_len, uint8_t *info, uint16_t info_len,
|
||||
uint8_t *okm, uint16_t *okm_len)
|
||||
{
|
||||
bootutil_hmac_sha256_context hmac;
|
||||
uint8_t salt[BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE];
|
||||
uint8_t prk[BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE];
|
||||
uint8_t T[BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE];
|
||||
uint16_t off;
|
||||
uint16_t len;
|
||||
uint8_t counter;
|
||||
bool first;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Extract
|
||||
*/
|
||||
|
||||
if (ikm == NULL || okm == NULL || ikm_len == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bootutil_hmac_sha256_init(&hmac);
|
||||
|
||||
memset(salt, 0, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE);
|
||||
rc = bootutil_hmac_sha256_set_key(&hmac, salt, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE);
|
||||
if (rc != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = bootutil_hmac_sha256_update(&hmac, ikm, ikm_len);
|
||||
if (rc != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = bootutil_hmac_sha256_finish(&hmac, prk, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE);
|
||||
if (rc != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expand
|
||||
*/
|
||||
|
||||
len = *okm_len;
|
||||
counter = 1;
|
||||
first = true;
|
||||
for (off = 0; len > 0; off += BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE, ++counter) {
|
||||
bootutil_hmac_sha256_init(&hmac);
|
||||
|
||||
rc = bootutil_hmac_sha256_set_key(&hmac, prk, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE);
|
||||
if (rc != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
rc = bootutil_hmac_sha256_update(&hmac, T, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE);
|
||||
if (rc != 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
rc = bootutil_hmac_sha256_update(&hmac, info, info_len);
|
||||
if (rc != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = bootutil_hmac_sha256_update(&hmac, &counter, 1);
|
||||
if (rc != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = bootutil_hmac_sha256_finish(&hmac, T, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE);
|
||||
if (rc != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (len > BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE) {
|
||||
memcpy(&okm[off], T, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE);
|
||||
len -= BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE;
|
||||
} else {
|
||||
memcpy(&okm[off], T, len);
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bootutil_hmac_sha256_drop(&hmac);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
bootutil_hmac_sha256_drop(&hmac);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
boot_enc_init(struct enc_key_data *enc_state, uint8_t slot)
|
||||
{
|
||||
bootutil_aes_ctr_init(&enc_state[slot].aes_ctr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
boot_enc_drop(struct enc_key_data *enc_state, uint8_t slot)
|
||||
{
|
||||
bootutil_aes_ctr_drop(&enc_state[slot].aes_ctr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
boot_enc_set_key(struct enc_key_data *enc_state, uint8_t slot,
|
||||
const struct boot_status *bs)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = bootutil_aes_ctr_set_key(&enc_state[slot].aes_ctr, bs->enckey[slot]);
|
||||
if (rc != 0) {
|
||||
boot_enc_drop(enc_state, slot);
|
||||
enc_state[slot].valid = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
enc_state[slot].valid = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define EXPECTED_ENC_LEN BOOT_ENC_TLV_SIZE
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_RSA)
|
||||
# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_RSA2048
|
||||
#elif defined(MCUBOOT_ENCRYPT_KW)
|
||||
# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_KW128
|
||||
#elif defined(MCUBOOT_ENCRYPT_EC256)
|
||||
# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_EC256
|
||||
# define EC_PUBK_INDEX (1)
|
||||
# define EC_TAG_INDEX (65)
|
||||
# define EC_CIPHERKEY_INDEX (65 + 32)
|
||||
_Static_assert(EC_CIPHERKEY_INDEX + 16 == EXPECTED_ENC_LEN,
|
||||
"Please fix ECIES-P256 component indexes");
|
||||
#elif defined(MCUBOOT_ENCRYPT_X25519)
|
||||
# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_X25519
|
||||
# define EC_PUBK_INDEX (0)
|
||||
# define EC_TAG_INDEX (32)
|
||||
# define EC_CIPHERKEY_INDEX (32 + 32)
|
||||
_Static_assert(EC_CIPHERKEY_INDEX + 16 == EXPECTED_ENC_LEN,
|
||||
"Please fix ECIES-X25519 component indexes");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Decrypt an encryption key TLV.
|
||||
*
|
||||
* @param buf An encryption TLV read from flash (build time fixed length)
|
||||
* @param enckey An AES-128 key sized buffer to store to plain key.
|
||||
*/
|
||||
int
|
||||
boot_enc_decrypt(const uint8_t *buf, uint8_t *enckey)
|
||||
{
|
||||
#if defined(MCUBOOT_ENCRYPT_RSA)
|
||||
mbedtls_rsa_context rsa;
|
||||
uint8_t *cp;
|
||||
uint8_t *cpend;
|
||||
size_t olen;
|
||||
#endif
|
||||
#if defined(MCUBOOT_ENCRYPT_EC256)
|
||||
bootutil_ecdh_p256_context ecdh_p256;
|
||||
#endif
|
||||
#if defined(MCUBOOT_ENCRYPT_X25519)
|
||||
bootutil_ecdh_x25519_context ecdh_x25519;
|
||||
#endif
|
||||
#if defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519)
|
||||
bootutil_hmac_sha256_context hmac;
|
||||
bootutil_aes_ctr_context aes_ctr;
|
||||
uint8_t tag[BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE];
|
||||
uint8_t shared[SHARED_KEY_LEN];
|
||||
uint8_t derived_key[BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE];
|
||||
uint8_t *cp;
|
||||
uint8_t *cpend;
|
||||
uint8_t private_key[PRIV_KEY_LEN];
|
||||
uint8_t counter[BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE];
|
||||
uint16_t len;
|
||||
#endif
|
||||
int rc = -1;
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_RSA)
|
||||
|
||||
mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256);
|
||||
|
||||
cp = (uint8_t *)bootutil_enc_key.key;
|
||||
cpend = cp + *bootutil_enc_key.len;
|
||||
|
||||
rc = parse_rsa_enckey(&rsa, &cp, cpend);
|
||||
if (rc) {
|
||||
mbedtls_rsa_free(&rsa);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = mbedtls_rsa_rsaes_oaep_decrypt(&rsa, NULL, NULL, MBEDTLS_RSA_PRIVATE,
|
||||
NULL, 0, &olen, buf, enckey, BOOT_ENC_KEY_SIZE);
|
||||
mbedtls_rsa_free(&rsa);
|
||||
|
||||
#endif /* defined(MCUBOOT_ENCRYPT_RSA) */
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_KW)
|
||||
|
||||
assert(*bootutil_enc_key.len == 16);
|
||||
rc = key_unwrap(buf, enckey);
|
||||
|
||||
#endif /* defined(MCUBOOT_ENCRYPT_KW) */
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_EC256)
|
||||
|
||||
cp = (uint8_t *)bootutil_enc_key.key;
|
||||
cpend = cp + *bootutil_enc_key.len;
|
||||
|
||||
/*
|
||||
* Load the stored EC256 decryption private key
|
||||
*/
|
||||
|
||||
rc = parse_ec256_enckey(&cp, cpend, private_key);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* is EC point uncompressed? */
|
||||
if (buf[0] != 0x04) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* First "element" in the TLV is the curve point (public key)
|
||||
*/
|
||||
bootutil_ecdh_p256_init(&ecdh_p256);
|
||||
|
||||
rc = bootutil_ecdh_p256_shared_secret(&ecdh_p256, &buf[EC_PUBK_INDEX], private_key, shared);
|
||||
bootutil_ecdh_p256_drop(&ecdh_p256);
|
||||
if (rc != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* defined(MCUBOOT_ENCRYPT_EC256) */
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_X25519)
|
||||
|
||||
cp = (uint8_t *)bootutil_enc_key.key;
|
||||
cpend = cp + *bootutil_enc_key.len;
|
||||
|
||||
/*
|
||||
* Load the stored X25519 decryption private key
|
||||
*/
|
||||
|
||||
rc = parse_x25519_enckey(&cp, cpend, private_key);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* First "element" in the TLV is the curve point (public key)
|
||||
*/
|
||||
|
||||
bootutil_ecdh_x25519_init(&ecdh_x25519);
|
||||
|
||||
rc = bootutil_ecdh_x25519_shared_secret(&ecdh_x25519, &buf[EC_PUBK_INDEX], private_key, shared);
|
||||
bootutil_ecdh_x25519_drop(&ecdh_x25519);
|
||||
if (!rc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* defined(MCUBOOT_ENCRYPT_X25519) */
|
||||
|
||||
#if defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519)
|
||||
|
||||
/*
|
||||
* Expand shared secret to create keys for AES-128-CTR + HMAC-SHA256
|
||||
*/
|
||||
|
||||
len = BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE;
|
||||
rc = hkdf(shared, SHARED_KEY_LEN, (uint8_t *)"MCUBoot_ECIES_v1", 16,
|
||||
derived_key, &len);
|
||||
if (rc != 0 || len != (BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* HMAC the key and check that our received MAC matches the generated tag
|
||||
*/
|
||||
|
||||
bootutil_hmac_sha256_init(&hmac);
|
||||
|
||||
rc = bootutil_hmac_sha256_set_key(&hmac, &derived_key[16], 32);
|
||||
if (rc != 0) {
|
||||
(void)bootutil_hmac_sha256_drop(&hmac);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = bootutil_hmac_sha256_update(&hmac, &buf[EC_CIPHERKEY_INDEX], 16);
|
||||
if (rc != 0) {
|
||||
(void)bootutil_hmac_sha256_drop(&hmac);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Assumes the tag bufer is at least sizeof(hmac_tag_size(state)) bytes */
|
||||
rc = bootutil_hmac_sha256_finish(&hmac, tag, BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE);
|
||||
if (rc != 0) {
|
||||
(void)bootutil_hmac_sha256_drop(&hmac);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bootutil_constant_time_compare(tag, &buf[EC_TAG_INDEX], 32) != 0) {
|
||||
(void)bootutil_hmac_sha256_drop(&hmac);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bootutil_hmac_sha256_drop(&hmac);
|
||||
|
||||
/*
|
||||
* Finally decrypt the received ciphered key
|
||||
*/
|
||||
|
||||
bootutil_aes_ctr_init(&aes_ctr);
|
||||
if (rc != 0) {
|
||||
bootutil_aes_ctr_drop(&aes_ctr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = bootutil_aes_ctr_set_key(&aes_ctr, derived_key);
|
||||
if (rc != 0) {
|
||||
bootutil_aes_ctr_drop(&aes_ctr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(counter, 0, BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE);
|
||||
rc = bootutil_aes_ctr_decrypt(&aes_ctr, counter, &buf[EC_CIPHERKEY_INDEX], BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE, 0, enckey);
|
||||
if (rc != 0) {
|
||||
bootutil_aes_ctr_drop(&aes_ctr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bootutil_aes_ctr_drop(&aes_ctr);
|
||||
|
||||
rc = 0;
|
||||
|
||||
#endif /* defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519) */
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load encryption key.
|
||||
*/
|
||||
int
|
||||
boot_enc_load(struct enc_key_data *enc_state, int image_index,
|
||||
const struct image_header *hdr, const struct flash_area *fap,
|
||||
struct boot_status *bs)
|
||||
{
|
||||
uint32_t off;
|
||||
uint16_t len;
|
||||
struct image_tlv_iter it;
|
||||
#if MCUBOOT_SWAP_SAVE_ENCTLV
|
||||
uint8_t *buf;
|
||||
#else
|
||||
uint8_t buf[EXPECTED_ENC_LEN];
|
||||
#endif
|
||||
uint8_t slot;
|
||||
int rc;
|
||||
|
||||
rc = flash_area_id_to_multi_image_slot(image_index, fap->fa_id);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
slot = rc;
|
||||
|
||||
/* Already loaded... */
|
||||
if (enc_state[slot].valid) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Initialize the AES context */
|
||||
boot_enc_init(enc_state, slot);
|
||||
|
||||
rc = bootutil_tlv_iter_begin(&it, hdr, fap, EXPECTED_ENC_TLV, false);
|
||||
if (rc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = bootutil_tlv_iter_next(&it, &off, &len, NULL);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (len != EXPECTED_ENC_LEN) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if MCUBOOT_SWAP_SAVE_ENCTLV
|
||||
buf = bs->enctlv[slot];
|
||||
memset(buf, 0xff, BOOT_ENC_TLV_ALIGN_SIZE);
|
||||
#endif
|
||||
|
||||
rc = flash_area_read(fap, off, buf, EXPECTED_ENC_LEN);
|
||||
if (rc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return boot_enc_decrypt(buf, bs->enckey[slot]);
|
||||
}
|
||||
|
||||
bool
|
||||
boot_enc_valid(struct enc_key_data *enc_state, int image_index,
|
||||
const struct flash_area *fap)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = flash_area_id_to_multi_image_slot(image_index, fap->fa_id);
|
||||
if (rc < 0) {
|
||||
/* can't get proper slot number - skip encryption, */
|
||||
/* postpone the error for a upper layer */
|
||||
return false;
|
||||
}
|
||||
|
||||
return enc_state[rc].valid;
|
||||
}
|
||||
|
||||
void
|
||||
boot_encrypt(struct enc_key_data *enc_state, int image_index,
|
||||
const struct flash_area *fap, uint32_t off, uint32_t sz,
|
||||
uint32_t blk_off, uint8_t *buf)
|
||||
{
|
||||
struct enc_key_data *enc;
|
||||
uint8_t nonce[16];
|
||||
int rc;
|
||||
|
||||
/* boot_copy_region will call boot_encrypt with sz = 0 when skipping over
|
||||
the TLVs. */
|
||||
if (sz == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(nonce, 0, 12);
|
||||
off >>= 4;
|
||||
nonce[12] = (uint8_t)(off >> 24);
|
||||
nonce[13] = (uint8_t)(off >> 16);
|
||||
nonce[14] = (uint8_t)(off >> 8);
|
||||
nonce[15] = (uint8_t)off;
|
||||
|
||||
rc = flash_area_id_to_multi_image_slot(image_index, fap->fa_id);
|
||||
if (rc < 0) {
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
enc = &enc_state[rc];
|
||||
assert(enc->valid == 1);
|
||||
bootutil_aes_ctr_encrypt(&enc->aes_ctr, nonce, buf, sz, blk_off, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears encrypted state after use.
|
||||
*/
|
||||
void
|
||||
boot_enc_zeroize(struct enc_key_data *enc_state)
|
||||
{
|
||||
uint8_t slot;
|
||||
for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
|
||||
(void)boot_enc_drop(enc_state, slot);
|
||||
}
|
||||
memset(enc_state, 0, sizeof(struct enc_key_data) * BOOT_NUM_SLOTS);
|
||||
}
|
||||
|
||||
#endif /* MCUBOOT_ENC_IMAGES */
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2020 Arm Limited
|
||||
*/
|
||||
|
||||
#include "bootutil/fault_injection_hardening.h"
|
||||
|
||||
#ifdef FIH_ENABLE_DOUBLE_VARS
|
||||
/* Variable that could be (but isn't) changed at runtime to force the compiler
|
||||
* not to optimize the double check. Value doesn't matter.
|
||||
*/
|
||||
volatile int _fih_mask = _FIH_MASK_VALUE;
|
||||
fih_int FIH_SUCCESS = {FIH_POSITIVE_VALUE, _FIH_MASK_VALUE ^ FIH_POSITIVE_VALUE};
|
||||
fih_int FIH_FAILURE = {FIH_NEGATIVE_VALUE, _FIH_MASK_VALUE ^ FIH_NEGATIVE_VALUE};
|
||||
#else
|
||||
fih_int FIH_SUCCESS = {FIH_POSITIVE_VALUE};
|
||||
fih_int FIH_FAILURE = {FIH_NEGATIVE_VALUE};
|
||||
#endif /* FIH_ENABLE_DOUBLE_VARS */
|
||||
|
||||
#ifdef FIH_ENABLE_CFI
|
||||
|
||||
#ifdef FIH_ENABLE_DOUBLE_VARS
|
||||
fih_int _fih_cfi_ctr = {0, 0 ^ _FIH_MASK_VALUE};
|
||||
#else
|
||||
fih_int _fih_cfi_ctr = {0};
|
||||
#endif /* FIH_ENABLE_DOUBLE_VARS */
|
||||
|
||||
/* Increment the CFI counter by one, and return the value before the increment.
|
||||
*/
|
||||
fih_int fih_cfi_get_and_increment(void)
|
||||
{
|
||||
fih_int saved = _fih_cfi_ctr;
|
||||
_fih_cfi_ctr = fih_int_encode(fih_int_decode(saved) + 1);
|
||||
return saved;
|
||||
}
|
||||
|
||||
/* Validate that the saved precall value is the same as the value of the global
|
||||
* counter. For this to be the case, a fih_ret must have been called between
|
||||
* these functions being executed. If the values aren't the same then panic.
|
||||
*/
|
||||
void fih_cfi_validate(fih_int saved)
|
||||
{
|
||||
if (fih_int_decode(saved) != fih_int_decode(_fih_cfi_ctr)) {
|
||||
FIH_PANIC;
|
||||
}
|
||||
}
|
||||
|
||||
/* Decrement the global CFI counter by one, so that it has the same value as
|
||||
* before the cfi_precall
|
||||
*/
|
||||
void fih_cfi_decrement(void)
|
||||
{
|
||||
_fih_cfi_ctr = fih_int_encode(fih_int_decode(_fih_cfi_ctr) - 1);
|
||||
}
|
||||
|
||||
#endif /* FIH_ENABLE_CFI */
|
||||
|
||||
#ifdef FIH_ENABLE_GLOBAL_FAIL
|
||||
/* Global failure loop for bootloader code. Uses attribute used to prevent
|
||||
* compiler removing due to non-standard calling procedure. Multiple loop jumps
|
||||
* used to make unlooping difficult.
|
||||
*/
|
||||
__attribute__((used))
|
||||
__attribute__((noinline))
|
||||
void fih_panic_loop(void)
|
||||
{
|
||||
__asm volatile ("b fih_panic_loop");
|
||||
__asm volatile ("b fih_panic_loop");
|
||||
__asm volatile ("b fih_panic_loop");
|
||||
__asm volatile ("b fih_panic_loop");
|
||||
__asm volatile ("b fih_panic_loop");
|
||||
__asm volatile ("b fih_panic_loop");
|
||||
__asm volatile ("b fih_panic_loop");
|
||||
__asm volatile ("b fih_panic_loop");
|
||||
__asm volatile ("b fih_panic_loop");
|
||||
}
|
||||
#endif /* FIH_ENABLE_GLOBAL_FAIL */
|
||||
Vendored
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2020 Arm Limited
|
||||
*/
|
||||
|
||||
#include "bootutil/fault_injection_hardening.h"
|
||||
|
||||
#ifdef FIH_ENABLE_DELAY
|
||||
|
||||
#include "mcuboot-mbedtls-cfg.h"
|
||||
#include "mbedtls/ctr_drbg.h"
|
||||
#include "mbedtls/entropy.h"
|
||||
|
||||
/* Mbedtls implementation of the delay RNG. Can be replaced by any other RNG
|
||||
* implementation that is backed by an entropy source by altering these
|
||||
* functions. This is not provided as a header API and a C file implementation
|
||||
* due to issues with inlining.
|
||||
*/
|
||||
|
||||
#ifdef MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES
|
||||
#error "FIH_ENABLE_DELAY requires an entropy source"
|
||||
#endif /* MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES */
|
||||
|
||||
mbedtls_entropy_context fih_entropy_ctx;
|
||||
mbedtls_ctr_drbg_context fih_drbg_ctx;
|
||||
|
||||
int fih_delay_init(void)
|
||||
{
|
||||
mbedtls_entropy_init(&fih_entropy_ctx);
|
||||
mbedtls_ctr_drbg_init(&fih_drbg_ctx);
|
||||
mbedtls_ctr_drbg_seed(&fih_drbg_ctx , mbedtls_entropy_func,
|
||||
&fih_entropy_ctx, NULL, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned char fih_delay_random_uchar(void)
|
||||
{
|
||||
unsigned char delay;
|
||||
|
||||
mbedtls_ctr_drbg_random(&fih_drbg_ctx,(unsigned char*) &delay,
|
||||
sizeof(delay));
|
||||
|
||||
return delay;
|
||||
}
|
||||
|
||||
#endif /* FIH_ENABLE_DELAY */
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2016-2018 JUUL Labs
|
||||
*
|
||||
* Original license:
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
#ifdef MCUBOOT_SIGN_EC
|
||||
#include "bootutil/sign_key.h"
|
||||
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#include "mbedtls/oid.h"
|
||||
#include "mbedtls/asn1.h"
|
||||
|
||||
#include "bootutil_priv.h"
|
||||
|
||||
/*
|
||||
* Declaring these like this adds NULL termination.
|
||||
*/
|
||||
static const uint8_t ec_pubkey_oid[] = MBEDTLS_OID_EC_ALG_UNRESTRICTED;
|
||||
static const uint8_t ec_secp224r1_oid[] = MBEDTLS_OID_EC_GRP_SECP224R1;
|
||||
|
||||
/*
|
||||
* Parse the public key used for signing.
|
||||
*/
|
||||
static int
|
||||
bootutil_parse_eckey(mbedtls_ecdsa_context *ctx, uint8_t **p, uint8_t *end)
|
||||
{
|
||||
size_t len;
|
||||
mbedtls_asn1_buf alg;
|
||||
mbedtls_asn1_buf param;
|
||||
|
||||
if (mbedtls_asn1_get_tag(p, end, &len,
|
||||
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) {
|
||||
return -1;
|
||||
}
|
||||
end = *p + len;
|
||||
|
||||
if (mbedtls_asn1_get_alg(p, end, &alg, ¶m)) {
|
||||
return -2;
|
||||
}
|
||||
if (alg.len != sizeof(ec_pubkey_oid) - 1 ||
|
||||
memcmp(alg.p, ec_pubkey_oid, sizeof(ec_pubkey_oid) - 1)) {
|
||||
return -3;
|
||||
}
|
||||
if (param.len != sizeof(ec_secp224r1_oid) - 1||
|
||||
memcmp(param.p, ec_secp224r1_oid, sizeof(ec_secp224r1_oid) - 1)) {
|
||||
return -4;
|
||||
}
|
||||
|
||||
if (mbedtls_ecp_group_load(&ctx->grp, MBEDTLS_ECP_DP_SECP224R1)) {
|
||||
return -5;
|
||||
}
|
||||
|
||||
if (mbedtls_asn1_get_bitstring_null(p, end, &len)) {
|
||||
return -6;
|
||||
}
|
||||
if (*p + len != end) {
|
||||
return -7;
|
||||
}
|
||||
|
||||
if (mbedtls_ecp_point_read_binary(&ctx->grp, &ctx->Q, *p, end - *p)) {
|
||||
return -8;
|
||||
}
|
||||
|
||||
if (mbedtls_ecp_check_pubkey(&ctx->grp, &ctx->Q)) {
|
||||
return -9;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
bootutil_cmp_sig(mbedtls_ecdsa_context *ctx, uint8_t *hash, uint32_t hlen,
|
||||
uint8_t *sig, size_t slen)
|
||||
{
|
||||
return mbedtls_ecdsa_read_signature(ctx, hash, hlen, sig, slen);
|
||||
}
|
||||
|
||||
int
|
||||
bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, size_t slen,
|
||||
uint8_t key_id)
|
||||
{
|
||||
int rc;
|
||||
uint8_t *cp;
|
||||
uint8_t *end;
|
||||
mbedtls_ecdsa_context ctx;
|
||||
|
||||
mbedtls_ecdsa_init(&ctx);
|
||||
|
||||
cp = (uint8_t *)bootutil_keys[key_id].key;
|
||||
end = cp + *bootutil_keys[key_id].len;
|
||||
|
||||
rc = bootutil_parse_eckey(&ctx, &cp, end);
|
||||
if (rc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (sig[slen - 1] == '\0') {
|
||||
slen--;
|
||||
}
|
||||
rc = bootutil_cmp_sig(&ctx, hash, hlen, sig, slen);
|
||||
mbedtls_ecdsa_free(&ctx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* MCUBOOT_SIGN_EC */
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2016-2019 JUUL Labs
|
||||
* Copyright (c) 2017 Linaro LTD
|
||||
*
|
||||
* Original license:
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
#ifdef MCUBOOT_SIGN_EC256
|
||||
#if defined (MCUBOOT_USE_CC310) || defined (MCUBOOT_USE_TI_CRYPTO)|| \
|
||||
defined(MCUBOOT_USE_MSP_CRYPTO)
|
||||
#define NUM_ECC_BYTES (256 / 8)
|
||||
#endif
|
||||
#if defined (MCUBOOT_USE_TINYCRYPT) || defined (MCUBOOT_USE_CC310) || \
|
||||
defined (MCUBOOT_USE_TI_CRYPTO) || defined(MCUBOOT_USE_MSP_CRYPTO)
|
||||
#include "bootutil/sign_key.h"
|
||||
|
||||
#include "mbedtls/oid.h"
|
||||
#include "mbedtls/asn1.h"
|
||||
#include "bootutil/crypto/ecdsa_p256.h"
|
||||
#include "bootutil_priv.h"
|
||||
|
||||
/*
|
||||
* Declaring these like this adds NULL termination.
|
||||
*/
|
||||
static const uint8_t ec_pubkey_oid[] = MBEDTLS_OID_EC_ALG_UNRESTRICTED;
|
||||
static const uint8_t ec_secp256r1_oid[] = MBEDTLS_OID_EC_GRP_SECP256R1;
|
||||
|
||||
/*
|
||||
* Parse the public key used for signing.
|
||||
*/
|
||||
static int
|
||||
bootutil_import_key(uint8_t **cp, uint8_t *end)
|
||||
{
|
||||
size_t len;
|
||||
mbedtls_asn1_buf alg;
|
||||
mbedtls_asn1_buf param;
|
||||
|
||||
if (mbedtls_asn1_get_tag(cp, end, &len,
|
||||
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) {
|
||||
return -1;
|
||||
}
|
||||
end = *cp + len;
|
||||
|
||||
/* ECParameters (RFC5480) */
|
||||
if (mbedtls_asn1_get_alg(cp, end, &alg, ¶m)) {
|
||||
return -2;
|
||||
}
|
||||
/* id-ecPublicKey (RFC5480) */
|
||||
if (alg.len != sizeof(ec_pubkey_oid) - 1 ||
|
||||
memcmp(alg.p, ec_pubkey_oid, sizeof(ec_pubkey_oid) - 1)) {
|
||||
return -3;
|
||||
}
|
||||
/* namedCurve (RFC5480) */
|
||||
if (param.len != sizeof(ec_secp256r1_oid) - 1 ||
|
||||
memcmp(param.p, ec_secp256r1_oid, sizeof(ec_secp256r1_oid) - 1)) {
|
||||
return -4;
|
||||
}
|
||||
/* ECPoint (RFC5480) */
|
||||
if (mbedtls_asn1_get_bitstring_null(cp, end, &len)) {
|
||||
return -6;
|
||||
}
|
||||
if (*cp + len != end) {
|
||||
return -7;
|
||||
}
|
||||
|
||||
if (len != 2 * NUM_ECC_BYTES + 1) {
|
||||
return -8;
|
||||
}
|
||||
/* Is uncompressed? */
|
||||
if (*cp[0] != 0x04) {
|
||||
return -9;
|
||||
}
|
||||
|
||||
(*cp)++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* cp points to ASN1 string containing an integer.
|
||||
* Verify the tag, and that the length is 32 bytes.
|
||||
*/
|
||||
static int
|
||||
bootutil_read_bigint(uint8_t i[NUM_ECC_BYTES], uint8_t **cp, uint8_t *end)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
if (mbedtls_asn1_get_tag(cp, end, &len, MBEDTLS_ASN1_INTEGER)) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (len >= NUM_ECC_BYTES) {
|
||||
memcpy(i, *cp + len - NUM_ECC_BYTES, NUM_ECC_BYTES);
|
||||
} else {
|
||||
memset(i, 0, NUM_ECC_BYTES - len);
|
||||
memcpy(i + NUM_ECC_BYTES - len, *cp, len);
|
||||
}
|
||||
*cp += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read in signature. Signature has r and s encoded as integers.
|
||||
*/
|
||||
static int
|
||||
bootutil_decode_sig(uint8_t signature[NUM_ECC_BYTES * 2], uint8_t *cp, uint8_t *end)
|
||||
{
|
||||
int rc;
|
||||
size_t len;
|
||||
|
||||
rc = mbedtls_asn1_get_tag(&cp, end, &len,
|
||||
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
|
||||
if (rc) {
|
||||
return -1;
|
||||
}
|
||||
if (cp + len > end) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
rc = bootutil_read_bigint(signature, &cp, end);
|
||||
if (rc) {
|
||||
return -3;
|
||||
}
|
||||
rc = bootutil_read_bigint(signature + NUM_ECC_BYTES, &cp, end);
|
||||
if (rc) {
|
||||
return -4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, size_t slen,
|
||||
uint8_t key_id)
|
||||
{
|
||||
int rc;
|
||||
bootutil_ecdsa_p256_context ctx;
|
||||
uint8_t *pubkey;
|
||||
uint8_t *end;
|
||||
|
||||
uint8_t signature[2 * NUM_ECC_BYTES];
|
||||
|
||||
pubkey = (uint8_t *)bootutil_keys[key_id].key;
|
||||
end = pubkey + *bootutil_keys[key_id].len;
|
||||
|
||||
rc = bootutil_import_key(&pubkey, end);
|
||||
if (rc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = bootutil_decode_sig(signature, sig, sig + slen);
|
||||
if (rc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is simplified, as the hash length is also 32 bytes.
|
||||
*/
|
||||
if (hlen != NUM_ECC_BYTES) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bootutil_ecdsa_p256_init(&ctx);
|
||||
rc = bootutil_ecdsa_p256_verify(&ctx, pubkey, hash, signature);
|
||||
bootutil_ecdsa_p256_drop(&ctx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* MCUBOOT_USE_TINYCRYPT || defined MCUBOOT_USE_CC310 || MCUBOOT_USE_TI_CRYPTO */
|
||||
#endif /* MCUBOOT_SIGN_EC256 */
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2019 JUUL Labs
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
#ifdef MCUBOOT_SIGN_ED25519
|
||||
#include "bootutil/sign_key.h"
|
||||
|
||||
#include "mbedtls/oid.h"
|
||||
#include "mbedtls/asn1.h"
|
||||
|
||||
#include "bootutil_priv.h"
|
||||
|
||||
static const uint8_t ed25519_pubkey_oid[] = MBEDTLS_OID_ISO_IDENTIFIED_ORG "\x65\x70";
|
||||
#define NUM_ED25519_BYTES 32
|
||||
|
||||
extern int ED25519_verify(const uint8_t *message, size_t message_len,
|
||||
const uint8_t signature[64],
|
||||
const uint8_t public_key[32]);
|
||||
|
||||
/*
|
||||
* Parse the public key used for signing.
|
||||
*/
|
||||
static int
|
||||
bootutil_import_key(uint8_t **cp, uint8_t *end)
|
||||
{
|
||||
size_t len;
|
||||
mbedtls_asn1_buf alg;
|
||||
mbedtls_asn1_buf param;
|
||||
|
||||
if (mbedtls_asn1_get_tag(cp, end, &len,
|
||||
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) {
|
||||
return -1;
|
||||
}
|
||||
end = *cp + len;
|
||||
|
||||
if (mbedtls_asn1_get_alg(cp, end, &alg, ¶m)) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (alg.len != sizeof(ed25519_pubkey_oid) - 1 ||
|
||||
memcmp(alg.p, ed25519_pubkey_oid, sizeof(ed25519_pubkey_oid) - 1)) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (mbedtls_asn1_get_bitstring_null(cp, end, &len)) {
|
||||
return -4;
|
||||
}
|
||||
if (*cp + len != end) {
|
||||
return -5;
|
||||
}
|
||||
|
||||
if (len != NUM_ED25519_BYTES) {
|
||||
return -6;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, size_t slen,
|
||||
uint8_t key_id)
|
||||
{
|
||||
int rc;
|
||||
uint8_t *pubkey;
|
||||
uint8_t *end;
|
||||
|
||||
if (hlen != 32 || slen != 64) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
pubkey = (uint8_t *)bootutil_keys[key_id].key;
|
||||
end = pubkey + *bootutil_keys[key_id].len;
|
||||
|
||||
rc = bootutil_import_key(&pubkey, end);
|
||||
if (rc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = ED25519_verify(hash, 32, sig, pubkey);
|
||||
if (rc == 0) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* MCUBOOT_SIGN_ED25519 */
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2017-2018 Linaro LTD
|
||||
* Copyright (c) 2017-2019 JUUL Labs
|
||||
* Copyright (c) 2020 Arm Limited
|
||||
*
|
||||
* Original license:
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
#ifdef MCUBOOT_SIGN_RSA
|
||||
#include "bootutil/sign_key.h"
|
||||
#include "bootutil/crypto/sha256.h"
|
||||
|
||||
#include "mbedtls/rsa.h"
|
||||
#include "mbedtls/asn1.h"
|
||||
#include "mbedtls/version.h"
|
||||
|
||||
#include "bootutil_priv.h"
|
||||
#include "bootutil/fault_injection_hardening.h"
|
||||
|
||||
/*
|
||||
* Constants for this particular constrained implementation of
|
||||
* RSA-PSS. In particular, we support RSA 2048, with a SHA256 hash,
|
||||
* and a 32-byte salt. A signature with different parameters will be
|
||||
* rejected as invalid.
|
||||
*/
|
||||
|
||||
/* The size, in octets, of the message. */
|
||||
#define PSS_EMLEN (MCUBOOT_SIGN_RSA_LEN / 8)
|
||||
|
||||
/* The size of the hash function. For SHA256, this is 32 bytes. */
|
||||
#define PSS_HLEN 32
|
||||
|
||||
/* Size of the salt, should be fixed. */
|
||||
#define PSS_SLEN 32
|
||||
|
||||
/* The length of the mask: emLen - hLen - 1. */
|
||||
#define PSS_MASK_LEN (PSS_EMLEN - PSS_HLEN - 1)
|
||||
|
||||
#define PSS_HASH_OFFSET PSS_MASK_LEN
|
||||
|
||||
/* For the mask itself, how many bytes should be all zeros. */
|
||||
#define PSS_MASK_ZERO_COUNT (PSS_MASK_LEN - PSS_SLEN - 1)
|
||||
#define PSS_MASK_ONE_POS PSS_MASK_ZERO_COUNT
|
||||
|
||||
/* Where the salt starts. */
|
||||
#define PSS_MASK_SALT_POS (PSS_MASK_ONE_POS + 1)
|
||||
|
||||
static const uint8_t pss_zeros[8] = {0};
|
||||
|
||||
/*
|
||||
* Parse the public key used for signing. Simple RSA format.
|
||||
*/
|
||||
static int
|
||||
bootutil_parse_rsakey(mbedtls_rsa_context *ctx, uint8_t **p, uint8_t *end)
|
||||
{
|
||||
int rc;
|
||||
size_t len;
|
||||
|
||||
if ((rc = mbedtls_asn1_get_tag(p, end, &len,
|
||||
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*p + len != end) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
if ((rc = mbedtls_asn1_get_mpi(p, end, &ctx->N)) != 0 ||
|
||||
(rc = mbedtls_asn1_get_mpi(p, end, &ctx->E)) != 0) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
ctx->len = mbedtls_mpi_size(&ctx->N);
|
||||
|
||||
if (*p != end) {
|
||||
return -4;
|
||||
}
|
||||
|
||||
/* The mbedtls version is more than 2.6.1 */
|
||||
#if MBEDTLS_VERSION_NUMBER > 0x02060100
|
||||
rc = mbedtls_rsa_import(ctx, &ctx->N, NULL, NULL, NULL, &ctx->E);
|
||||
if (rc != 0) {
|
||||
return -5;
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = mbedtls_rsa_check_pubkey(ctx);
|
||||
if (rc != 0) {
|
||||
return -6;
|
||||
}
|
||||
|
||||
ctx->len = mbedtls_mpi_size(&ctx->N);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the RSA-PSS mask-generation function, MGF1. Assumptions
|
||||
* are that the mask length will be less than 256 * PSS_HLEN, and
|
||||
* therefore we never need to increment anything other than the low
|
||||
* byte of the counter.
|
||||
*
|
||||
* This is described in PKCS#1, B.2.1.
|
||||
*/
|
||||
static void
|
||||
pss_mgf1(uint8_t *mask, const uint8_t *hash)
|
||||
{
|
||||
bootutil_sha256_context ctx;
|
||||
uint8_t counter[4] = { 0, 0, 0, 0 };
|
||||
uint8_t htmp[PSS_HLEN];
|
||||
int count = PSS_MASK_LEN;
|
||||
int bytes;
|
||||
|
||||
while (count > 0) {
|
||||
bootutil_sha256_init(&ctx);
|
||||
bootutil_sha256_update(&ctx, hash, PSS_HLEN);
|
||||
bootutil_sha256_update(&ctx, counter, 4);
|
||||
bootutil_sha256_finish(&ctx, htmp);
|
||||
|
||||
counter[3]++;
|
||||
|
||||
bytes = PSS_HLEN;
|
||||
if (bytes > count)
|
||||
bytes = count;
|
||||
|
||||
memcpy(mask, htmp, bytes);
|
||||
mask += bytes;
|
||||
count -= bytes;
|
||||
}
|
||||
|
||||
bootutil_sha256_drop(&ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate an RSA signature, using RSA-PSS, as described in PKCS #1
|
||||
* v2.2, section 9.1.2, with many parameters required to have fixed
|
||||
* values.
|
||||
*/
|
||||
static fih_int
|
||||
bootutil_cmp_rsasig(mbedtls_rsa_context *ctx, uint8_t *hash, uint32_t hlen,
|
||||
uint8_t *sig)
|
||||
{
|
||||
bootutil_sha256_context shactx;
|
||||
uint8_t em[MBEDTLS_MPI_MAX_SIZE];
|
||||
uint8_t db_mask[PSS_MASK_LEN];
|
||||
uint8_t h2[PSS_HLEN];
|
||||
int i;
|
||||
int rc = 0;
|
||||
fih_int fih_rc = FIH_FAILURE;
|
||||
|
||||
if (ctx->len != PSS_EMLEN || PSS_EMLEN > MBEDTLS_MPI_MAX_SIZE) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (hlen != PSS_HLEN) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mbedtls_rsa_public(ctx, sig, em)) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* PKCS #1 v2.2, 9.1.2 EMSA-PSS-Verify
|
||||
*
|
||||
* emBits is 2048
|
||||
* emLen = ceil(emBits/8) = 256
|
||||
*
|
||||
* The salt length is not known at the beginning.
|
||||
*/
|
||||
|
||||
/* Step 1. The message is constrained by the address space of a
|
||||
* 32-bit processor, which is far less than the 2^61-1 limit of
|
||||
* SHA-256.
|
||||
*/
|
||||
|
||||
/* Step 2. mHash is passed in as 'hash', with hLen the hlen
|
||||
* argument. */
|
||||
|
||||
/* Step 3. if emLen < hLen + sLen + 2, inconsistent and stop.
|
||||
* The salt length is not known at this point.
|
||||
*/
|
||||
|
||||
/* Step 4. If the rightmost octet of EM does have the value
|
||||
* 0xbc, output inconsistent and stop.
|
||||
*/
|
||||
if (em[PSS_EMLEN - 1] != 0xbc) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Step 5. Let maskedDB be the leftmost emLen - hLen - 1 octets
|
||||
* of EM, and H be the next hLen octets.
|
||||
*
|
||||
* maskedDB is then the first 256 - 32 - 1 = 0-222
|
||||
* H is 32 bytes 223-254
|
||||
*/
|
||||
|
||||
/* Step 6. If the leftmost 8emLen - emBits bits of the leftmost
|
||||
* octet in maskedDB are not all equal to zero, output
|
||||
* inconsistent and stop.
|
||||
*
|
||||
* 8emLen - emBits is zero, so there is nothing to test here.
|
||||
*/
|
||||
|
||||
/* Step 7. let dbMask = MGF(H, emLen - hLen - 1). */
|
||||
pss_mgf1(db_mask, &em[PSS_HASH_OFFSET]);
|
||||
|
||||
/* Step 8. let DB = maskedDB xor dbMask.
|
||||
* To avoid needing an additional buffer, store the 'db' in the
|
||||
* same buffer as db_mask. From now, to the end of this function,
|
||||
* db_mask refers to the unmasked 'db'. */
|
||||
for (i = 0; i < PSS_MASK_LEN; i++) {
|
||||
db_mask[i] ^= em[i];
|
||||
}
|
||||
|
||||
/* Step 9. Set the leftmost 8emLen - emBits bits of the leftmost
|
||||
* octet in DB to zero.
|
||||
* pycrypto seems to always make the emBits 2047, so we need to
|
||||
* clear the top bit. */
|
||||
db_mask[0] &= 0x7F;
|
||||
|
||||
/* Step 10. If the emLen - hLen - sLen - 2 leftmost octets of DB
|
||||
* are not zero or if the octet at position emLen - hLen - sLen -
|
||||
* 1 (the leftmost position is "position 1") does not have
|
||||
* hexadecimal value 0x01, output "inconsistent" and stop. */
|
||||
for (i = 0; i < PSS_MASK_ZERO_COUNT; i++) {
|
||||
if (db_mask[i] != 0) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (db_mask[PSS_MASK_ONE_POS] != 1) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Step 11. Let salt be the last sLen octets of DB */
|
||||
|
||||
/* Step 12. Let M' = 0x00 00 00 00 00 00 00 00 || mHash || salt; */
|
||||
|
||||
/* Step 13. Let H' = Hash(M') */
|
||||
bootutil_sha256_init(&shactx);
|
||||
bootutil_sha256_update(&shactx, pss_zeros, 8);
|
||||
bootutil_sha256_update(&shactx, hash, PSS_HLEN);
|
||||
bootutil_sha256_update(&shactx, &db_mask[PSS_MASK_SALT_POS], PSS_SLEN);
|
||||
bootutil_sha256_finish(&shactx, h2);
|
||||
bootutil_sha256_drop(&shactx);
|
||||
|
||||
/* Step 14. If H = H', output "consistent". Otherwise, output
|
||||
* "inconsistent". */
|
||||
FIH_CALL(boot_fih_memequal, fih_rc, h2, &em[PSS_HASH_OFFSET], PSS_HLEN);
|
||||
|
||||
out:
|
||||
if (rc) {
|
||||
fih_rc = fih_int_encode(rc);
|
||||
}
|
||||
|
||||
FIH_RET(fih_rc);
|
||||
}
|
||||
|
||||
fih_int
|
||||
bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, size_t slen,
|
||||
uint8_t key_id)
|
||||
{
|
||||
mbedtls_rsa_context ctx;
|
||||
int rc;
|
||||
fih_int fih_rc = FIH_FAILURE;
|
||||
uint8_t *cp;
|
||||
uint8_t *end;
|
||||
|
||||
mbedtls_rsa_init(&ctx, 0, 0);
|
||||
|
||||
cp = (uint8_t *)bootutil_keys[key_id].key;
|
||||
end = cp + *bootutil_keys[key_id].len;
|
||||
|
||||
rc = bootutil_parse_rsakey(&ctx, &cp, end);
|
||||
if (rc || slen != ctx.len) {
|
||||
mbedtls_rsa_free(&ctx);
|
||||
goto out;
|
||||
}
|
||||
FIH_CALL(bootutil_cmp_rsasig, fih_rc, &ctx, hash, hlen, sig);
|
||||
|
||||
out:
|
||||
mbedtls_rsa_free(&ctx);
|
||||
|
||||
FIH_RET(fih_rc);
|
||||
}
|
||||
#endif /* MCUBOOT_SIGN_RSA */
|
||||
@@ -0,0 +1,545 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2017-2019 Linaro LTD
|
||||
* Copyright (c) 2016-2019 JUUL Labs
|
||||
* Copyright (c) 2019-2020 Arm Limited
|
||||
*
|
||||
* Original license:
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <flash_map_backend/flash_map_backend.h>
|
||||
|
||||
#include "bootutil/image.h"
|
||||
#include "bootutil/crypto/sha256.h"
|
||||
#include "bootutil/sign_key.h"
|
||||
#include "bootutil/security_cnt.h"
|
||||
#include "bootutil/fault_injection_hardening.h"
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
#include "bootutil/enc_key.h"
|
||||
#endif
|
||||
#if defined(MCUBOOT_SIGN_RSA)
|
||||
#include "mbedtls/rsa.h"
|
||||
#endif
|
||||
#if defined(MCUBOOT_SIGN_EC) || defined(MCUBOOT_SIGN_EC256)
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#endif
|
||||
#if defined(MCUBOOT_ENC_IMAGES) || defined(MCUBOOT_SIGN_RSA) || \
|
||||
defined(MCUBOOT_SIGN_EC) || defined(MCUBOOT_SIGN_EC256)
|
||||
#include "mbedtls/asn1.h"
|
||||
#endif
|
||||
#ifdef TI_CUSTOM_TLV_DYNAMIC_KEY_MANAGEMENT
|
||||
#include "keystore.h"
|
||||
#endif
|
||||
|
||||
#include "bootutil_priv.h"
|
||||
|
||||
/*
|
||||
* Compute SHA256 over the image.
|
||||
*/
|
||||
static int
|
||||
bootutil_img_hash(struct enc_key_data *enc_state, int image_index,
|
||||
struct image_header *hdr, const struct flash_area *fap,
|
||||
uint8_t *tmp_buf, uint32_t tmp_buf_sz, uint8_t *hash_result,
|
||||
uint8_t *seed, int seed_len)
|
||||
{
|
||||
bootutil_sha256_context sha256_ctx;
|
||||
uint32_t blk_sz;
|
||||
uint32_t size;
|
||||
uint16_t hdr_size;
|
||||
uint32_t off;
|
||||
int rc;
|
||||
uint32_t blk_off;
|
||||
uint32_t tlv_off;
|
||||
|
||||
#if (BOOT_IMAGE_NUMBER == 1) || !defined(MCUBOOT_ENC_IMAGES) || \
|
||||
defined(MCUBOOT_RAM_LOAD)
|
||||
(void)enc_state;
|
||||
(void)image_index;
|
||||
(void)hdr_size;
|
||||
(void)blk_off;
|
||||
(void)tlv_off;
|
||||
#ifdef MCUBOOT_RAM_LOAD
|
||||
(void)blk_sz;
|
||||
(void)off;
|
||||
(void)rc;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
/* Encrypted images only exist in the secondary slot */
|
||||
if (MUST_DECRYPT(fap, image_index, hdr) &&
|
||||
!boot_enc_valid(enc_state, image_index, fap)) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
bootutil_sha256_init(&sha256_ctx);
|
||||
|
||||
/* in some cases (split image) the hash is seeded with data from
|
||||
* the loader image */
|
||||
if (seed && (seed_len > 0)) {
|
||||
bootutil_sha256_update(&sha256_ctx, seed, seed_len);
|
||||
}
|
||||
|
||||
/* Hash is computed over image header and image itself. */
|
||||
size = hdr_size = hdr->ih_hdr_size;
|
||||
size += hdr->ih_img_size;
|
||||
tlv_off = size;
|
||||
|
||||
/* If protected TLVs are present they are also hashed. */
|
||||
size += hdr->ih_protect_tlv_size;
|
||||
|
||||
#ifdef MCUBOOT_RAM_LOAD
|
||||
bootutil_sha256_update(&sha256_ctx,(void*)(hdr->ih_load_addr), size);
|
||||
#else
|
||||
for (off = 0; off < size; off += blk_sz) {
|
||||
blk_sz = size - off;
|
||||
if (blk_sz > tmp_buf_sz) {
|
||||
blk_sz = tmp_buf_sz;
|
||||
}
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
/* The only data that is encrypted in an image is the payload;
|
||||
* both header and TLVs (when protected) are not.
|
||||
*/
|
||||
if ((off < hdr_size) && ((off + blk_sz) > hdr_size)) {
|
||||
/* read only the header */
|
||||
blk_sz = hdr_size - off;
|
||||
}
|
||||
if ((off < tlv_off) && ((off + blk_sz) > tlv_off)) {
|
||||
/* read only up to the end of the image payload */
|
||||
blk_sz = tlv_off - off;
|
||||
}
|
||||
#endif
|
||||
rc = flash_area_read(fap, off, tmp_buf, blk_sz);
|
||||
if (rc) {
|
||||
bootutil_sha256_drop(&sha256_ctx);
|
||||
return rc;
|
||||
}
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
if (MUST_DECRYPT(fap, image_index, hdr)) {
|
||||
/* Only payload is encrypted (area between header and TLVs) */
|
||||
if (off >= hdr_size && off < tlv_off) {
|
||||
blk_off = (off - hdr_size) & 0xf;
|
||||
boot_encrypt(enc_state, image_index, fap, off - hdr_size,
|
||||
blk_sz, blk_off, tmp_buf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
bootutil_sha256_update(&sha256_ctx, tmp_buf, blk_sz);
|
||||
}
|
||||
#endif /* MCUBOOT_RAM_LOAD */
|
||||
bootutil_sha256_finish(&sha256_ctx, hash_result);
|
||||
bootutil_sha256_drop(&sha256_ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Currently, we only support being able to verify one type of
|
||||
* signature, because there is a single verification function that we
|
||||
* call. List the type of TLV we are expecting. If we aren't
|
||||
* configured for any signature, don't define this macro.
|
||||
*/
|
||||
#if (defined(MCUBOOT_SIGN_RSA) + \
|
||||
defined(MCUBOOT_SIGN_EC) + \
|
||||
defined(MCUBOOT_SIGN_EC256) + \
|
||||
defined(MCUBOOT_SIGN_ED25519)) > 1
|
||||
#error "Only a single signature type is supported!"
|
||||
#endif
|
||||
|
||||
#if defined(MCUBOOT_SIGN_RSA)
|
||||
# if MCUBOOT_SIGN_RSA_LEN == 2048
|
||||
# define EXPECTED_SIG_TLV IMAGE_TLV_RSA2048_PSS
|
||||
# elif MCUBOOT_SIGN_RSA_LEN == 3072
|
||||
# define EXPECTED_SIG_TLV IMAGE_TLV_RSA3072_PSS
|
||||
# else
|
||||
# error "Unsupported RSA signature length"
|
||||
# endif
|
||||
# define SIG_BUF_SIZE (MCUBOOT_SIGN_RSA_LEN / 8)
|
||||
# define EXPECTED_SIG_LEN(x) ((x) == SIG_BUF_SIZE) /* 2048 bits */
|
||||
#elif defined(MCUBOOT_SIGN_EC)
|
||||
# define EXPECTED_SIG_TLV IMAGE_TLV_ECDSA224
|
||||
# define SIG_BUF_SIZE 128
|
||||
# define EXPECTED_SIG_LEN(x) (1) /* always true, ASN.1 will validate */
|
||||
#elif defined(MCUBOOT_SIGN_EC256)
|
||||
# define EXPECTED_SIG_TLV IMAGE_TLV_ECDSA256
|
||||
# define SIG_BUF_SIZE 128
|
||||
# define EXPECTED_SIG_LEN(x) (1) /* always true, ASN.1 will validate */
|
||||
#elif defined(MCUBOOT_SIGN_ED25519)
|
||||
# define EXPECTED_SIG_TLV IMAGE_TLV_ED25519
|
||||
# define SIG_BUF_SIZE 64
|
||||
# define EXPECTED_SIG_LEN(x) ((x) == SIG_BUF_SIZE)
|
||||
#else
|
||||
# define SIG_BUF_SIZE 32 /* no signing, sha256 digest only */
|
||||
#endif
|
||||
|
||||
#ifdef EXPECTED_SIG_TLV
|
||||
#if !defined(MCUBOOT_HW_KEY)
|
||||
static int
|
||||
bootutil_find_key(uint8_t *keyhash, uint8_t keyhash_len)
|
||||
{
|
||||
bootutil_sha256_context sha256_ctx;
|
||||
int i;
|
||||
const struct bootutil_key *key;
|
||||
uint8_t hash[32];
|
||||
|
||||
if (keyhash_len > 32) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < bootutil_key_cnt; i++) {
|
||||
key = &bootutil_keys[i];
|
||||
bootutil_sha256_init(&sha256_ctx);
|
||||
bootutil_sha256_update(&sha256_ctx, key->key, *key->len);
|
||||
bootutil_sha256_finish(&sha256_ctx, hash);
|
||||
if (!memcmp(hash, keyhash, keyhash_len)) {
|
||||
bootutil_sha256_drop(&sha256_ctx);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
bootutil_sha256_drop(&sha256_ctx);
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
extern unsigned int pub_key_len;
|
||||
static int
|
||||
bootutil_find_key(uint8_t image_index, uint8_t *key, uint16_t key_len)
|
||||
{
|
||||
bootutil_sha256_context sha256_ctx;
|
||||
uint8_t hash[32];
|
||||
uint8_t key_hash[32];
|
||||
size_t key_hash_size = sizeof(key_hash);
|
||||
int rc;
|
||||
fih_int fih_rc;
|
||||
|
||||
bootutil_sha256_init(&sha256_ctx);
|
||||
bootutil_sha256_update(&sha256_ctx, key, key_len);
|
||||
bootutil_sha256_finish(&sha256_ctx, hash);
|
||||
bootutil_sha256_drop(&sha256_ctx);
|
||||
|
||||
rc = boot_retrieve_public_key_hash(image_index, key_hash, &key_hash_size);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Adding hardening to avoid this potential attack:
|
||||
* - Image is signed with an arbitrary key and the corresponding public
|
||||
* key is added as a TLV field.
|
||||
* - During public key validation (comparing against key-hash read from
|
||||
* HW) a fault is injected to accept the public key as valid one.
|
||||
*/
|
||||
FIH_CALL(boot_fih_memequal, fih_rc, hash, key_hash, key_hash_size);
|
||||
if (fih_eq(fih_rc, FIH_SUCCESS)) {
|
||||
bootutil_keys[0].key = key;
|
||||
pub_key_len = key_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
#endif /* !MCUBOOT_HW_KEY */
|
||||
#endif
|
||||
|
||||
#ifdef MCUBOOT_HW_ROLLBACK_PROT
|
||||
/**
|
||||
* Reads the value of an image's security counter.
|
||||
*
|
||||
* @param hdr Pointer to the image header structure.
|
||||
* @param fap Pointer to a description structure of the image's
|
||||
* flash area.
|
||||
* @param security_cnt Pointer to store the security counter value.
|
||||
*
|
||||
* @return 0 on success; nonzero on failure.
|
||||
*/
|
||||
int32_t
|
||||
bootutil_get_img_security_cnt(struct image_header *hdr,
|
||||
const struct flash_area *fap,
|
||||
uint32_t *img_security_cnt)
|
||||
{
|
||||
struct image_tlv_iter it;
|
||||
uint32_t off;
|
||||
uint16_t len;
|
||||
int32_t rc;
|
||||
|
||||
if ((hdr == NULL) ||
|
||||
(fap == NULL) ||
|
||||
(img_security_cnt == NULL)) {
|
||||
/* Invalid parameter. */
|
||||
return BOOT_EBADARGS;
|
||||
}
|
||||
|
||||
/* The security counter TLV is in the protected part of the TLV area. */
|
||||
if (hdr->ih_protect_tlv_size == 0) {
|
||||
return BOOT_EBADIMAGE;
|
||||
}
|
||||
|
||||
rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_SEC_CNT, true);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Traverse through the protected TLV area to find
|
||||
* the security counter TLV.
|
||||
*/
|
||||
|
||||
rc = bootutil_tlv_iter_next(&it, &off, &len, NULL);
|
||||
if (rc != 0) {
|
||||
/* Security counter TLV has not been found. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (len != sizeof(*img_security_cnt)) {
|
||||
/* Security counter is not valid. */
|
||||
return BOOT_EBADIMAGE;
|
||||
}
|
||||
|
||||
rc = LOAD_IMAGE_DATA(hdr, fap, off, img_security_cnt, len);
|
||||
if (rc != 0) {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* MCUBOOT_HW_ROLLBACK_PROT */
|
||||
|
||||
/*
|
||||
* Verify the integrity of the image.
|
||||
* Return non-zero if image could not be validated/does not validate.
|
||||
*/
|
||||
fih_int
|
||||
bootutil_img_validate(struct enc_key_data *enc_state, int image_index,
|
||||
struct image_header *hdr, const struct flash_area *fap,
|
||||
uint8_t *tmp_buf, uint32_t tmp_buf_sz, uint8_t *seed,
|
||||
int seed_len, uint8_t *out_hash)
|
||||
{
|
||||
uint32_t off;
|
||||
uint16_t len;
|
||||
uint16_t type;
|
||||
int sha256_valid = 0;
|
||||
#ifdef EXPECTED_SIG_TLV
|
||||
fih_int valid_signature = FIH_FAILURE;
|
||||
int key_id = -1;
|
||||
#ifdef MCUBOOT_HW_KEY
|
||||
/* Few extra bytes for encoding and for public exponent. */
|
||||
uint8_t key_buf[SIG_BUF_SIZE + 24];
|
||||
#endif
|
||||
#endif /* EXPECTED_SIG_TLV */
|
||||
struct image_tlv_iter it;
|
||||
uint8_t buf[SIG_BUF_SIZE];
|
||||
uint8_t hash[32];
|
||||
int rc = 0;
|
||||
fih_int fih_rc = FIH_FAILURE;
|
||||
#ifdef MCUBOOT_HW_ROLLBACK_PROT
|
||||
fih_int security_cnt = fih_int_encode(INT_MAX);
|
||||
uint32_t img_security_cnt = 0;
|
||||
fih_int security_counter_valid = FIH_FAILURE;
|
||||
#endif
|
||||
|
||||
rc = bootutil_img_hash(enc_state, image_index, hdr, fap, tmp_buf,
|
||||
tmp_buf_sz, hash, seed, seed_len);
|
||||
if (rc) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (out_hash) {
|
||||
memcpy(out_hash, hash, 32);
|
||||
}
|
||||
|
||||
rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, false);
|
||||
if (rc) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Traverse through all of the TLVs, performing any checks we know
|
||||
* and are able to do.
|
||||
*/
|
||||
while (true) {
|
||||
rc = bootutil_tlv_iter_next(&it, &off, &len, &type);
|
||||
if (rc < 0) {
|
||||
goto out;
|
||||
} else if (rc > 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == IMAGE_TLV_SHA256) {
|
||||
/*
|
||||
* Verify the SHA256 image hash. This must always be
|
||||
* present.
|
||||
*/
|
||||
if (len != sizeof(hash)) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, sizeof(hash));
|
||||
if (rc) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
FIH_CALL(boot_fih_memequal, fih_rc, hash, buf, sizeof(hash));
|
||||
if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
sha256_valid = 1;
|
||||
#ifdef EXPECTED_SIG_TLV
|
||||
#ifndef MCUBOOT_HW_KEY
|
||||
} else if (type == IMAGE_TLV_KEYHASH) {
|
||||
/*
|
||||
* Determine which key we should be checking.
|
||||
*/
|
||||
if (len > 32) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, len);
|
||||
if (rc) {
|
||||
goto out;
|
||||
}
|
||||
key_id = bootutil_find_key(buf, len);
|
||||
/*
|
||||
* The key may not be found, which is acceptable. There
|
||||
* can be multiple signatures, each preceded by a key.
|
||||
*/
|
||||
#else
|
||||
} else if (type == IMAGE_TLV_PUBKEY) {
|
||||
/*
|
||||
* Determine which key we should be checking.
|
||||
*/
|
||||
if (len > sizeof(key_buf)) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
rc = LOAD_IMAGE_DATA(hdr, fap, off, key_buf, len);
|
||||
if (rc) {
|
||||
goto out;
|
||||
}
|
||||
key_id = bootutil_find_key(image_index, key_buf, len);
|
||||
/*
|
||||
* The key may not be found, which is acceptable. There
|
||||
* can be multiple signatures, each preceded by a key.
|
||||
*/
|
||||
#endif /* !MCUBOOT_HW_KEY */
|
||||
} else if (type == EXPECTED_SIG_TLV) {
|
||||
/* Ignore this signature if it is out of bounds. */
|
||||
if (key_id < 0 || key_id >= bootutil_key_cnt) {
|
||||
key_id = -1;
|
||||
continue;
|
||||
}
|
||||
if (!EXPECTED_SIG_LEN(len) || len > sizeof(buf)) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, len);
|
||||
if (rc) {
|
||||
goto out;
|
||||
}
|
||||
FIH_CALL(bootutil_verify_sig, valid_signature, hash, sizeof(hash),
|
||||
buf, len, key_id);
|
||||
key_id = -1;
|
||||
#endif /* EXPECTED_SIG_TLV */
|
||||
#ifdef MCUBOOT_HW_ROLLBACK_PROT
|
||||
} else if (type == IMAGE_TLV_SEC_CNT) {
|
||||
/*
|
||||
* Verify the image's security counter.
|
||||
* This must always be present.
|
||||
*/
|
||||
if (len != sizeof(img_security_cnt)) {
|
||||
/* Security counter is not valid. */
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = LOAD_IMAGE_DATA(hdr, fap, off, &img_security_cnt, len);
|
||||
if (rc) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
FIH_CALL(boot_nv_security_counter_get, fih_rc, image_index,
|
||||
&security_cnt);
|
||||
if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Compare the new image's security counter value against the
|
||||
* stored security counter value.
|
||||
*/
|
||||
fih_rc = fih_int_encode_zero_equality(img_security_cnt <
|
||||
fih_int_decode(security_cnt));
|
||||
if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The image's security counter has been successfully verified. */
|
||||
security_counter_valid = fih_rc;
|
||||
#endif /* MCUBOOT_HW_ROLLBACK_PROT */
|
||||
#ifdef TI_CUSTOM_TLV_DYNAMIC_KEY_MANAGEMENT
|
||||
} else if (type == IMAGE_TLV_CUSTOM_TI_ADD_KEY){
|
||||
/* Check if Length is correct, and contains a full 256-bit hash */
|
||||
if(len != 0x20){
|
||||
// not a complete hash
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
/* The address of the hash pointer will be the current flash area
|
||||
* plus the provided offset. */
|
||||
FIH_CALL(Keystore_newKeyRequest, fih_rc, (fap->fa_off + off));
|
||||
} else if (type == IMAGE_TLV_CUSTOM_TI_REVOKE_KEY){
|
||||
if(len != 0x20){
|
||||
// not a complete hash
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
FIH_CALL(Keystore_revokeKeyRequest, fih_rc, (fap->fa_off + off));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
rc = !sha256_valid;
|
||||
if (rc) {
|
||||
goto out;
|
||||
}
|
||||
#ifdef EXPECTED_SIG_TLV
|
||||
fih_rc = fih_int_encode_zero_equality(fih_not_eq(valid_signature,
|
||||
FIH_SUCCESS));
|
||||
#endif
|
||||
#ifdef MCUBOOT_HW_ROLLBACK_PROT
|
||||
if (fih_not_eq(security_counter_valid, FIH_SUCCESS)) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
out:
|
||||
if (rc) {
|
||||
fih_rc = fih_int_encode(rc);
|
||||
}
|
||||
|
||||
FIH_RET(fih_rc);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2019 JUUL Labs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "bootutil/bootutil.h"
|
||||
#include "bootutil_priv.h"
|
||||
#include "swap_priv.h"
|
||||
#include "bootutil/bootutil_log.h"
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
MCUBOOT_LOG_MODULE_DECLARE(mcuboot);
|
||||
|
||||
#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE)
|
||||
int
|
||||
swap_erase_trailer_sectors(const struct boot_loader_state *state,
|
||||
const struct flash_area *fap)
|
||||
{
|
||||
uint8_t slot;
|
||||
uint32_t sector;
|
||||
uint32_t trailer_sz;
|
||||
uint32_t total_sz;
|
||||
uint32_t off;
|
||||
uint32_t sz;
|
||||
int fa_id_primary;
|
||||
int fa_id_secondary;
|
||||
uint8_t image_index;
|
||||
int rc;
|
||||
|
||||
BOOT_LOG_DBG("erasing trailer; fa_id=%d", fap->fa_id);
|
||||
|
||||
image_index = BOOT_CURR_IMG(state);
|
||||
fa_id_primary = flash_area_id_from_multi_image_slot(image_index,
|
||||
BOOT_PRIMARY_SLOT);
|
||||
fa_id_secondary = flash_area_id_from_multi_image_slot(image_index,
|
||||
BOOT_SECONDARY_SLOT);
|
||||
|
||||
if (fap->fa_id == fa_id_primary) {
|
||||
slot = BOOT_PRIMARY_SLOT;
|
||||
} else if (fap->fa_id == fa_id_secondary) {
|
||||
slot = BOOT_SECONDARY_SLOT;
|
||||
} else {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
|
||||
/* delete starting from last sector and moving to beginning */
|
||||
sector = boot_img_num_sectors(state, slot) - 1;
|
||||
trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
|
||||
total_sz = 0;
|
||||
do {
|
||||
sz = boot_img_sector_size(state, slot, sector);
|
||||
off = boot_img_sector_off(state, slot, sector);
|
||||
rc = boot_erase_region(fap, off, sz);
|
||||
assert(rc == 0);
|
||||
|
||||
sector--;
|
||||
total_sz += sz;
|
||||
} while (total_sz < trailer_sz);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
swap_status_init(const struct boot_loader_state *state,
|
||||
const struct flash_area *fap,
|
||||
const struct boot_status *bs)
|
||||
{
|
||||
struct boot_swap_state swap_state;
|
||||
uint8_t image_index;
|
||||
int rc;
|
||||
|
||||
#if (BOOT_IMAGE_NUMBER == 1)
|
||||
(void)state;
|
||||
#endif
|
||||
|
||||
image_index = BOOT_CURR_IMG(state);
|
||||
|
||||
BOOT_LOG_DBG("initializing status; fa_id=%d", fap->fa_id);
|
||||
|
||||
rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SECONDARY(image_index),
|
||||
&swap_state);
|
||||
assert(rc == 0);
|
||||
|
||||
if (bs->swap_type != BOOT_SWAP_TYPE_NONE) {
|
||||
rc = boot_write_swap_info(fap, bs->swap_type, image_index);
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
if (swap_state.image_ok == BOOT_FLAG_SET) {
|
||||
rc = boot_write_image_ok(fap);
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
rc = boot_write_swap_size(fap, bs->swap_size);
|
||||
assert(rc == 0);
|
||||
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
rc = boot_write_enc_key(fap, 0, bs);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = boot_write_enc_key(fap, 1, bs);
|
||||
assert(rc == 0);
|
||||
#endif
|
||||
|
||||
rc = boot_write_magic(fap);
|
||||
assert(rc == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
swap_read_status(struct boot_loader_state *state, struct boot_status *bs)
|
||||
{
|
||||
const struct flash_area *fap;
|
||||
uint32_t off;
|
||||
uint8_t swap_info;
|
||||
int area_id;
|
||||
int rc;
|
||||
|
||||
bs->source = swap_status_source(state);
|
||||
switch (bs->source) {
|
||||
case BOOT_STATUS_SOURCE_NONE:
|
||||
return 0;
|
||||
|
||||
#if MCUBOOT_SWAP_USING_SCRATCH
|
||||
case BOOT_STATUS_SOURCE_SCRATCH:
|
||||
area_id = FLASH_AREA_IMAGE_SCRATCH;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case BOOT_STATUS_SOURCE_PRIMARY_SLOT:
|
||||
area_id = FLASH_AREA_IMAGE_PRIMARY(BOOT_CURR_IMG(state));
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
return BOOT_EBADARGS;
|
||||
}
|
||||
|
||||
rc = flash_area_open(area_id, &fap);
|
||||
if (rc != 0) {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
|
||||
rc = swap_read_status_bytes(fap, state, bs);
|
||||
if (rc == 0) {
|
||||
off = boot_swap_info_off(fap);
|
||||
rc = flash_area_read(fap, off, &swap_info, sizeof swap_info);
|
||||
if (rc != 0) {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
|
||||
if (bootutil_buffer_is_erased(fap, &swap_info, sizeof swap_info)) {
|
||||
BOOT_SET_SWAP_INFO(swap_info, 0, BOOT_SWAP_TYPE_NONE);
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
/* Extract the swap type info */
|
||||
bs->swap_type = BOOT_GET_SWAP_TYPE(swap_info);
|
||||
}
|
||||
|
||||
flash_area_close(fap);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
swap_set_copy_done(uint8_t image_index)
|
||||
{
|
||||
const struct flash_area *fap;
|
||||
int rc;
|
||||
|
||||
rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index),
|
||||
&fap);
|
||||
if (rc != 0) {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
|
||||
rc = boot_write_copy_done(fap);
|
||||
flash_area_close(fap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
swap_set_image_ok(uint8_t image_index)
|
||||
{
|
||||
const struct flash_area *fap;
|
||||
struct boot_swap_state state;
|
||||
int rc;
|
||||
|
||||
rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index),
|
||||
&fap);
|
||||
if (rc != 0) {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
|
||||
rc = boot_read_swap_state(fap, &state);
|
||||
if (rc != 0) {
|
||||
rc = BOOT_EFLASH;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (state.image_ok == BOOT_FLAG_UNSET) {
|
||||
rc = boot_write_image_ok(fap);
|
||||
}
|
||||
|
||||
out:
|
||||
flash_area_close(fap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#endif /* defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) */
|
||||
@@ -0,0 +1,523 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2019 JUUL Labs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "bootutil/bootutil.h"
|
||||
#include "bootutil_priv.h"
|
||||
#include "swap_priv.h"
|
||||
#include "bootutil/bootutil_log.h"
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
MCUBOOT_LOG_MODULE_DECLARE(mcuboot);
|
||||
|
||||
#ifdef MCUBOOT_SWAP_USING_MOVE
|
||||
|
||||
#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
|
||||
/*
|
||||
* FIXME: this might have to be updated for threaded sim
|
||||
*/
|
||||
int boot_status_fails = 0;
|
||||
#define BOOT_STATUS_ASSERT(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
boot_status_fails++; \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define BOOT_STATUS_ASSERT(x) ASSERT(x)
|
||||
#endif
|
||||
|
||||
static uint32_t g_last_idx = UINT32_MAX;
|
||||
|
||||
int
|
||||
boot_read_image_header(struct boot_loader_state *state, int slot,
|
||||
struct image_header *out_hdr, struct boot_status *bs)
|
||||
{
|
||||
const struct flash_area *fap;
|
||||
uint32_t off;
|
||||
uint32_t sz;
|
||||
int area_id;
|
||||
int rc;
|
||||
|
||||
#if (BOOT_IMAGE_NUMBER == 1)
|
||||
(void)state;
|
||||
#endif
|
||||
|
||||
off = 0;
|
||||
if (bs) {
|
||||
sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0);
|
||||
if (bs->op == BOOT_STATUS_OP_MOVE) {
|
||||
if (slot == 0 && bs->idx > g_last_idx) {
|
||||
/* second sector */
|
||||
off = sz;
|
||||
}
|
||||
} else if (bs->op == BOOT_STATUS_OP_SWAP) {
|
||||
if (bs->idx > 1 && bs->idx <= g_last_idx) {
|
||||
if (slot == 0) {
|
||||
slot = 1;
|
||||
} else {
|
||||
slot = 0;
|
||||
}
|
||||
} else if (bs->idx == 1) {
|
||||
if (slot == 0) {
|
||||
off = sz;
|
||||
}
|
||||
if (slot == 1 && bs->state == 2) {
|
||||
slot = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot);
|
||||
rc = flash_area_open(area_id, &fap);
|
||||
if (rc != 0) {
|
||||
rc = BOOT_EFLASH;
|
||||
goto done;
|
||||
}
|
||||
|
||||
rc = flash_area_read(fap, off, out_hdr, sizeof *out_hdr);
|
||||
if (rc != 0) {
|
||||
rc = BOOT_EFLASH;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* We only know where the headers are located when bs is valid */
|
||||
if (bs != NULL && out_hdr->ih_magic != IMAGE_MAGIC) {
|
||||
rc = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
done:
|
||||
flash_area_close(fap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
swap_read_status_bytes(const struct flash_area *fap,
|
||||
struct boot_loader_state *state, struct boot_status *bs)
|
||||
{
|
||||
uint32_t off;
|
||||
uint8_t status;
|
||||
int max_entries;
|
||||
int found_idx;
|
||||
uint8_t write_sz;
|
||||
int move_entries;
|
||||
int rc;
|
||||
int last_rc;
|
||||
int erased_sections;
|
||||
int i;
|
||||
|
||||
max_entries = boot_status_entries(BOOT_CURR_IMG(state), fap);
|
||||
if (max_entries < 0) {
|
||||
return BOOT_EBADARGS;
|
||||
}
|
||||
|
||||
erased_sections = 0;
|
||||
found_idx = -1;
|
||||
/* skip erased sectors at the end */
|
||||
last_rc = 1;
|
||||
write_sz = BOOT_WRITE_SZ(state);
|
||||
off = boot_status_off(fap);
|
||||
for (i = max_entries; i > 0; i--) {
|
||||
rc = flash_area_read(fap, off + (i - 1) * write_sz, &status, 1);
|
||||
if (rc < 0) {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
|
||||
if (bootutil_buffer_is_erased(fap, &status, 1)) {
|
||||
if (rc != last_rc) {
|
||||
erased_sections++;
|
||||
}
|
||||
} else {
|
||||
if (found_idx == -1) {
|
||||
found_idx = i;
|
||||
}
|
||||
}
|
||||
last_rc = rc;
|
||||
}
|
||||
|
||||
if (erased_sections > 1) {
|
||||
/* This means there was an error writing status on the last
|
||||
* swap. Tell user and move on to validation!
|
||||
*/
|
||||
#if !defined(__BOOTSIM__)
|
||||
BOOT_LOG_ERR("Detected inconsistent status!");
|
||||
#endif
|
||||
|
||||
#if !defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
|
||||
/* With validation of the primary slot disabled, there is no way
|
||||
* to be sure the swapped primary slot is OK, so abort!
|
||||
*/
|
||||
assert(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
move_entries = BOOT_MAX_IMG_SECTORS * BOOT_STATUS_MOVE_STATE_COUNT;
|
||||
if (found_idx == -1) {
|
||||
/* no swap status found; nothing to do */
|
||||
} else if (found_idx < move_entries) {
|
||||
bs->op = BOOT_STATUS_OP_MOVE;
|
||||
bs->idx = (found_idx / BOOT_STATUS_MOVE_STATE_COUNT) + BOOT_STATUS_IDX_0;
|
||||
bs->state = (found_idx % BOOT_STATUS_MOVE_STATE_COUNT) + BOOT_STATUS_STATE_0;;
|
||||
} else {
|
||||
bs->op = BOOT_STATUS_OP_SWAP;
|
||||
bs->idx = ((found_idx - move_entries) / BOOT_STATUS_SWAP_STATE_COUNT) + BOOT_STATUS_IDX_0;
|
||||
bs->state = ((found_idx - move_entries) % BOOT_STATUS_SWAP_STATE_COUNT) + BOOT_STATUS_STATE_0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
boot_status_internal_off(const struct boot_status *bs, int elem_sz)
|
||||
{
|
||||
uint32_t off;
|
||||
int idx_sz;
|
||||
|
||||
idx_sz = elem_sz * ((bs->op == BOOT_STATUS_OP_MOVE) ?
|
||||
BOOT_STATUS_MOVE_STATE_COUNT : BOOT_STATUS_SWAP_STATE_COUNT);
|
||||
|
||||
off = ((bs->op == BOOT_STATUS_OP_MOVE) ?
|
||||
0 : (BOOT_MAX_IMG_SECTORS * BOOT_STATUS_MOVE_STATE_COUNT * elem_sz)) +
|
||||
(bs->idx - BOOT_STATUS_IDX_0) * idx_sz +
|
||||
(bs->state - BOOT_STATUS_STATE_0) * elem_sz;
|
||||
|
||||
return off;
|
||||
}
|
||||
|
||||
int
|
||||
boot_slots_compatible(struct boot_loader_state *state)
|
||||
{
|
||||
size_t num_sectors_pri;
|
||||
size_t num_sectors_sec;
|
||||
size_t sector_sz_pri = 0;
|
||||
size_t sector_sz_sec = 0;
|
||||
size_t i;
|
||||
|
||||
num_sectors_pri = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
|
||||
num_sectors_sec = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
|
||||
if ((num_sectors_pri != num_sectors_sec) &&
|
||||
(num_sectors_pri != (num_sectors_sec + 1))) {
|
||||
BOOT_LOG_WRN("Cannot upgrade: not a compatible amount of sectors");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (num_sectors_pri > BOOT_MAX_IMG_SECTORS) {
|
||||
BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_sectors_sec; i++) {
|
||||
sector_sz_pri = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
|
||||
sector_sz_sec = boot_img_sector_size(state, BOOT_SECONDARY_SLOT, i);
|
||||
if (sector_sz_pri != sector_sz_sec) {
|
||||
BOOT_LOG_WRN("Cannot upgrade: not same sector layout");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_sectors_pri > num_sectors_sec) {
|
||||
if (sector_sz_pri != boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i)) {
|
||||
BOOT_LOG_WRN("Cannot upgrade: not same sector layout");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define BOOT_LOG_SWAP_STATE(area, state) \
|
||||
BOOT_LOG_INF("%s: magic=%s, swap_type=0x%x, copy_done=0x%x, " \
|
||||
"image_ok=0x%x", \
|
||||
(area), \
|
||||
((state)->magic == BOOT_MAGIC_GOOD ? "good" : \
|
||||
(state)->magic == BOOT_MAGIC_UNSET ? "unset" : \
|
||||
"bad"), \
|
||||
(state)->swap_type, \
|
||||
(state)->copy_done, \
|
||||
(state)->image_ok)
|
||||
|
||||
int
|
||||
swap_status_source(struct boot_loader_state *state)
|
||||
{
|
||||
struct boot_swap_state state_primary_slot;
|
||||
struct boot_swap_state state_secondary_slot;
|
||||
int rc;
|
||||
uint8_t source;
|
||||
uint8_t image_index;
|
||||
|
||||
#if (BOOT_IMAGE_NUMBER == 1)
|
||||
(void)state;
|
||||
#endif
|
||||
|
||||
image_index = BOOT_CURR_IMG(state);
|
||||
|
||||
rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY(image_index),
|
||||
&state_primary_slot);
|
||||
assert(rc == 0);
|
||||
|
||||
BOOT_LOG_SWAP_STATE("Primary image", &state_primary_slot);
|
||||
|
||||
rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SECONDARY(image_index),
|
||||
&state_secondary_slot);
|
||||
assert(rc == 0);
|
||||
|
||||
BOOT_LOG_SWAP_STATE("Secondary image", &state_secondary_slot);
|
||||
|
||||
if (state_primary_slot.magic == BOOT_MAGIC_GOOD &&
|
||||
state_primary_slot.copy_done == BOOT_FLAG_UNSET &&
|
||||
state_secondary_slot.magic != BOOT_MAGIC_GOOD) {
|
||||
|
||||
source = BOOT_STATUS_SOURCE_PRIMARY_SLOT;
|
||||
|
||||
BOOT_LOG_INF("Boot source: primary slot");
|
||||
return source;
|
||||
}
|
||||
|
||||
BOOT_LOG_INF("Boot source: none");
|
||||
return BOOT_STATUS_SOURCE_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* "Moves" the sector located at idx - 1 to idx.
|
||||
*/
|
||||
static void
|
||||
boot_move_sector_up(int idx, uint32_t sz, struct boot_loader_state *state,
|
||||
struct boot_status *bs, const struct flash_area *fap_pri,
|
||||
const struct flash_area *fap_sec)
|
||||
{
|
||||
uint32_t new_off;
|
||||
uint32_t old_off;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* FIXME: assuming sectors of size == sz, a single off variable
|
||||
* would be enough
|
||||
*/
|
||||
|
||||
/* Calculate offset from start of image area. */
|
||||
new_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx);
|
||||
old_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx - 1);
|
||||
|
||||
if (bs->idx == BOOT_STATUS_IDX_0) {
|
||||
if (bs->source != BOOT_STATUS_SOURCE_PRIMARY_SLOT) {
|
||||
rc = swap_erase_trailer_sectors(state, fap_pri);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = swap_status_init(state, fap_pri, bs);
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
rc = swap_erase_trailer_sectors(state, fap_sec);
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
rc = boot_erase_region(fap_pri, new_off, sz);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = boot_copy_region(state, fap_pri, fap_pri, old_off, new_off, sz);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = boot_write_status(state, bs);
|
||||
|
||||
bs->idx++;
|
||||
BOOT_STATUS_ASSERT(rc == 0);
|
||||
}
|
||||
|
||||
static void
|
||||
boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
|
||||
struct boot_status *bs, const struct flash_area *fap_pri,
|
||||
const struct flash_area *fap_sec)
|
||||
{
|
||||
uint32_t pri_off;
|
||||
uint32_t pri_up_off;
|
||||
uint32_t sec_off;
|
||||
int rc;
|
||||
|
||||
pri_up_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx);
|
||||
pri_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx - 1);
|
||||
sec_off = boot_img_sector_off(state, BOOT_SECONDARY_SLOT, idx - 1);
|
||||
|
||||
if (bs->state == BOOT_STATUS_STATE_0) {
|
||||
rc = boot_erase_region(fap_pri, pri_off, sz);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = boot_copy_region(state, fap_sec, fap_pri, sec_off, pri_off, sz);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = boot_write_status(state, bs);
|
||||
bs->state = BOOT_STATUS_STATE_1;
|
||||
BOOT_STATUS_ASSERT(rc == 0);
|
||||
}
|
||||
|
||||
if (bs->state == BOOT_STATUS_STATE_1) {
|
||||
rc = boot_erase_region(fap_sec, sec_off, sz);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = boot_copy_region(state, fap_pri, fap_sec, pri_up_off, sec_off, sz);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = boot_write_status(state, bs);
|
||||
bs->idx++;
|
||||
bs->state = BOOT_STATUS_STATE_0;
|
||||
BOOT_STATUS_ASSERT(rc == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When starting a revert the swap status exists in the primary slot, and
|
||||
* the status in the secondary slot is erased. To start the swap, the status
|
||||
* area in the primary slot must be re-initialized; if during the small
|
||||
* window of time between re-initializing it and writing the first metadata
|
||||
* a reset happens, the swap process is broken and cannot be resumed.
|
||||
*
|
||||
* This function handles the issue by making the revert look like a permanent
|
||||
* upgrade (by initializing the secondary slot).
|
||||
*/
|
||||
void
|
||||
fixup_revert(const struct boot_loader_state *state, struct boot_status *bs,
|
||||
const struct flash_area *fap_sec, uint8_t sec_id)
|
||||
{
|
||||
struct boot_swap_state swap_state;
|
||||
int rc;
|
||||
|
||||
#if (BOOT_IMAGE_NUMBER == 1)
|
||||
(void)state;
|
||||
#endif
|
||||
|
||||
/* No fixup required */
|
||||
if (bs->swap_type != BOOT_SWAP_TYPE_REVERT ||
|
||||
bs->op != BOOT_STATUS_OP_MOVE ||
|
||||
bs->idx != BOOT_STATUS_IDX_0) {
|
||||
return;
|
||||
}
|
||||
|
||||
rc = boot_read_swap_state_by_id(sec_id, &swap_state);
|
||||
assert(rc == 0);
|
||||
|
||||
BOOT_LOG_SWAP_STATE("Secondary image", &swap_state);
|
||||
|
||||
if (swap_state.magic == BOOT_MAGIC_UNSET) {
|
||||
rc = swap_erase_trailer_sectors(state, fap_sec);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = boot_write_image_ok(fap_sec);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = boot_write_swap_size(fap_sec, bs->swap_size);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = boot_write_magic(fap_sec);
|
||||
assert(rc == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
swap_run(struct boot_loader_state *state, struct boot_status *bs,
|
||||
uint32_t copy_size)
|
||||
{
|
||||
uint32_t sz;
|
||||
uint32_t sector_sz;
|
||||
uint32_t idx;
|
||||
uint32_t trailer_sz;
|
||||
uint32_t first_trailer_idx;
|
||||
uint8_t image_index;
|
||||
const struct flash_area *fap_pri;
|
||||
const struct flash_area *fap_sec;
|
||||
int rc;
|
||||
|
||||
sz = 0;
|
||||
g_last_idx = 0;
|
||||
|
||||
sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0);
|
||||
while (1) {
|
||||
sz += sector_sz;
|
||||
/* Skip to next sector because all sectors will be moved up. */
|
||||
g_last_idx++;
|
||||
if (sz >= copy_size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When starting a new swap upgrade, check that there is enough space.
|
||||
*/
|
||||
if (boot_status_is_reset(bs)) {
|
||||
sz = 0;
|
||||
trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
|
||||
first_trailer_idx = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1;
|
||||
|
||||
while (1) {
|
||||
sz += sector_sz;
|
||||
if (sz >= trailer_sz) {
|
||||
break;
|
||||
}
|
||||
first_trailer_idx--;
|
||||
}
|
||||
|
||||
if (g_last_idx >= first_trailer_idx) {
|
||||
BOOT_LOG_WRN("Not enough free space to run swap upgrade");
|
||||
bs->swap_type = BOOT_SWAP_TYPE_NONE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
image_index = BOOT_CURR_IMG(state);
|
||||
|
||||
rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index), &fap_pri);
|
||||
assert (rc == 0);
|
||||
|
||||
rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index), &fap_sec);
|
||||
assert (rc == 0);
|
||||
|
||||
fixup_revert(state, bs, fap_sec, FLASH_AREA_IMAGE_SECONDARY(image_index));
|
||||
|
||||
if (bs->op == BOOT_STATUS_OP_MOVE) {
|
||||
idx = g_last_idx;
|
||||
while (idx > 0) {
|
||||
if (idx <= (g_last_idx - bs->idx + 1)) {
|
||||
boot_move_sector_up(idx, sector_sz, state, bs, fap_pri, fap_sec);
|
||||
}
|
||||
idx--;
|
||||
}
|
||||
bs->idx = BOOT_STATUS_IDX_0;
|
||||
}
|
||||
|
||||
bs->op = BOOT_STATUS_OP_SWAP;
|
||||
|
||||
idx = 1;
|
||||
while (idx <= g_last_idx) {
|
||||
if (idx >= bs->idx) {
|
||||
boot_swap_sectors(idx, sector_sz, state, bs, fap_pri, fap_sec);
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
flash_area_close(fap_pri);
|
||||
flash_area_close(fap_sec);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2019 JUUL Labs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef H_SWAP_PRIV_
|
||||
#define H_SWAP_PRIV_
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE)
|
||||
|
||||
/**
|
||||
* Calculates the amount of space required to store the trailer, and erases
|
||||
* all sectors required for this storage in the given flash_area.
|
||||
*/
|
||||
int swap_erase_trailer_sectors(const struct boot_loader_state *state,
|
||||
const struct flash_area *fap);
|
||||
|
||||
/**
|
||||
* Initialize the given flash_area with the metadata required to start a new
|
||||
* swap upgrade.
|
||||
*/
|
||||
int swap_status_init(const struct boot_loader_state *state,
|
||||
const struct flash_area *fap,
|
||||
const struct boot_status *bs);
|
||||
|
||||
/**
|
||||
* Tries to locate an interrupted swap status (metadata). If not metadata
|
||||
* was found returns BOOT_STATUS_SOURCE_NONE.
|
||||
*
|
||||
* Must return one of:
|
||||
* - BOOT_STATUS_SOURCE_NONE
|
||||
* - BOOT_STATUS_SOURCE_SCRATCH
|
||||
* - BOOT_STATUS_SOURCE_PRIMARY_SLOT
|
||||
*/
|
||||
int swap_status_source(struct boot_loader_state *state);
|
||||
|
||||
/**
|
||||
* Reads the boot status from the flash. The boot status contains
|
||||
* the current state of an interrupted image copy operation. If the boot
|
||||
* status is not present, or it indicates that previous copy finished,
|
||||
* there is no operation in progress.
|
||||
*/
|
||||
int swap_read_status(struct boot_loader_state *state, struct boot_status *bs);
|
||||
|
||||
/**
|
||||
* Iterate over the swap status bytes in the given flash_area and populate
|
||||
* the given boot_status with the calculated index where a swap upgrade was
|
||||
* interrupted.
|
||||
*/
|
||||
int swap_read_status_bytes(const struct flash_area *fap,
|
||||
struct boot_loader_state *state,
|
||||
struct boot_status *bs);
|
||||
|
||||
/**
|
||||
* Marks the image in the primary slot as fully copied.
|
||||
*/
|
||||
int swap_set_copy_done(uint8_t image_index);
|
||||
|
||||
/**
|
||||
* Marks a reverted image in the primary slot as confirmed. This is necessary to
|
||||
* ensure the status bytes from the image revert operation don't get processed
|
||||
* on a subsequent boot.
|
||||
*
|
||||
* NOTE: image_ok is tested before writing because if there's a valid permanent
|
||||
* image installed on the primary slot and the new image to be upgrade to has a
|
||||
* bad sig, image_ok would be overwritten.
|
||||
*/
|
||||
int swap_set_image_ok(uint8_t image_index);
|
||||
|
||||
/**
|
||||
* Start a new or resume an interrupted swap according to the parameters
|
||||
* found in the given boot_status.
|
||||
*/
|
||||
void swap_run(struct boot_loader_state *state,
|
||||
struct boot_status *bs,
|
||||
uint32_t copy_size);
|
||||
|
||||
#if MCUBOOT_SWAP_USING_SCRATCH
|
||||
#define BOOT_SCRATCH_AREA(state) ((state)->scratch.area)
|
||||
|
||||
static inline size_t boot_scratch_area_size(const struct boot_loader_state *state)
|
||||
{
|
||||
return BOOT_SCRATCH_AREA(state)->fa_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) */
|
||||
|
||||
#endif /* H_SWAP_PRIV_ */
|
||||
@@ -0,0 +1,728 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2019 JUUL Labs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "bootutil/bootutil.h"
|
||||
#include "bootutil_priv.h"
|
||||
#include "swap_priv.h"
|
||||
#include "bootutil/bootutil_log.h"
|
||||
|
||||
#include "mcuboot_config/mcuboot_config.h"
|
||||
|
||||
MCUBOOT_LOG_MODULE_DECLARE(mcuboot);
|
||||
|
||||
#if !defined(MCUBOOT_SWAP_USING_MOVE)
|
||||
|
||||
#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
|
||||
/*
|
||||
* FIXME: this might have to be updated for threaded sim
|
||||
*/
|
||||
int boot_status_fails = 0;
|
||||
#define BOOT_STATUS_ASSERT(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
boot_status_fails++; \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define BOOT_STATUS_ASSERT(x) ASSERT(x)
|
||||
#endif
|
||||
|
||||
int
|
||||
boot_read_image_header(struct boot_loader_state *state, int slot,
|
||||
struct image_header *out_hdr, struct boot_status *bs)
|
||||
{
|
||||
const struct flash_area *fap;
|
||||
int area_id;
|
||||
int rc;
|
||||
|
||||
(void)bs;
|
||||
|
||||
#if (BOOT_IMAGE_NUMBER == 1)
|
||||
(void)state;
|
||||
#endif
|
||||
|
||||
area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot);
|
||||
rc = flash_area_open(area_id, &fap);
|
||||
if (rc != 0) {
|
||||
rc = BOOT_EFLASH;
|
||||
goto done;
|
||||
}
|
||||
|
||||
rc = flash_area_read(fap, 0, out_hdr, sizeof *out_hdr);
|
||||
if (rc != 0) {
|
||||
rc = BOOT_EFLASH;
|
||||
goto done;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
done:
|
||||
flash_area_close(fap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)
|
||||
/**
|
||||
* Reads the status of a partially-completed swap, if any. This is necessary
|
||||
* to recover in case the boot lodaer was reset in the middle of a swap
|
||||
* operation.
|
||||
*/
|
||||
int
|
||||
swap_read_status_bytes(const struct flash_area *fap,
|
||||
struct boot_loader_state *state, struct boot_status *bs)
|
||||
{
|
||||
uint32_t off;
|
||||
uint8_t status;
|
||||
int max_entries;
|
||||
int found;
|
||||
int found_idx;
|
||||
int invalid;
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
off = boot_status_off(fap);
|
||||
max_entries = boot_status_entries(BOOT_CURR_IMG(state), fap);
|
||||
if (max_entries < 0) {
|
||||
return BOOT_EBADARGS;
|
||||
}
|
||||
|
||||
found = 0;
|
||||
found_idx = 0;
|
||||
invalid = 0;
|
||||
for (i = 0; i < max_entries; i++) {
|
||||
rc = flash_area_read(fap, off + i * BOOT_WRITE_SZ(state),
|
||||
&status, 1);
|
||||
if (rc < 0) {
|
||||
return BOOT_EFLASH;
|
||||
}
|
||||
|
||||
if (bootutil_buffer_is_erased(fap, &status, 1)) {
|
||||
if (found && !found_idx) {
|
||||
found_idx = i;
|
||||
}
|
||||
} else if (!found) {
|
||||
found = 1;
|
||||
} else if (found_idx) {
|
||||
invalid = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalid) {
|
||||
/* This means there was an error writing status on the last
|
||||
* swap. Tell user and move on to validation!
|
||||
*/
|
||||
#if !defined(__BOOTSIM__)
|
||||
BOOT_LOG_ERR("Detected inconsistent status!");
|
||||
#endif
|
||||
|
||||
#if !defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
|
||||
/* With validation of the primary slot disabled, there is no way
|
||||
* to be sure the swapped primary slot is OK, so abort!
|
||||
*/
|
||||
assert(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (found) {
|
||||
if (!found_idx) {
|
||||
found_idx = i;
|
||||
}
|
||||
bs->idx = (found_idx / BOOT_STATUS_STATE_COUNT) + 1;
|
||||
bs->state = (found_idx % BOOT_STATUS_STATE_COUNT) + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
boot_status_internal_off(const struct boot_status *bs, int elem_sz)
|
||||
{
|
||||
int idx_sz;
|
||||
|
||||
idx_sz = elem_sz * BOOT_STATUS_STATE_COUNT;
|
||||
|
||||
return (bs->idx - BOOT_STATUS_IDX_0) * idx_sz +
|
||||
(bs->state - BOOT_STATUS_STATE_0) * elem_sz;
|
||||
}
|
||||
|
||||
/*
|
||||
* Slots are compatible when all sectors that store up to to size of the image
|
||||
* round up to sector size, in both slot's are able to fit in the scratch
|
||||
* area, and have sizes that are a multiple of each other (powers of two
|
||||
* presumably!).
|
||||
*/
|
||||
int
|
||||
boot_slots_compatible(struct boot_loader_state *state)
|
||||
{
|
||||
size_t num_sectors_primary;
|
||||
size_t num_sectors_secondary;
|
||||
size_t sz0, sz1;
|
||||
size_t primary_slot_sz, secondary_slot_sz;
|
||||
#ifndef MCUBOOT_OVERWRITE_ONLY
|
||||
size_t scratch_sz;
|
||||
#endif
|
||||
size_t i, j;
|
||||
int8_t smaller;
|
||||
|
||||
num_sectors_primary = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
|
||||
num_sectors_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
|
||||
if ((num_sectors_primary > BOOT_MAX_IMG_SECTORS) ||
|
||||
(num_sectors_secondary > BOOT_MAX_IMG_SECTORS)) {
|
||||
BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef MCUBOOT_OVERWRITE_ONLY
|
||||
scratch_sz = boot_scratch_area_size(state);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The following loop scans all sectors in a linear fashion, assuring that
|
||||
* for each possible sector in each slot, it is able to fit in the other
|
||||
* slot's sector or sectors. Slot's should be compatible as long as any
|
||||
* number of a slot's sectors are able to fit into another, which only
|
||||
* excludes cases where sector sizes are not a multiple of each other.
|
||||
*/
|
||||
i = sz0 = primary_slot_sz = 0;
|
||||
j = sz1 = secondary_slot_sz = 0;
|
||||
smaller = 0;
|
||||
while (i < num_sectors_primary || j < num_sectors_secondary) {
|
||||
if (sz0 == sz1) {
|
||||
sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
|
||||
sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
|
||||
i++;
|
||||
j++;
|
||||
} else if (sz0 < sz1) {
|
||||
sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
|
||||
/* Guarantee that multiple sectors of the secondary slot
|
||||
* fit into the primary slot.
|
||||
*/
|
||||
if (smaller == 2) {
|
||||
BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
|
||||
return 0;
|
||||
}
|
||||
smaller = 1;
|
||||
i++;
|
||||
} else {
|
||||
sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
|
||||
/* Guarantee that multiple sectors of the primary slot
|
||||
* fit into the secondary slot.
|
||||
*/
|
||||
if (smaller == 1) {
|
||||
BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
|
||||
return 0;
|
||||
}
|
||||
smaller = 2;
|
||||
j++;
|
||||
}
|
||||
#ifndef MCUBOOT_OVERWRITE_ONLY
|
||||
if (sz0 == sz1) {
|
||||
primary_slot_sz += sz0;
|
||||
secondary_slot_sz += sz1;
|
||||
/* Scratch has to fit each swap operation to the size of the larger
|
||||
* sector among the primary slot and the secondary slot.
|
||||
*/
|
||||
if (sz0 > scratch_sz || sz1 > scratch_sz) {
|
||||
BOOT_LOG_WRN("Cannot upgrade: not all sectors fit inside scratch");
|
||||
return 0;
|
||||
}
|
||||
smaller = sz0 = sz1 = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((i != num_sectors_primary) ||
|
||||
(j != num_sectors_secondary) ||
|
||||
(primary_slot_sz != secondary_slot_sz)) {
|
||||
BOOT_LOG_WRN("Cannot upgrade: slots are not compatible");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define BOOT_LOG_SWAP_STATE(area, state) \
|
||||
BOOT_LOG_INF("%s: magic=%s, swap_type=0x%x, copy_done=0x%x, " \
|
||||
"image_ok=0x%x", \
|
||||
(area), \
|
||||
((state)->magic == BOOT_MAGIC_GOOD ? "good" : \
|
||||
(state)->magic == BOOT_MAGIC_UNSET ? "unset" : \
|
||||
"bad"), \
|
||||
(state)->swap_type, \
|
||||
(state)->copy_done, \
|
||||
(state)->image_ok)
|
||||
|
||||
struct boot_status_table {
|
||||
uint8_t bst_magic_primary_slot;
|
||||
uint8_t bst_magic_scratch;
|
||||
uint8_t bst_copy_done_primary_slot;
|
||||
uint8_t bst_status_source;
|
||||
};
|
||||
|
||||
/**
|
||||
* This set of tables maps swap state contents to boot status location.
|
||||
* When searching for a match, these tables must be iterated in order.
|
||||
*/
|
||||
static const struct boot_status_table boot_status_tables[] = {
|
||||
{
|
||||
/* | primary slot | scratch |
|
||||
* ----------+--------------+--------------|
|
||||
* magic | Good | Any |
|
||||
* copy-done | Set | N/A |
|
||||
* ----------+--------------+--------------'
|
||||
* source: none |
|
||||
* ----------------------------------------'
|
||||
*/
|
||||
.bst_magic_primary_slot = BOOT_MAGIC_GOOD,
|
||||
.bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
|
||||
.bst_copy_done_primary_slot = BOOT_FLAG_SET,
|
||||
.bst_status_source = BOOT_STATUS_SOURCE_NONE,
|
||||
},
|
||||
|
||||
{
|
||||
/* | primary slot | scratch |
|
||||
* ----------+--------------+--------------|
|
||||
* magic | Good | Any |
|
||||
* copy-done | Unset | N/A |
|
||||
* ----------+--------------+--------------'
|
||||
* source: primary slot |
|
||||
* ----------------------------------------'
|
||||
*/
|
||||
.bst_magic_primary_slot = BOOT_MAGIC_GOOD,
|
||||
.bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
|
||||
.bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
|
||||
.bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
|
||||
},
|
||||
|
||||
{
|
||||
/* | primary slot | scratch |
|
||||
* ----------+--------------+--------------|
|
||||
* magic | Any | Good |
|
||||
* copy-done | Any | N/A |
|
||||
* ----------+--------------+--------------'
|
||||
* source: scratch |
|
||||
* ----------------------------------------'
|
||||
*/
|
||||
.bst_magic_primary_slot = BOOT_MAGIC_ANY,
|
||||
.bst_magic_scratch = BOOT_MAGIC_GOOD,
|
||||
.bst_copy_done_primary_slot = BOOT_FLAG_ANY,
|
||||
.bst_status_source = BOOT_STATUS_SOURCE_SCRATCH,
|
||||
},
|
||||
{
|
||||
/* | primary slot | scratch |
|
||||
* ----------+--------------+--------------|
|
||||
* magic | Unset | Any |
|
||||
* copy-done | Unset | N/A |
|
||||
* ----------+--------------+--------------|
|
||||
* source: varies |
|
||||
* ----------------------------------------+--------------------------+
|
||||
* This represents one of two cases: |
|
||||
* o No swaps ever (no status to read, so no harm in checking). |
|
||||
* o Mid-revert; status in primary slot. |
|
||||
* -------------------------------------------------------------------'
|
||||
*/
|
||||
.bst_magic_primary_slot = BOOT_MAGIC_UNSET,
|
||||
.bst_magic_scratch = BOOT_MAGIC_ANY,
|
||||
.bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
|
||||
.bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
|
||||
},
|
||||
};
|
||||
|
||||
#define BOOT_STATUS_TABLES_COUNT \
|
||||
(sizeof boot_status_tables / sizeof boot_status_tables[0])
|
||||
|
||||
/**
|
||||
* Determines where in flash the most recent boot status is stored. The boot
|
||||
* status is necessary for completing a swap that was interrupted by a boot
|
||||
* loader reset.
|
||||
*
|
||||
* @return A BOOT_STATUS_SOURCE_[...] code indicating where status should
|
||||
* be read from.
|
||||
*/
|
||||
int
|
||||
swap_status_source(struct boot_loader_state *state)
|
||||
{
|
||||
const struct boot_status_table *table;
|
||||
struct boot_swap_state state_scratch;
|
||||
struct boot_swap_state state_primary_slot;
|
||||
int rc;
|
||||
size_t i;
|
||||
uint8_t source;
|
||||
uint8_t image_index;
|
||||
|
||||
#if (BOOT_IMAGE_NUMBER == 1)
|
||||
(void)state;
|
||||
#endif
|
||||
|
||||
image_index = BOOT_CURR_IMG(state);
|
||||
rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY(image_index),
|
||||
&state_primary_slot);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH, &state_scratch);
|
||||
assert(rc == 0);
|
||||
|
||||
BOOT_LOG_SWAP_STATE("Primary image", &state_primary_slot);
|
||||
BOOT_LOG_SWAP_STATE("Scratch", &state_scratch);
|
||||
|
||||
for (i = 0; i < BOOT_STATUS_TABLES_COUNT; i++) {
|
||||
table = &boot_status_tables[i];
|
||||
|
||||
if (boot_magic_compatible_check(table->bst_magic_primary_slot,
|
||||
state_primary_slot.magic) &&
|
||||
boot_magic_compatible_check(table->bst_magic_scratch,
|
||||
state_scratch.magic) &&
|
||||
(table->bst_copy_done_primary_slot == BOOT_FLAG_ANY ||
|
||||
table->bst_copy_done_primary_slot == state_primary_slot.copy_done))
|
||||
{
|
||||
source = table->bst_status_source;
|
||||
|
||||
#if (BOOT_IMAGE_NUMBER > 1)
|
||||
/* In case of multi-image boot it can happen that if boot status
|
||||
* info is found on scratch area then it does not belong to the
|
||||
* currently examined image.
|
||||
*/
|
||||
if (source == BOOT_STATUS_SOURCE_SCRATCH &&
|
||||
state_scratch.image_num != BOOT_CURR_IMG(state)) {
|
||||
source = BOOT_STATUS_SOURCE_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
BOOT_LOG_INF("Boot source: %s",
|
||||
source == BOOT_STATUS_SOURCE_NONE ? "none" :
|
||||
source == BOOT_STATUS_SOURCE_SCRATCH ? "scratch" :
|
||||
source == BOOT_STATUS_SOURCE_PRIMARY_SLOT ?
|
||||
"primary slot" : "BUG; can't happen");
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
BOOT_LOG_INF("Boot source: none");
|
||||
return BOOT_STATUS_SOURCE_NONE;
|
||||
}
|
||||
|
||||
#ifndef MCUBOOT_OVERWRITE_ONLY
|
||||
/**
|
||||
* Calculates the number of sectors the scratch area can contain. A "last"
|
||||
* source sector is specified because images are copied backwards in flash
|
||||
* (final index to index number 0).
|
||||
*
|
||||
* @param last_sector_idx The index of the last source sector
|
||||
* (inclusive).
|
||||
* @param out_first_sector_idx The index of the first source sector
|
||||
* (inclusive) gets written here.
|
||||
*
|
||||
* @return The number of bytes comprised by the
|
||||
* [first-sector, last-sector] range.
|
||||
*/
|
||||
static uint32_t
|
||||
boot_copy_sz(const struct boot_loader_state *state, int last_sector_idx,
|
||||
int *out_first_sector_idx)
|
||||
{
|
||||
size_t scratch_sz;
|
||||
uint32_t new_sz;
|
||||
uint32_t sz;
|
||||
int i;
|
||||
|
||||
sz = 0;
|
||||
|
||||
scratch_sz = boot_scratch_area_size(state);
|
||||
for (i = last_sector_idx; i >= 0; i--) {
|
||||
new_sz = sz + boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
|
||||
/*
|
||||
* The secondary slot is not being checked here, because
|
||||
* `boot_slots_compatible` already provides assurance that the copy size
|
||||
* will be compatible with the primary slot and scratch.
|
||||
*/
|
||||
if (new_sz > scratch_sz) {
|
||||
break;
|
||||
}
|
||||
sz = new_sz;
|
||||
}
|
||||
|
||||
/* i currently refers to a sector that doesn't fit or it is -1 because all
|
||||
* sectors have been processed. In both cases, exclude sector i.
|
||||
*/
|
||||
*out_first_sector_idx = i + 1;
|
||||
return sz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps the contents of two flash regions within the two image slots.
|
||||
*
|
||||
* @param idx The index of the first sector in the range of
|
||||
* sectors being swapped.
|
||||
* @param sz The number of bytes to swap.
|
||||
* @param bs The current boot status. This struct gets
|
||||
* updated according to the outcome.
|
||||
*
|
||||
* @return 0 on success; nonzero on failure.
|
||||
*/
|
||||
static void
|
||||
boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
|
||||
struct boot_status *bs)
|
||||
{
|
||||
const struct flash_area *fap_primary_slot;
|
||||
const struct flash_area *fap_secondary_slot;
|
||||
const struct flash_area *fap_scratch;
|
||||
uint32_t copy_sz;
|
||||
uint32_t trailer_sz;
|
||||
uint32_t img_off;
|
||||
uint32_t scratch_trailer_off;
|
||||
struct boot_swap_state swap_state;
|
||||
size_t last_sector;
|
||||
bool erase_scratch;
|
||||
uint8_t image_index;
|
||||
int rc;
|
||||
|
||||
/* Calculate offset from start of image area. */
|
||||
img_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx);
|
||||
|
||||
copy_sz = sz;
|
||||
trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
|
||||
|
||||
/* sz in this function is always sized on a multiple of the sector size.
|
||||
* The check against the start offset of the last sector
|
||||
* is to determine if we're swapping the last sector. The last sector
|
||||
* needs special handling because it's where the trailer lives. If we're
|
||||
* copying it, we need to use scratch to write the trailer temporarily.
|
||||
*
|
||||
* NOTE: `use_scratch` is a temporary flag (never written to flash) which
|
||||
* controls if special handling is needed (swapping last sector).
|
||||
*/
|
||||
last_sector = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1;
|
||||
if ((img_off + sz) >
|
||||
boot_img_sector_off(state, BOOT_PRIMARY_SLOT, last_sector)) {
|
||||
copy_sz -= trailer_sz;
|
||||
}
|
||||
|
||||
bs->use_scratch = (bs->idx == BOOT_STATUS_IDX_0 && copy_sz != sz);
|
||||
|
||||
image_index = BOOT_CURR_IMG(state);
|
||||
|
||||
rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index),
|
||||
&fap_primary_slot);
|
||||
assert (rc == 0);
|
||||
|
||||
rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index),
|
||||
&fap_secondary_slot);
|
||||
assert (rc == 0);
|
||||
|
||||
rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap_scratch);
|
||||
assert (rc == 0);
|
||||
|
||||
if (bs->state == BOOT_STATUS_STATE_0) {
|
||||
BOOT_LOG_DBG("erasing scratch area");
|
||||
rc = boot_erase_region(fap_scratch, 0, fap_scratch->fa_size);
|
||||
assert(rc == 0);
|
||||
|
||||
if (bs->idx == BOOT_STATUS_IDX_0) {
|
||||
/* Write a trailer to the scratch area, even if we don't need the
|
||||
* scratch area for status. We need a temporary place to store the
|
||||
* `swap-type` while we erase the primary trailer.
|
||||
*/
|
||||
rc = swap_status_init(state, fap_scratch, bs);
|
||||
assert(rc == 0);
|
||||
|
||||
if (!bs->use_scratch) {
|
||||
/* Prepare the primary status area... here it is known that the
|
||||
* last sector is not being used by the image data so it's safe
|
||||
* to erase.
|
||||
*/
|
||||
rc = swap_erase_trailer_sectors(state, fap_primary_slot);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = swap_status_init(state, fap_primary_slot, bs);
|
||||
assert(rc == 0);
|
||||
|
||||
/* Erase the temporary trailer from the scratch area. */
|
||||
rc = boot_erase_region(fap_scratch, 0, fap_scratch->fa_size);
|
||||
assert(rc == 0);
|
||||
}
|
||||
}
|
||||
|
||||
rc = boot_copy_region(state, fap_secondary_slot, fap_scratch,
|
||||
img_off, 0, copy_sz);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = boot_write_status(state, bs);
|
||||
bs->state = BOOT_STATUS_STATE_1;
|
||||
BOOT_STATUS_ASSERT(rc == 0);
|
||||
}
|
||||
|
||||
if (bs->state == BOOT_STATUS_STATE_1) {
|
||||
rc = boot_erase_region(fap_secondary_slot, img_off, sz);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = boot_copy_region(state, fap_primary_slot, fap_secondary_slot,
|
||||
img_off, img_off, copy_sz);
|
||||
assert(rc == 0);
|
||||
|
||||
if (bs->idx == BOOT_STATUS_IDX_0 && !bs->use_scratch) {
|
||||
/* If not all sectors of the slot are being swapped,
|
||||
* guarantee here that only the primary slot will have the state.
|
||||
*/
|
||||
rc = swap_erase_trailer_sectors(state, fap_secondary_slot);
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
rc = boot_write_status(state, bs);
|
||||
bs->state = BOOT_STATUS_STATE_2;
|
||||
BOOT_STATUS_ASSERT(rc == 0);
|
||||
}
|
||||
|
||||
if (bs->state == BOOT_STATUS_STATE_2) {
|
||||
rc = boot_erase_region(fap_primary_slot, img_off, sz);
|
||||
assert(rc == 0);
|
||||
|
||||
/* NOTE: If this is the final sector, we exclude the image trailer from
|
||||
* this copy (copy_sz was truncated earlier).
|
||||
*/
|
||||
rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
|
||||
0, img_off, copy_sz);
|
||||
assert(rc == 0);
|
||||
|
||||
if (bs->use_scratch) {
|
||||
scratch_trailer_off = boot_status_off(fap_scratch);
|
||||
|
||||
/* copy current status that is being maintained in scratch */
|
||||
rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
|
||||
scratch_trailer_off, img_off + copy_sz,
|
||||
(BOOT_STATUS_STATE_COUNT - 1) * BOOT_WRITE_SZ(state));
|
||||
BOOT_STATUS_ASSERT(rc == 0);
|
||||
|
||||
rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH,
|
||||
&swap_state);
|
||||
assert(rc == 0);
|
||||
|
||||
if (swap_state.image_ok == BOOT_FLAG_SET) {
|
||||
rc = boot_write_image_ok(fap_primary_slot);
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
if (swap_state.swap_type != BOOT_SWAP_TYPE_NONE) {
|
||||
rc = boot_write_swap_info(fap_primary_slot,
|
||||
swap_state.swap_type, image_index);
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
rc = boot_write_swap_size(fap_primary_slot, bs->swap_size);
|
||||
assert(rc == 0);
|
||||
|
||||
#ifdef MCUBOOT_ENC_IMAGES
|
||||
rc = boot_write_enc_key(fap_primary_slot, 0, bs);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = boot_write_enc_key(fap_primary_slot, 1, bs);
|
||||
assert(rc == 0);
|
||||
#endif
|
||||
rc = boot_write_magic(fap_primary_slot);
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
/* If we wrote a trailer to the scratch area, erase it after we persist
|
||||
* a trailer to the primary slot. We do this to prevent mcuboot from
|
||||
* reading a stale status from the scratch area in case of immediate
|
||||
* reset.
|
||||
*/
|
||||
erase_scratch = bs->use_scratch;
|
||||
bs->use_scratch = 0;
|
||||
|
||||
rc = boot_write_status(state, bs);
|
||||
bs->idx++;
|
||||
bs->state = BOOT_STATUS_STATE_0;
|
||||
BOOT_STATUS_ASSERT(rc == 0);
|
||||
|
||||
if (erase_scratch) {
|
||||
rc = boot_erase_region(fap_scratch, 0, sz);
|
||||
assert(rc == 0);
|
||||
}
|
||||
}
|
||||
|
||||
flash_area_close(fap_primary_slot);
|
||||
flash_area_close(fap_secondary_slot);
|
||||
flash_area_close(fap_scratch);
|
||||
}
|
||||
|
||||
void
|
||||
swap_run(struct boot_loader_state *state, struct boot_status *bs,
|
||||
uint32_t copy_size)
|
||||
{
|
||||
uint32_t sz;
|
||||
int first_sector_idx;
|
||||
int last_sector_idx;
|
||||
uint32_t swap_idx;
|
||||
int last_idx_secondary_slot;
|
||||
uint32_t primary_slot_size;
|
||||
uint32_t secondary_slot_size;
|
||||
primary_slot_size = 0;
|
||||
secondary_slot_size = 0;
|
||||
last_sector_idx = 0;
|
||||
last_idx_secondary_slot = 0;
|
||||
|
||||
/*
|
||||
* Knowing the size of the largest image between both slots, here we
|
||||
* find what is the last sector in the primary slot that needs swapping.
|
||||
* Since we already know that both slots are compatible, the secondary
|
||||
* slot's last sector is not really required after this check is finished.
|
||||
*/
|
||||
while (1) {
|
||||
if ((primary_slot_size < copy_size) ||
|
||||
(primary_slot_size < secondary_slot_size)) {
|
||||
primary_slot_size += boot_img_sector_size(state,
|
||||
BOOT_PRIMARY_SLOT,
|
||||
last_sector_idx);
|
||||
}
|
||||
if ((secondary_slot_size < copy_size) ||
|
||||
(secondary_slot_size < primary_slot_size)) {
|
||||
secondary_slot_size += boot_img_sector_size(state,
|
||||
BOOT_SECONDARY_SLOT,
|
||||
last_idx_secondary_slot);
|
||||
}
|
||||
if (primary_slot_size >= copy_size &&
|
||||
secondary_slot_size >= copy_size &&
|
||||
primary_slot_size == secondary_slot_size) {
|
||||
break;
|
||||
}
|
||||
last_sector_idx++;
|
||||
last_idx_secondary_slot++;
|
||||
}
|
||||
|
||||
swap_idx = 0;
|
||||
while (last_sector_idx >= 0) {
|
||||
sz = boot_copy_sz(state, last_sector_idx, &first_sector_idx);
|
||||
if (swap_idx >= (bs->idx - BOOT_STATUS_IDX_0)) {
|
||||
boot_swap_sectors(first_sector_idx, sz, state, bs);
|
||||
}
|
||||
|
||||
last_sector_idx = first_sector_idx - 1;
|
||||
swap_idx++;
|
||||
}
|
||||
|
||||
}
|
||||
#endif /* !MCUBOOT_OVERWRITE_ONLY */
|
||||
|
||||
#endif /* !MCUBOOT_DIRECT_XIP && !MCUBOOT_RAM_LOAD */
|
||||
|
||||
#endif /* !MCUBOOT_SWAP_USING_MOVE */
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Copyright (c) 2019 JUUL Labs
|
||||
* Copyright (c) 2020 Arm Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "bootutil/bootutil.h"
|
||||
#include "bootutil/image.h"
|
||||
#include "bootutil_priv.h"
|
||||
|
||||
/*
|
||||
* Initialize a TLV iterator.
|
||||
*
|
||||
* @param it An iterator struct
|
||||
* @param hdr image_header of the slot's image
|
||||
* @param fap flash_area of the slot which is storing the image
|
||||
* @param type Type of TLV to look for
|
||||
* @param prot true if TLV has to be stored in the protected area, false otherwise
|
||||
*
|
||||
* @returns 0 if the TLV iterator was successfully started
|
||||
* -1 on errors
|
||||
*/
|
||||
int
|
||||
bootutil_tlv_iter_begin(struct image_tlv_iter *it, const struct image_header *hdr,
|
||||
const struct flash_area *fap, uint16_t type, bool prot)
|
||||
{
|
||||
uint32_t off_;
|
||||
struct image_tlv_info info;
|
||||
|
||||
if (it == NULL || hdr == NULL || fap == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
off_ = BOOT_TLV_OFF(hdr);
|
||||
if (LOAD_IMAGE_DATA(hdr, fap, off_, &info, sizeof(info))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) {
|
||||
if (hdr->ih_protect_tlv_size != info.it_tlv_tot) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (LOAD_IMAGE_DATA(hdr, fap, off_ + info.it_tlv_tot,
|
||||
&info, sizeof(info))) {
|
||||
return -1;
|
||||
}
|
||||
} else if (hdr->ih_protect_tlv_size != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (info.it_magic != IMAGE_TLV_INFO_MAGIC) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
it->hdr = hdr;
|
||||
it->fap = fap;
|
||||
it->type = type;
|
||||
it->prot = prot;
|
||||
it->prot_end = off_ + it->hdr->ih_protect_tlv_size;
|
||||
it->tlv_end = off_ + it->hdr->ih_protect_tlv_size + info.it_tlv_tot;
|
||||
// position on first TLV
|
||||
it->tlv_off = off_ + sizeof(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find next TLV
|
||||
*
|
||||
* @param it The image TLV iterator struct
|
||||
* @param off The offset of the TLV's payload in flash
|
||||
* @param len The length of the TLV's payload
|
||||
* @param type If not NULL returns the type of TLV found
|
||||
*
|
||||
* @returns 0 if a TLV with with matching type was found
|
||||
* 1 if no more TLVs with matching type are available
|
||||
* -1 on errors
|
||||
*/
|
||||
int
|
||||
bootutil_tlv_iter_next(struct image_tlv_iter *it, uint32_t *off, uint16_t *len,
|
||||
uint16_t *type)
|
||||
{
|
||||
struct image_tlv tlv;
|
||||
int rc;
|
||||
|
||||
if (it == NULL || it->hdr == NULL || it->fap == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (it->tlv_off < it->tlv_end) {
|
||||
if (it->hdr->ih_protect_tlv_size > 0 && it->tlv_off == it->prot_end) {
|
||||
it->tlv_off += sizeof(struct image_tlv_info);
|
||||
}
|
||||
|
||||
rc = LOAD_IMAGE_DATA(it->hdr, it->fap, it->tlv_off, &tlv, sizeof tlv);
|
||||
if (rc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* No more TLVs in the protected area */
|
||||
if (it->prot && it->tlv_off >= it->prot_end) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (it->type == IMAGE_TLV_ANY || tlv.it_type == it->type) {
|
||||
if (type != NULL) {
|
||||
*type = tlv.it_type;
|
||||
}
|
||||
*off = it->tlv_off + sizeof(tlv);
|
||||
*len = tlv.it_len;
|
||||
it->tlv_off += sizeof(tlv) + tlv.it_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
it->tlv_off += sizeof(tlv) + tlv.it_len;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user