diff --git a/CHANGELOG.md b/CHANGELOG.md index 93407de..f82a840 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,40 @@ ## Changelog for Pelion Device Management Client Lite +### Release 1.4.0-lite (21.09.2021) + +* Fixed bug where client assumed that event_id and event_type are both 0 when event handler is initialized. Added definition `PDMC_CONNECT_STARTUP_EVENT_TYPE -1 ` to LWM2M interface. Client uses it initializes connection event handler. +* Removed the `need_reboot = false` option in the `fota_component_add()` API. When registering a component, the `need_reboot` option must always be `true`. +* Updated to support Mbed OS 6.8.0. Baremetal mbedtls is now only supported with Mbed OS version >= 6.8.0. +* The new Connection ID (CID) feature eliminates unnecessary DTLS handshake traffic between the client and the cloud during reconnection. To have the client persist the CID during reboot, the application can call the `pause()` API before shutting down the application. This call stores the CID context in persistent memory for use after reboot. The client then uses the CID to establish a secure connection to the cloud without requiring a DTLS handshake. The `PROTOMAN_USE_SSL_SESSION_RESUME` feature flag, which controls this feature, is enabled by default for Mbed OS, and disabled by default for other platforms. + * Added a compile-time check to require the mandatory Mbed TLS flags are defined when the Connection ID feature (`PROTOMAN_USE_SSL_SESSION_RESUME`) is enabled. +* Fixed FOTA full resume. +* Changes to implementation of update candidate image encryption: + * Added new `FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY` option to `MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION`. + * Replaced `FOTA_USE_DEVICE_KEY` with `FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY` as the default value for `MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION` due to security vulnerability found in `FOTA_USE_DEVICE_KEY`. + * Using `FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY` is a breaking change and requires a new bootloader that support this feature. + * Deprecated the `FOTA_USE_DEVICE_KEY` option, which will be removed in a future version. +* Added `fota_app_postpone_reboot()`. Calling this API postpones device reboot, which is required to complete the FOTA process, until the device application explicitly initiates reboot. +* Changed `fota_app_defer()` behavior such that FOTA candidate image download or install resumes only after the device application explicitly calls `fota_app_resume()`. +* Support calling `fota_app_reject()` after calling `fota_app_defer()`. +* Fix: Support for resuming installation after an unexpected interruption (for example, power loss) of a component image. +* Added support for updating device firmware with a cloud-encrypted update image. + * Enabled by the `MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT` option. + * Limitation: Not supported when `MBED_CLOUD_CLIENT_FOTA_CANDIDATE_BLOCK_SIZE` is not 1024. +* Fixed coverity issues. +* Fixed compilation for Client Lite Linux, release mode. KVStore failed to compile in release mode. +* Fixed a bug that prevented Firmware-Over-the-Air (FOTA) from running successfully after devices were provisioned in the production flow. +* Fixed update flow when the update candidate version is 0.0.10. +* Fota block device configuration changes: `FOTA_INTERNAL_FLASH_BD` changed to `FOTA_INTERNAL_FLASH_MBED_OS_BD`, `FOTA_CUSTOM_BD` changed to `FOTA_CUSTOM_MBED_OS_BD`, added default block device configuration : `FOTA_DEFAULT_MBED_OS_BD`. +* Changed FOTA application interface APIs: + * `fota_app_on_install_authorization(uint32 token)` -> `fota_app_on_install_authorization()` (removed token) + * `fota_app_on_download_authorization(uint32_t token, ...)` -> `fota_app_on_download_authorization(...)` (removed token) + * `fota_app_authorize_update()` -> `fota_app_authorize()` (reverted to the deprecated API) + * `fota_app_reject_update()` -> `fota_app_reject()` (reverted to the deprecated API) + * `fota_app_defer_update()` -> `fota_app_defer()` (reverted to the deprecated API) +* On Linux targets, all FOTA related files (candidate, header etc.) were moved to the the configuration directory (PAL/KVstore). +* Require defining `MBED_CLOUD_CLIENT_FOTA_LINUX_SINGLE_MAIN_FILE` in Linux MCCE, Testapp or any Linux app that has a single file update. + + ### Release 1.3.0-lite (07.12.2020) * Fixed the `COAP_MSG_CODE_RESPONSE_BAD_REQUEST` and `COAP_MSG_CODE_RESPONSE_FORBIDDEN` responses. Now client re-bootstraps when the server rejects registration. @@ -30,4 +65,3 @@ ### Release 1.0.0-lite (31.01.2020) * Initial alpha release for public preview. Not suitable for production use. - diff --git a/device-management-client/lwm2m_types.h b/device-management-client/lwm2m_types.h index 07515a9..4cff108 100644 --- a/device-management-client/lwm2m_types.h +++ b/device-management-client/lwm2m_types.h @@ -66,7 +66,10 @@ typedef enum registry_notification_status_e { NOTIFICATION_STATUS_DELIVERED, ///< Received ACK from server. NOTIFICATION_STATUS_SEND_FAILED, ///< Message sending failed (retransmission completed). NOTIFICATION_STATUS_SUBSCRIBED, ///< Server has started the observation. - NOTIFICATION_STATUS_UNSUBSCRIBED ///< Server has stopped the observation (RESET message or GET with observe 1). + NOTIFICATION_STATUS_UNSUBSCRIBED, ///< Server has stopped the observation (RESET message or GET with observe 1). +#ifdef MBED_CLOUD_CLIENT_DISABLE_REGISTRY + NOTIFICATION_STATUS_NOT_REGISTERED, ///< Failed due to not being registered +#endif } registry_notification_status_t; /** diff --git a/fota/bspatch/bspatch.c b/fota/bspatch/bspatch.c index ba62710..f1dfdda 100755 --- a/fota/bspatch/bspatch.c +++ b/fota/bspatch/bspatch.c @@ -1,7 +1,7 @@ /*- * Copyright 2003-2005 Colin Percival * Copyright 2012 Matthew Endsley - * Copyright (c) 2018-2019 ARM Limited + * Copyright 2019-2021 Pelion Ltd. * All rights reserved * * Redistribution and use in source and binary forms, with or without diff --git a/fota/bspatch/bspatch.h b/fota/bspatch/bspatch.h index dfd576b..00609de 100755 --- a/fota/bspatch/bspatch.h +++ b/fota/bspatch/bspatch.h @@ -1,7 +1,7 @@ /*- * Copyright 2003-2005 Colin Percival * Copyright 2012 Matthew Endsley - * Copyright (c) 2018-2019 ARM Limited + * Copyright 2019-2021 Pelion Ltd. * All rights reserved * * Redistribution and use in source and binary forms, with or without diff --git a/fota/bspatch/common.h b/fota/bspatch/bspatch_common.h similarity index 96% rename from fota/bspatch/common.h rename to fota/bspatch/bspatch_common.h index aa3b012..1bbfc0e 100644 --- a/fota/bspatch/common.h +++ b/fota/bspatch/bspatch_common.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2019 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // diff --git a/fota/bspatch/bspatch_private.h b/fota/bspatch/bspatch_private.h index 3cd0828..a38f5c6 100644 --- a/fota/bspatch/bspatch_private.h +++ b/fota/bspatch/bspatch_private.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2019 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -20,7 +20,7 @@ #define INCLUDE_BSPATCH_PRIVATE_H_ #include "bspatch.h" -#include "common.h" +#include "bspatch_common.h" /* Patch applied successfully, but new file is not ready yet. User should re-fill patch buffer and call bspatch again. */ #define BSPATCH_NEED_MORE_PATCH_DATA 1 diff --git a/fota/bspatch/import_ref.txt b/fota/bspatch/import_ref.txt index 721ae19..b6b8b74 100644 --- a/fota/bspatch/import_ref.txt +++ b/fota/bspatch/import_ref.txt @@ -1 +1 @@ -Imported from delta-tool at hash: f2029f7867d37023cbf6dc5957c73b669d68a2a4 +Imported from delta-tool at hash: c2f98daa8b75cfc53f6175b418f50bdb7d4c7c03 diff --git a/fota/bspatch/lz4.c b/fota/bspatch/lz4.c index a299a72..94a9640 100755 --- a/fota/bspatch/lz4.c +++ b/fota/bspatch/lz4.c @@ -177,6 +177,7 @@ typedef uint8_t BYTE; typedef uint16_t U16; typedef uint32_t U32; + typedef int8_t S8; typedef int32_t S32; typedef uint64_t U64; typedef uintptr_t uptrval; @@ -184,6 +185,7 @@ typedef unsigned char BYTE; typedef unsigned short U16; typedef unsigned int U32; + typedef signed char S8; typedef signed int S32; typedef unsigned long long U64; typedef size_t uptrval; /* generally true, except OpenVMS-64 */ @@ -198,11 +200,21 @@ /*-************************************ * Reading and writing into memory **************************************/ +#ifdef __CC_ARM +#ifdef __BIG_ENDIAN +#define LZ4_isLittleEndian() 0U +#else +#define LZ4_isLittleEndian() 1U +#endif +#elif defined __BYTE_ORDER__ && defined __ORDER_LITTLE_ENDIAN__ +#define LZ4_isLittleEndian() ((unsigned) (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) +#else static unsigned LZ4_isLittleEndian(void) { const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ return one.c[0]; } +#endif #if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2) @@ -1425,8 +1437,8 @@ LZ4_decompress_generic( BYTE* cpy; const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize; - const unsigned inc32table[8] = {0, 1, 2, 1, 0, 4, 4, 4}; - const int dec64table[8] = {0, 0, 0, -1, -4, 1, 2, 3}; + static const BYTE inc32table[8] = {0, 1, 2, 1, 0, 4, 4, 4}; + static const S8 dec64table[8] = {0, 0, 0, -1, -4, 1, 2, 3}; const int safeDecode = (endOnInput==endOnInputSize); const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB))); diff --git a/fota/bspatch/mbed_lib.json b/fota/bspatch/mbed_lib.json deleted file mode 100644 index 8115c32..0000000 --- a/fota/bspatch/mbed_lib.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "delta-tool", - "config": { - "BS_PATCH_COMPILE_TIME_MEMORY_ALLOC": { - "help": "memory allocated statically during compile time to bspatch module, will need to be around 2* frame size defined in bsdiff during delta creation. If defined to 0, malloc will be used instead", - "macro_name": "BS_PATCH_COMPILE_TIME_MEMORY_ALLOC", - "value": null - } - } -} diff --git a/fota/bspatch/varint.c b/fota/bspatch/varint.c index c389e5c..1104109 100644 --- a/fota/bspatch/varint.c +++ b/fota/bspatch/varint.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2018-2019 ARM Limited + * Copyright 2019-2021 Pelion Ltd. * All rights reserved * * Redistribution and use in source and binary forms, with or without diff --git a/fota/bspatch/varint.h b/fota/bspatch/varint.h index 89806e3..85a2163 100644 --- a/fota/bspatch/varint.h +++ b/fota/bspatch/varint.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2019 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // diff --git a/fota/fota.c b/fota/fota.c index 9184340..ee5ac6d 100644 --- a/fota/fota.c +++ b/fota/fota.c @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -29,7 +29,7 @@ #include "fota/fota_source.h" #include "fota/fota_delta.h" #include "fota/fota_app_ifs.h" -#include "fota/fota_platform.h" +#include "fota_platform_hooks.h" #include "fota/fota_nvm.h" #include "fota/fota_block_device.h" #include "fota/fota_crypto.h" @@ -40,42 +40,78 @@ #include "fota/fota_component.h" #include "fota/fota_component_internal.h" #include "fota/fota_fw_download.h" +#include "fota/fota_ext_downloader.h" #include #include #ifdef __MBED__ +#include "fota_device_key.h" #include "mbed_power_mgmt.h" #endif -#if MBED_CLOUD_CLIENT_PROFILE == MBED_CLOUD_CLIENT_PROFILE_LITE -#include "platform/reboot.h" -#define REBOOT_NOW() mbed_client_default_reboot() -#elif MBED_CLOUD_CLIENT_PROFILE == MBED_CLOUD_CLIENT_PROFILE_FULL +#if MBED_CLOUD_CLIENT_FOTA_SUPPORT_PAL #include "pal.h" #define REBOOT_NOW() pal_osReboot() #else -#error reboot porting is missing +#include "platform/reboot.h" +#define REBOOT_NOW() mbed_client_default_reboot() #endif #if defined(TARGET_LIKE_LINUX) -#include "fota_platform_linux.h" +#include "fota/platform/linux/fota_platform_linux.h" #endif #if (MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT == FOTA_RESUME_SUPPORT_RESUME) && !FOTA_HEADER_HAS_CANDIDATE_READY #error Full resume feature is not supported for legacy/external images #endif -#define MAIN_COMP_NUM 0 - static fota_context_t *fota_ctx = NULL; +static fota_persistent_context_t fota_persistent_ctx; static int handle_fw_fragment(uint8_t *buf, size_t size, bool last); -static void handle_manifest(uint8_t *manifest_buf, size_t manifest_size, bool is_resume); +static int handle_manifest(uint8_t *manifest_buf, size_t manifest_size, bool is_resume, bool is_multicast); static void on_reboot(void); static int finalize_update(void); -static void fota_on_install_authorize(bool defer); +static void fota_on_download_authorize(); +static void fota_on_install_authorize(fota_install_state_e fota_install_type); static bool initialized = false; +static size_t storage_available; + +static bool fota_defer_by_user = false; +static bool erase_candidate_image = true; + +bool fota_resume_download_after_user_auth = true; //indication if resume flow executed after reboot, used also in test_fota_core.cpp. +fota_install_state_e fota_install_state = FOTA_INSTALL_STATE_IDLE; //FOTA installation state, used also in test_fota_core.cpp. + + +// Multicast related variables here should not be part of the FOTA context, as they live also outside of FOTA scope +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT != FOTA_MULTICAST_UNSUPPORTED) +static size_t mc_image_data_addr; +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_BR_MODE) +#if !(defined(TARGET_LIKE_LINUX)) +static int multicast_br_candidate_iterate_handler(fota_candidate_iterate_callback_info *info); +#endif +static int multicast_br_post_install_handler(const char *component_name, const fota_header_info_t *expected_header_info); +#elif (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_NODE_MODE) +#if MBED_CLOUD_CLIENT_FOTA_EXTERNAL_DOWNLOADER +static void ext_downloader_manifest_post_action_cb(int ret); +#endif +static bool mc_node_new_image = false; +static size_t mc_node_image_size = 0; +static size_t mc_node_frag_size = 0; +static void fota_multicast_node_on_fragment(void); +#endif +#endif + +static inline void clear_buffer_from_mem(void *buffer, size_t size) +{ +#if !defined(FOTA_UNIT_TEST) + // Clear buffer from memory due to security reasons + // Skip it in unit tests, as buffer may still be needed for test logic + memset(buffer, 0, size); +#endif +} static int manifest_get(uint8_t *buffer, size_t size, size_t *bytes_read) { @@ -128,12 +164,40 @@ static void free_context_buffers(void) } #endif // !defined(FOTA_DISABLE_DELTA) - if (fota_ctx->enc_ctx) { - fota_encrypt_finalize(&fota_ctx->enc_ctx); +#if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) + fota_encrypt_finalize(&fota_ctx->enc_ctx); +#endif + + fota_hash_finish(&fota_ctx->payload_hash_ctx); +#if !defined(FOTA_DISABLE_DELTA) + fota_hash_finish(&fota_ctx->installed_hash_ctx); +#endif +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_NODE_MODE) + free(fota_ctx->mc_node_frag_buf); + fota_ctx->mc_node_frag_buf = NULL; +#endif +} + +static inline int handle_fota_app_on_complete(int32_t status) +{ +#if FOTA_COMPONENT_SUPPORT + // Not a real update - no need to notify application + if (fota_component_is_internal_component(fota_ctx->comp_id)) { + return FOTA_STATUS_SUCCESS; } - if (fota_ctx->curr_fw_hash_ctx) { - fota_hash_finish(&fota_ctx->curr_fw_hash_ctx); +#endif + return fota_app_on_complete(status); +} + +static inline void handle_fota_app_on_download_progress(size_t downloaded_size, size_t current_chunk_size, size_t total_size) +{ +#if FOTA_COMPONENT_SUPPORT + // Not a real update - no need to notify application + if (fota_component_is_internal_component(fota_ctx->comp_id)) { + return; } +#endif + fota_app_on_download_progress(downloaded_size, current_chunk_size, total_size); } static void update_cleanup(void) @@ -144,19 +208,21 @@ static void update_cleanup(void) free(fota_ctx); fota_ctx = NULL; } + fota_source_enable_auto_observable_resources_reporting(true); } -static void abort_update(int ret, const char *msg) +static void do_abort_update(int ret, const char *msg) { int upd_res; bool do_terminate_update = true; - - if (!fota_is_active_update()) { - return; - } + bool do_report_update_result = true; FOTA_TRACE_ERROR("Update aborted: (ret code %d) %s", ret, msg); + if (ret == FOTA_STATUS_MULTICAST_UPDATE_ABORTED_INTERNAL) { + do_report_update_result = false; + } + if (ret == FOTA_STATUS_FAIL_UPDATE_STATE || ret == FOTA_STATUS_UPDATE_DEFERRED || ret == FOTA_STATUS_TRANSIENT_FAILURE) { @@ -166,22 +232,58 @@ static void abort_update(int ret, const char *msg) } if (do_terminate_update) { - fota_source_report_update_result(upd_res); + if (upd_res > -1 * FOTA_STATUS_INTERNAL_ERR_BASE) { + // map all internal errors to a generic internal error + upd_res = -1 * FOTA_STATUS_INTERNAL_ERROR; + } + if (do_report_update_result) { + fota_source_report_update_result(upd_res); + } fota_source_report_state(FOTA_SOURCE_STATE_IDLE, NULL, NULL); manifest_delete(); + fota_nvm_fw_encryption_key_delete(); } else { fota_source_report_state(FOTA_SOURCE_STATE_PROCESSING_MANIFEST, NULL, NULL); } - fota_nvm_fw_encryption_key_delete(); - const fota_component_desc_t *comp_desc; - fota_component_get_desc(fota_ctx->comp_id, &comp_desc); + fota_component_get_desc(fota_persistent_ctx.comp_id, &comp_desc); fota_platform_abort_update_hook(comp_desc->name); +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_BR_MODE) + if (fota_persistent_ctx.mc_br_update) { + FOTA_DBG_ASSERT(fota_persistent_ctx.mc_br_post_action_callback); + fota_persistent_ctx.mc_br_post_action_callback(ret); + } +#elif (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_NODE_MODE) + if (fota_persistent_ctx.mc_node_update && fota_persistent_ctx.mc_node_post_action_callback) { + fota_persistent_ctx.mc_node_post_action_callback(ret); + } +#endif + fota_app_on_complete(ret); //notify application update_cleanup(); } +static void abort_update(int ret, const char *msg) +{ + if (!fota_is_active_update()) { + return; + } + + //fill FOTA persistent context + fota_persistent_ctx.comp_id = fota_ctx->comp_id; + fota_persistent_ctx.state = fota_ctx->state; +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_BR_MODE) + fota_persistent_ctx.mc_br_update = fota_ctx->mc_br_update; + fota_persistent_ctx.mc_br_post_action_callback = fota_ctx->mc_br_post_action_callback; +#elif (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_NODE_MODE) + fota_persistent_ctx.mc_node_update = fota_ctx->mc_node_update; + fota_persistent_ctx.mc_node_post_action_callback = fota_ctx->mc_node_post_action_callback; +#endif + + do_abort_update(ret, msg); +} + static void on_state_set_failure(void) { abort_update(FOTA_STATUS_FAIL_UPDATE_STATE, "Failed to deliver FOTA state"); @@ -195,15 +297,15 @@ bool fota_is_active_update(void) int fota_is_ready(uint8_t *data, size_t size, fota_state_e *fota_state) { size_t manifest_size; - uint8_t *manifest = malloc(FOTA_MANIFEST_MAX_SIZE); + uint8_t *manifest = calloc(1, FOTA_MANIFEST_MAX_SIZE); if (!manifest) { FOTA_TRACE_ERROR("FOTA manifest - allocation failed"); *fota_state = FOTA_STATE_INVALID; return FOTA_STATUS_OUT_OF_MEMORY; } - memset(manifest, 0, FOTA_MANIFEST_MAX_SIZE); int ret = manifest_get(manifest, FOTA_MANIFEST_MAX_SIZE, &manifest_size); - if (ret) { // cannot find saved manifest - ready to start an update + if (ret) { + // cannot find saved manifest - ready to start an update *fota_state = FOTA_STATE_IDLE; goto CLEANUP; } @@ -225,60 +327,125 @@ static inline void fota_dev_init(void) { int ret; + // Failure in below functions doesn't fail the process + // The items can come from the default file of update_default_resources.c + // When using this file, the capmaign will fail #if defined(MBED_CLOUD_DEV_UPDATE_ID) && !defined(FOTA_USE_EXTERNAL_IDS) ret = fota_nvm_update_class_id_set(); - FOTA_ASSERT(!ret); + FOTA_TRACE_DEBUG("fota_nvm_update_class_id_set ret code %d", ret); ret = fota_nvm_update_vendor_id_set(); - FOTA_ASSERT(!ret); + FOTA_TRACE_DEBUG("fota_nvm_update_vendor_id_set ret code %d", ret); #endif -#if defined(FOTA_USE_UPDATE_X509) && defined(MBED_CLOUD_DEV_UPDATE_CERT) && !defined(FOTA_USE_EXTERNAL_CERT) +#if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT == FOTA_X509_PUBLIC_KEY_FORMAT) && defined(MBED_CLOUD_DEV_UPDATE_CERT) && !defined(FOTA_USE_EXTERNAL_CERT) ret = fota_nvm_update_cert_set(); - FOTA_ASSERT(!ret); + FOTA_TRACE_DEBUG("fota_nvm_update_cert_set ret code %d", ret); #endif -#if defined(FOTA_USE_UPDATE_RAW_PUBLIC_KEY) && defined(MBED_CLOUD_DEV_UPDATE_RAW_PUBLIC_KEY) && !defined(FOTA_USE_EXTERNAL_UPDATE_RAW_PUBLIC_KEY) +#if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT == FOTA_RAW_PUBLIC_KEY_FORMAT) && defined(MBED_CLOUD_DEV_UPDATE_RAW_PUBLIC_KEY) && !defined(FOTA_USE_EXTERNAL_UPDATE_RAW_PUBLIC_KEY) ret = fota_nvm_set_update_public_key(); - FOTA_ASSERT(!ret); + FOTA_TRACE_DEBUG("fota_nvm_set_update_public_key ret code %d", ret); #endif (void)ret; // fix unused variable warning in production } -#if (FOTA_NUM_COMPONENTS > 1) || defined(TARGET_LIKE_LINUX) -int fota_install_verify(const fota_component_desc_t *comp_desc, unsigned int comp_id, uint64_t new_ver) +static int on_main_app_verify_install(const char *comp_name, const fota_header_info_t *expected_header_info) +{ +#if FOTA_CUSTOM_MAIN_APP_VERIFY_INSTALL + return fota_app_on_main_app_verify_install(expected_header_info); +#else + FOTA_DBG_ASSERT(!strcmp(comp_name, FOTA_COMPONENT_MAIN_COMPONENT_NAME)); + size_t curr_fw_size; + uint8_t *curr_digest; + uint64_t curr_version; + int ret; + (void) curr_fw_size; + (void) curr_digest; + (void) curr_version; + (void) ret; + +#if defined(TARGET_LIKE_LINUX) +#if defined(MBED_CLOUD_CLIENT_FOTA_LINUX_SINGLE_MAIN_FILE) + uint8_t calc_digest[FOTA_CRYPTO_HASH_SIZE]; + curr_digest = calc_digest; + ret = fota_linux_get_curr_fw_size(&curr_fw_size); + if (ret) { + return ret; + } + + ret = fota_linux_get_curr_fw_digest(curr_fw_size, curr_digest); + if (ret) { + return ret; + } + // We don't know the current version yet (we're about to update it) + curr_version = expected_header_info->version; +#else // !MBED_CLOUD_CLIENT_FOTA_LINUX_SINGLE_MAIN_FILE + // Nothing to check here + return FOTA_STATUS_SUCCESS; +#endif + +#elif MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION >= 1 + // Own supported current firmware + fota_header_info_t curr_header_info; + ret = fota_curr_fw_read_header(&curr_header_info); + if (ret) { + return ret; + } + curr_fw_size = curr_header_info.fw_size; + curr_digest = curr_header_info.digest; + curr_version = curr_header_info.version; + +#else // Not Linux, not supported header + // Nothing to do here + return FOTA_STATUS_SUCCESS; +#endif + + if ((expected_header_info->fw_size != curr_fw_size) || + (expected_header_info->version != curr_version) || + (memcmp(expected_header_info->digest, curr_digest, FOTA_CRYPTO_HASH_SIZE))) { + FOTA_TRACE_ERROR("Main app verify installation failed!"); + return FOTA_STATUS_FW_INSTALLATION_FAILED; + } + return FOTA_STATUS_SUCCESS; +#endif // !FOTA_CUSTOM_MAIN_APP_VERIFY_INSTALL +} + +#if FOTA_VERIFY_INSTALLATION_AFTER_UPGRADE || FOTA_COMPONENT_SUPPORT +static int comp_install_verify(const fota_component_desc_t *comp_desc, unsigned int comp_id, const fota_header_info_t *expected_header_info) { int ret = FOTA_STATUS_SUCCESS; - if (comp_desc->desc_info.component_post_install_cb) { - // callback to check if installed succeded. - char nvm_semver[FOTA_COMPONENT_MAX_SEMVER_STR_SIZE] = {0}; - ret = fota_component_version_int_to_semver(new_ver, nvm_semver); - if (ret) { - FOTA_TRACE_ERROR("Failed to convert to sem version %d", ret); - return ret; - } + if (comp_desc->desc_info.component_verify_install_cb) { + FOTA_TRACE_DEBUG("Verifying installation of component %s", comp_desc->name); - ret = comp_desc->desc_info.component_post_install_cb(nvm_semver); + ret = comp_desc->desc_info.component_verify_install_cb(comp_desc->name, expected_header_info); if (ret) { - FOTA_TRACE_ERROR("Failed to verify installation %d", ret); + FOTA_TRACE_ERROR("Failed to verify installation. ret %d", ret); return ret; } } - // Successful finish actions - fota_component_set_curr_version(comp_id, new_ver); + // Verification complete - update current version + + fota_component_set_curr_version(comp_id, expected_header_info->version); // Not saving version for the MAIN component - fota_nvm_comp_version_set(comp_desc->name, new_ver); + if (comp_id != FOTA_COMPONENT_MAIN_COMP_NUM) { + ret = fota_nvm_comp_version_set(comp_desc->name, expected_header_info->version); + if (ret) { + FOTA_TRACE_ERROR("fota_nvm_comp_version_set ret %d", ret); + } + } return ret; } -#endif // (FOTA_NUM_COMPONENTS > 1) || defined(TARGET_LIKE_LINUX) +#endif -int fota_handle_post_install() +int fota_verify_installation_after_upgrade() { int ret = FOTA_STATUS_SUCCESS; -#if (FOTA_NUM_COMPONENTS > 1) +#if FOTA_VERIFY_INSTALLATION_AFTER_UPGRADE + int erase_ret; size_t bd_read_size; size_t bd_prog_size; unsigned int comp_id; @@ -290,7 +457,7 @@ int fota_handle_post_install() ret = fota_bd_init(); if (ret) { FOTA_TRACE_ERROR("fota_bd_init failed %d.", ret); - goto fail; + return ret; } ret = fota_bd_get_read_size(&bd_read_size); @@ -304,9 +471,10 @@ int fota_handle_post_install() FOTA_TRACE_ERROR("fota_bd_get_program_size failed %d.", ret); goto fail; } - // check what component was updated to call post install callback - // in case all done at candidate_post_install_cb, we can remove salt and report install complete + addr = fota_candidate_get_config()->storage_start_addr; + +#if FOTA_HEADER_HAS_CANDIDATE_READY ret = fota_candidate_read_candidate_ready_header(&addr, bd_read_size, bd_prog_size, &comp_header); if (ret) { goto fail; @@ -316,6 +484,10 @@ int fota_handle_post_install() if (ret) { goto fail; } +#else + (void) comp_header; + comp_id = FOTA_COMPONENT_MAIN_COMP_NUM; +#endif fota_component_get_desc(comp_id, &comp_desc); ret = fota_candidate_read_header(&addr, bd_read_size, bd_prog_size, &header); @@ -324,27 +496,42 @@ int fota_handle_post_install() goto fail; } - FOTA_TRACE_DEBUG("install verify component name %s, version %" PRIu64 " ", comp_header.comp_name, header.version); - ret = fota_install_verify(comp_desc, comp_id, header.version); + ret = comp_install_verify(comp_desc, comp_id, &header); + +#if defined(TARGET_LIKE_LINUX) + // In Linux we don't have a bootloader that updates the current FW header in case of the main component, + // So do it ourselves here - only after installation has been verified to succeed + if (!ret && (comp_id == FOTA_COMPONENT_MAIN_COMP_NUM)) { + ret = fota_linux_update_curr_fw_header(&header); + } +#endif fail: -#endif // FOTA_NUM_COMPONENTS > 1 + erase_ret = fota_candidate_erase(); + if (erase_ret) { + FOTA_TRACE_ERROR("fota_candidate_erase failed. ret = %d", erase_ret); + // Silently ignore failure here, not related to installation verification + } +#endif // FOTA_VERIFY_INSTALLATION_AFTER_UPGRADE -#if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 0) - ret = fota_bd_init(); + return ret; +} + +static int calc_available_storage(void) +{ + size_t storage_start_addr, storage_end_addr, erase_size; + storage_start_addr = fota_candidate_get_config()->storage_start_addr; + storage_end_addr = storage_start_addr + fota_candidate_get_config()->storage_size; + int ret = fota_bd_get_erase_size(storage_end_addr - 1, &erase_size); if (ret) { - FOTA_TRACE_ERROR("fota_bd_init failed %d.", ret); - } else { - ret = fota_candidate_erase(); - if (ret) { - FOTA_TRACE_ERROR("fota_candidate_erase failed %d.", ret); - } + FOTA_TRACE_ERROR("Get erase size failed. ret %d", ret); + return ret; } -#endif - // in case fota failed to prevent infinite loop, remove FW key and report failure as post installed failed - fota_nvm_fw_encryption_key_delete(); - return ret; + // Check for storage size misconfiguration + FOTA_ASSERT(storage_end_addr == FOTA_ALIGN_UP(storage_end_addr, erase_size)); + storage_available = storage_end_addr - storage_start_addr; + return FOTA_STATUS_SUCCESS; } int fota_init(void *m2m_interface, void *resource_list) @@ -391,10 +578,12 @@ int fota_init(void *m2m_interface, void *resource_list) ret = manifest_get((uint8_t *)&dummy, sizeof(dummy), &manifest_size); if (ret != FOTA_STATUS_NOT_FOUND) { source_state = FOTA_SOURCE_STATE_PROCESSING_MANIFEST; + FOTA_TRACE_DEBUG("manifest exists on fota_init()"); + fota_resume_download_after_user_auth = true; //fota_init() was called and manifest exists - we assume that resume flow will be initiated after reboot } else { uint8_t fw_key[FOTA_ENCRYPT_KEY_SIZE]; ret = fota_nvm_fw_encryption_key_get(fw_key); - memset(fw_key, 0, sizeof(fw_key)); + fota_fi_memset(fw_key, 0, sizeof(fw_key)); after_upgrade = !ret; } @@ -409,49 +598,56 @@ int fota_init(void *m2m_interface, void *resource_list) fota_component_clean(); +#if (MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION >= 3) + // If header v3, assume component version is always represent SemVer. + header_info.version |= FOTA_COMPONENT_SEMVER_BIT; +#endif + // register main component (should be done before platform init hook, which registers all other components). // "Factory" version here is what we read from main firmware header, as we don't save it to NVM. char factory_version[FOTA_COMPONENT_MAX_SEMVER_STR_SIZE]; fota_component_version_int_to_semver(header_info.version, factory_version); main_component_desc.need_reboot = true; + main_component_desc.component_verify_install_cb = on_main_app_verify_install; + // In case of Linux, delta is supported on main component only in case of a single file +#if !defined(FOTA_DISABLE_DELTA) && !(defined(TARGET_LIKE_LINUX) && !defined(MBED_CLOUD_CLIENT_FOTA_LINUX_SINGLE_MAIN_FILE)) main_component_desc.support_delta = true; main_component_desc.curr_fw_read = fota_curr_fw_read; +#endif +// Get digest is used also for precursor calculation to be used by our bootloader, not only for delta +#if !(defined(TARGET_LIKE_LINUX) && !defined(MBED_CLOUD_CLIENT_FOTA_LINUX_SINGLE_MAIN_FILE)) main_component_desc.curr_fw_get_digest = fota_curr_fw_get_digest; - - ret = fota_component_add(&main_component_desc, FOTA_COMPONENT_MAIN_COMPONENT_NAME, factory_version); - FOTA_DBG_ASSERT(!ret); -#if (MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION >= 3) - // Don't show that in legacy case - FOTA_TRACE_INFO("Registered %s component, version %s", FOTA_COMPONENT_MAIN_COMPONENT_NAME, factory_version); #endif - ret = fota_source_add_component(MAIN_COMP_NUM, FOTA_COMPONENT_MAIN_COMPONENT_NAME, factory_version); + ret = fota_component_add(&main_component_desc, FOTA_COMPONENT_MAIN_COMPONENT_NAME, factory_version); FOTA_DBG_ASSERT(!ret); + fota_component_set_curr_version(FOTA_COMPONENT_MAIN_COMP_NUM, header_info.version); ret = fota_platform_init_hook(after_upgrade); FOTA_ASSERT(!ret); if (after_upgrade) { - FOTA_TRACE_DEBUG("After upgrade, issuing post install actions"); - ret = fota_handle_post_install(); + FOTA_TRACE_DEBUG("After upgrade, verifying installation"); + ret = fota_verify_installation_after_upgrade(); if (ret) { fota_source_report_update_result(FOTA_STATUS_FW_INSTALLATION_FAILED); } + fota_nvm_fw_encryption_key_delete(); } -// Code saving - only relevant if we have additional components other than the main one -#if (FOTA_NUM_COMPONENTS > 1) +#if (FOTA_COMPONENT_SUPPORT) // Now we should have all components registered, report them all unsigned int num_comps = fota_component_num_components(); - for (unsigned int i = 1; i < num_comps; i++) { + for (unsigned int i = 0; i < num_comps; i++) { const fota_component_desc_t *comp_desc; char semver[FOTA_COMPONENT_MAX_SEMVER_STR_SIZE] = {0}; fota_component_version_t version; fota_component_get_desc(i, &comp_desc); ret = fota_nvm_comp_version_get(comp_desc->name, &version); - // if not found, take factory version from comp_desc - if (ret != FOTA_STATUS_SUCCESS) { - version = comp_desc->version; + // if not found, take factory version, set as current version. + // Always true in main component case, which shouldn't be saved in NVM. + if ((ret != FOTA_STATUS_SUCCESS) || (i == FOTA_COMPONENT_MAIN_COMP_NUM)) { + fota_component_get_curr_version(i, &version); } ret = fota_component_version_int_to_semver(version, semver); FOTA_DBG_ASSERT(!ret); @@ -461,9 +657,30 @@ int fota_init(void *m2m_interface, void *resource_list) FOTA_DBG_ASSERT(!ret); fota_component_set_curr_version(i, version); } -#endif // FOTA_NUM_COMPONENTS > 1 +#else // !FOTA_COMPONENT_SUPPORT + // Code saving - explicitly report main component only + ret = fota_source_add_component(FOTA_COMPONENT_MAIN_COMP_NUM, FOTA_COMPONENT_MAIN_COMPONENT_NAME, factory_version); + FOTA_DBG_ASSERT(!ret); +#if (MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION >= 3) + // Don't show that in legacy case + FOTA_TRACE_INFO("Registered %s component, version %s", FOTA_COMPONENT_MAIN_COMPONENT_NAME, factory_version); +#endif +#endif // !FOTA_COMPONENT_SUPPORT + +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_BR_MODE) + // Register internal component, which handles the Multicast BR installer + fota_component_desc_info_t multicast_br_component_desc = {0}; + multicast_br_component_desc.need_reboot = false; +#if !(defined(TARGET_LIKE_LINUX)) + multicast_br_component_desc.candidate_iterate_cb = multicast_br_candidate_iterate_handler; +#endif + multicast_br_component_desc.component_verify_install_cb = multicast_br_post_install_handler; + ret = fota_component_add(&multicast_br_component_desc, FOTA_MULTICAST_BR_INT_COMP_NAME, "0.0.0"); + FOTA_DBG_ASSERT(!ret); +#endif initialized = true; + FOTA_TRACE_DEBUG("init complete"); return FOTA_STATUS_SUCCESS; @@ -478,21 +695,29 @@ int fota_deinit(void) FOTA_TRACE_DEBUG("fota_deinit"); +#if !defined(FOTA_UNIT_TEST) + FOTA_ASSERT(!fota_ctx); +#endif + update_cleanup(); fota_component_clean(); fota_source_deinit(); fota_random_deinit(); fota_event_handler_deinit(); fota_bd_deinit(); +#if defined(TARGET_LIKE_LINUX) + fota_linux_deinit(); +#endif initialized = false; + return FOTA_STATUS_SUCCESS; } -static int init_encryption(void) +static int init_encryption(manifest_firmware_info_t *fw_info) { int ret = FOTA_STATUS_NOT_FOUND; - uint8_t fw_key[FOTA_ENCRYPT_KEY_SIZE]; + uint8_t fw_key[FOTA_ENCRYPT_KEY_SIZE] = {0}; #if (MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT == FOTA_RESUME_SUPPORT_RESUME) if (fota_ctx->resume_state == FOTA_RESUME_STATE_STARTED) { @@ -508,15 +733,52 @@ static int init_encryption(void) #endif if (ret) { - if (fota_gen_random(fw_key, sizeof(fw_key))) { - FOTA_TRACE_ERROR("Unable to generate random FW key. ret %d", ret); - return ret; - } + for (;;) { + uint8_t zero_key[FOTA_ENCRYPT_KEY_SIZE] = {0}; + volatile size_t loop_check; + +#if (MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION == FOTA_USE_DEVICE_KEY) + ret = fota_get_device_key_128bit(fw_key, FOTA_ENCRYPT_KEY_SIZE); +#elif (MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION == FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY) + if (fw_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_RAW) { + // copy the key from manifest_firmware_info_t + // and clear it from the fota_ctx + fota_fi_memcpy(fw_key, fota_ctx->encryption_key, sizeof(fw_key)); + fota_fi_memset(fota_ctx->encryption_key, 0, FOTA_ENCRYPT_KEY_SIZE); + ret = FOTA_STATUS_SUCCESS; + } else { + ret = fota_gen_random(fw_key, sizeof(fw_key)); + } +#else + // encryption support disabled + ret = fota_gen_random(fw_key, sizeof(fw_key)); +#endif + if (ret) { + FOTA_TRACE_ERROR("Unable to generate random FW key. ret %d", ret); + return ret; + } + // safely check that key is non zero + FOTA_FI_SAFE_COND((fota_fi_memcmp(fw_key, zero_key, FOTA_ENCRYPT_KEY_SIZE, &loop_check) + && (loop_check == FOTA_ENCRYPT_KEY_SIZE)), FOTA_STATUS_INTERNAL_ERROR, + "Zero encryption key - retry"); - ret = fota_nvm_fw_encryption_key_set(fw_key); - if (ret) { - FOTA_TRACE_ERROR("Unable to set FW key. ret %d", ret); - return ret; + ret = fota_nvm_fw_encryption_key_set(fw_key); + if (ret) { + FOTA_TRACE_ERROR("Unable to set FW key. ret %d", ret); + return ret; + } + // non zero key + break; + +fail: + // zero key +#if (MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION == FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY) + if (fw_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_RAW) { + // don't retry since the fota_ctx->encryption_key will not changed + return ret; + } +#endif + ;// retry here } FOTA_TRACE_DEBUG("New FOTA key saved"); @@ -525,7 +787,7 @@ static int init_encryption(void) #if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) ret = fota_encrypt_decrypt_start(&fota_ctx->enc_ctx, fw_key, sizeof(fw_key)); - memset(fw_key, 0, sizeof(fw_key)); + fota_fi_memset(fw_key, 0, sizeof(fw_key)); if (ret) { FOTA_TRACE_ERROR("Unable to start encryption engine. ret %d", ret); return ret; @@ -542,6 +804,13 @@ static int init_header(size_t prog_size) // Reserve space for candidate ready header (if not legacy header version) #if FOTA_HEADER_HAS_CANDIDATE_READY fota_ctx->candidate_header_size = FOTA_ALIGN_UP(sizeof(fota_candidate_ready_header_t), prog_size); + // Special case - legacy header with candidate header enabled. This is in case we wish to have a legacy BL, + // with component update enabled. In this case, disable the candidate ready header only for the main component. +#if (MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION < 3) + if (fota_ctx->comp_id == FOTA_COMPONENT_MAIN_COMP_NUM) { + fota_ctx->candidate_header_size = 0; + } +#endif #else fota_ctx->candidate_header_size = 0; #endif @@ -557,7 +826,6 @@ void request_download_auth(void) fota_component_get_curr_version(fota_ctx->comp_id, &curr_ver); int ret = fota_app_on_download_authorization( - 0, fota_ctx->fw_info, curr_ver ); @@ -567,41 +835,51 @@ void request_download_auth(void) } } -static void handle_manifest(uint8_t *manifest_buf, size_t manifest_size, bool is_resume) +static int handle_manifest_init(void) { - int ret; - int manifest_save_ret = FOTA_STATUS_INTERNAL_ERROR; - const fota_component_desc_t *comp_desc; - fota_component_version_t curr_fw_version; - uint8_t curr_fw_digest[FOTA_CRYPTO_HASH_SIZE] = {0}; - - // this should never happen as lwm2m on_manifest callback should verify that fota is idle - FOTA_ASSERT(!fota_ctx); + if (fota_ctx) { + // Already called + return FOTA_STATUS_SUCCESS; + } - fota_ctx = (fota_context_t *)malloc(sizeof(*fota_ctx)); + fota_ctx = (fota_context_t *)calloc(1, sizeof(*fota_ctx)); if (!fota_ctx) { - ret = FOTA_STATUS_OUT_OF_MEMORY; FOTA_TRACE_ERROR("Unable to allocate FOTA context."); - goto fail; + return FOTA_STATUS_OUT_OF_MEMORY; } - memset(fota_ctx, 0, sizeof(*fota_ctx)); fota_ctx->fw_info = (manifest_firmware_info_t *) malloc(sizeof(manifest_firmware_info_t)); if (!fota_ctx->fw_info) { FOTA_TRACE_ERROR("Unable to allocate FW info."); - ret = FOTA_STATUS_OUT_OF_MEMORY; - goto fail; + return FOTA_STATUS_OUT_OF_MEMORY; + } + int ret = fota_random_init(NULL, 0); + if (ret) { + FOTA_TRACE_DEBUG("Unable to initialize random %d", ret); + return ret; } + return FOTA_STATUS_SUCCESS; +} - FOTA_TRACE_INFO("Firmware update initiated."); +static int handle_manifest(uint8_t *manifest_buf, size_t manifest_size, bool is_resume, bool is_multicast) +{ + int ret; + int manifest_save_ret = FOTA_STATUS_INTERNAL_ERROR; + const fota_component_desc_t *comp_desc; + fota_component_version_t curr_fw_version; + uint8_t curr_fw_digest[FOTA_CRYPTO_HASH_SIZE] = {0}; + fota_source_state_e report_state = FOTA_SOURCE_STATE_AWAITING_DOWNLOAD_APPROVAL; - ret = fota_random_init(NULL, 0); + ret = handle_manifest_init(); if (ret) { - FOTA_TRACE_DEBUG("Unable to initialize random %d", ret); goto fail; } - if (is_resume) { + FOTA_TRACE_INFO("Firmware update initiated."); + + if (is_multicast) { + report_state = FOTA_SOURCE_STATE_DOWNLOADING; + } else if (is_resume) { fota_ctx->resume_state = FOTA_RESUME_STATE_STARTED; } else { manifest_save_ret = manifest_set(manifest_buf, manifest_size); @@ -618,7 +896,7 @@ static void handle_manifest(uint8_t *manifest_buf, size_t manifest_size, bool is fota_ctx->fw_info); // Reset manifest data, no need to keep it anymore - memset(manifest_buf, 0, manifest_size); + clear_buffer_from_mem(manifest_buf, manifest_size); if (ret) { FOTA_TRACE_DEBUG("Pelion FOTA manifest rejected %d", ret); @@ -627,9 +905,13 @@ static void handle_manifest(uint8_t *manifest_buf, size_t manifest_size, bool is FOTA_TRACE_DEBUG("Pelion FOTA manifest is valid"); -#if (FOTA_NUM_COMPONENTS == 1) - //main component in case only one component. - strcpy(fota_ctx->fw_info->component_name, FOTA_COMPONENT_MAIN_COMPONENT_NAME); +#if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) + if (fota_ctx->fw_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_RAW) { + // move encryption_key from fw_info to fota_ctx + // to hide it from fota_app_on_download_authorization + fota_fi_memcpy(fota_ctx->encryption_key, fota_ctx->fw_info->encryption_key, FOTA_ENCRYPT_KEY_SIZE); + fota_fi_memset(fota_ctx->fw_info->encryption_key, 0, FOTA_ENCRYPT_KEY_SIZE); + } #endif ret = fota_component_name_to_id(fota_ctx->fw_info->component_name, &fota_ctx->comp_id); @@ -648,7 +930,8 @@ static void handle_manifest(uint8_t *manifest_buf, size_t manifest_size, bool is FOTA_FI_SAFE_COND(fota_ctx->fw_info->version > curr_fw_version, FOTA_STATUS_MANIFEST_VERSION_REJECTED, "Manifest payload-version rejected - too old"); - FOTA_TRACE_DEBUG("get manifest : curr version %" PRIu64 ", new version %" PRIu64 " ", curr_fw_version, fota_ctx->fw_info->version); + FOTA_TRACE_DEBUG("Handle manifest: component %s, curr version %" PRIu64 ", new version %" PRIu64 "", + fota_ctx->fw_info->component_name, curr_fw_version, fota_ctx->fw_info->version); if (fota_ctx->fw_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_DELTA) { #if defined(FOTA_DISABLE_DELTA) @@ -670,54 +953,109 @@ static void handle_manifest(uint8_t *manifest_buf, size_t manifest_size, bool is memcpy(fota_ctx->fw_info->precursor_digest, curr_fw_digest, FOTA_CRYPTO_HASH_SIZE); } - fota_ctx->state = FOTA_STATE_AWAIT_DOWNLOAD_AUTHORIZATION; - - fota_source_report_state(FOTA_SOURCE_STATE_AWAITING_DOWNLOAD_APPROVAL, request_download_auth, on_state_set_failure); + if ((is_resume == false) || (fota_resume_download_after_user_auth == true)) { //ask for authorization + fota_ctx->state = FOTA_STATE_AWAIT_DOWNLOAD_AUTHORIZATION; + FOTA_TRACE_DEBUG("Ask for user authorization"); + fota_source_report_state(report_state, request_download_auth, on_state_set_failure); + } else { // resume without asking authorization + //skip asking authorization from user since he has already provided one + FOTA_TRACE_DEBUG("Resuming download..."); + fota_source_report_state(report_state, fota_on_download_authorize, on_state_set_failure); + } - return; + fota_resume_download_after_user_auth = false; + return FOTA_STATUS_SUCCESS; fail: if (manifest_save_ret == FOTA_STATUS_SUCCESS) { manifest_delete(); } // Reset buffer received from network and failed authorization/verification - memset(manifest_buf, 0, manifest_size); + clear_buffer_from_mem(manifest_buf, manifest_size); abort_update(ret, "on manifest event failed"); + return ret; } void fota_on_manifest(uint8_t *data, size_t size) { - handle_manifest(data, size, /*is_resume*/ false); +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_NODE_MODE) +#if MBED_CLOUD_CLIENT_FOTA_EXTERNAL_DOWNLOADER + // External downloader means that every received manifest is treated as a multicast one + fota_multicast_node_on_manifest(data, size, ext_downloader_manifest_post_action_cb); + return; +#endif + if (fota_ctx && fota_ctx->mc_node_update) { + if (fota_ctx->mc_node_update_activated) { + FOTA_TRACE_DEBUG("Received manifest when multicast update is activated - ignored"); + return; + } + // Not activated - unicast update should take precedence over multicast one. Abort multicast one. + abort_update(FOTA_STATUS_MULTICAST_UPDATE_ABORTED_INTERNAL, "Overridden by unicast manifest"); + } +#endif + + // this should never happen as lwm2m on_manifest callback should verify that fota is idle + FOTA_ASSERT(!fota_ctx); + + handle_manifest(data, size, /*is_resume*/ false, false); } void fota_on_reject(int32_t status) { - FOTA_ASSERT(fota_ctx); + FOTA_ASSERT(initialized == true); FOTA_TRACE_ERROR("Application rejected update - reason %" PRId32, status); - if (fota_ctx->state == FOTA_STATE_AWAIT_DOWNLOAD_AUTHORIZATION) { + if (!fota_ctx) { + // We just need to know whether manifest is present, so no need to allocate a full size manifest + size_t manifest_size = 0; + uint8_t dummy; + + if (manifest_get((uint8_t *)&dummy, sizeof(dummy), &manifest_size) != FOTA_STATUS_NOT_FOUND) { + // these steps should be performed even if fota context does not exits, but manifest exists. + // one possible scenario is if defer was called before reject, then the context is released (but manifest still exists). + // when fota reject called, manifest should be removed and fota flow terminated. + + if (fota_persistent_ctx.state == FOTA_STATE_AWAIT_DOWNLOAD_AUTHORIZATION) { + do_abort_update(FOTA_STATUS_DOWNLOAD_AUTH_NOT_GRANTED, "Download Authorization not granted"); + } else { + do_abort_update(FOTA_STATUS_INSTALL_AUTH_NOT_GRANTED, "Install Authorization not granted"); + } + } + return; + } else if (fota_ctx->state == FOTA_STATE_AWAIT_DOWNLOAD_AUTHORIZATION) { abort_update(FOTA_STATUS_DOWNLOAD_AUTH_NOT_GRANTED, "Download Authorization not granted"); - } else { + } else { //FOTA_STATE_AWAIT_INSTALL_AUTHORIZATION abort_update(FOTA_STATUS_INSTALL_AUTH_NOT_GRANTED, "Install Authorization not granted"); } } -void fota_on_defer(int32_t status) +void fota_on_defer(int32_t param) { - (void)status; + FOTA_ASSERT(initialized == true); if (!fota_ctx) { return; // gracefully ignore this call if update is not running } + if (fota_ctx->state == FOTA_STATE_INSTALLING) { + return; //don't allow defer/postpone during install + } + + /* mark call to defer only if FOTA is active */ + fota_defer_by_user = true; // for now we assume that defer called always by user app + if (fota_ctx->state == FOTA_STATE_AWAIT_INSTALL_AUTHORIZATION) { - FOTA_TRACE_INFO("Installation deferred by application."); - fota_on_install_authorize(true); + fota_on_install_authorize((fota_install_state_e) param); return; } +#if (MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT == FOTA_RESUME_UNSUPPORTED) + FOTA_TRACE_ERROR("Got update defer - resume not supported"); + abort_update(FOTA_STATUS_INTERNAL_ERROR, "Update aborted due to defer request"); +#else abort_update(FOTA_STATUS_UPDATE_DEFERRED, "Update deferred by application"); +#endif } static void on_reboot(void) @@ -727,6 +1065,13 @@ static void on_reboot(void) const fota_component_desc_t *comp_desc; fota_component_get_desc(fota_ctx->comp_id, &comp_desc); +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_NODE_MODE) + if (fota_ctx->mc_node_update) { + FOTA_DBG_ASSERT(fota_ctx->mc_node_post_action_callback); + fota_ctx->mc_node_post_action_callback(FOTA_STATUS_SUCCESS); + } +#endif + // Reason this is here is that platform hook may cut communication with service, // so due to reliable report policy, this hook may not be reached. fota_platform_finish_update_hook(comp_desc->name); @@ -736,16 +1081,15 @@ static void on_reboot(void) REBOOT_NOW(); } -#if FOTA_HEADER_HAS_CANDIDATE_READY static int write_candidate_ready(const char *comp_name) { +#if FOTA_HEADER_HAS_CANDIDATE_READY int ret; - uint8_t *header_buf = malloc(fota_ctx->candidate_header_size); + uint8_t *header_buf = calloc(1, fota_ctx->candidate_header_size); if (!header_buf) { FOTA_TRACE_ERROR("FOTA header_buf - allocation failed"); return FOTA_STATUS_OUT_OF_MEMORY; } - memset(header_buf, 0, fota_ctx->candidate_header_size); fota_candidate_ready_header_t *header = (fota_candidate_ready_header_t *) header_buf; #if MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT == FOTA_RESUME_SUPPORT_RESUME @@ -777,22 +1121,20 @@ static int write_candidate_ready(const char *comp_name) free(header_buf); return ret; -} +#else // FOTA_HEADER_HAS_CANDIDATE_READY + return FOTA_STATUS_SUCCESS; #endif +} static void install_component() { unsigned int comp_id = fota_ctx->comp_id; const fota_component_desc_t *comp_desc; + int ret = FOTA_STATUS_SUCCESS; + (void) ret; - manifest_delete(); + fota_ctx->state = FOTA_STATE_INSTALLING; - // reading the version before free fota_ctx - // free fota_ctx before installation, saving RAM -#if (FOTA_NUM_COMPONENTS > 1) || defined(TARGET_LIKE_LINUX) - fota_component_version_t new_ver; - new_ver = fota_ctx->fw_info->version; -#endif #if defined(__MBED__) // At this point we don't need our fota context buffers any more, for mbed @@ -801,86 +1143,165 @@ static void install_component() #endif fota_component_get_desc(comp_id, &comp_desc); - FOTA_TRACE_INFO("Installing new version for component %s", comp_desc->name); // Code saving - only relevant if we have additional components other than the main one -#if (FOTA_NUM_COMPONENTS > 1) || defined(TARGET_LIKE_LINUX) +#if FOTA_COMPONENT_SUPPORT // Installer and successful finish actions apply to all components but the main one + bool do_install; + fota_candidate_iterate_handler_t iterate_handler; + size_t install_alignment; + #if defined(TARGET_LIKE_LINUX) - { - // always execute the next code block + // Linux platform: Always execute our own iterate handler, even for main component + do_install = true; + iterate_handler = fota_linux_candidate_iterate; + install_alignment = 1; #else - if (comp_id != MAIN_COMP_NUM) { + // Embedded platform: Bootloader will run the installation on main component, rest are done here + do_install = (comp_id == FOTA_COMPONENT_MAIN_COMP_NUM) ? false : true; + iterate_handler = comp_desc->desc_info.candidate_iterate_cb; + install_alignment = comp_desc->desc_info.install_alignment; #endif - // Run the installer using the candidate iterate service - int ret = fota_candidate_iterate_image(true, (bool) MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT, - comp_desc->name, comp_desc->desc_info.install_alignment, -#if defined(TARGET_LIKE_LINUX) - fota_linux_candidate_iterate -#else - comp_desc->desc_info.candidate_iterate_cb -#endif - ); + + if (do_install) { + FOTA_TRACE_INFO("Installing new version for component %s", comp_desc->name); + + // Run the installer using the candidate iterate service + ret = fota_candidate_iterate_image(true, (bool) MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT, + comp_desc->name, install_alignment, + iterate_handler); if (ret) { abort_update(ret, "Failed on component update"); return; } #if defined(TARGET_LIKE_LINUX) - ret = fota_app_on_install_candidate(MBED_CLOUD_CLIENT_FOTA_LINUX_CANDIDATE_FILENAME, fota_ctx->fw_info); - if (ret) { - FOTA_TRACE_ERROR("Application candidate install callback for %s failed %d", comp_desc->name, ret); - abort_update(FOTA_STATUS_FW_INSTALLATION_FAILED, "Failed on component install"); - return; + if (!fota_component_is_internal_component(comp_id)) { + ret = fota_app_on_install_candidate(fota_linux_get_candidate_file_name(), fota_ctx->fw_info); + if (ret) { + FOTA_TRACE_ERROR("Application candidate install callback for %s failed %d", comp_desc->name, ret); + abort_update(FOTA_STATUS_FW_INSTALLATION_FAILED, "Failed on component install"); + return; + } } #endif if (!comp_desc->desc_info.need_reboot) { - ret = fota_install_verify(comp_desc, comp_id, new_ver); + size_t bd_read_size, bd_prog_size, offest = fota_ctx->fw_header_offset; + fota_header_info_t header; + ret = fota_bd_get_read_size(&bd_read_size); + if (ret) { + goto fail; + } + ret = fota_bd_get_program_size(&bd_prog_size); + if (ret) { + goto fail; + } + ret = fota_candidate_read_header(&offest, bd_read_size, bd_prog_size, &header); + if (ret) { + goto fail; + } + + ret = comp_install_verify(comp_desc, comp_id, &header); +fail: fota_nvm_fw_encryption_key_delete(); - fota_app_on_complete(ret); //notify application on after install, no reset + handle_fota_app_on_complete(ret); //notify application on after install, no reset } } -#endif // (FOTA_NUM_COMPONENTS > 1) || defined(TARGET_LIKE_LINUX) +#endif // FOTA_COMPONENT_SUPPORT - if (comp_desc->desc_info.need_reboot) { + // remove manifest after candidate installation finished and before potential reboot. + // MAIN component for mbed-os will be installed by the bootloader + manifest_delete(); + + if ((comp_desc->desc_info.need_reboot) && (fota_install_state == FOTA_INSTALL_STATE_AUTHORIZE)) { + fota_ctx->state = FOTA_STATE_IDLE; fota_source_report_state(FOTA_SOURCE_STATE_REBOOTING, on_reboot, on_reboot); return; } - fota_platform_finish_update_hook(comp_desc->name); - fota_source_report_update_result(FOTA_STATUS_FW_UPDATE_OK); - fota_source_report_state(FOTA_SOURCE_STATE_IDLE, NULL, NULL); + if (fota_install_state == FOTA_INSTALL_STATE_AUTHORIZE) { + +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_NODE_MODE) + if (fota_ctx->mc_node_update) { + FOTA_DBG_ASSERT(fota_ctx->mc_node_post_action_callback); + fota_ctx->mc_node_post_action_callback(ret); + } +#endif + fota_platform_finish_update_hook(comp_desc->name); + fota_source_report_update_result(FOTA_STATUS_FW_UPDATE_OK); + fota_source_report_state(FOTA_SOURCE_STATE_IDLE, NULL, NULL); + +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_BR_MODE) + if (fota_ctx->mc_br_update) { + // don't erase candidate image in case of node update by border router + erase_candidate_image = false; + } +#endif + + if (erase_candidate_image == true) + { + fota_candidate_erase(); + } + } + update_cleanup(); } -static int prepare_and_program_header() +static int prepare_and_program_header(void) { int ret; fota_header_info_t header_info = { 0 }; size_t header_buf_actual_size = 0; - uint8_t *header_buf = (uint8_t *) malloc(fota_ctx->fw_header_bd_size); + uint8_t *header_buf = (uint8_t *) calloc(1, fota_ctx->fw_header_bd_size); if (!header_buf) { ret = FOTA_STATUS_OUT_OF_MEMORY; FOTA_TRACE_ERROR("FOTA scratch buffer - allocation failed"); goto fail; } - memset(&header_info, 0, sizeof(header_info)); fota_set_header_info_magic(&header_info); header_info.fw_size = fota_ctx->fw_info->installed_size; header_info.version = fota_ctx->fw_info->version; + header_info.external_header_size = (uint16_t)(sizeof(fota_header_info_t) - offsetof(fota_header_info_t, internal_header_barrier)); memcpy(header_info.digest, fota_ctx->fw_info->installed_digest, FOTA_CRYPTO_HASH_SIZE); memcpy(header_info.precursor, fota_ctx->fw_info->precursor_digest, FOTA_CRYPTO_HASH_SIZE); + memcpy(header_info.vendor_data, fota_ctx->fw_info->vendor_data, FOTA_MANIFEST_VENDOR_DATA_SIZE); #if defined(MBED_CLOUD_CLIENT_FOTA_SIGNED_IMAGE_SUPPORT) memcpy(header_info.signature, fota_ctx->fw_info->installed_signature, FOTA_IMAGE_RAW_SIGNATURE_SIZE); #endif // defined(MBED_CLOUD_CLIENT_FOTA_SIGNED_IMAGE_SUPPORT) - header_info.block_size = MBED_CLOUD_CLIENT_FOTA_CANDIDATE_BLOCK_SIZE; +#if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) && \ + (MBED_CLOUD_CLIENT_FOTA_CANDIDATE_BLOCK_SIZE != FOTA_CLOUD_ENCRYPTION_BLOCK_SIZE) + if (fota_ctx->fw_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_RAW) { + header_info.block_size = FOTA_CLOUD_ENCRYPTION_BLOCK_SIZE; + } else +#endif + { + header_info.block_size = MBED_CLOUD_CLIENT_FOTA_CANDIDATE_BLOCK_SIZE; + } #if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) header_info.flags |= FOTA_HEADER_ENCRYPTED_FLAG; +#if (MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION == FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY) + // encrypt fw_key buffer using device key and store it in the header + uint8_t fw_key[FOTA_ENCRYPT_KEY_SIZE]; + ret = fota_nvm_fw_encryption_key_get(fw_key); + if (ret) { + FOTA_TRACE_DEBUG("Encryption key not found"); + goto fail; + } + ret = fota_encrypt_fw_key(fw_key, + header_info.encrypted_fw_key, + header_info.encrypted_fw_key_tag, + &header_info.encrypted_fw_key_iv); + fota_fi_memset(fw_key, 0, sizeof(fw_key)); + if (ret) { + FOTA_TRACE_ERROR("Failed to start encryption engine. ret %d", ret); + goto fail; + } +#endif // FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY #endif #if MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT == FOTA_RESUME_SUPPORT_RESUME @@ -928,7 +1349,7 @@ static int check_if_blank(size_t addr, size_t size, uint8_t erase_val, size_t *b return ret; } -static int analyze_resume_state(fota_state_e *next_fota_state, uint32_t storage_available) +static int analyze_resume_state(fota_state_e *next_fota_state) { int ret = FOTA_STATUS_SUCCESS; int int_erase_val = 0; @@ -940,6 +1361,8 @@ static int analyze_resume_state(fota_state_e *next_fota_state, uint32_t storage_ #if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT != 1) fota_candidate_block_checksum_t checksum = 0; +#else + fota_hash_context_t *payload_temp_hash_ctx = NULL; #endif if (fota_ctx->resume_state == FOTA_RESUME_STATE_INACTIVE) { @@ -1011,6 +1434,15 @@ static int analyze_resume_state(fota_state_e *next_fota_state, uint32_t storage_ goto no_resume; } +#if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) + if (fota_ctx->fw_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_RAW) { + ret = fota_hash_start(&payload_temp_hash_ctx); + if (ret) { + goto no_resume; + } + } +#endif + num_blocks_available = storage_available / fota_ctx->page_buf_size; num_blocks_left = FOTA_ALIGN_UP(fota_ctx->fw_info->payload_size, fota_ctx->effective_page_buf_size) / fota_ctx->effective_page_buf_size; @@ -1038,8 +1470,22 @@ static int analyze_resume_state(fota_state_e *next_fota_state, uint32_t storage_ } #if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) + size_t data_offset = 0; + if (fota_ctx->fw_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_RAW) { + // update data offset to skip the tag + data_offset = FOTA_ENCRYPT_TAG_SIZE; + // on an encrypted payload, update payload_temp_hash_ctx before decrypting + // and copy it to payload_hash_ctx only if decrypt succeeds. + ret = fota_hash_update(payload_temp_hash_ctx, fota_ctx->effective_page_buf, chunk); + if (ret) { + goto no_resume; + } + } // decrypt data with tag (at the beginning of page_buf) - ret = fota_decrypt_data(fota_ctx->enc_ctx, fota_ctx->effective_page_buf, chunk, fota_ctx->effective_page_buf, + ret = fota_decrypt_data(fota_ctx->enc_ctx, + fota_ctx->effective_page_buf + data_offset, + chunk - data_offset, + fota_ctx->effective_page_buf + data_offset, fota_ctx->page_buf); if (ret) { // Decryption failure - Skip the block @@ -1059,11 +1505,22 @@ static int analyze_resume_state(fota_state_e *next_fota_state, uint32_t storage_ } #endif - // Block verified as OK - update num blocks left, hash and IV (if encrypted) - ret = fota_hash_update(fota_ctx->curr_fw_hash_ctx, fota_ctx->effective_page_buf, chunk); - if (ret) { - goto no_resume; +#if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) + if (fota_ctx->fw_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_RAW) { + // on encrypted payload, copy payload_temp_hash_ctx to payload_hash_ctx + fota_hash_clone(fota_ctx->payload_hash_ctx, payload_temp_hash_ctx); } + else +#endif + { + // update payload_hash_ctx after decryption + ret = fota_hash_update(fota_ctx->payload_hash_ctx, fota_ctx->effective_page_buf, chunk); + if (ret) { + goto no_resume; + } + } + + // Block verified as OK - update num blocks left num_blocks_left--; fota_ctx->payload_offset += chunk; fota_ctx->fw_bytes_written += chunk; @@ -1073,6 +1530,13 @@ static int analyze_resume_state(fota_state_e *next_fota_state, uint32_t storage_ fota_ctx->storage_addr += fota_ctx->page_buf_size; } +#if !defined(FOTA_DISABLE_DELTA) + // for a delta patch, copy payload_hash_ctx to installed_hash_ctx + if (fota_ctx->fw_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_DELTA) { + fota_hash_clone(fota_ctx->installed_hash_ctx, fota_ctx->payload_hash_ctx); + } +#endif + // Got here means that the whole firmware has been written, but candidate ready header is blank. // This means we can converge to the regular install authorization flow. *next_fota_state = FOTA_STATE_AWAIT_INSTALL_AUTHORIZATION; @@ -1086,8 +1550,17 @@ static int analyze_resume_state(fota_state_e *next_fota_state, uint32_t storage_ fota_ctx->storage_addr = save_storage_addr; fota_ctx->fw_bytes_written = 0; fota_ctx->payload_offset = 0; - fota_hash_finish(&fota_ctx->curr_fw_hash_ctx); - fota_hash_start(&fota_ctx->curr_fw_hash_ctx); + // reset payload_hash_ctx + fota_hash_finish(&fota_ctx->payload_hash_ctx); + fota_hash_start(&fota_ctx->payload_hash_ctx); +#if !defined(FOTA_DISABLE_DELTA) + if (fota_ctx->fw_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_DELTA) { + // reset installed_hash_ctx + fota_hash_finish(&fota_ctx->installed_hash_ctx); + fota_hash_start(&fota_ctx->installed_hash_ctx); + } +#endif + #if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) fota_encryption_stream_reset(fota_ctx->enc_ctx); #endif @@ -1095,20 +1568,89 @@ static int analyze_resume_state(fota_state_e *next_fota_state, uint32_t storage_ finish: free(fota_ctx->page_buf); fota_ctx->page_buf = NULL; +#if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) + fota_hash_finish(&payload_temp_hash_ctx); +#endif return ret; } #endif // MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT == FOTA_RESUME_SUPPORT_RESUME +static int calc_and_erase_needed_storage() +{ + int ret; + size_t storage_needed = 0, erase_size, total_erase_size, end_addr; + + if (fota_ctx) { + // Calculate needed space for FW data in storage: + // This will align the non-encrypted image up to page buf size and recalculate the storage space + // needed for interleaved data and tags in the encrypted case. + storage_needed = fota_ctx->storage_addr - fota_candidate_get_config()->storage_start_addr + + FOTA_ALIGN_UP(fota_ctx->fw_info->installed_size, fota_ctx->effective_page_buf_size) / + fota_ctx->effective_page_buf_size * fota_ctx->page_buf_size; + +#if MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT == FOTA_RESUME_SUPPORT_RESUME + // In case we support resume, erase twice as much as we need (capped by entire available storage), + // covering bad blocks on the way (should be more than enough). + storage_needed = MIN(2 * storage_needed, storage_available); +#endif + } + +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_NODE_MODE) + if (!fota_ctx) { + // Got here, this means we need to use candidate storage for non FOTA image. + // Just take it from start, for as much size as Multicast module requires. + mc_node_new_image = false; + mc_image_data_addr = fota_candidate_get_config()->storage_start_addr; + storage_needed = mc_node_image_size; + } else if (fota_ctx->mc_node_update) { + // Multicast FOTA case, need to tweak our needs + mc_node_new_image = false; + mc_node_image_size = fota_ctx->fw_info->payload_size; + if (fota_ctx->fw_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_DELTA) { + // Delta case - need to add space for delta image right after candidate image. + // Multicast reading should be on the delta image. + mc_image_data_addr = fota_candidate_get_config()->storage_start_addr + storage_needed; + storage_needed += mc_node_image_size; + } else { + // Full image case - keep needed storage as is. + // Multicast read should be on the data right after the headers (current storage address). + mc_image_data_addr = fota_ctx->storage_addr; + } + } +#endif + + if (storage_needed > storage_available) { + FOTA_TRACE_ERROR("Insufficient storage for image"); + return FOTA_STATUS_INSUFFICIENT_STORAGE; + } + + end_addr = fota_candidate_get_config()->storage_start_addr + storage_needed; + ret = fota_bd_get_erase_size(end_addr - 1, &erase_size); + if (ret) { + FOTA_TRACE_ERROR("Get erase size failed %d", ret); + return ret; + } + + // Align erase size to the end of last sector + total_erase_size = end_addr % erase_size ? FOTA_ALIGN_DOWN(end_addr, erase_size) + erase_size - fota_candidate_get_config()->storage_start_addr : + storage_needed; + FOTA_TRACE_DEBUG("Erasing storage at %zu, size %zu", fota_candidate_get_config()->storage_start_addr, total_erase_size); + ret = fota_bd_erase(fota_candidate_get_config()->storage_start_addr, total_erase_size); + if (ret) { + FOTA_TRACE_ERROR("Erase storage failed %d", ret); + } + + return ret; +} + static void fota_on_download_authorize() { int ret; size_t prog_size; - size_t storage_needed, storage_available; - size_t storage_start_addr, storage_end_addr; - size_t erase_size; const fota_component_desc_t *comp_desc; #if MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT == FOTA_RESUME_SUPPORT_RESUME + int erase_val; fota_state_e next_fota_state = FOTA_STATE_DOWNLOADING; #endif @@ -1127,6 +1669,14 @@ static void fota_on_download_authorize() } FOTA_TRACE_DEBUG("FOTA BlockDevice initialized"); +#if (MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT == FOTA_RESUME_SUPPORT_RESUME) + ret = fota_bd_get_erase_value(&erase_val); + if (ret || (erase_val < 0)) { + FOTA_TRACE_ERROR("Full resume not supported for devices that have no erase"); + FOTA_ASSERT(0); + } +#endif + ret = fota_bd_get_program_size(&prog_size); if (ret) { FOTA_TRACE_ERROR("Get program size failed. ret %d", ret); @@ -1139,9 +1689,17 @@ static void fota_on_download_authorize() goto fail; } - fota_ctx->page_buf_size = FOTA_ALIGN_UP(MBED_CLOUD_CLIENT_FOTA_CANDIDATE_BLOCK_SIZE, prog_size); +#if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) && \ + (MBED_CLOUD_CLIENT_FOTA_CANDIDATE_BLOCK_SIZE != FOTA_CLOUD_ENCRYPTION_BLOCK_SIZE) + if (fota_ctx->fw_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_RAW) { + fota_ctx->page_buf_size = FOTA_ALIGN_UP(FOTA_CLOUD_ENCRYPTION_BLOCK_SIZE, prog_size); + } else +#endif + { + fota_ctx->page_buf_size = FOTA_ALIGN_UP(MBED_CLOUD_CLIENT_FOTA_CANDIDATE_BLOCK_SIZE, prog_size); + } - ret = init_encryption(); + ret = init_encryption(fota_ctx->fw_info); if (ret) { goto fail; } @@ -1149,46 +1707,38 @@ static void fota_on_download_authorize() fota_ctx->effective_page_buf_size = fota_ctx->page_buf_size; #if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) - fota_ctx->effective_page_buf_size -= FOTA_ENCRYPT_TAG_SIZE; + if (fota_ctx->fw_info->payload_format != FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_RAW) { + // on encrypted payload, skip reducing tag size + fota_ctx->effective_page_buf_size -= FOTA_ENCRYPT_TAG_SIZE; + } #elif (MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT == FOTA_RESUME_SUPPORT_RESUME) // Reduce checksum size fota_ctx->effective_page_buf_size -= sizeof(fota_candidate_block_checksum_t); #endif - ret = fota_hash_start(&fota_ctx->curr_fw_hash_ctx); + ret = fota_hash_start(&fota_ctx->payload_hash_ctx); if (ret) { goto fail; } - storage_start_addr = fota_candidate_get_config()->storage_start_addr; - storage_end_addr = storage_start_addr + fota_candidate_get_config()->storage_size; - ret = fota_bd_get_erase_size(storage_end_addr - 1, &erase_size); - if (ret) { - FOTA_TRACE_ERROR("Get erase size failed. ret %d", ret); - goto fail; +#if !defined(FOTA_DISABLE_DELTA) + if (fota_ctx->fw_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_DELTA) { + ret = fota_hash_start(&fota_ctx->installed_hash_ctx); + if (ret) { + goto fail; + } } +#endif - // Check for storage size misconfiguration - FOTA_ASSERT(storage_end_addr == FOTA_ALIGN_UP(storage_end_addr, erase_size)); - storage_available = storage_end_addr - storage_start_addr; - - // Calculate needed space for FW data in storage: - // This will align the non-encrypted image up to page buf size and recalculate the storage space - // needed for interleaved data and tags in the encrypted case. - storage_needed = fota_ctx->storage_addr - storage_start_addr + - FOTA_ALIGN_UP(fota_ctx->fw_info->installed_size, fota_ctx->effective_page_buf_size) / - fota_ctx->effective_page_buf_size * fota_ctx->page_buf_size; + fota_ctx->fw_header_offset = fota_ctx->storage_addr - fota_ctx->fw_header_bd_size; - if (storage_needed > storage_available) { - FOTA_TRACE_ERROR("Insufficient storage for firmware"); - ret = FOTA_STATUS_INSUFFICIENT_STORAGE; + ret = calc_available_storage(); + if (ret) { goto fail; } - fota_ctx->fw_header_offset = fota_ctx->storage_addr - fota_ctx->fw_header_bd_size; - #if MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT == FOTA_RESUME_SUPPORT_RESUME - ret = analyze_resume_state(&next_fota_state, storage_available); + ret = analyze_resume_state(&next_fota_state); if (!ret && next_fota_state == FOTA_STATE_AWAIT_INSTALL_AUTHORIZATION) { finalize_update(); return; @@ -1199,35 +1749,20 @@ static void fota_on_download_authorize() // Erase storage (if we're resuming, this has already been done) if (fota_ctx->resume_state == FOTA_RESUME_STATE_INACTIVE) { - size_t total_erase_size; - // In case we support resume, erase all available storage, covering bad blocks on the way. - // Otherwise, just erase needed storage. -#if MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT == FOTA_RESUME_SUPPORT_RESUME - total_erase_size = storage_available; -#else - ret = fota_bd_get_erase_size(storage_start_addr + storage_needed - 1, &erase_size); - if (ret) { - FOTA_TRACE_ERROR("Get erase size failed %d", ret); - goto fail; - } - total_erase_size = FOTA_ALIGN_UP(storage_needed, erase_size); -#endif - FOTA_TRACE_DEBUG("Erasing storage at %zu, size %zu", storage_start_addr, total_erase_size); - ret = fota_bd_erase(storage_start_addr, total_erase_size); + ret = calc_and_erase_needed_storage(); if (ret) { - FOTA_TRACE_ERROR("Erase storage failed %d", ret); goto fail; } - // In non legacy headers we can and should program the FW header already here, as the candidate ready header - // will be programmed at install phase, telling that the candidate is ready. -#if FOTA_HEADER_HAS_CANDIDATE_READY - ret = prepare_and_program_header(); - if (ret) { - goto fail; + // In non legacy headers we can and should program the FW header already here, to support full resume (as resume needs info from header). + // This is OK, as the candidate ready header will be programmed at install phase. + if (fota_ctx->candidate_header_size) { + ret = prepare_and_program_header(); + if (ret) { + goto fail; + } } -#endif } // At this point, we have converged to regular state, even if we were resuming @@ -1259,6 +1794,21 @@ static void fota_on_download_authorize() } #endif // defined(FOTA_DISABLE_DELTA) +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_NODE_MODE) + if (fota_ctx->mc_node_update) { +#if MBED_CLOUD_CLIENT_FOTA_EXTERNAL_DOWNLOADER + // External downloader mode - supply fragment size ourselves + // (use smallest possible size to allow maximal flexibility) + fota_multicast_node_set_fragment_size(prog_size); +#endif + // Notify Multicast module of manifest stage finish + FOTA_DBG_ASSERT(fota_ctx->mc_node_post_action_callback); + fota_ctx->mc_node_post_action_callback(FOTA_STATUS_SUCCESS); + fota_ctx->mc_node_post_action_callback = NULL; + return; + } +#endif + fota_ctx->state = FOTA_STATE_DOWNLOADING; fota_source_report_state(FOTA_SOURCE_STATE_DOWNLOADING, NULL, NULL); @@ -1281,49 +1831,42 @@ static void fota_on_download_authorize() abort_update(ret, "Failed on download authorization event"); } -static void fota_on_install_authorize(bool defer) +static void fota_on_install_authorize(fota_install_state_e fota_install_type) { int ret; const fota_component_desc_t *comp_desc; + fota_install_state = fota_install_type; + fota_component_get_desc(fota_ctx->comp_id, &comp_desc); free(fota_ctx->page_buf); fota_ctx->page_buf = NULL; -#if FOTA_HEADER_HAS_CANDIDATE_READY - ret = write_candidate_ready(comp_desc->name); - if (ret) { - FOTA_TRACE_ERROR("FOTA write_candidate_ready - failed %d", ret); - goto fail; - } -#else - ret = prepare_and_program_header(); - if (ret) { - FOTA_TRACE_ERROR("prepare_and_program_header - failed %d", ret); - goto fail; + if (fota_install_state != FOTA_INSTALL_STATE_DEFER) { + if (fota_ctx->candidate_header_size) { + ret = write_candidate_ready(comp_desc->name); + } else { + ret = prepare_and_program_header(); + } + if (ret) { + FOTA_TRACE_ERROR("FOTA write final header - failed %d", ret); + goto fail; + } } -#endif - // Install defer means that we skip the installation for now - if (defer) { - if (fota_ctx->comp_id == MAIN_COMP_NUM) { - // Main component is a special case - bootloader will install the FW upon next reset, - // so no need to keep the manifest. - manifest_delete(); - } else { - // All other components will use the resume flow for that, so manifest should be kept. -#if MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT != FOTA_RESUME_SUPPORT_RESUME - abort_update(FOTA_STATUS_INTERNAL_ERROR, + if ((fota_install_state == FOTA_INSTALL_STATE_AUTHORIZE) || (fota_install_state == FOTA_INSTALL_STATE_POSTPONE_REBOOT)) { + fota_source_report_state(FOTA_SOURCE_STATE_UPDATING, install_component, on_state_set_failure); + } else { //FOTA_INSTALL_STATE_DEFER - we skip the installation for now +#if MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT == FOTA_RESUME_SUPPORT_RESUME + FOTA_TRACE_INFO("FOTA install deferred until further user instruction"); +#else + abort_update(FOTA_STATUS_INTERNAL_ERROR, "Component install defer requires resume support"); - return; #endif - } update_cleanup(); - return; } - fota_source_report_state(FOTA_SOURCE_STATE_UPDATING, install_component, on_state_set_failure); return; fail: @@ -1331,20 +1874,19 @@ static void fota_on_install_authorize(bool defer) abort_update(ret, "Failed on install authorization event"); } -void fota_on_authorize(int32_t status) +void fota_on_authorize(int32_t param) { - (void)status; //unused warning - FOTA_ASSERT(fota_ctx); FOTA_ASSERT( (fota_ctx->state == FOTA_STATE_AWAIT_DOWNLOAD_AUTHORIZATION) || (fota_ctx->state == FOTA_STATE_AWAIT_INSTALL_AUTHORIZATION) - ) + ); if (fota_ctx->state == FOTA_STATE_AWAIT_INSTALL_AUTHORIZATION) { + FOTA_ASSERT(param == FOTA_INSTALL_STATE_AUTHORIZE); FOTA_TRACE_INFO("Install authorization granted."); - fota_on_install_authorize(false); + fota_on_install_authorize((fota_install_state_e)param); return; } @@ -1359,6 +1901,16 @@ static int program_to_storage(uint8_t *buf, size_t addr, uint32_t size) uint8_t *src_buf = buf; uint8_t *prog_buf = buf; int ret; + bool do_program = true; + +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_NODE_MODE) + // In case of a full multicast node update, image was already placed there by Multicast module. + // Just skip programming (but keep all other calculations). + if (fota_ctx && fota_ctx->mc_node_update && + (fota_ctx->fw_info->payload_format != FOTA_MANIFEST_PAYLOAD_FORMAT_DELTA)) { + do_program = false; + } +#endif if (fota_ctx->effective_page_buf_size < fota_ctx->page_buf_size) { data_size = MIN(fota_ctx->effective_page_buf_size, size); @@ -1374,11 +1926,14 @@ static int program_to_storage(uint8_t *buf, size_t addr, uint32_t size) do { #if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) - uint8_t *tag = fota_ctx->page_buf; - ret = fota_encrypt_data(fota_ctx->enc_ctx, src_buf, data_size, src_buf, tag); - if (ret) { - FOTA_TRACE_ERROR("encryption failed %d", ret); - return ret; + if (fota_ctx->fw_info->payload_format != FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_RAW) { + // on encrypted payload, data already encrypted + uint8_t *tag = fota_ctx->page_buf; + ret = fota_encrypt_data(fota_ctx->enc_ctx, src_buf, data_size, src_buf, tag); + if (ret) { + FOTA_TRACE_ERROR("encryption failed %d", ret); + return ret; + } } #elif MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT == FOTA_RESUME_SUPPORT_RESUME fota_candidate_block_checksum_t *checksum = (fota_candidate_block_checksum_t *) fota_ctx->page_buf; @@ -1393,11 +1948,13 @@ static int program_to_storage(uint8_t *buf, size_t addr, uint32_t size) // We are on the very last page, align up to page buffer size prog_size = FOTA_ALIGN_UP(prog_size, fota_ctx->page_buf_size); } - ret = fota_bd_program(prog_buf, addr, prog_size); - if (ret) { - FOTA_TRACE_ERROR("Write to storage failed, address 0x%zx, size %" PRIu32 " %d", - addr, size, ret); - return ret; + if (do_program) { + ret = fota_bd_program(prog_buf, addr, prog_size); + if (ret) { + FOTA_TRACE_ERROR("Write to storage failed, address 0x%zx, size %" PRIu32 " %d", + addr, size, ret); + return ret; + } } src_buf += data_size; addr += prog_size; @@ -1416,11 +1973,6 @@ static int handle_fw_fragment(uint8_t *buf, size_t size, bool last) uint32_t prog_size; uint32_t chunk; - int ret = fota_hash_update(fota_ctx->curr_fw_hash_ctx, buf, size); - if (ret) { - return ret; - } - while (size) { // Two cases here: // 1. The "hard" one - If our fragment is not aligned to a whole page: @@ -1442,7 +1994,7 @@ static int handle_fw_fragment(uint8_t *buf, size_t size, bool last) source_buf += chunk; if ((prog_size >= fota_ctx->effective_page_buf_size) || last) { - ret = program_to_storage(prog_buf, + int ret = program_to_storage(prog_buf, fota_ctx->storage_addr, prog_size); if (ret) { @@ -1458,7 +2010,7 @@ static int handle_fw_fragment(uint8_t *buf, size_t size, bool last) static void on_approve_state_delivered(void) { FOTA_TRACE_DEBUG("Install Authorization requested"); - int ret = fota_app_on_install_authorization(0); + int ret = fota_app_on_install_authorization(); if (ret) { abort_update(ret, "Failed to deliver install authorization"); } @@ -1467,7 +2019,17 @@ static void on_approve_state_delivered(void) static int finalize_update(void) { int ret; - uint8_t curr_fw_hash_buf[FOTA_CRYPTO_HASH_SIZE]; + uint8_t calced_hash_buf[FOTA_CRYPTO_HASH_SIZE]; + fota_hash_context_t *calced_hash_ctx = fota_ctx->payload_hash_ctx; + uint8_t *expected_digest = fota_ctx->fw_info->payload_digest; + +#if !defined(FOTA_DISABLE_DELTA) + // on delta, digest is calced on the install/unpatch data + if (fota_ctx->fw_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_DELTA) { + calced_hash_ctx = fota_ctx->installed_hash_ctx; + expected_digest = fota_ctx->fw_info->installed_digest; + } +#endif // Ongoing resume state here means that all authentication has been done before. // Can jump straight to finish. @@ -1475,22 +2037,32 @@ static int finalize_update(void) goto finished; } - ret = fota_hash_result(fota_ctx->curr_fw_hash_ctx, curr_fw_hash_buf); + ret = fota_hash_result(calced_hash_ctx, calced_hash_buf); if (ret) { return ret; } + #if defined(MBED_CLOUD_CLIENT_FOTA_SIGNED_IMAGE_SUPPORT) - ret = fota_verify_signature_prehashed( - curr_fw_hash_buf, - fota_ctx->fw_info->installed_signature, FOTA_IMAGE_RAW_SIGNATURE_SIZE - ); - FOTA_FI_SAFE_COND( - (ret == FOTA_STATUS_SUCCESS), - FOTA_STATUS_MANIFEST_PAYLOAD_CORRUPTED, - "Candidate image is not authentic" - ); +#if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) + if (fota_ctx->fw_info->payload_format != FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_RAW) + // on encrypted payload, skip verifing signature as + // we can't calc the hash of the installed payload. + // It will be verified later by the bootloader. +#endif + { + ret = fota_verify_signature_prehashed( + calced_hash_buf, + fota_ctx->fw_info->installed_signature, FOTA_IMAGE_RAW_SIGNATURE_SIZE + ); + FOTA_FI_SAFE_COND( + (ret == FOTA_STATUS_SUCCESS), + FOTA_STATUS_MANIFEST_PAYLOAD_CORRUPTED, + "Candidate image is not authentic" + ); + } #else - FOTA_FI_SAFE_MEMCMP(curr_fw_hash_buf, fota_ctx->fw_info->installed_digest, FOTA_CRYPTO_HASH_SIZE, + // compare expected_digest against calced digest + FOTA_FI_SAFE_MEMCMP(calced_hash_buf, expected_digest, FOTA_CRYPTO_HASH_SIZE, FOTA_STATUS_MANIFEST_PAYLOAD_CORRUPTED, "Downloaded FW hash does not match manifest hash"); #endif @@ -1508,8 +2080,24 @@ static int finalize_update(void) FOTA_TRACE_INFO("Firmware download finished"); +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_BR_MODE) + if (fota_ctx->mc_br_update) { + // No need to authorize on BR mode, jump straight to installation + fota_on_install_authorize(FOTA_INSTALL_STATE_AUTHORIZE); + return FOTA_STATUS_SUCCESS; + } +#endif + fota_ctx->state = FOTA_STATE_AWAIT_INSTALL_AUTHORIZATION; +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_NODE_MODE) + if (fota_ctx->mc_node_update) { + // No need to report this state on node mode, as it was reported already. Jump straight to next state. + on_approve_state_delivered(); + return FOTA_STATUS_SUCCESS; + } +#endif + fota_source_report_state(FOTA_SOURCE_STATE_AWAITING_APPLICATION_APPROVAL, on_approve_state_delivered, on_state_set_failure); return FOTA_STATUS_SUCCESS; @@ -1526,6 +2114,21 @@ void fota_on_fragment_failure(int32_t status) abort_update(FOTA_STATUS_DOWNLOAD_FRAGMENT_FAILED, "Failed to fetch fragment"); } +static inline int get_next_fragment() +{ +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_NODE_MODE) + if (fota_ctx && fota_ctx->mc_node_update) { + // In Multicast node mode, we already have the image fragments in storage. + // Just call the function that reads them from there. + // Do it in a deferred event, to avoid recursions and let the system breathe. + fota_event_handler_defer_with_data(fota_multicast_node_on_fragment, NULL, 0); + return FOTA_STATUS_SUCCESS; + } +#endif + + return fota_download_request_next_fragment(fota_ctx->download_handle, fota_ctx->fw_info->uri, fota_ctx->payload_offset); +} + void fota_on_fragment(uint8_t *buf, size_t size) { int ret = 0; @@ -1546,7 +2149,10 @@ void fota_on_fragment(uint8_t *buf, size_t size) return; } - fota_app_on_download_progress(fota_ctx->payload_offset, size, fota_ctx->fw_info->payload_size); + handle_fota_app_on_download_progress(fota_ctx->payload_offset, size, fota_ctx->fw_info->payload_size); + + // update payload_hash_ctx with fragment + ret = fota_hash_update(fota_ctx->payload_hash_ctx, buf, size); if (fota_ctx->fw_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_DELTA) { #if !defined(FOTA_DISABLE_DELTA) @@ -1580,6 +2186,11 @@ void fota_on_fragment(uint8_t *buf, size_t size) } if (actual_frag_size) { last_fragment = ((fota_ctx->fw_bytes_written + fota_ctx->page_buf_offset + actual_frag_size) == fota_ctx->fw_info->installed_size); + // update installed_hash_ctx with delta_buf + ret = fota_hash_update(fota_ctx->installed_hash_ctx, fota_ctx->delta_buf, actual_frag_size); + if (ret) { + goto fail; + } ret = handle_fw_fragment(fota_ctx->delta_buf, actual_frag_size, last_fragment); if (ret) { goto fail; @@ -1593,6 +2204,9 @@ void fota_on_fragment(uint8_t *buf, size_t size) FOTA_ASSERT(0); #endif // #if !defined(FOTA_DISABLE_DELTA) } else { + if (ret) { + goto fail; + } last_fragment = ((payload_bytes_left - size) == 0); ret = handle_fw_fragment(buf, size, last_fragment); if (ret) { @@ -1603,7 +2217,7 @@ void fota_on_fragment(uint8_t *buf, size_t size) fota_ctx->payload_offset += size; - memset(buf, 0, size); + clear_buffer_from_mem(buf, size); if (!payload_bytes_left) { ret = finalize_update(); @@ -1613,7 +2227,7 @@ void fota_on_fragment(uint8_t *buf, size_t size) return; } - ret = fota_download_request_next_fragment(fota_ctx->download_handle, fota_ctx->fw_info->uri, fota_ctx->payload_offset); + ret = get_next_fragment(); if (ret) { goto fail; } @@ -1621,21 +2235,45 @@ void fota_on_fragment(uint8_t *buf, size_t size) return; fail: - memset(buf, 0, size); + clear_buffer_from_mem(buf, size); abort_update(ret, "Failed on fragment event"); } -void fota_on_resume(int32_t status) +void fota_on_resume(int32_t param) { #if (MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT != FOTA_RESUME_UNSUPPORTED) - (void)status; // unused + + bool fota_resume_by_user = !param; // param=0 - called from user app + // param=1 - called from internal flow + if (fota_ctx) { + /* + * FOTA context exists therefore defer was not called. Got here because of internal resume event, continue update + */ + FOTA_TRACE_DEBUG("FOTA already running"); return; // FOTA is already running - ignore } + if (fota_install_state == FOTA_INSTALL_STATE_POSTPONE_REBOOT) { + FOTA_TRACE_DEBUG("FOTA resume not supported after postpone"); + return; + } + + FOTA_TRACE_INFO("fota_on_resume - resume by %u", fota_resume_by_user); + + //if we got here, there is no fota context: + // either defer was called or context was deleted because of internal error + if ((fota_defer_by_user == true) && (fota_resume_by_user == false)) { + /* fota was deferred by user app and resume was called from internal flow + * ignore the resume for now and wait for call from user app + */ + FOTA_TRACE_INFO("Internal resume followed by user app defer - abort!"); + return; // don't resume now, wait for explicit user call for resume + } + size_t manifest_size; - uint8_t *manifest = malloc(FOTA_MANIFEST_MAX_SIZE); + uint8_t *manifest = calloc(1, FOTA_MANIFEST_MAX_SIZE); if (!manifest) { FOTA_TRACE_ERROR("FOTA manifest - allocation failed"); @@ -1643,12 +2281,10 @@ void fota_on_resume(int32_t status) return; } - memset(manifest, 0, FOTA_MANIFEST_MAX_SIZE); - int ret = manifest_get(manifest, FOTA_MANIFEST_MAX_SIZE, &manifest_size); if (!ret) { FOTA_TRACE_INFO("Found manifest - resuming update"); - handle_manifest(manifest, manifest_size, /*is_resume*/ true); + handle_manifest(manifest, manifest_size, /*is_resume*/ true, false); } free(manifest); @@ -1660,7 +2296,462 @@ void fota_on_resume(int32_t status) if (ret) { FOTA_TRACE_ERROR("failed to load manifest from NVM (ret code %d) - update resume aborted.", ret); } + fota_defer_by_user = false; //resume completed, remove the flag #endif } +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT != FOTA_MULTICAST_UNSUPPORTED) + +// Local read from image API - support both Multicast node & BR modes +int fota_multicast_read_from_image(void *buffer, size_t offset, size_t size) +{ + int ret; + size_t read_size, addr; + + ret = fota_bd_get_read_size(&read_size); + if (ret) { + return ret; + } + addr = mc_image_data_addr + offset; + + // Likely case - read is aligned in both start address and size (as read size is likely to be 1) + if (!(addr % read_size) && !(size % read_size)) { + return fota_bd_read(buffer, addr, size); + } + + // Unlikely case, start or end not aligned to read size + + size_t chunk; + uint8_t *buf = (uint8_t *) buffer; + uint8_t *aligned_read_buf = (uint8_t *) malloc(read_size); + if (!aligned_read_buf) { + return FOTA_STATUS_OUT_OF_MEMORY; + } + + // Handle unaligned start + if (addr % read_size) { + chunk = MIN(read_size - addr % read_size, size); + ret = fota_bd_read(aligned_read_buf, FOTA_ALIGN_DOWN(addr, read_size), read_size); + if (ret) { + FOTA_TRACE_ERROR("Unable to get read size"); + goto end; + } + memcpy(buf, aligned_read_buf + addr % read_size, chunk); + buf += chunk; + addr += chunk; + size -= chunk; + } + + // Handle aligned portion + chunk = FOTA_ALIGN_DOWN(size, read_size); + if (chunk) { + ret = fota_bd_read(buf, addr, chunk); + if (ret) { + goto end; + } + buf += chunk; + addr += chunk; + size -= chunk; + } + + // Handle unaligned end + if (size) { + ret = fota_bd_read(aligned_read_buf, addr, read_size); + if (ret) { + goto end; + } + memcpy(buf, aligned_read_buf, size); + } + +end: + free(aligned_read_buf); + return ret; +} + +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_NODE_MODE) + +static void fota_multicast_node_on_fragment(void) +{ + int ret; + FOTA_DBG_ASSERT(mc_node_frag_size); + FOTA_DBG_ASSERT(fota_ctx); + if (!fota_ctx->mc_node_frag_buf) { + fota_ctx->mc_node_frag_buf = malloc(mc_node_frag_size); + if (!fota_ctx->mc_node_frag_buf) { + ret = FOTA_STATUS_OUT_OF_MEMORY; + goto fail; + } + } + size_t read_size = MIN(mc_node_frag_size, fota_ctx->fw_info->payload_size - fota_ctx->payload_offset); + ret = fota_multicast_read_from_image(fota_ctx->mc_node_frag_buf, fota_ctx->payload_offset, read_size); + if (ret) { + FOTA_TRACE_ERROR("Unable to read from image"); + goto fail; + } + + // Handle fragment with the one we read from storage + fota_on_fragment(fota_ctx->mc_node_frag_buf, read_size); + return; + +fail: + abort_update(ret, "Failed on multicast fragment event"); +} + +static int fota_multicast_node_check_update_status(bool require_mc_update) +{ + if (fota_ctx && !fota_ctx->mc_node_update) { + FOTA_TRACE_DEBUG("FOTA multicast command ignored - Unicast update active"); + return FOTA_STATUS_RESOURCE_BUSY; + } + if (require_mc_update && !(fota_ctx && fota_ctx->mc_node_update)) { + FOTA_TRACE_ERROR("FOTA multicast command ignored - Multicast update not active"); + return FOTA_STATUS_INTERNAL_ERROR; + } + return FOTA_STATUS_SUCCESS; +} + +int fota_multicast_node_on_manifest(uint8_t *data, size_t size, + fota_multicast_node_post_action_callback_t on_manifest_cb) +{ + FOTA_ASSERT(on_manifest_cb); + FOTA_TRACE_DEBUG("Multicast manifest received"); + + uint8_t manifest_hash[FOTA_CRYPTO_HASH_SIZE] = {0}; + int ret = fota_multicast_node_check_update_status(false); + if (ret) { + return ret; + } + + fota_hash_context_t *manifest_hash_ctx; + ret = fota_hash_start(&manifest_hash_ctx); + if (ret) { + return ret; + } + ret = fota_hash_update(manifest_hash_ctx, data, size); + if (ret) { + return ret; + } + ret = fota_hash_result(manifest_hash_ctx, manifest_hash); + if (ret) { + return ret; + } + fota_hash_finish(&manifest_hash_ctx); + + if (fota_ctx) { + if (fota_ctx->mc_node_update_activated) { + FOTA_TRACE_DEBUG("Current multicast update activated, can't override it"); + return FOTA_STATUS_MULTICAST_UPDATE_ACTIVATED; + } else { + if (memcmp(manifest_hash, fota_ctx->mc_node_manifest_hash, FOTA_CRYPTO_HASH_SIZE)) { + FOTA_TRACE_DEBUG("Got a new multicast manifest, aborting previous FOTA session"); + abort_update(FOTA_STATUS_MULTICAST_UPDATE_ABORTED_INTERNAL, "Multicast manifest overridden"); + } else { + FOTA_TRACE_DEBUG("Same multicast manifest received, silently ignored"); + return FOTA_STATUS_SUCCESS; + } + } + } + ret = handle_manifest_init(); + if (ret) { + return ret; + } + + fota_ctx->mc_node_update = true; + fota_ctx->mc_node_post_action_callback = on_manifest_cb; + memcpy(fota_ctx->mc_node_manifest_hash, manifest_hash, FOTA_CRYPTO_HASH_SIZE); + + return handle_manifest(data, size, false, true); +} + +int fota_multicast_node_on_image_ready(void) +{ + FOTA_TRACE_DEBUG("Multicast image ready"); + int ret = fota_multicast_node_check_update_status(false); + if (ret) { + return ret; + } + if (fota_ctx && fota_ctx->mc_node_update) { + fota_ctx->state = FOTA_STATE_DOWNLOADING; + // From service POV, image is already downloaded,so report application approval + fota_source_report_state(FOTA_SOURCE_STATE_AWAITING_APPLICATION_APPROVAL, NULL, NULL); + } + return FOTA_STATUS_SUCCESS; +} + +int fota_multicast_node_on_activate(size_t activate_in_sec, + fota_multicast_node_post_action_callback_t activate_finish_cb) +{ + FOTA_TRACE_DEBUG("Multicast activation in %ld seconds", activate_in_sec); + int ret = fota_multicast_node_check_update_status(true); + if (ret) { + return ret; + } + if (fota_ctx && fota_ctx->mc_node_update_activated) { + FOTA_TRACE_ERROR("Multicast FOTA already activated, activate command ignored"); + return FOTA_STATUS_MULTICAST_UPDATE_ACTIVATED; + } + + fota_ctx->mc_node_update_activated = true; + fota_ctx->mc_node_post_action_callback = activate_finish_cb; + + // Time the first fragment handling to the time requested by Multicast + fota_event_handler_defer_with_data_in_ms(fota_multicast_node_on_fragment, NULL, 0, activate_in_sec * 1000); + + return FOTA_STATUS_SUCCESS; +} + +int fota_multicast_node_on_abort(void) +{ + FOTA_TRACE_DEBUG("Multicast abort requested"); + int ret = fota_multicast_node_check_update_status(true); + if (ret) { + return ret; + } + if (fota_ctx) { + if (fota_ctx->mc_node_update_activated) { + FOTA_TRACE_DEBUG("Current multicast update activated, can't abort"); + return FOTA_STATUS_MULTICAST_UPDATE_ACTIVATED; + } else { + abort_update(FOTA_STATUS_MULTICAST_UPDATE_ABORTED, "Multicast abort requested"); + } + } + return FOTA_STATUS_SUCCESS; +} + +int fota_multicast_node_get_ready_for_image(size_t image_size) +{ + FOTA_TRACE_DEBUG("Multicast - get ready for a new image"); + int ret = fota_multicast_node_check_update_status(false); + if (ret) { + return ret; + } + + // TODO: Is this logic correct? + if (fota_ctx) { + if (fota_ctx->mc_node_update_activated) { + FOTA_TRACE_DEBUG("Current multicast update activated, can't override it"); + return FOTA_STATUS_MULTICAST_UPDATE_ACTIVATED; + } else { + abort_update(FOTA_STATUS_MULTICAST_UPDATE_ABORTED, "Multicast update overridden"); + } + } + + // Just mark image as new, but don't erase yet, as we don't know location and size yet + mc_node_new_image = true; + mc_node_image_size = image_size; + + return FOTA_STATUS_SUCCESS; +} + +int fota_multicast_node_write_image_fragment(const void *buffer, size_t offset, size_t size) +{ + int ret = fota_multicast_node_check_update_status(false); + if (ret) { + return ret; + } + if (!mc_node_frag_size) { + FOTA_TRACE_ERROR("FOTA multicast command ignored - fragment size not set"); + return FOTA_STATUS_INTERNAL_ERROR; + } + if (offset % mc_node_frag_size) { + FOTA_TRACE_ERROR("FOTA multicast node - attempted to write to storage with an invalid offset"); + return FOTA_STATUS_STORAGE_WRITE_FAILED; + } + + if (mc_node_new_image) { + // Got here with new image flag still set. This means that no manifest was received, + // so this is a non FOTA image. Erase storage now. + ret = calc_and_erase_needed_storage(); + if (ret) { + return ret; + } + } + + size_t prog_size, addr; + + ret = fota_bd_get_program_size(&prog_size); + if (ret) { + FOTA_TRACE_ERROR("Unable to get program size"); + return ret; + } + addr = mc_image_data_addr + offset; + + // Likely case - size is aligned to program size (true in all but last fragment perhaps) + if (!(size % prog_size)) { + return fota_bd_program(buffer, addr, size); + } + + // Less likely case, end not aligned to program size (start must be) + + size_t chunk; + uint8_t *buf = (uint8_t *) buffer; + uint8_t *aligned_prog_buf = (uint8_t *) malloc(prog_size); + if (!aligned_prog_buf) { + return FOTA_STATUS_OUT_OF_MEMORY; + } + + // Handle aligned portion + chunk = FOTA_ALIGN_DOWN(size, prog_size); + if (chunk) { + ret = fota_bd_program(buf, addr, chunk); + if (ret) { + goto end; + } + buf += chunk; + addr += chunk; + size -= chunk; + } + + // Handle unaligned end + memcpy(aligned_prog_buf, buf, size); + memset(aligned_prog_buf + size, 0, prog_size - size); + ret = fota_bd_program(aligned_prog_buf, addr, prog_size); + +end: + free(aligned_prog_buf); + return ret; +} + +int fota_multicast_node_read_image_fragment(void *buffer, size_t offset, size_t size) +{ + int ret = fota_multicast_node_check_update_status(false); + if (ret) { + return ret; + } + return fota_multicast_read_from_image(buffer, offset, size); +} + +int fota_multicast_node_set_fragment_size(size_t frag_size) +{ + size_t prog_size; + FOTA_TRACE_DEBUG("Multicast - set fragment size to %ld", frag_size); + + int ret = fota_bd_init(); + if (ret) { + FOTA_TRACE_ERROR("Failed to initialize block device"); + return ret; + } + + ret = fota_bd_get_program_size(&prog_size); + if (ret) { + FOTA_TRACE_ERROR("FOTA multicast set fragment size - unable to get BD program size"); + return ret; + } + if (frag_size % prog_size) { + FOTA_TRACE_ERROR("FOTA multicast set fragment size - rejected"); + return FOTA_STATUS_INTERNAL_ERROR; + } + + ret = calc_available_storage(); + if (ret) { + return ret; + } + + mc_node_frag_size = frag_size; + return FOTA_STATUS_SUCCESS; +} + +#if MBED_CLOUD_CLIENT_FOTA_EXTERNAL_DOWNLOADER + +// Those APIs are envelopes to the multicast node ones +static void ext_downloader_manifest_post_action_cb(int ret) +{ +} + +static void ext_downloader_activate_post_action_cb(int ret) +{ +} + +int fota_ext_downloader_write_image_fragment(const void *buffer, size_t offset, size_t size) +{ + return fota_multicast_node_write_image_fragment(buffer, offset, size); +} + +int fota_ext_downloader_on_image_ready(void) +{ + int ret = fota_multicast_node_on_image_ready(); + if (ret) { + return ret; + } + return fota_multicast_node_on_activate(0, ext_downloader_activate_post_action_cb); +} + +#endif // MBED_CLOUD_CLIENT_FOTA_EXTERNAL_DOWNLOADER + +#elif (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_BR_MODE) + +#if !(defined(TARGET_LIKE_LINUX)) +static int multicast_br_candidate_iterate_handler(fota_candidate_iterate_callback_info *info) +{ + // Nothing to do - candidate already here + return FOTA_STATUS_SUCCESS; +} +#endif + +static int multicast_br_post_install_handler(const char *component_name, const fota_header_info_t *expected_header_info) +{ + // Actual image data starts right after FW header + mc_image_data_addr = fota_ctx->fw_header_bd_size + fota_ctx->fw_header_offset; + FOTA_DBG_ASSERT(fota_ctx->mc_br_post_action_callback); + fota_ctx->mc_br_post_action_callback(FOTA_STATUS_SUCCESS); + return FOTA_STATUS_SUCCESS; +} + +int fota_multicast_br_on_image_request(const fota_multicast_br_image_params *image_params, + fota_multicast_br_post_action_callback_t image_ready_cb) +{ + int ret; + FOTA_ASSERT(image_ready_cb); + + fota_header_info_t header_info; + ret = fota_curr_fw_read_header(&header_info); + FOTA_ASSERT(!ret); + + if (fota_ctx) { + ret = FOTA_STATUS_RESOURCE_BUSY; + goto fail; + } + + ret = handle_manifest_init(); + if (ret) { + goto fail; + } + + // Masquerade this as a manifest now + memset(fota_ctx->fw_info, 0, sizeof(manifest_firmware_info_t)); + fota_ctx->fw_info->payload_format = FOTA_MANIFEST_PAYLOAD_FORMAT_RAW; + fota_ctx->fw_info->payload_size = image_params->payload_size; + fota_ctx->fw_info->installed_size = image_params->payload_size; + memcpy(fota_ctx->fw_info->payload_digest, image_params->payload_digest, FOTA_CRYPTO_HASH_SIZE); + memcpy(fota_ctx->fw_info->installed_digest, image_params->payload_digest, FOTA_CRYPTO_HASH_SIZE); + memcpy(fota_ctx->fw_info->precursor_digest, header_info.digest, FOTA_CRYPTO_HASH_SIZE); + memcpy(fota_ctx->fw_info->uri, image_params->uri, FOTA_MANIFEST_URI_SIZE); + strcpy(fota_ctx->fw_info->component_name, FOTA_MULTICAST_BR_INT_COMP_NAME); + + ret = fota_component_name_to_id(FOTA_MULTICAST_BR_INT_COMP_NAME, &fota_ctx->comp_id); + FOTA_DBG_ASSERT(!ret); + fota_ctx->mc_br_update = true; + fota_ctx->mc_br_post_action_callback = image_ready_cb; + + // This is not a real update - don't report states to service + fota_source_enable_auto_observable_resources_reporting(false); + + // Jump right to download authorize state (no need for download authorization) + fota_on_download_authorize(); + + return FOTA_STATUS_SUCCESS; + +fail: + abort_update(ret, "Multicast BR image request aborted"); + return ret; +} + +int fota_multicast_br_read_from_image(void *buffer, size_t offset, size_t size) +{ + return fota_multicast_read_from_image(buffer, offset, size); +} + +#endif // FOTA_MULTICAST_BR_MODE +#endif // != FOTA_MULTICAST_UNSUPPORTED + #endif // MBED_CLOUD_CLIENT_FOTA_ENABLE diff --git a/fota/fota.h b/fota/fota.h index b33c482..00ec926 100644 --- a/fota/fota.h +++ b/fota/fota.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -21,7 +21,7 @@ #include "fota/fota_config.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) // TODO: move to delta - when integrated #if !defined(MBED_CLOUD_CLIENT_FOTA_DELTA_BLOCK_SIZE) @@ -30,6 +30,7 @@ #include "fota/fota_status.h" +#include #ifdef __cplusplus extern "C" { @@ -49,15 +50,24 @@ int fota_init(void *m2m_interface, void *resource_list); /* * Deinitialize Pelion FOTA component. + * This method must not be called when FOTA is active * * \return FOTA_STATUS_SUCCESS on success */ int fota_deinit(void); + +/* + * Tell if FOTA update is active + * + * \return true if FOTA update is active + */ +bool fota_is_active_update(void); + #ifdef __cplusplus } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif // __FOTA_H_ diff --git a/fota/fota_app_ifs.c b/fota/fota_app_ifs.c index 0c2dfd2..22d94ef 100644 --- a/fota/fota_app_ifs.c +++ b/fota/fota_app_ifs.c @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -30,45 +30,38 @@ #include -void fota_app_authorize_update() -{ - fota_event_handler_defer_with_result(fota_on_authorize, 0); -} +#if defined(TARGET_LIKE_LINUX) +#include +#include +#include +#endif // defined(TARGET_LIKE_LINUX) -void fota_app_authorize(uint32_t token) +void fota_app_authorize() { - (void) token; - fota_app_authorize_update(); + fota_event_handler_defer_with_result(fota_on_authorize, FOTA_INSTALL_STATE_AUTHORIZE /*This parameter is relevant only to the FOTA install stage.*/); } -void fota_app_reject_update(int32_t reason) +void fota_app_reject(int32_t reason) { fota_event_handler_defer_with_result(fota_on_reject, reason); } -void fota_app_reject(uint32_t token, int32_t reason) -{ - (void) token; - fota_app_reject_update(reason); -} - -void fota_app_defer_update() +void fota_app_defer() { - fota_event_handler_defer_with_result(fota_on_defer, 0); + fota_event_handler_defer_with_result(fota_on_defer, FOTA_INSTALL_STATE_DEFER /*This parameter is relevant only to the FOTA install stage.*/); } -void fota_app_defer(uint32_t token) +void fota_app_postpone_reboot() { - (void) token; - fota_app_defer_update(); + fota_event_handler_defer_with_result(fota_on_defer, FOTA_INSTALL_STATE_POSTPONE_REBOOT /*This parameter is relevant only to the FOTA install stage.*/); } void fota_app_resume(void) { - fota_event_handler_defer_with_result_ignore_busy(fota_on_resume, 0); + fota_event_handler_defer_with_result_ignore_busy(fota_on_resume, /*fota resume by user app */ 0); } -#if FOTA_DEFAULT_APP_IFS +#if defined (FOTA_DEFAULT_APP_IFS) && FOTA_DEFAULT_APP_IFS==1 void fota_app_on_download_progress(size_t downloaded_size, size_t current_chunk_size, size_t total_size) { FOTA_ASSERT(total_size); @@ -103,13 +96,12 @@ int fota_app_on_complete(int32_t status) The user application is supposed to save all current work before rebooting. - Note: the authorization call can be postponed and called later. + Note: The authorization call can be deferred and called later. This doesn't affect the performance of the Cloud Client. */ -int fota_app_on_install_authorization(uint32_t token) +int fota_app_on_install_authorization(void) { - (void) token; // unused; - fota_app_authorize_update(); + fota_app_authorize(); FOTA_APP_PRINT("Install authorization granted"); return FOTA_STATUS_SUCCESS; } @@ -125,12 +117,10 @@ int fota_app_on_install_authorization(uint32_t token) This doesn't affect the performance of the Cloud Client. */ int fota_app_on_download_authorization( - uint32_t token, const manifest_firmware_info_t *candidate_info, fota_component_version_t curr_fw_version ) { - (void) token; // unused; char curr_semver[FOTA_COMPONENT_MAX_SEMVER_STR_SIZE] = { 0 }; char new_semver[FOTA_COMPONENT_MAX_SEMVER_STR_SIZE] = { 0 }; fota_component_version_int_to_semver(curr_fw_version, curr_semver); @@ -149,22 +139,62 @@ int fota_app_on_download_authorization( candidate_info->payload_size, candidate_info->installed_size ); + } else if (candidate_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_RAW) { + FOTA_APP_PRINT("Update size %zuB (Encrypted image size %zuB)", + candidate_info->installed_size, + candidate_info->payload_size + ); } else { FOTA_APP_PRINT("Update size %zuB", candidate_info->payload_size); } FOTA_APP_PRINT("---------------------------------------------------"); FOTA_APP_PRINT("Download authorization granted"); - fota_app_authorize_update(); + fota_app_authorize(); /* Application can reject an update in the following way - fota_app_reject_update(127); + fota_app_reject(127); Reason error code will be logged. Alternatively application can defer the update by calling - fota_app_defer_update(); + fota_app_defer(); Deferred update will be restarted on next boot or by calling fota_app_resume() API. */ return FOTA_STATUS_SUCCESS; } -#endif // FOTA_DEFAULT_APP_IFS +#endif // #if defined (FOTA_DEFAULT_APP_IFS) && FOTA_DEFAULT_APP_IFS==1 + +#if defined(TARGET_LIKE_LINUX) +#if defined(MBED_CLOUD_CLIENT_FOTA_LINUX_SINGLE_MAIN_FILE) + +int fota_app_install_main_app(const char *candidate_file_name) +{ + unsigned int file_mode = ALLPERMS; + struct stat statbuf; + + FOTA_TRACE_INFO("Installing MAIN application"); + + // get current file permissions + if (stat(MBED_CLOUD_CLIENT_FOTA_LINUX_CURR_FW_FILENAME, &statbuf) == 0) { + file_mode = statbuf.st_mode & 0x1FF; + } + + // unlink current file + if (unlink(MBED_CLOUD_CLIENT_FOTA_LINUX_CURR_FW_FILENAME) != 0) { + FOTA_TRACE_ERROR("Failed to unlink file %s: %s", MBED_CLOUD_CLIENT_FOTA_LINUX_CURR_FW_FILENAME, strerror(errno)); + return FOTA_STATUS_INTERNAL_ERROR; + } + + // change file permission to same as previously + chmod(candidate_file_name, file_mode); + + if (rename(candidate_file_name, MBED_CLOUD_CLIENT_FOTA_LINUX_CURR_FW_FILENAME) != 0) { + FOTA_TRACE_ERROR("Failed to rename file %s: %s", candidate_file_name, strerror(errno)); + return FOTA_STATUS_INTERNAL_ERROR; + } + + return FOTA_STATUS_SUCCESS; +} + +#endif // defined(MBED_CLOUD_CLIENT_FOTA_LINUX_SINGLE_MAIN_FILE) +#endif // defined(TARGET_LIKE_LINUX) #endif // MBED_CLOUD_CLIENT_FOTA_ENABLE diff --git a/fota/fota_app_ifs.h b/fota/fota_app_ifs.h index e4c8d19..e2a8e88 100644 --- a/fota/fota_app_ifs.h +++ b/fota/fota_app_ifs.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -21,7 +21,7 @@ #include "fota/fota_config.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #include "fota/fota_status.h" #include "fota/fota_header_info.h" @@ -32,170 +32,208 @@ extern "C" { #endif -// TODO: remove this enum definition -typedef enum { - FOTA_APP_AUTHORIZATION_TYPE_DOWNLOAD, /**< Request authorization for downloading update payload. */ - FOTA_APP_AUTHORIZATION_TYPE_INSTALL, /**< Request authorization for installing update. */ -} fota_app_request_type_e; +/** + * @file fota_app_ifs.h + * \brief Callbacks the device application can use to manage the firmware update flow. + * If your application does not require an implementation of any special logic, FOTA provides a default implementation for the update callbacks. + * To enable the default implementation, inject the ::FOTA_DEFAULT_APP_IFS define into the application build. + */ /** - * FOTA download authorization callback to be implemented by an application. - * - * Application authorization is required by FOTA client to start downloading the update. - * - * The callback implementation is expected to call one of the APIs listed below: - * - fota_app_authorize() - authorize FOTA request. - * - fota_app_reject() - reject FOTA request and discard the update. The update will not be reprompted. - * - fota_app_defer() - defer the update to a later phase. This will abort current update attempt, while preserving update manifest. - * Update will be restarted on next boot. Alternatively update can be restarted by calling fota_app_resume(). - * - * \note only required if MBED_CLOUD_CLIENT_FOTA_ENABLE build flag is specified - * \note the FW versions in this callback are in internal library format and should be converted to string using fota_component_version_int_to_semver() before use. - * \param[in] token (unused) - * \param[in] candidate_info update candidate descriptor - * \param[in] curr_fw_version current component FW version - * \return FOTA_STATUS_SUCCESS for acknowledgment that authorization callback was received properly by the application. + * FOTA download authorization callback to be implemented by the device application. + * + * The application must implement this callback if you want the application to authorize the FOTA client to start downloading the candidate image. + * The client invokes this callback for the first time when the device receives the update manifest from Device Management. + * + * FOTA expects the callback implementation to call one of these APIs: + * - ::fota_app_authorize() - Authorize request to download image. The download phase will proceed. + * - ::fota_app_reject() - Reject request to download image and discard the manifest. The client will not re-prompt the update. + * - ::fota_app_defer() - Defer image download to a later phase. This aborts the current image download attempt, while preserving the update manifest. + * Image download continues on the next boot after device registration or when the device application calls the :fota_app_resume() API. + * The client invokes ::fota_app_on_download_authorization when the update flow continues. + * Both ::fota_app_defer() and ::fota_app_resume() APIs are implemented only if the ::MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT build flag is not equal to ::FOTA_RESUME_UNSUPPORTED. + * + * \note Only required if the ::MBED_CLOUD_CLIENT_FOTA_ENABLE build flag is specified. + * \note Only required if the ::FOTA_DEFAULT_APP_IFS build flag is disabled. + * \note The firmware versions in this callback are in internal library format and should be converted to strings using ::fota_component_version_int_to_semver() before use. + * + * \param[in] candidate_info Candidate image descriptor. + * \param[in] curr_fw_version Firmware version of the component currently on the device. + * + * \return ::FOTA_STATUS_SUCCESS to acknowledge that the application received the authorization callback properly. */ int fota_app_on_download_authorization( - uint32_t token, const manifest_firmware_info_t *candidate_info, fota_component_version_t curr_fw_version ); /** - * FOTA install authorization callback to be implemented by an application. + * Pelion FOTA install authorization callback to be implemented by the device application. + * + * Should be implemented by the application if it wants to authorize FOTA to install the update. + * The client invokes this callback for the first time when the device fully downloads the update candidate image. * - * Application authorization is required by FOTA client to apply the update. - * The implementation expected to call one of the APIs listed below: - * - fota_app_authorize() - authorize FOTA install the update candidate - reboot or connectivity lost may occur during candidate installation operation. - * This phase considered as a critical section - powerloss can potentially brick the device. - * - fota_app_reject() - reject FOTA request and discard the update. The update will not be reprompted. - * - fota_app_defer() - defer the install to a later later phase. This will mark the candidate as valid but will not perform reboot + * FOTA client expects the callback implementation to call one of these APIs: + * - ::fota_app_authorize() - Authorize FOTA to install the candidate image. Reboot or connectivity loss may occur during installation. + * This phase is critical because power loss can brick the device. + * - ::fota_app_reject() - Reject request to install, and discard the update. The update will not be re-prompted. + * - ::fota_app_defer() - Defer the installation to a later phase. This marks the candidate image as valid, but the device will not reboot. + * For the main component, the installation proceeds automatically after the device reboots. + * For user components, the update flow proceeds on the next boot after device registration or when the device application calls the ::fota_app_resume() API. + * The application invokes the ::fota_app_on_download_authorization and ::fota_app_on_install_authorization() callbacks when the update flow proceeds. + * The client implements the ::fota_app_defer() and ::fota_app_resume() APIs only if the ::MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT build flag is not equal to ::FOTA_RESUME_UNSUPPORTED. * - * \note only required if MBED_CLOUD_CLIENT_FOTA_ENABLE build flag is specified - * \note after deferring the installation by fota_app_defer() call - fota_app_resume() call will have no effect - reboot is required for installing the candidate. + * \note Only required if the ::MBED_CLOUD_CLIENT_FOTA_ENABLE build flag is specified. + * \note Only required if the ::FOTA_DEFAULT_APP_IFS build flag is disabled. * - * \param[in] token (unused) - * \return FOTA_STATUS_SUCCESS for acknowledgment that authorization callback was received properly by the application. + * \return ::FOTA_STATUS_SUCCESS to acknowledge that the application received the authorization callback properly. */ -int fota_app_on_install_authorization(uint32_t token); +int fota_app_on_install_authorization(void); /** - * Pelion FOTA complete callback to be implemented by an application. + * Pelion FOTA complete callback to be implemented by the device application. + * + * Should be implemented by the application if it wants to receive a notification that the update process is done/terminated. + * The update result can be determined based on the status argument. + * + * \note Only required if the ::MBED_CLOUD_CLIENT_FOTA_ENABLE build flag is specified. + * \note Only required if the ::FOTA_DEFAULT_APP_IFS build flag is disabled. + * \note This callback will not be called if the device reboots as part of update installation. * - * Pelion FOTA client notifies the application that update process is done/terminated. - * Update result can be determined based in status argument. + * \param[in] status Pelion FOTA status code. ::FOTA_STATUS_SUCCESS if the update is deployed successfully. * - * \param[in] status pelion FOTA status code. FOTA_STATUS_SUCCESS in case update deployed successfully. - * \return FOTA_STATUS_SUCCESS for acknowledgment that authorization callback was received properly by the application. + * \return ::FOTA_STATUS_SUCCESS to acknowledge that the application received authorization callback properly. */ int fota_app_on_complete(int32_t status); /** * Resume Pelion FOTA update. * - * In case update process was interupted - application can restart it by calling this function. - */ + * If the update process is interrupted, the application can call this function to resume the process. + * This API invokes ::fota_app_on_download_authorization() CB. + * + * \note The function is implemented only if the ::MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT build flag is not equal to ::FOTA_RESUME_UNSUPPORTED. + * \note If ::MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT build flag is equal to ::FOTA_RESUME_SUPPORT_RESTART, the update flow restarts from the beginning. + * \note If ::MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT build flag is equal to ::FOTA_RESUME_SUPPORT_RESUME, the update flow resumes from the point that it was interrupted. + * \note FOTA update resume is not supported after the device application calls ::fota_app_postpone_reboot(). + * + */ void fota_app_resume(void); /** - * Authorize Pelion FOTA client to proceed with an update - * - * This API expected to be called from fota_app_on_authorization_request() application callback. + * Authorize Pelion FOTA client to proceed with an update. * - * \param[in] token request token as received in fota_app_on_authorization_request(). - * \note unexpected token considered as unrecoverable programming error and will cause panic. + * FOTA client expects the ::fota_app_on_download_authorization() and ::fota_app_on_install_authorization() application callbacks to call this API. * - * \deprecated Use the fota_app_authorize_update */ -void fota_app_authorize(uint32_t token) fota_deprecated; +void fota_app_authorize(void); /** - * Reject Pelion FOTA update + * Reject and terminate FOTA update. * - * This API expected to be called from fota_app_on_authorization_request() application callback. - * - * \param[in] token request token as received in fota_app_on_authorization_request(). - * \param[in] reason reject reason code. - * \note unexpected token considered as unrecoverable programming error and will cause panic. - * - * \deprecated Use the fota_app_reject_update + * ::fota_app_on_download_authorization() and ::fota_app_on_install_authorization() application callbacks may call this API. * + * \param[in] reason Reject reason code. */ -void fota_app_reject(uint32_t token, int32_t reason) fota_deprecated; +void fota_app_reject(int32_t reason); /** - * Defer Pelion FOTA update + * Defer FOTA update. * - * FOTA client resources will be released and update will be reattempted on next boot or by - * calling fota_app_resume() API. - * This API expected to be called from fota_app_on_authorization_request() application callback. + * Stop all operations related to update process. + * The FOTA client pauses the FOTA process, releases resources and reattempts the update when the device application calls + * the ::fota_app_resume() API. + * + * ::fota_app_on_download_authorization() and ::fota_app_on_install_authorization() application callbacks may call this API. * - * \param[in] token request token as received in fota_app_on_authorization_request(). - * \note unexpected token considered as unrecoverable programming error and will cause panic. + * \note The function is implemented only if the ::MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT build flag is not equal to ::FOTA_RESUME_UNSUPPORTED. + * \note If the client rebooted, the client calls fota_app_on_download_authorization() or fota_app_on_install_authorization() again to re-check for the device application's approval. + * \note The client supports installation defer only before installation starts. Calling this API during the installation process has no effect. * - * \deprecated Use the fota_app_defer_update */ -void fota_app_defer(uint32_t token) fota_deprecated; - +void fota_app_defer(void); /** - * Authorize Pelion FOTA client to proceed with an update. + * Postpone device reboot after FOTA update. * - * This API expected to be called from fota_app_on_authorization_request() application callback. + * For the MAIN mbed-os component, the FOTA client doesn't boot by itself after download is completed. The device application must trigger reboot. + * After reboot, the bootloader installs the MAIN mbed-os component and completes the FOTA process. + * For other components: The FOTA client installs the component but doesn't boot itself. The device application must trigger reboot. + * Only ::fota_app_on_install_authorization() application callbacks may call this API. * - * \param[in] token request token as received in fota_app_on_authorization_request(). - * \note unexpected token considered as unrecoverable programming error and will cause panic. + * \note The function is implemented only if the ::MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT build flag is not equal to ::FOTA_RESUME_UNSUPPORTED. + * \note The FOTA client supports postpone reboot only before installation starts. Calling this API during installation process has no effect. */ -void fota_app_authorize_update(void); +void fota_app_postpone_reboot(void); + /** - * Reject Pelion FOTA update. + * Progress bar support for Pelion FOTA update. * - * This API expected to be called from fota_app_on_authorization_request() application callback. + * The application should implement this API.(Optional) + * FOTA client calls this API when the download progresses by 5% percent (approximately). * - * \param[in] reason reject reason code. + * \param[in] downloaded_size Number of bytes already downloaded to the device. + * \param[in] current_chunk_size Size, in bytes, of the currently downloaded chunk. + * \param[in] total_size Total image size in bytes. */ -void fota_app_reject_update(int32_t reason); +void fota_app_on_download_progress(size_t downloaded_size, size_t current_chunk_size, size_t total_size); /** - * Defer Pelion FOTA update. + * FOTA callback for verifying installation of the main application, to be implemented by the application. + * + * Should be implemented by the application if it has custom logic to verify installation of the main application. + * If custom logic is not required, FOTA uses the default implementation. + * + * The ::expected_header_info field includes the whole candidate header, including the vendor_data field, which can + * store vendor-specific data to help verify installation of the main app (for example, a vendor-specific application hash). * - * FOTA client resources will be released and update will be reattempted on next boot or by - * calling fota_app_resume() API. - * This API expected to be called from fota_app_on_authorization_request() application callback. + * \note Only required if the ::MBED_CLOUD_CLIENT_FOTA_ENABLE build flag is specified. + * \note Only required if the ::FOTA_CUSTOM_MAIN_APP_VERIFY_INSTALL is set to 1. + * + * \param[in] expected_header_info Expected candidate header information that the client can use to verify the newly installed app. + * + * \return ::FOTA_STATUS_SUCCESS to acknowledge that the verification succeeded. */ -void fota_app_defer_update(void); +int fota_app_on_main_app_verify_install(const fota_header_info_t *expected_header_info); +#if defined(TARGET_LIKE_LINUX) + /** - * Progress bar support for Pelion FOTA update. + * Pelion FOTA install callback to be implemented by application. + * + * FOTA client expects the callback to install the candidate and return ::FOTA_STATUS_SUCCESS or reboot the system. * - * This API expected to be implemented by application.(Optional) - * It called approximately on every 5 percent download progress. + * \param[in] candidate_fs_name Candidate image file name. + * \param[in] firmware_info Parsed update manifest. * - * \param[in] already downloaded image size in bytes - * \param[in] current downloaded chunk size in bytes - * \param[in] total image size in bytes + * \return ::FOTA_STATUS_SUCCESS for successful installation; otherwise, return an error code. */ -void fota_app_on_download_progress(size_t downloaded_size, size_t current_chunk_size, size_t total_size); +int fota_app_on_install_candidate(const char *candidate_fs_name, const manifest_firmware_info_t *firmware_info); -#if defined(TARGET_LIKE_LINUX) +#if defined(MBED_CLOUD_CLIENT_FOTA_LINUX_SINGLE_MAIN_FILE) /** - * Pelion FOTA install callback to be implemented by application. + * Install main application by overwriting current executable file. * - * The callback is expected to install the candidate and return FOTA_STATUS_SUCCESS or reboot the system. + * This function overwrites the executable file and relaunches the process. + * The client expects the ::fota_app_on_install_candidate() application + * callback to call this API. + * It is only available if there is a single main file. * - * \param[in] candidate_fs_name candidate file name - * \param[in] firmware_info parsed update manifest + * \note This function does not validate candidate file integrity or authenticity. + * \note Candidate image file will be deleted from its original path after this callback execution. * - * \return FOTA_STATUS_SUCCESS for successful installation or error code. + * \param[in] candidate_file_name Candidate image file name as found in the file system. + * + * \return ::FOTA_STATUS_SUCCESS for successful installation of the main application. */ +int fota_app_install_main_app(const char *candidate_file_name); -int fota_app_on_install_candidate(const char *candidate_fs_name, const manifest_firmware_info_t *firmware_info); +#endif // defined(MBED_CLOUD_CLIENT_FOTA_LINUX_SINGLE_MAIN_FILE) #endif // defined(TARGET_LIKE_LINUX) @@ -203,6 +241,6 @@ int fota_app_on_install_candidate(const char *candidate_fs_name, const manifest_ } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif // __FOTA_APP_IFS_H_ diff --git a/fota/fota_base.h b/fota/fota_base.h index e27867c..dc04b90 100644 --- a/fota/fota_base.h +++ b/fota/fota_base.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -23,6 +23,7 @@ #include #include #include +#include #include "fota/fota_config.h" #ifdef __cplusplus extern "C" { @@ -75,8 +76,15 @@ extern "C" { #endif #if !defined(FOTA_HALT) +#if defined(FOTA_UNIT_TEST) +void unitest_halt(void); +#define FOTA_HALT unitest_halt() +#elif defined(TARGET_LIKE_LINUX) +#define FOTA_HALT assert(0) +#else #define FOTA_HALT for(;;) #endif +#endif #define FOTA_ASSERT(COND) { \ if (!(COND)) { \ @@ -95,6 +103,14 @@ extern "C" { #define FOTA_ALIGN_DOWN(val, size) ((val) / (size) * (size)) + +#if defined(__GNUC__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define FOTA_UINT64_TO_LE __builtin_bswap64 +#else +#define FOTA_UINT64_TO_LE +#endif + + #ifdef __cplusplus } #endif diff --git a/fota/fota_block_device.h b/fota/fota_block_device.h index 2b93b90..4dd3017 100644 --- a/fota/fota_block_device.h +++ b/fota/fota_block_device.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -21,109 +21,129 @@ #include "fota/fota_base.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #ifdef __cplusplus extern "C" { #endif -/* - * Pelion FOTA block device initialize +/** + * @file fota_block_device.h + * \brief Pelion FOTA uses the BlockDevice interface to save and read the candidate to non-volatile storage. + * When you port Pelion FOTA to a new platform, you must implement the functions specified below. + * By default, FOTA writes to 0 offset in the block device. + * Use the ::MBED_CLOUD_CLIENT_FOTA_STORAGE_START_ADDR macro to specify an alternative address. + */ + + +/** + * Pelion FOTA block device initialize. * - * Block device is used for storing update candidate. + * FOTA uses block device to store the update candidate image. * - * \return FOTA_STATUS_SUCCESS on success + * \return ::FOTA_STATUS_SUCCESS on success. */ int fota_bd_init(void); -/* - * Pelion FOTA block device deinitialize +/** + * Pelion FOTA block device deinitialize. * - * \return FOTA_STATUS_SUCCESS on success + * \return ::FOTA_STATUS_SUCCESS on success. */ int fota_bd_deinit(void); -/* - * Pelion FOTA block device size getter +/** + * Pelion FOTA block device size getter. * - * \param[out] size Block device size. - * \return FOTA_STATUS_SUCCESS on success + * \param[out] size The total block device size. + * \return ::FOTA_STATUS_SUCCESS on success. */ int fota_bd_size(size_t *size); -/* +/** * Pelion FOTA block device read. * - * \param[out] buffer Destination buffer to be filled. The buffer size must be greater or equal to size argument. - * \param[in] addr Read address - * \param[in] size Size to read in bytes, must be a multiple of the read block size - * \return FOTA_STATUS_SUCCESS on success - * \note If a failure occurs, it is not possible to determine how many bytes succeeded + * \param[out] buffer Destination buffer to be filled. The buffer size must be greater or equal to the ::size argument. + * \param[in] addr Read address. + * \param[in] size Size to read in bytes. Must be a multiple of the read block size that ::fota_bd_get_read_size() retrieves. + * \return ::FOTA_STATUS_SUCCESS on success. + * \note If a failure occurs, it is not possible to determine how many bytes were read successfully. */ int fota_bd_read(void *buffer, size_t addr, size_t size); -/* +/** * Pelion FOTA block device program. * - * Programs block to a block device. - * The blocks must have been erased prior to being programmed. + * Programs a block to a block device. + * You must erase the blocks using :fota_bd_erase() before programming them. * - * \param[in] buffer Source buffer to be programed. - * \param[in] addr Address of block to begin writing to. - * \param[in] size Size to write in bytes, must be a multiple of the program block size - * \return FOTA_STATUS_SUCCESS on success - * \note If a failure occurs, it is not possible to determine how many bytes succeeded + * \param[in] buffer Source buffer to be programmed. + * \param[in] addr Address of the block to begin writing to. + * \param[in] size Size to write in bytes. Must be a multiple of the program block size that ::fota_bd_get_program_size() retrieves. + * \return ::FOTA_STATUS_SUCCESS on success. + * \note If a failure occurs, it is not possible to determine how many bytes succeeded. */ int fota_bd_program(const void *buffer, size_t addr, size_t size); -/* +/** * Pelion FOTA block device erase. * - * Erase blocks on a block device. Size must be a multiple of the erase block size. + * Erases blocks on a block device. * - * \param[in] addr Address of block to begin erasing. - * \param[in] size Size to erase in bytes, must be a multiple of the erase block size. - * \return FOTA_STATUS_SUCCESS on success + * \param[in] addr Address of a block to begin erasing. + * \param[in] size Size to erase in bytes. Must be a multiple of the erase block size that ::fota_bd_get_erase_size() retrieves. + * \return ::FOTA_STATUS_SUCCESS on success. */ int fota_bd_erase(size_t addr, size_t size); -/* +/** * Pelion FOTA block device get the size of a readable block. * * \param[out] read_size The size of a readable block in bytes. - * \return FOTA_STATUS_SUCCESS on success + * \return ::FOTA_STATUS_SUCCESS on success. */ int fota_bd_get_read_size(size_t *read_size); -/* +/** * Pelion FOTA block device get the size of a programmable block. * - * \param[out] prog_size The size of a programmable block in bytes. A positive value on success 0 otherwise. - * \return FOTA_STATUS_SUCCESS on success + * \param[out] prog_size The size of a programmable block in bytes. + * \return ::FOTA_STATUS_SUCCESS on success. */ int fota_bd_get_program_size(size_t *prog_size); -/* - * Pelion FOTA block device get the size of a erasable block given address. +/** + * Pelion FOTA block device get the size of an erasable block for a given address. * * \param[in] addr Address of erasable block. - * \param[out] erase_size The size of an erasable block in bytes. A positive value on success 0 otherwise. - * \return FOTA_STATUS_SUCCESS on success + * \param[out] erase_size The size of an erasable block in bytes. + * \return ::FOTA_STATUS_SUCCESS on success. */ int fota_bd_get_erase_size(size_t addr, size_t *erase_size); -/* - * Pelion FOTA block device get the value of storage when erased. +/** + * Pelion FOTA block device get the value of the storage when erased. * - * \param[out] erase_value erase value if non negative. - * If negative, means that the one can't rely on this value for this block device. - * \return FOTA_STATUS_SUCCESS on success + * \param[out] erase_value Erase value, if non-negative. + * A negative value means that this block device does not support erase. + * \return :FOTA_STATUS_SUCCESS on success */ int fota_bd_get_erase_value(int *erase_value); +/** + * Pelion FOTA block device translate physical address to logical one. + * It is required that block device addresses will be continuous and start from 0. + * In most devices, this is the case and this function should simply return the physical address. + * Devices like like internal flash, where addresses don't start from 0. require a less trivial translation logic. + * + * \param[in] phys_addr Physical address. + * \return Logical address + */ +size_t fota_bd_physical_addr_to_logical_addr(size_t phys_addr); + #ifdef __cplusplus } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif // __FOTA_BLOCK_DEVICE_H_ diff --git a/fota/fota_block_device_linux.c b/fota/fota_block_device_linux.c deleted file mode 100644 index 7c1f4da..0000000 --- a/fota/fota_block_device_linux.c +++ /dev/null @@ -1,196 +0,0 @@ -// ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. -// -// SPDX-License-Identifier: Apache-2.0 -// -// 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 "fota/fota_base.h" - -#ifdef MBED_CLOUD_CLIENT_FOTA_ENABLE -#if defined(TARGET_LIKE_LINUX) -#if (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_EXTERNAL_BD) - -#define TRACE_GROUP "FOTA" - -#include -#include "fota/fota_block_device.h" -#include "fota/fota_status.h" - -#include -#include -#include - -#define BD_ERASE_VALUE 0x0 -#define BD_ERASE_SIZE 0x1 -#define BD_READ_SIZE 0x1 -#define BD_PROGRAM_SIZE 0x1 -#define BD_ERASE_BUFFER_SIZE 0x1000 - -static FILE *bd_backend = NULL; - -static size_t get_bd_backend_file_size() -{ - struct stat st; - - if (stat(MBED_CLOUD_CLIENT_FOTA_LINUX_UPDATE_STORAGE_FILENAME, &st) == 0) { - return (st.st_size); - } else { - FOTA_TRACE_ERROR("stat failed: %s", strerror(errno)); - assert(0); - return 0; - } -} - -int fota_bd_size(size_t *size) -{ - assert(bd_backend); - *size = 0xFFFFFFFFUL; - return FOTA_STATUS_SUCCESS; -} - -int fota_bd_init(void) -{ - if (!bd_backend) { - bd_backend = fopen(MBED_CLOUD_CLIENT_FOTA_LINUX_UPDATE_STORAGE_FILENAME, "rb+"); - if (NULL == bd_backend) { - bd_backend = fopen(MBED_CLOUD_CLIENT_FOTA_LINUX_UPDATE_STORAGE_FILENAME, "wb+"); - if (NULL == bd_backend) { - FOTA_TRACE_ERROR("fopen failed: %s", strerror(errno)); - FOTA_TRACE_ERROR("Failed to initialize BlockDevice - failed to create file %s", MBED_CLOUD_CLIENT_FOTA_LINUX_UPDATE_STORAGE_FILENAME); - return FOTA_STATUS_STORAGE_WRITE_FAILED; - } - } - } - - FOTA_TRACE_DEBUG("FOTA BlockDevice init file is %s", MBED_CLOUD_CLIENT_FOTA_LINUX_UPDATE_STORAGE_FILENAME); - return FOTA_STATUS_SUCCESS; -} - -int fota_bd_deinit(void) -{ - if (bd_backend) { - if (fclose(bd_backend)) { - FOTA_TRACE_ERROR("fclose failed: %s", strerror(errno)); - FOTA_TRACE_ERROR("Failed to deinit BlockDevice - failed to close %s", MBED_CLOUD_CLIENT_FOTA_LINUX_UPDATE_STORAGE_FILENAME); - } else { - FOTA_TRACE_DEBUG("Closed FOTA BlockDevice file %s", MBED_CLOUD_CLIENT_FOTA_LINUX_UPDATE_STORAGE_FILENAME); - } - bd_backend = NULL; - } - - FOTA_TRACE_DEBUG("FOTA BlockDevice deinit file is %s", MBED_CLOUD_CLIENT_FOTA_LINUX_UPDATE_STORAGE_FILENAME); - return FOTA_STATUS_SUCCESS; -} - -int fota_bd_read(void *buffer, size_t addr, size_t size) -{ - assert(bd_backend); - - size_t file_size = get_bd_backend_file_size(); - size_t read_addr = MIN(file_size, addr); - - if (fseek(bd_backend, read_addr, SEEK_SET)) { - FOTA_TRACE_ERROR("fseek failed: %s", strerror(errno)); - return FOTA_STATUS_STORAGE_READ_FAILED; - } - - size_t bytes_read = fread(buffer, 1, size, bd_backend); - memset(buffer + bytes_read, BD_ERASE_VALUE, size - bytes_read); - return FOTA_STATUS_SUCCESS; -} - -int fota_bd_program(const void *buffer, size_t addr, size_t size) -{ - assert(bd_backend); - - if (fseek(bd_backend, addr, SEEK_SET)) { - FOTA_TRACE_ERROR("fseek failed: %s", strerror(errno)); - return FOTA_STATUS_STORAGE_WRITE_FAILED; - } - - size_t bytes_written = fwrite(buffer, 1, size, bd_backend); - if (bytes_written != size) { - FOTA_TRACE_ERROR("fwrite failed: %s", strerror(errno)); - return FOTA_STATUS_STORAGE_WRITE_FAILED; - } - - return FOTA_STATUS_SUCCESS; -} - -int fota_bd_erase(size_t addr, size_t size) -{ - assert(bd_backend); - - size_t file_size = get_bd_backend_file_size(); - - if (addr >= file_size) { - return FOTA_STATUS_SUCCESS; - } - - if (fseek(bd_backend, addr, SEEK_SET)) { - FOTA_TRACE_ERROR("fseek failed: %s", strerror(errno)); - return FOTA_STATUS_STORAGE_WRITE_FAILED; - } - - size_t erase_size = size; - if ((addr + size) > file_size) { - erase_size = file_size - addr; - } - - uint8_t erase_buff[BD_ERASE_BUFFER_SIZE]; - memset(erase_buff, BD_ERASE_VALUE, sizeof(erase_buff)); - size_t bytes_written = 0; - size_t size_to_write; - while (erase_size) { - size_to_write = MIN(sizeof(erase_buff), erase_size); - bytes_written = fwrite(erase_buff, 1, size_to_write, bd_backend); - if (bytes_written != size_to_write) { - FOTA_TRACE_ERROR("fwrite failed: %s", strerror(errno)); - FOTA_TRACE_ERROR("Write failed BlockDevice - file %s", MBED_CLOUD_CLIENT_FOTA_LINUX_UPDATE_STORAGE_FILENAME); - return FOTA_STATUS_STORAGE_WRITE_FAILED; - } - erase_size -= size_to_write; - } - - return FOTA_STATUS_SUCCESS; -} - -int fota_bd_get_read_size(size_t *read_size) -{ - *read_size = BD_READ_SIZE; - return FOTA_STATUS_SUCCESS; -} - -int fota_bd_get_program_size(size_t *prog_size) -{ - *prog_size = BD_PROGRAM_SIZE; - return FOTA_STATUS_SUCCESS; -} - -int fota_bd_get_erase_size(size_t addr, size_t *erase_size) -{ - *erase_size = BD_ERASE_SIZE; - return FOTA_STATUS_SUCCESS; -} - -int fota_bd_get_erase_value(int *erase_value) -{ - *erase_value = BD_ERASE_VALUE; - return FOTA_STATUS_SUCCESS; -} - -#endif // (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE != FOTA_EXTERNAL_BD) -#endif // defined(TARGET_LIKE_LINUX) -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE diff --git a/fota/fota_candidate.c b/fota/fota_candidate.c index 48db344..a868f89 100644 --- a/fota/fota_candidate.c +++ b/fota/fota_candidate.c @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -26,12 +26,14 @@ #include "fota/fota_status.h" #include "fota/fota_block_device.h" #include "fota/fota_crypto.h" -#include "fota/fota_block_device.h" #include "fota/fota_nvm.h" #include #include -#define MIN_FRAG_SIZE 128 +#if defined(TARGET_LIKE_LINUX) +#include +#include "fota/platform/linux/fota_platform_linux.h" +#endif //(TARGET_LIKE_LINUX) typedef struct { size_t bd_read_size; @@ -60,8 +62,6 @@ static fota_candidate_config_t fota_candidate_config = { static candidate_contex_t *ctx = NULL; -uint32_t fota_bd_physical_addr_to_logical_addr(uint32_t phys_addr); - void fota_candidate_set_config(fota_candidate_config_t *in_fota_candidate_config) { FOTA_ASSERT(in_fota_candidate_config->storage_size); @@ -106,16 +106,25 @@ int fota_candidate_read_candidate_ready_header(size_t *addr, uint32_t bd_read_si goto end; } - // Advance read address for next calls - *addr += FOTA_ALIGN_UP(chunk_size, bd_prog_size); - memcpy(header, aligned_read_buf, sizeof(fota_candidate_ready_header_t)); if (header->magic != FOTA_CANDIDATE_READY_MAGIC) { +#if (MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION < 3) + // This code is practically available for testing only, as fota_candidate code is not called on legacy bootloaders. + // In case of a legacy header, if we don't have magic, this probably means that the candidate ready header is + // missing for the main component. + FOTA_TRACE_DEBUG("Probably main component on a legacy device"); + strcpy(header->comp_name, FOTA_COMPONENT_MAIN_COMPONENT_NAME); + ret = FOTA_STATUS_SUCCESS; +#else FOTA_TRACE_INFO("No image found on storage"); ret = FOTA_STATUS_NOT_FOUND; +#endif goto end; } + // Advance read address for next calls + *addr += FOTA_ALIGN_UP(chunk_size, bd_prog_size); + end: if (chunk_size > sizeof(read_buf)) { free(aligned_read_buf); @@ -130,9 +139,7 @@ static void cleanup() return; } #if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) - if (ctx->enc_ctx) { - fota_encrypt_finalize(&ctx->enc_ctx); - } + fota_encrypt_finalize(&ctx->enc_ctx); #endif free(ctx->fragment_buf); free(ctx); @@ -151,7 +158,6 @@ int fota_candidate_read_header(size_t *addr, uint32_t bd_read_size, uint32_t bd_ } int ret = fota_bd_read(header_buf, *addr, read_size); - *addr += FOTA_ALIGN_UP(header_size, bd_prog_size); if (ret) { goto end; @@ -162,6 +168,11 @@ int fota_candidate_read_header(size_t *addr, uint32_t bd_read_size, uint32_t bd_ goto end; } + if (header_size < header->external_header_size + offsetof(fota_header_info_t, internal_header_barrier)) { + *addr += FOTA_ALIGN_UP(header->external_header_size + offsetof(fota_header_info_t, internal_header_barrier), bd_prog_size); + } else { + *addr += FOTA_ALIGN_UP(header_size, bd_prog_size); + } end: free(header_buf); return ret; @@ -174,12 +185,11 @@ static int fota_candidate_extract_start(bool force_encrypt, const char *expected uint32_t alloc_size, block_size; if (!ctx) { - ctx = (candidate_contex_t *) malloc(sizeof(candidate_contex_t)); + ctx = (candidate_contex_t *) calloc(1, sizeof(candidate_contex_t)); if (!ctx) { FOTA_TRACE_ERROR("FOTA candidate_contex_t - allocation failed"); return FOTA_STATUS_OUT_OF_MEMORY; } - memset(ctx, 0, sizeof(candidate_contex_t)); ret = fota_bd_get_read_size(&ctx->bd_read_size); if (ret) { @@ -234,10 +244,14 @@ static int fota_candidate_extract_start(bool force_encrypt, const char *expected if (ctx->header_info.flags & (FOTA_HEADER_ENCRYPTED_FLAG | FOTA_HEADER_SUPPORT_RESUME_FLAG)) { block_size = ctx->header_info.block_size; } else { - block_size = MIN_FRAG_SIZE; + block_size = MBED_CLOUD_CLIENT_FOTA_CANDIDATE_BLOCK_SIZE; } block_size = FOTA_ALIGN_UP(block_size, ctx->bd_read_size); + // A very large install alignment size can be supported, but requires much extra logic. + // Enlarging block size should take care of it instead. + FOTA_ASSERT(block_size % install_alignment == 0); + // Block checker can be different here and have different sizes: // Tag (8 bytes) in encrypted case, checksum (2 bytes) in non-encrypted case (with resume support). if (ctx->header_info.flags & FOTA_HEADER_ENCRYPTED_FLAG) { @@ -251,16 +265,30 @@ static int fota_candidate_extract_start(bool force_encrypt, const char *expected #if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) if (ctx->header_info.flags & FOTA_HEADER_ENCRYPTED_FLAG) { - uint8_t fw_key[FOTA_ENCRYPT_KEY_SIZE]; + uint8_t fw_key[FOTA_ENCRYPT_KEY_SIZE] = {0}; + uint8_t zero_key[FOTA_ENCRYPT_KEY_SIZE] = {0}; + size_t volatile loop_check; +#if (MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION != FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY) ret = fota_nvm_fw_encryption_key_get(fw_key); +#else + ret = fota_decrypt_fw_key(fw_key, + ctx->header_info.encrypted_fw_key, + ctx->header_info.encrypted_fw_key_tag, + ctx->header_info.encrypted_fw_key_iv); +#endif if (ret) { FOTA_TRACE_ERROR("FW encryption key get failed. ret %d", ret); goto fail; } + // safely check that read key is non zero + FOTA_FI_SAFE_COND((fota_fi_memcmp(fw_key, zero_key, FOTA_ENCRYPT_KEY_SIZE, &loop_check) + && (loop_check == FOTA_ENCRYPT_KEY_SIZE)), FOTA_STATUS_INTERNAL_ERROR, + "Invalid encryption key read"); + ret = fota_encrypt_decrypt_start(&ctx->enc_ctx, fw_key, FOTA_ENCRYPT_KEY_SIZE); - memset(fw_key, 0, sizeof(fw_key)); + fota_fi_memset(fw_key, 0, sizeof(fw_key)); if (ret) { FOTA_TRACE_ERROR("Decrypt start failed. ret %d", ret); goto fail; @@ -275,14 +303,14 @@ static int fota_candidate_extract_start(bool force_encrypt, const char *expected ctx->curr_addr = ctx->data_start_addr; ctx->bytes_completed = 0; + ctx->frag_extra_bytes = 0; #if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) if (ctx->header_info.flags & FOTA_HEADER_ENCRYPTED_FLAG) { fota_encryption_stream_reset(ctx->enc_ctx); } #endif - // Install alignment of zero is just like an alignment of 1 (i.e. no limitation) - ctx->install_alignment = install_alignment ? install_alignment : 1; + ctx->install_alignment = install_alignment; free(ctx->fragment_buf); alloc_size = ctx->effective_block_size + ctx->block_checker_size; @@ -313,7 +341,6 @@ static int fota_candidate_extract_fragment(uint8_t **buf, size_t *actual_size, b FOTA_DBG_ASSERT(ctx); - // Move extra bytes from last time from end to beginning of buffer if (!*ignore) { memcpy(ctx->fragment_buf, ctx->fragment_buf + *actual_size, ctx->frag_extra_bytes); @@ -407,7 +434,16 @@ int fota_candidate_iterate_image(uint8_t validate, bool force_encrypt, const cha FOTA_ASSERT(handler); - ret = fota_candidate_extract_start(force_encrypt, expected_comp_name, 0); + // Make sure previous context is cleared (relevant mainly in tests) + cleanup(); + + // Install alignment of zero is just like an alignment of 1 (i.e. no limitation) + install_alignment = install_alignment ? install_alignment : 1; + + // Validation phase + + // Can use install alignment of 1 here, as this is just validation, no installation yet + ret = fota_candidate_extract_start(force_encrypt, expected_comp_name, 1); if (ret) { goto fail; } @@ -464,7 +500,6 @@ int fota_candidate_iterate_image(uint8_t validate, bool force_encrypt, const cha // Start iteration phase actual_size = 0; - ctx->frag_extra_bytes = 0; ignore = false; ret = fota_candidate_extract_start(force_encrypt, expected_comp_name, install_alignment); @@ -511,13 +546,10 @@ int fota_candidate_iterate_image(uint8_t validate, bool force_encrypt, const cha ret = handler(&cb_info); if (ret) { FOTA_TRACE_ERROR("Candidate user handler failed on finish, ret %d", ret); - goto fail; } fail: - if (hash_ctx) { - fota_hash_finish(&hash_ctx); - } + fota_hash_finish(&hash_ctx); cleanup(); return ret; } @@ -530,6 +562,14 @@ int fota_candidate_erase(void) return ret; } ret = fota_bd_erase(fota_candidate_get_config()->storage_start_addr, erase_size); + +#if defined(TARGET_LIKE_LINUX) + // for Linux remove both "update_storage" (blockdevice file) and "candidate" file(the FOTA candidate) + FOTA_TRACE_DEBUG("removing blockdevice and candidate files %s, %s:", fota_linux_get_update_storage_file_name(), fota_linux_get_candidate_file_name()); + remove(fota_linux_get_update_storage_file_name()); + remove(fota_linux_get_candidate_file_name()); //might not exist if user app moved it. +#endif + return ret; } diff --git a/fota/fota_candidate.h b/fota/fota_candidate.h index b5ef23f..e670455 100644 --- a/fota/fota_candidate.h +++ b/fota/fota_candidate.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -21,7 +21,7 @@ #include "fota/fota_base.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #include "fota/fota_header_info.h" #include "fota/fota_crypto_defs.h" @@ -146,6 +146,6 @@ int fota_candidate_erase(void); } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif // __FOTA_CANDIDATE_H_ diff --git a/fota/fota_component.c b/fota/fota_component.c index 08ff17b..5f9c5cd 100644 --- a/fota/fota_component.c +++ b/fota/fota_component.c @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -27,88 +27,185 @@ #include "fota/fota_component_internal.h" #include "fota/fota_status.h" -#if defined(TARGET_LIKE_LINUX) -#include -#include -#include -#endif // defined(TARGET_LIKE_LINUX) - #include #include #include -static unsigned int num_components = 0; -static fota_component_desc_t comp_table[FOTA_NUM_COMPONENTS]; +static unsigned int num_user_components = 0; +static fota_component_desc_t user_comp_table[FOTA_NUM_COMPONENTS]; +#define INT_COMP_ID_FLAG 0x80000000 +#ifdef FOTA_INTERNAL_COMPONENTS_SUPPORT +// Internal components - not reported to service +static unsigned int num_int_components = 0; +static fota_component_desc_t *int_comp_table; +#endif -#define MAJOR_NUM_BITS 24 +#define MAJOR_NUM_BITS 23 #define MINOR_NUM_BITS 24 #define SPLIT_NUM_BITS 16 #define MAX_VER 999 //num is between 0 and 999 (MAX_VER) -static char *append_number_to_string(char *str, uint_fast16_t num, char trail) { - if (num > 100) { - char p = '0' + num/100; +static char *append_number_to_string(char *str, uint_fast16_t num, char trail) +{ + if (num >= 100) { + char p = '0' + num / 100; *str++ = p; } - if (num > 10) { - *str++ = '0' + (num%100)/10; + if (num >= 10) { + *str++ = '0' + (num % 100) / 10; } - *str++ = '0' + (num%10); + *str++ = '0' + (num % 10); *str++ = trail; return str; } void fota_component_clean(void) { - num_components = 0; - memset(comp_table, 0, sizeof(comp_table)); + num_user_components = 0; + memset(user_comp_table, 0, sizeof(user_comp_table)); +#ifdef FOTA_INTERNAL_COMPONENTS_SUPPORT + num_int_components = 0; + free(int_comp_table); + int_comp_table = NULL; +#endif +} + +#ifdef FOTA_INTERNAL_COMPONENTS_SUPPORT +static inline void comp_id_translate(unsigned int *comp_id, fota_component_desc_t * *comp_table, unsigned int *num_components) +{ + if (*comp_id & INT_COMP_ID_FLAG) { + *comp_id &= ~INT_COMP_ID_FLAG; + *comp_table = int_comp_table; + *num_components = num_int_components; + } else { + *comp_table = user_comp_table; + *num_components = num_user_components; + } +} + +#endif + +bool fota_component_is_internal_component(unsigned int comp_id) +{ + return (comp_id & INT_COMP_ID_FLAG) ? true : false; } int fota_component_add(const fota_component_desc_info_t *comp_desc_info, const char *comp_name, const char *comp_semver) { - FOTA_ASSERT(num_components < FOTA_NUM_COMPONENTS); - FOTA_ASSERT(!(comp_desc_info->support_delta && (!comp_desc_info->curr_fw_get_digest || !comp_desc_info->curr_fw_read))); + fota_component_desc_t *comp_table = user_comp_table; + unsigned int *num_components = &num_user_components; - memcpy(&comp_table[num_components].desc_info, comp_desc_info, sizeof(*comp_desc_info)); - strncpy(comp_table[num_components].name, comp_name, FOTA_COMPONENT_MAX_NAME_SIZE - 1); - fota_component_version_semver_to_int(comp_semver, &comp_table[num_components].version); + if (!comp_name) { + FOTA_TRACE_ERROR("Empty component name"); + return FOTA_STATUS_INVALID_ARGUMENT; + } + if (comp_desc_info->support_delta && (!comp_desc_info->curr_fw_get_digest || !comp_desc_info->curr_fw_read)) { + FOTA_TRACE_ERROR("empty fields in component description"); + return FOTA_STATUS_INVALID_ARGUMENT; + } + +#ifdef FOTA_INTERNAL_COMPONENTS_SUPPORT + if (comp_name[0] == '%') { + comp_table = malloc((num_int_components + 1) * sizeof(fota_component_desc_t)); + if (!comp_table) { + FOTA_TRACE_ERROR("comp_table wasn't allocated"); + return FOTA_STATUS_OUT_OF_MEMORY; + } + memcpy(comp_table, int_comp_table, num_int_components * sizeof(fota_component_desc_t)); + free(int_comp_table); + int_comp_table = comp_table; + num_components = &num_int_components; + } else { + if (*num_components > FOTA_NUM_COMPONENTS) { + FOTA_TRACE_ERROR("Wrong number of components"); + return FOTA_STATUS_INVALID_ARGUMENT; + } + } +#else + if (*num_components > FOTA_NUM_COMPONENTS) { + FOTA_TRACE_ERROR("Wrong number of components"); + return FOTA_STATUS_INVALID_ARGUMENT; + } +#endif + if (comp_desc_info->need_reboot == false && comp_table == user_comp_table){ - num_components++; + FOTA_TRACE_ERROR("Component with need_reboot false is not supported"); + return FOTA_STATUS_INVALID_ARGUMENT; + } + memcpy(&comp_table[*num_components].desc_info, comp_desc_info, sizeof(*comp_desc_info)); + strncpy(comp_table[*num_components].name, comp_name, FOTA_COMPONENT_MAX_NAME_SIZE - 1); + fota_component_version_semver_to_int(comp_semver, &comp_table[*num_components].version); + + (*num_components)++; return FOTA_STATUS_SUCCESS; } unsigned int fota_component_num_components(void) { - return num_components; + return num_user_components; } void fota_component_get_desc(unsigned int comp_id, const fota_component_desc_t * *comp_desc) { - FOTA_ASSERT(comp_id < num_components) + fota_component_desc_t *comp_table = user_comp_table; + unsigned int num_components = num_user_components; + +#ifdef FOTA_INTERNAL_COMPONENTS_SUPPORT + comp_id_translate(&comp_id, &comp_table, &num_components); +#endif + + FOTA_ASSERT(comp_id < num_components); *comp_desc = &comp_table[comp_id]; } void fota_component_get_curr_version(unsigned int comp_id, fota_component_version_t *version) { - FOTA_ASSERT(comp_id < num_components) + fota_component_desc_t *comp_table = user_comp_table; + unsigned int num_components = num_user_components; + +#ifdef FOTA_INTERNAL_COMPONENTS_SUPPORT + comp_id_translate(&comp_id, &comp_table, &num_components); +#endif + FOTA_ASSERT(comp_id < num_components); *version = comp_table[comp_id].version; } void fota_component_set_curr_version(unsigned int comp_id, fota_component_version_t version) { - FOTA_ASSERT(comp_id < num_components) + fota_component_desc_t *comp_table = user_comp_table; + unsigned int num_components = num_user_components; + +#ifdef FOTA_INTERNAL_COMPONENTS_SUPPORT + comp_id_translate(&comp_id, &comp_table, &num_components); +#endif + FOTA_ASSERT(comp_id < num_components); comp_table[comp_id].version = version; } int fota_component_name_to_id(const char *name, unsigned int *comp_id) { + fota_component_desc_t *comp_table = user_comp_table; + unsigned int num_components = num_user_components; + +#ifdef FOTA_INTERNAL_COMPONENTS_SUPPORT + if (name[0] == '%') { + comp_table = int_comp_table; + num_components = num_int_components; + } +#endif + int i = num_components; // One or more components do { if (!strncmp(name, comp_table[num_components - i].name, FOTA_COMPONENT_MAX_NAME_SIZE)) { *comp_id = num_components - i; +#ifdef FOTA_INTERNAL_COMPONENTS_SUPPORT + if (name[0] == '%') { + *comp_id |= INT_COMP_ID_FLAG; + } +#endif return FOTA_STATUS_SUCCESS; } } while (--i); @@ -118,25 +215,32 @@ int fota_component_name_to_id(const char *name, unsigned int *comp_id) int fota_component_version_int_to_semver(fota_component_version_t version, char *sem_ver) { -#if MAJOR_NUM_BITS > 32 || MINOR_NUM_BITS > 32 || SPLIT_NUM_BITS > 32 -#error "Assuming 32-bit version components" -#endif uint32_t major, minor, split; uint64_t full_mask = 0xFFFFFFFFFFFFFFFFULL; int ret = FOTA_STATUS_SUCCESS; char *tmp = sem_ver; - split = version & ~(full_mask << SPLIT_NUM_BITS); - minor = (version & ~(full_mask << (SPLIT_NUM_BITS + MINOR_NUM_BITS))) >> SPLIT_NUM_BITS; - major = version >> (SPLIT_NUM_BITS + MINOR_NUM_BITS); + if (version & FOTA_COMPONENT_SEMVER_BIT) { + split = version & ~(full_mask << SPLIT_NUM_BITS); + minor = (version & ~(full_mask << (SPLIT_NUM_BITS + MINOR_NUM_BITS))) >> SPLIT_NUM_BITS; + major = (version & ~FOTA_COMPONENT_SEMVER_BIT) >> (SPLIT_NUM_BITS + MINOR_NUM_BITS); - if ((major > MAX_VER) || (minor > MAX_VER) || (split > MAX_VER)) { - ret = FOTA_STATUS_INTERNAL_ERROR; + if ((major > MAX_VER) || (minor > MAX_VER) || (split > MAX_VER)) { + ret = FOTA_STATUS_INTERNAL_ERROR; + } + + //These are only needed if above check fails (unittests only) + split = MIN(split, MAX_VER); + minor = MIN(minor, MAX_VER); + major = MIN(major, MAX_VER); + + } else { + // assume client migrate to fota and the version represent v1 timestamp + // set default SemVer to 0.0.0 + split = 0; + minor = 0; + major = 0; } - //These are only needed if above check fails (unittests only) - split = MIN(split, MAX_VER); - minor = MIN(minor, MAX_VER); - major = MIN(major, MAX_VER); //ouput is "major.minor.split\0" tmp = append_number_to_string(tmp, major, '.'); @@ -172,43 +276,12 @@ int fota_component_version_semver_to_int(const char *sem_ver, fota_component_ver minor = MIN(minor, MAX_VER); major = MIN(major, MAX_VER); - *version = ((uint64_t) split) | ((uint64_t) minor << SPLIT_NUM_BITS) | ((uint64_t) major << (SPLIT_NUM_BITS + MINOR_NUM_BITS)); + *version = FOTA_COMPONENT_SEMVER_BIT | + ((uint64_t) split) | + ((uint64_t) minor << SPLIT_NUM_BITS) | + ((uint64_t) major << (SPLIT_NUM_BITS + MINOR_NUM_BITS)); } return ret; } -#if defined(TARGET_LIKE_LINUX) - -extern char *program_invocation_name; - -int fota_component_install_main(const char *candidate_file_name) -{ - unsigned int file_mode = ALLPERMS; - struct stat statbuf; - - FOTA_TRACE_INFO("Installing MAIN component"); - - // get current file permissions - if (stat(program_invocation_name, &statbuf) == 0) { - file_mode = statbuf.st_mode & 0x1FF; - } - - // unlink current file - if (unlink(program_invocation_name) != 0) { - FOTA_TRACE_ERROR("Failed to unlink file %s: %s", program_invocation_name, strerror(errno)); - return FOTA_STATUS_INTERNAL_ERROR; - } - - // change file permission to same as previously - chmod(candidate_file_name, file_mode); - - if (rename(candidate_file_name, program_invocation_name) != 0) { - FOTA_TRACE_ERROR("Failed to rename file %s: %s", candidate_file_name, strerror(errno)); - return FOTA_STATUS_INTERNAL_ERROR; - } - - return FOTA_STATUS_SUCCESS; -} -#endif //defined(TARGET_LIKE_LINUX) - #endif // MBED_CLOUD_CLIENT_FOTA_ENABLE diff --git a/fota/fota_component.h b/fota/fota_component.h index 22d90d8..f824926 100644 --- a/fota/fota_component.h +++ b/fota/fota_component.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -21,52 +21,75 @@ #include "fota/fota_base.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #include "fota/fota_component_defs.h" #include "fota/fota_crypto_defs.h" #include "fota/fota_candidate.h" +#include "fota/fota_header_info.h" #ifdef __cplusplus extern "C" { #endif /** - * Callback to read current firmware, required only if delta supported. + * @file fota_component.h + * \brief Functions required for implementing the component update feature. + */ + +/** + * A callback function to read the current firmware. + * Required only if delta update is supported for this component. + * + * \param[out] buf Buffer with data about the current firmware. + * \param[in] offset Offset from which to read. + * \param[in] size Output buffer size. + * \param[in] num_read Actual size read. * - * \param[out] buf buffer with curr fw data. - * \param[in] offset offset where to read from. - * \param[in] size output buffer size. - * \param[in] num_read actual size read. - * \return FOTA_STATUS_SUCCESS for succesfull read. + * \return ::FOTA_STATUS_SUCCESS for a successful read operation. */ typedef int (*fota_component_curr_fw_read)(uint8_t *buf, size_t offset, size_t size, size_t *num_read); /** - * Callback to get current firmware digest, required only if delta supported. + * A callback function to get the current firmware digest. + * Required only if delta update is supported for this component. * - * \param[out] buf buffer with current firmware digest, should be big enought to hold it. - * \return FOTA_STATUS_SUCCESS on success. + * \param[out] buf A buffer with the current firmware digest. + * Make sure the size of the buffer is sufficient to hold the digest. + * + * \return ::FOTA_STATUS_SUCCESS on success. */ typedef int (*fota_component_curr_fw_get_digest)(uint8_t *buf); /** - * Callback called handle post install. + * A callback function to verify component installation success. + * Executed after component installation. + * + * \param[in] component_name Name of the installed component. The same name that was specified as an argument to ::fota_component_add(). + * \param[in] expected_header_info Header with expected values for installed components. * - * \return FOTA_STATUS_SUCCESS on success. + * \return ::FOTA_STATUS_SUCCESS on success. */ -typedef int (*fota_component_post_install_handler_t)(const char *new_sem_ver); +typedef int (*fota_component_verify_install_handler_t)(const char *comp_name, const fota_header_info_t *expected_header_info); + /** - * Component description info + * Component description information. * - * install_alignment If set to non-zero, fragment sizes returned to the user will be aligned to this value. - * candidate_iterate_cb callback to candidate iterate firmware function. Note: for Linux systems this iterative callback is replaced by fota_app_on_install_candidate - * support_delta if delta update supported for component. - * component_post_install_cb callback to for post install component check. - * curr_fw_read callback to read current firmware. Required only if delta is supported for current component, NULL otherwise. - * curr_fw_get_digest callback to get current firmware digest. Required only if delta is supported for current component, NULL otherwise. - * need_reboot if reboot required after installation. + * @param install_alignment The preferred installer fragment size. Typically equal to the flash program size. + * @param support_delta Specify whether the component supports differential (delta) update. + * @param need_reboot Specify whether the component requires system reboot after the component installation has completed. + Only `true` parameter is currently supported. + * @param candidate_iterate_cb A callback function the FOTA client calls for installing the candidate. + * The FOTA client calls the callback iteratively with a firmware fragment buffer pointer as an argument. + * Note: For Linux systems, this iterative callback is replaced by ::fota_app_on_install_candidate(). + * @param component_verify_install_cb A callback function to be executed after installation, verifying installation. + * @param curr_fw_read Only required if ::support delta is set to true. + * A helper function for reading the currently installed firmware of the component. + * A callback to read the current firmware. + * @param curr_fw_get_digest Only required if ::support delta is set to true. + * A helper function for calculating the SHA256 digest of the currently installed firmware of the component. + * A callback to get the current firmware digest. */ typedef struct { uint32_t install_alignment; @@ -75,50 +98,41 @@ typedef struct { #if !defined(TARGET_LIKE_LINUX) fota_candidate_iterate_handler_t candidate_iterate_cb; #endif - fota_component_post_install_handler_t component_post_install_cb; + fota_component_verify_install_handler_t component_verify_install_cb; fota_component_curr_fw_read curr_fw_read; fota_component_curr_fw_get_digest curr_fw_get_digest; } fota_component_desc_info_t; /** - * Component registration, adding to component database. - * Component description should reside in text section to prevent unnecessary allocations and memory copies. + * Component registration. + * Adds the component to the component database. + * The function should be called from the ::fota_platform_init_hook() function. + * The component description should reside in the stack to prevent unnecessary allocations and memory copies. + * + * \param[in] comp_desc Component description with required information. + * \param[in] comp_name A string value representing the component name to add. Maximum length is ::FOTA_COMPONENT_MAX_NAME_SIZE including NULL termination. Must not start with "%". + * \param[in] comp_semver A string value representing the [Semantic Version](https://semver.org/) of the component firmware installed at the factory. * - * \param[in] comp_desc component description info with required information. - * \param[in] comp_name component name to add. - * \param[in] comp_semver component semver. - * \return FOTA_STATUS_SUCCESS on success. + * \return ::FOTA_STATUS_SUCCESS on success. */ int fota_component_add(const fota_component_desc_info_t *comp_desc, const char *comp_name, const char *comp_semver); /** - * Convert internal FOTA library semantic version representation to human readable string. + * Convert internal FOTA library semantic version representation to a human-readable string. * - * The version in internal FOTA library representation passed to fota_app_on_download_authorization() and + * The version in the internal FOTA library representation passed to ::fota_app_on_download_authorization() and * candidate callback APIs. + * + * \param[in] version Internal version representation. Component description with required information. + * \param[in] sem_ver + * + * \return ::FOTA_STATUS_SUCCESS on success. */ int fota_component_version_int_to_semver(fota_component_version_t version, char *sem_ver); - -#if defined(TARGET_LIKE_LINUX) -/** - * Install MAIN component by overwriting current executable file - * - * This function will overwrite the executable file and relaunch the process. - * The API is expected to be called from fota_app_on_install_candidate() application - * callback - * - * \note This function does not vavlidate candidate file integrity or authenticity. - * - * \param candidate_file_name candidate file name as found on file system. - */ -int fota_component_install_main(const char *candidate_file_name); - -#endif // defined(TARGET_LIKE_LINUX) - #ifdef __cplusplus } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif // __FOTA_COMPONENT_H_ diff --git a/fota/fota_component_defs.h b/fota/fota_component_defs.h index ed8b214..c9de19d 100644 --- a/fota/fota_component_defs.h +++ b/fota/fota_component_defs.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -27,8 +27,11 @@ #endif #endif +#define FOTA_COMPONENT_SEMVER_BIT ((uint64_t)1 << 55) #define FOTA_COMPONENT_MAX_NAME_SIZE 9 #define FOTA_COMPONENT_MAX_SEMVER_STR_SIZE 12 + +#define FOTA_COMPONENT_MAIN_COMP_NUM 0 #define FOTA_COMPONENT_MAIN_COMPONENT_NAME "MAIN" typedef uint64_t fota_component_version_t; diff --git a/fota/fota_component_internal.h b/fota/fota_component_internal.h index af3a1ac..3a81f73 100644 --- a/fota/fota_component_internal.h +++ b/fota/fota_component_internal.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -21,7 +21,7 @@ #include "fota/fota_base.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #include "fota/fota_component.h" @@ -47,10 +47,12 @@ int fota_component_name_to_id(const char *name, unsigned int *comp_id); // Semantic version translation int fota_component_version_semver_to_int(const char *sem_ver, fota_component_version_t *version); +bool fota_component_is_internal_component(unsigned int comp_id); + #ifdef __cplusplus } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif // __FOTA_COMPONENT_INTERNAL_H_ diff --git a/fota/fota_config.h b/fota/fota_config.h index 3cd840a..3d9a59d 100644 --- a/fota/fota_config.h +++ b/fota/fota_config.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -37,45 +37,189 @@ #define FOTA_RESUME_SUPPORT_RESTART 1 #define FOTA_RESUME_SUPPORT_RESUME 2 -#define FOTA_INTERNAL_FLASH_BD 1 -#define FOTA_CUSTOM_BD 2 -#define FOTA_EXTERNAL_BD 3 +#define FOTA_INTERNAL_FLASH_MBED_OS_BD 1 +#define FOTA_CUSTOM_MBED_OS_BD 2 +#define FOTA_EXTERNAL_BD 3 +#define FOTA_DEFAULT_MBED_OS_BD 4 -#if defined(TARGET_LIKE_LINUX) -#define MBED_CLOUD_CLIENT_FOTA_STORAGE_START_ADDR 0 +#define FOTA_MULTICAST_UNSUPPORTED 0 +#define FOTA_MULTICAST_NODE_MODE 1 +#define FOTA_MULTICAST_BR_MODE 2 -#if !defined(MBED_CLOUD_CLIENT_FOTA_STORAGE_SIZE) -#define MBED_CLOUD_CLIENT_FOTA_STORAGE_SIZE 0x100000000 +#define MBED_CLOUD_CLIENT_FOTA_COAP_DOWNLOAD 1 +#define MBED_CLOUD_CLIENT_FOTA_CURL_HTTP_DOWNLOAD 2 + +#if defined(MBED_CLOUD_CLIENT_SUPPORT_UPDATE) + +#define FOTA_SHIM_LAYER +#define FOTA_MANIFEST_SCHEMA_VERSION 3 + +#ifndef FOTA_DEFAULT_APP_IFS +#define FOTA_DEFAULT_APP_IFS 0 #endif -#define MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE FOTA_EXTERNAL_BD +#undef MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT +#define MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT FOTA_RESUME_UNSUPPORTED -#if !defined(FOTA_UNIT_TEST) -#define FOTA_CUSTOM_CURR_FW_STRUCTURE 1 +#define MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION 2 + +#define ARM_UCP_FLASHIAP_MCUBOOT 10100 +#define ARM_UCP_FLASHIAP_BLOCKDEVICE 10101 +#define ARM_UCP_FLASHIAP 10110 + +#if MBED_CLOUD_CLIENT_UPDATE_STORAGE == ARM_UCP_FLASHIAP + +#define MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE FOTA_INTERNAL_FLASH_MBED_OS_BD +#define MBED_CLOUD_CLIENT_FOTA_STORAGE_SIZE MBED_CONF_UPDATE_CLIENT_STORAGE_SIZE +#define MBED_CLOUD_CLIENT_FOTA_STORAGE_START_ADDR MBED_CONF_UPDATE_CLIENT_STORAGE_ADDRESS + MBED_ROM_START + +#elif MBED_CLOUD_CLIENT_UPDATE_STORAGE == ARM_UCP_FLASHIAP_BLOCKDEVICE + +#define MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE FOTA_DEFAULT_MBED_OS_BD +#define MBED_CLOUD_CLIENT_FOTA_STORAGE_SIZE MBED_CONF_UPDATE_CLIENT_STORAGE_SIZE +#define MBED_CLOUD_CLIENT_FOTA_STORAGE_START_ADDR MBED_CONF_UPDATE_CLIENT_STORAGE_ADDRESS +#define MBED_CLOUD_CLIENT_FOTA_FW_HEADER_EXTERNAL 1 // v2 external header + +#elif MBED_CLOUD_CLIENT_UPDATE_STORAGE == ARM_UCP_FLASHIAP_MCUBOOT +#error "ARM_UCP_FLASHIAP_MCUBOOT is not supported" + +#endif // MBED_CLOUD_CLIENT_UPDATE_STORAGE == ARM_UCP_FLASHIAP + +#define MBED_CLOUD_CLIENT_UPDATE_DOWNLOAD_PROTOCOL_COAP 1 +#define MBED_CLOUD_CLIENT_UPDATE_DOWNLOAD_PROTOCOL_HTTP 2 + +#if defined(MBED_CONF_MBED_CLOUD_CLIENT_UPDATE_DOWNLOAD_PROTOCOL) +#if MBED_CONF_MBED_CLOUD_CLIENT_UPDATE_DOWNLOAD_PROTOCOL == MBED_CLOUD_CLIENT_UPDATE_DOWNLOAD_PROTOCOL_COAP +#define MBED_CLOUD_CLIENT_FOTA_DOWNLOAD MBED_CLOUD_CLIENT_FOTA_COAP_DOWNLOAD +#elif MBED_CONF_MBED_CLOUD_CLIENT_UPDATE_DOWNLOAD_PROTOCOL == MBED_CLOUD_CLIENT_UPDATE_DOWNLOAD_PROTOCOL_HTTP +#define MBED_CLOUD_CLIENT_FOTA_DOWNLOAD MBED_CLOUD_CLIENT_FOTA_CURL_HTTP_DOWNLOAD +#else +#error "Invalid value for MBED_CONF_MBED_CLOUD_CLIENT_UPDATE_DOWNLOAD_PROTOCOL. Must be either MBED_CLOUD_CLIENT_UPDATE_DOWNLOAD_PROTOCOL_HTTP or MBED_CLOUD_CLIENT_UPDATE_DOWNLOAD_PROTOCOL_COAP." +#endif +#endif // MBED_CONF_MBED_CLOUD_CLIENT_UPDATE_DOWNLOAD_PROTOCOL + +#if defined(MBED_CLOUD_CLIENT_SUPPORT_MULTICAST_UPDATE) +#if defined(MBED_CLOUD_CLIENT_MULTICAST_BORDER_ROUTER) +#define MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT FOTA_MULTICAST_BR_MODE +#else +#define MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT FOTA_MULTICAST_NODE_MODE +#endif +#endif // MBED_CLOUD_CLIENT_SUPPORT_MULTICAST_UPDATE + +#endif //defined(MBED_CLOUD_CLIENT_SUPPORT_UPDATE) + +// Right now, PAL support has a one to one relation with client profile. +// This can change in the future. +#if MBED_CLOUD_CLIENT_PROFILE == MBED_CLOUD_CLIENT_PROFILE_LITE +#ifndef MBED_CLOUD_CLIENT_FOTA_SUPPORT_PAL +#define MBED_CLOUD_CLIENT_FOTA_SUPPORT_PAL 0 +#endif +#elif MBED_CLOUD_CLIENT_PROFILE == MBED_CLOUD_CLIENT_PROFILE_FULL +#ifndef MBED_CLOUD_CLIENT_FOTA_SUPPORT_PAL +#define MBED_CLOUD_CLIENT_FOTA_SUPPORT_PAL 1 +#endif +#else +#error Client profile not defined #endif +#if defined(TARGET_LIKE_LINUX) + +#if !defined(MBED_CLOUD_CLIENT_FOTA_STORAGE_SIZE) +// No limit +#define MBED_CLOUD_CLIENT_FOTA_STORAGE_SIZE ((size_t) -1) +#endif + +// Following files are preceded by mbed client config directory, which defaults to the one used by storage +// It can be overridden by defining MBED_CLOUD_CLIENT_FOTA_LINUX_CONFIG_DIR + #if !defined(MBED_CLOUD_CLIENT_FOTA_LINUX_HEADER_FILENAME) #define MBED_CLOUD_CLIENT_FOTA_LINUX_HEADER_FILENAME "fota_fw_metadata" #endif +#define MBED_CLOUD_CLIENT_FOTA_LINUX_TEMP_HEADER_FILENAME MBED_CLOUD_CLIENT_FOTA_LINUX_HEADER_FILENAME ".tmp" + #if !defined(MBED_CLOUD_CLIENT_FOTA_LINUX_UPDATE_STORAGE_FILENAME) -#define MBED_CLOUD_CLIENT_FOTA_LINUX_UPDATE_STORAGE_FILENAME "fota_candidate" +#define MBED_CLOUD_CLIENT_FOTA_LINUX_UPDATE_STORAGE_FILENAME "fota_update_storage" #endif #if !defined(MBED_CLOUD_CLIENT_FOTA_LINUX_CANDIDATE_FILENAME) -#define MBED_CLOUD_CLIENT_FOTA_LINUX_CANDIDATE_FILENAME "fota_raw_candidate" +#define MBED_CLOUD_CLIENT_FOTA_LINUX_CANDIDATE_FILENAME "fota_candidate" #endif +#if defined(FOTA_UNIT_TEST) +// Unit tests don't actually replace the current running app, but use a test file +extern char *unitest_curr_fw_filename; +#define MBED_CLOUD_CLIENT_FOTA_LINUX_CURR_FW_FILENAME unitest_curr_fw_filename +#else +// Real application - replace real exe file +#include +extern char *program_invocation_name; +#define MBED_CLOUD_CLIENT_FOTA_LINUX_CURR_FW_FILENAME program_invocation_name +#endif + +// No legacy bootloader here - force up to date header +#undef MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION +#define MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION 3 + #endif // defined(TARGET_LIKE_LINUX) +// Set this flag to 1 to use custom verification logic for main app installation +#if !defined(FOTA_CUSTOM_MAIN_APP_VERIFY_INSTALL) +// Use default verification logic otherwise +#define FOTA_CUSTOM_MAIN_APP_VERIFY_INSTALL 0 +#endif + +// Set this flag to 0 to save post upgrade verification code +#if !defined(FOTA_VERIFY_INSTALLATION_AFTER_UPGRADE) +// Logic should be enabled by default otherwise +#define FOTA_VERIFY_INSTALLATION_AFTER_UPGRADE 1 +#endif + #ifndef MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE -#error Block device type must be defined +#define MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE FOTA_EXTERNAL_BD // if not defined - fall back to an external configuration +#endif + +#if MBED_CLOUD_CLIENT_FOTA_EXTERNAL_DOWNLOADER +// External downloader means that fragments are received externally, just like multicast node mode +// So this basically imitates the multicast node mode +#if defined(MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT) && (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT != FOTA_MULTICAST_NODE_MODE) +#error External downloader mode can only be supported with multicast node mode +#else +#undef MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT +#define MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT FOTA_MULTICAST_NODE_MODE +#endif +#endif + +#ifndef MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT +#define MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT FOTA_MULTICAST_UNSUPPORTED #endif +#if (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_INTERNAL_FLASH_MBED_OS_BD) || (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_DEFAULT_MBED_OS_BD ) || (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_CUSTOM_MBED_OS_BD ) + +// on Mbed-OS port, when using internal flash or custom BlockDevices - it is expected for configuration to +// provide storage start address and size #if !defined(MBED_CLOUD_CLIENT_FOTA_STORAGE_SIZE) || (MBED_CLOUD_CLIENT_FOTA_STORAGE_SIZE == 0) #error Storage size should be defined and have a nonzero value #endif +#if !defined(MBED_CLOUD_CLIENT_FOTA_STORAGE_START_ADDR) +#error "MBED_CLOUD_CLIENT_FOTA_STORAGE_START_ADDR must be set" +#endif + +#if (MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT == FOTA_RESUME_SUPPORT_RESUME) +#if !defined(FOTA_BD_SIMULATE_ERASE) +#define FOTA_BD_SIMULATE_ERASE 1 +#endif +#endif + +#endif // (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_INTERNAL_FLASH_MBED_OS_BD) || (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_DEFAULT_MBED_OS_BD ) || (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_CUSTOM_MBED_OS_BD ) + +#if MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_EXTERNAL_BD +#ifndef MBED_CLOUD_CLIENT_FOTA_STORAGE_START_ADDR +#define MBED_CLOUD_CLIENT_FOTA_STORAGE_START_ADDR 0 +#endif +#endif + #if !defined(FOTA_MANIFEST_SCHEMA_VERSION) #define FOTA_MANIFEST_SCHEMA_VERSION 3 #endif @@ -102,22 +246,65 @@ #define MBED_CLOUD_CLIENT_FOTA_CANDIDATE_BLOCK_SIZE 1024 #endif -#if defined(FOTA_USE_EXTERNAL_UPDATE_RAW_PUBLIC_KEY) && !defined(FOTA_USE_UPDATE_RAW_PUBLIC_KEY) -#define FOTA_USE_UPDATE_RAW_PUBLIC_KEY +#if !defined(MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT) +#define MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT 0 +#endif // !defined(MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT) + +#define FOTA_USE_DEVICE_KEY 1 +#define FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY 2 + +#if MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1 && !defined(MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION) +#define MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY +#endif + +#if MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 0 + +#if defined(MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION) +#warning MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION is ignored if MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT is disabled +#endif + +#else // MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1 + +#if (MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION != FOTA_USE_DEVICE_KEY) && \ + (MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION != FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY) +#error Unknown MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION type +#endif + +#if (MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION == FOTA_USE_DEVICE_KEY) +#warning FOTA_USE_DEVICE_KEY is deprecated and contains security vulnerability. \ + Please consider using FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY instead. #endif -#if !(FOTA_MANIFEST_SCHEMA_VERSION == 1) +#if (MBED_CLOUD_CLIENT_FOTA_CANDIDATE_BLOCK_SIZE != FOTA_CLOUD_ENCRYPTION_BLOCK_SIZE) +#warning 'encrypted-raw' payload format will not be supported because \ + MBED_CLOUD_CLIENT_FOTA_CANDIDATE_BLOCK_SIZE is different than cloud encryption block size. +#endif + +#endif + +#define FOTA_PUBLIC_KEY_NOT_SUPPORTED_FORMAT 0 +#define FOTA_RAW_PUBLIC_KEY_FORMAT 1 +#define FOTA_X509_PUBLIC_KEY_FORMAT 2 + // manifest schema V3 (and newer) support public key in both // uncompressed elliptic curve point format (X9.62) and x509 // x509 is used by default. x9.62 is used for optimizations // but requires integration with FCU tool and crypto backend. -#if !defined(FOTA_USE_UPDATE_RAW_PUBLIC_KEY) -#define FOTA_USE_UPDATE_X509 +#if !defined(MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT) +#if defined(FOTA_USE_EXTERNAL_UPDATE_RAW_PUBLIC_KEY) +#define MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT FOTA_RAW_PUBLIC_KEY_FORMAT +#else +#define MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT FOTA_X509_PUBLIC_KEY_FORMAT +#endif +#endif + +#if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT == FOTA_RAW_PUBLIC_KEY_FORMAT) && (FOTA_MANIFEST_SCHEMA_VERSION == 1) +#error manifest schema V1 only supports public key in x.509 format +#endif + +#if defined(FOTA_USE_EXTERNAL_UPDATE_RAW_PUBLIC_KEY) && (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT == FOTA_X509_PUBLIC_KEY_FORMAT) +#error public key in x.509 format not allowed for external raw public key configuration #endif -#else // (FOTA_MANIFEST_SCHEMA_VERSION == 1) -// manifest schema V1 only supports public key in x.509 format -#define FOTA_USE_UPDATE_X509 -#endif //!(FOTA_MANIFEST_SCHEMA_VERSION == 1) #if (FOTA_MANIFEST_SCHEMA_VERSION < 3) @@ -152,27 +339,41 @@ #define MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION 3 #endif -#if (MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION >= 3) +#undef FOTA_INTERNAL_COMPONENTS_SUPPORT +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_BR_MODE) +#if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) || (MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT == FOTA_RESUME_SUPPORT_RESUME) +#error Multicast border router mode does not support encryption or full resume +#endif +#define FOTA_INTERNAL_COMPONENTS_SUPPORT 1 +#endif -#define FOTA_HEADER_HAS_CANDIDATE_READY 1 +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_NODE_MODE) +#if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) || (MBED_CLOUD_CLIENT_FOTA_RESUME_SUPPORT == FOTA_RESUME_SUPPORT_RESUME) +#error Multicast node mode does not support encryption or full resume +#endif +#endif -#if !defined(MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT) -// set candidate encryption flag to false by default for internal flash -#if (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_INTERNAL_FLASH_BD) -#define MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT 0 -#else -#define MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT 1 +#undef FOTA_COMPONENT_SUPPORT +#if (FOTA_NUM_COMPONENTS > 1) || defined(FOTA_INTERNAL_COMPONENTS_SUPPORT) || defined(TARGET_LIKE_LINUX) +#define FOTA_COMPONENT_SUPPORT 1 #endif -#endif // !defined(MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT) +#if (MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION >= 3) + +#define FOTA_HEADER_HAS_CANDIDATE_READY 1 #else // LEGACY profile (MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION == 2) +#if (FOTA_NUM_COMPONENTS > 1) || defined(FOTA_INTERNAL_COMPONENTS_SUPPORT) +// Special case: support component update on legacy devices. +// Here main component won't have the candidate ready header, but all other components will. +#define FOTA_HEADER_HAS_CANDIDATE_READY 1 +#else +// Regular legacy case #define FOTA_HEADER_HAS_CANDIDATE_READY 0 +#endif -#if !defined(MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT) -#define MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT 0 -#elif (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) +#if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) #error MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT enabled only for header version >= 3 #endif // !defined(MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT) @@ -184,9 +385,6 @@ #endif // (MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION >= 3) -#define MBED_CLOUD_CLIENT_FOTA_COAP_DOWNLOAD 1 -#define MBED_CLOUD_CLIENT_FOTA_CURL_HTTP_DOWNLOAD 2 - #if !defined(MBED_CLOUD_CLIENT_FOTA_DOWNLOAD) #if defined(TARGET_LIKE_LINUX) #define MBED_CLOUD_CLIENT_FOTA_DOWNLOAD MBED_CLOUD_CLIENT_FOTA_CURL_HTTP_DOWNLOAD @@ -212,10 +410,6 @@ #define FOTA_MCCP_PROTOCOL_VERSION 4 #endif -#if !defined(MBED_CLOUD_CLIENT_FOTA_STORAGE_START_ADDR) -#error "MBED_CLOUD_CLIENT_FOTA_STORAGE_START_ADDR must be set" -#endif - #endif // MBED_CLOUD_CLIENT_FOTA_ENABLE #endif // __FOTA_CONFIG_H_ diff --git a/fota/fota_crypto.c b/fota/fota_crypto.c index 547d550..126b4f7 100644 --- a/fota/fota_crypto.c +++ b/fota/fota_crypto.c @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -26,25 +26,27 @@ #include "fota/fota_status.h" #include "fota/fota_crypto_defs.h" #include "fota/fota_nvm.h" +#include "fota_device_key.h" #include "mbedtls/sha256.h" #include "mbedtls/entropy.h" #include "mbedtls/ccm.h" #include "mbedtls/aes.h" +#include "mbedtls/md.h" #include "mbedtls/platform_util.h" -#if defined(FOTA_USE_UPDATE_RAW_PUBLIC_KEY) && defined(MBEDTLS_USE_TINYCRYPT) +#if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT == FOTA_RAW_PUBLIC_KEY_FORMAT) && defined(MBEDTLS_USE_TINYCRYPT) #include "tinycrypt/ecc.h" #include "tinycrypt/ecc_dsa.h" #include "fota/fota_nvm.h" #endif -#if defined(FOTA_USE_UPDATE_RAW_PUBLIC_KEY) && defined(MBEDTLS_ECDSA_C) +#if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT == FOTA_RAW_PUBLIC_KEY_FORMAT) && defined(MBEDTLS_ECDSA_C) #include "mbedtls/ecdsa.h" #include "mbedtls/ecp.h" #include "mbedtls/bignum.h" #endif -#if defined(FOTA_USE_UPDATE_X509) +#if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT == FOTA_X509_PUBLIC_KEY_FORMAT) #include "mbedtls/x509_crt.h" #include "mbedtls/x509.h" #endif @@ -76,45 +78,51 @@ typedef struct fota_encrypt_context_s { #define FOTA_TRACE_TLS_ERR(err) FOTA_TRACE_DEBUG("mbedTLS error %d", err) -#if FOTA_KEY_FORCE_DERIVATION -static int derive_key(uint8_t *key, uint32_t key_size) +#define FOTA_DERIVE_KEY_BITS 128 + +#if (MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION == FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY) +#define INITIAL_IV_VALUE 1 +#else +#define INITIAL_IV_VALUE 0 +#endif + +#if !defined(FOTA_USE_EXTERNAL_SECRET_DERIVATION_STRING) +// Key derivation according to NIST Special Publication 800-108 +// Using KDF in Counter Mode +// For i = 1 to n, do +// K(i) := PRF (KI, [i]2 || Label || 0x00 || Context || [L]2) +// We have only one iteration here, key size is 128 bits + +// Building the input : +// 01 - i +// FOTA - Label +// 00 - separator +// ranadom value - Context +// [L]2 - key lenght +const unsigned char* fota_get_derivation_string(void) { - static const uint8_t derivation_param[] = "DeriveParam"; - uint8_t sha256_buf[FOTA_CRYPTO_HASH_SIZE]; - int ret; - int flow_control = 0; - FOTA_DBG_ASSERT(key); - FOTA_DBG_ASSERT(key_size >= FOTA_ENCRYPT_KEY_SIZE); - FOTA_DBG_ASSERT(key_size <= FOTA_CRYPTO_HASH_SIZE); - // We will only be using 128 bits secret key for key derivation. - // Larger input keys will be truncated to 128 bit length */ - ret = mbedtls_sha256_ret(derivation_param, sizeof(derivation_param), sha256_buf, 0); - if (ret) { - FOTA_TRACE_TLS_ERR(ret); - return FOTA_STATUS_INTERNAL_CRYPTO_ERROR; - } - flow_control++; - mbedtls_aes_context ctx = {0}; - mbedtls_aes_init(&ctx); - ret = mbedtls_aes_setkey_enc(&ctx, key, key_size * 8); - if (ret) { - FOTA_TRACE_TLS_ERR(ret); - mbedtls_aes_free(&ctx); - return FOTA_STATUS_INTERNAL_CRYPTO_ERROR; - } - flow_control++; - /* Encrypting only first 128 bits, the reset is discarded */ - ret = mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, sha256_buf, key); - mbedtls_aes_free(&ctx); - if (ret) { - FOTA_TRACE_TLS_ERR(ret); - return FOTA_STATUS_INTERNAL_CRYPTO_ERROR; - } - flow_control++; - return (flow_control == 3) ? FOTA_STATUS_SUCCESS : FOTA_STATUS_INTERNAL_ERROR; +#if (MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION == FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY) + static const unsigned char derivation_string[FOTA_ENCRYPT_KEY_SIZE] = + "\x01" "FOTA" "\x00\x56\x3b\xe9\x8a\x94\xfd\x0d\xc0\x65\x80"; + return derivation_string; +#else + return (const unsigned char*)"01FOTA00563be98a94fd0dc0651c0a80"; +#endif } +#endif + +static int derive_key(uint8_t *key) +{ + uint8_t key_buf_hmac[FOTA_CRYPTO_HASH_SIZE]; -#endif // FOTA_KEY_FORCE_DERIVATION + if (mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), (const unsigned char *) key, FOTA_ENCRYPT_KEY_SIZE, + fota_get_derivation_string(), 0x10, key_buf_hmac) == 0) { + fota_fi_memcpy(key, key_buf_hmac, FOTA_ENCRYPT_KEY_SIZE); + return FOTA_STATUS_SUCCESS; + } + + return FOTA_STATUS_INTERNAL_CRYPTO_ERROR; +} int fota_encrypt_decrypt_start(fota_encrypt_context_t **ctx, const uint8_t *key, uint32_t key_size) { @@ -123,11 +131,16 @@ int fota_encrypt_decrypt_start(fota_encrypt_context_t **ctx, const uint8_t *key, *ctx = NULL; -#if FOTA_KEY_FORCE_DERIVATION - ret = derive_key(key, key_size); + const uint8_t *key_to_use = key; + +#if (MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION == FOTA_USE_DEVICE_KEY) + uint8_t derived_key[key_size]; + fota_fi_memcpy(derived_key, key_to_use, key_size); + ret = derive_key(derived_key); if (ret) { return ret; } + key_to_use = derived_key; #endif fota_encrypt_context_t *enc_ctx = (fota_encrypt_context_t *) malloc(sizeof(fota_encrypt_context_t)); @@ -136,12 +149,13 @@ int fota_encrypt_decrypt_start(fota_encrypt_context_t **ctx, const uint8_t *key, } mbedtls_ccm_init(&enc_ctx->ccm_ctx); - enc_ctx->iv = 0; + enc_ctx->iv = INITIAL_IV_VALUE; - ret = mbedtls_ccm_setkey(&enc_ctx->ccm_ctx, MBEDTLS_CIPHER_ID_AES, key, key_size * 8); + ret = mbedtls_ccm_setkey(&enc_ctx->ccm_ctx, MBEDTLS_CIPHER_ID_AES, key_to_use, FOTA_DERIVE_KEY_BITS); if (ret) { FOTA_TRACE_TLS_ERR(ret); mbedtls_ccm_free(&enc_ctx->ccm_ctx); + free(enc_ctx); return FOTA_STATUS_INTERNAL_CRYPTO_ERROR; } @@ -153,7 +167,7 @@ int fota_encrypt_decrypt_start(fota_encrypt_context_t **ctx, const uint8_t *key, void fota_encryption_stream_reset(fota_encrypt_context_t *ctx) { FOTA_DBG_ASSERT(ctx); - ctx->iv = 0; + ctx->iv = INITIAL_IV_VALUE; } @@ -174,10 +188,12 @@ int fota_encrypt_data( FOTA_DBG_ASSERT(tag); int ret; - int flow_control = 0; + volatile int flow_control = 0; + uint64_t le_iv = FOTA_UINT64_TO_LE(ctx->iv); + ret = mbedtls_ccm_encrypt_and_tag( &ctx->ccm_ctx, buf_size, - (const unsigned char *) &ctx->iv, sizeof(ctx->iv), + (const unsigned char *) &le_iv, sizeof(ctx->iv), NULL, 0, in_buf, out_buf, tag, FOTA_ENCRYPT_TAG_SIZE); @@ -188,8 +204,7 @@ int fota_encrypt_data( } flow_control++; fota_encryption_iv_increment(ctx); - flow_control++; - return (3 == flow_control) ? FOTA_STATUS_SUCCESS : FOTA_STATUS_INTERNAL_ERROR; + return (2 == flow_control) ? FOTA_STATUS_SUCCESS : FOTA_STATUS_INTERNAL_ERROR; } int fota_decrypt_data( @@ -201,11 +216,13 @@ int fota_decrypt_data( FOTA_DBG_ASSERT(in_buf); FOTA_DBG_ASSERT(out_buf); FOTA_DBG_ASSERT(tag); + int ret; + uint64_t le_iv = FOTA_UINT64_TO_LE(ctx->iv); ret = mbedtls_ccm_auth_decrypt( &ctx->ccm_ctx, buf_size, - (const unsigned char *) &ctx->iv, sizeof(ctx->iv), + (const unsigned char *) &le_iv, sizeof(ctx->iv), NULL, 0, in_buf, out_buf, tag, FOTA_ENCRYPT_TAG_SIZE); @@ -222,8 +239,7 @@ int fota_decrypt_data( int fota_encrypt_finalize(fota_encrypt_context_t **ctx) { - FOTA_DBG_ASSERT(ctx); - if (*ctx) { + if (ctx && *ctx) { mbedtls_ccm_free(&(*ctx)->ccm_ctx); free(*ctx); *ctx = NULL; @@ -232,6 +248,127 @@ int fota_encrypt_finalize(fota_encrypt_context_t **ctx) return FOTA_STATUS_SUCCESS; } +#if (MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION == FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY) + +static int encrypt_decrypt_fw_key_start(fota_encrypt_context_t **ctx) +{ + int ret; + uint8_t dev_key[FOTA_ENCRYPT_KEY_SIZE] = {0}; + + // init CCM contex with key derived from device key + + ret = fota_get_device_key_128bit(dev_key, FOTA_ENCRYPT_KEY_SIZE); + if (ret) { + FOTA_TRACE_ERROR("Failed to encrypt key. ret %d", ret); + return ret; + } + + ret = derive_key(dev_key); + if (ret) { + FOTA_TRACE_ERROR("Failed to derive key. ret %d", ret); + goto fail; + } + + ret = fota_encrypt_decrypt_start(ctx, dev_key, sizeof(dev_key)); + if (ret) { + FOTA_TRACE_ERROR("Failed to start encryption engine. ret %d", ret); + goto fail; + } + +fail: + // Clear key's buffer from memory due to security reasons + memset(dev_key, 0, FOTA_ENCRYPT_KEY_SIZE); + return ret; +} + +/* + * Encrypt fw key. + * + * \param[in] plain_key Key buffer to encrypt + * \param[out] encrypted_fw_key Buffer holding the encrypted data + * \param[out] encrypted_fw_key_tag Buffer holding the encrypted tag + * \param[out] encrypted_fw_key_iv Buffer holding the encrypted buffer + * \return FOTA_STATUS_SUCCESS on success + */ +int fota_encrypt_fw_key(uint8_t plain_key[FOTA_ENCRYPT_KEY_SIZE], + uint8_t encrypted_fw_key[FOTA_ENCRYPT_KEY_SIZE], + uint8_t encrypted_fw_key_tag[FOTA_ENCRYPT_TAG_SIZE], + uint64_t *encrypted_fw_key_iv) +{ + FOTA_DBG_ASSERT(encrypted_fw_key_iv); + int ret; + fota_encrypt_context_t *temp_ctx = NULL; + + // encrypt gen_key buffer using device key and store it in the header + + ret = encrypt_decrypt_fw_key_start(&temp_ctx); + if (ret) { + return ret; + } + + // generate random iv + ret = fota_gen_random((uint8_t*)encrypted_fw_key_iv, sizeof(uint64_t)); + if (ret) { + FOTA_TRACE_ERROR("Unable to generate random data. ret %d", ret); + return ret; + } + + // set ccm to use generated iv + temp_ctx->iv = *encrypted_fw_key_iv; + + ret = fota_encrypt_data(temp_ctx, + plain_key, FOTA_ENCRYPT_KEY_SIZE, + encrypted_fw_key, encrypted_fw_key_tag); + if (ret) { + FOTA_TRACE_ERROR("Failed to encrypt buffer. ret %d", ret); + goto fail; + } + +fail: + fota_encrypt_finalize(&temp_ctx); + return ret; +} + +/* + * Decrypt fw key. + * + * \param[out] plain_key Key buffer to encrypt + * \param[in] encrypted_fw_key Buffer holding the encrypted data + * \param[in] encrypted_fw_key_tag Buffer holding the encrypted tag + * \param[in] encrypted_fw_key_iv Buffer holding the encrypted buffer + * \return FOTA_STATUS_SUCCESS on success + */ +int fota_decrypt_fw_key(uint8_t plain_key[FOTA_ENCRYPT_KEY_SIZE], + uint8_t encrypted_fw_key[FOTA_ENCRYPT_KEY_SIZE], + uint8_t encrypted_fw_key_tag[FOTA_ENCRYPT_TAG_SIZE], + uint64_t encrypted_fw_key_iv) +{ + int ret; + fota_encrypt_context_t *temp_ctx = NULL; + + // decrypt encrypted_fw_key buffer using key derived from device key + + ret = encrypt_decrypt_fw_key_start(&temp_ctx); + if (ret) { + return ret; + } + + // set ccm to use generated iv + temp_ctx->iv = encrypted_fw_key_iv; + + ret = fota_decrypt_data(temp_ctx, encrypted_fw_key, FOTA_ENCRYPT_KEY_SIZE, + plain_key, encrypted_fw_key_tag); + if (ret) { + FOTA_TRACE_ERROR("Failed to encrypt buffer. ret %d", ret); + goto fail; + } + +fail: + fota_encrypt_finalize(&temp_ctx); + return ret; +} +#endif // (MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION == FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY) + int fota_hash_start(fota_hash_context_t **ctx) { FOTA_DBG_ASSERT(ctx); @@ -266,6 +403,14 @@ int fota_hash_update(fota_hash_context_t *ctx, const uint8_t *buf, uint32_t buf_ return FOTA_STATUS_SUCCESS; } +void fota_hash_clone(fota_hash_context_t *dst_ctx, const fota_hash_context_t *src_ctx) +{ + FOTA_DBG_ASSERT(dst_ctx); + FOTA_DBG_ASSERT(src_ctx); + mbedtls_sha256_clone(&dst_ctx->sha256_ctx, &src_ctx->sha256_ctx); + return; +} + int fota_hash_result(fota_hash_context_t *ctx, uint8_t *hash_buf) { FOTA_DBG_ASSERT(ctx); @@ -280,8 +425,7 @@ int fota_hash_result(fota_hash_context_t *ctx, uint8_t *hash_buf) void fota_hash_finish(fota_hash_context_t **ctx) { - FOTA_DBG_ASSERT(ctx); - if (*ctx) { + if (ctx && *ctx) { mbedtls_sha256_free(&(*ctx)->sha256_ctx); free(*ctx); *ctx = NULL; @@ -349,13 +493,13 @@ int fota_fi_memcmp(const uint8_t *ptr1, const uint8_t *ptr2, size_t num, volatil } #endif // #if FOTA_FI_MITIGATION_ENABLE -#if defined(FOTA_USE_UPDATE_RAW_PUBLIC_KEY) && defined(MBEDTLS_USE_TINYCRYPT) +#if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT == FOTA_RAW_PUBLIC_KEY_FORMAT) && defined(MBEDTLS_USE_TINYCRYPT) int fota_verify_signature_prehashed( const uint8_t *data_digest, const uint8_t *sig, size_t sig_len ) { - int flow_control = 0; + volatile int flow_control = 0; uint8_t public_key[FOTA_UPDATE_RAW_PUBLIC_KEY_SIZE]; int ret = fota_nvm_get_update_public_key(public_key); @@ -382,13 +526,13 @@ int fota_verify_signature_prehashed( } #endif -#if defined(FOTA_USE_UPDATE_RAW_PUBLIC_KEY) && defined(MBEDTLS_ECDSA_C) +#if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT == FOTA_RAW_PUBLIC_KEY_FORMAT) && defined(MBEDTLS_ECDSA_C) int fota_verify_signature_prehashed( const uint8_t *data_digest, const uint8_t *sig, size_t sig_len ) { - int flow_control = 0; + volatile int flow_control = 0; uint8_t public_key[FOTA_UPDATE_RAW_PUBLIC_KEY_SIZE]; int ret = FOTA_STATUS_INTERNAL_ERROR; int tmp_ret = fota_nvm_get_update_public_key(public_key); @@ -484,14 +628,14 @@ int fota_verify_signature_prehashed( } #endif -#if defined(FOTA_USE_UPDATE_X509) +#if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT == FOTA_X509_PUBLIC_KEY_FORMAT) int fota_verify_signature_prehashed( const uint8_t *data_digest, const uint8_t *sig, size_t sig_len ) { int ret; - int flow_control = 0; + volatile int flow_control = 0; int fota_status = FOTA_STATUS_INTERNAL_ERROR; uint8_t *update_crt_data; mbedtls_pk_context *pk_ctx_ptr = NULL; @@ -520,7 +664,13 @@ int fota_verify_signature_prehashed( mbedtls_x509_crt_init(&crt); +/*mbedtls_x509_crt_parse_der_nocopy not supported for mbedtls 2.16.0 and lower versions, + use older version of x509 cert parse function */ +#if (MBEDTLS_VERSION_NUMBER < 0x02110000) + ret = mbedtls_x509_crt_parse_der( +#else ret = mbedtls_x509_crt_parse_der_nocopy( +#endif &crt, update_crt_data, update_crt_size ); @@ -573,14 +723,14 @@ int fota_verify_signature_prehashed( free(update_crt_data); return fota_status; } -#endif // defined(FOTA_USE_UPDATE_X509) +#endif // #if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT == FOTA_X509_PUBLIC_KEY_FORMAT) int fota_verify_signature( const uint8_t *signed_data, size_t signed_data_size, const uint8_t *sig, size_t sig_len ) { - int flow_control = 0; + volatile int flow_control = 0; uint8_t digest[FOTA_CRYPTO_HASH_SIZE] = {0}; mbedtls_sha256_context sha256_ctx = {0}; mbedtls_sha256_init(&sha256_ctx); diff --git a/fota/fota_crypto.h b/fota/fota_crypto.h index 9744b5f..45f1ef4 100644 --- a/fota/fota_crypto.h +++ b/fota/fota_crypto.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -21,7 +21,7 @@ #include "fota/fota_base.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #include "fota/fota_crypto_defs.h" @@ -49,10 +49,40 @@ int fota_decrypt_data( uint8_t *tag); int fota_encrypt_finalize(fota_encrypt_context_t **ctx); +#if (MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION == FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY) +/* + * Encrypt fw key. + * + * \param[in] plain_key Key buffer to encrypt + * \param[out] encrypted_fw_key Buffer holding the encrypted data + * \param[out] encrypted_fw_key_tag Buffer holding the encrypted tag + * \param[out] encrypted_fw_key_iv Buffer holding the encrypted buffer + * \return FOTA_STATUS_SUCCESS on success + */ +int fota_encrypt_fw_key(uint8_t plain_key[FOTA_ENCRYPT_KEY_SIZE], + uint8_t encrypted_fw_key[FOTA_ENCRYPT_KEY_SIZE], + uint8_t encrypted_fw_key_tag[FOTA_ENCRYPT_TAG_SIZE], + uint64_t *encrypted_fw_key_iv); +/* + * Decrypt fw key. + * + * \param[out] plain_key Key buffer to encrypt + * \param[in] encrypted_fw_key Buffer holding the encrypted data + * \param[in] encrypted_fw_key_tag Buffer holding the encrypted tag + * \param[in] encrypted_fw_key_iv Buffer holding the encrypted buffer + * \return FOTA_STATUS_SUCCESS on success + */ +int fota_decrypt_fw_key(uint8_t plain_key[FOTA_ENCRYPT_KEY_SIZE], + uint8_t encrypted_fw_key[FOTA_ENCRYPT_KEY_SIZE], + uint8_t encrypted_fw_key_tag[FOTA_ENCRYPT_TAG_SIZE], + uint64_t encrypted_fw_key_iv); +#endif // (MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION == FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY) + typedef struct fota_hash_context_s fota_hash_context_t; int fota_hash_start(fota_hash_context_t **ctx); int fota_hash_update(fota_hash_context_t *ctx, const uint8_t *buf, uint32_t buf_size); +void fota_hash_clone(fota_hash_context_t *dst_ctx, const fota_hash_context_t *src_ctx); int fota_hash_result(fota_hash_context_t *ctx, uint8_t *hash_buf); void fota_hash_finish(fota_hash_context_t **ctx); @@ -93,6 +123,16 @@ do { \ FOTA_FI_SAFE_COND((!fota_fi_memcmp((PTR1), (PTR2), (NUM), &loop_check) && (loop_check == (NUM))), RET, MSG, ##__VA_ARGS__); \ } while (0) +static inline void* fota_fi_memcpy(void *dst, const void *src, size_t num) +{ + return mbedtls_platform_memcpy(dst, src, num); +} + +static inline void* fota_fi_memset(void *ptr, int value, size_t num) +{ + return mbedtls_platform_memset(ptr, value, num); +} + #else // no FI support // No FI mitigation, simple handling @@ -115,6 +155,16 @@ static inline int fota_fi_memcmp(const uint8_t *ptr1, const uint8_t *ptr2, size_ return memcmp(ptr1, ptr2, num); } +static inline void* fota_fi_memcpy(void *dst, const void *src, size_t num) +{ + return memcpy(dst, src, num); +} + +static inline void* fota_fi_memset(void *ptr, int value, size_t num) +{ + return memset(ptr, value, num); +} + #endif // #if FOTA_FI_MITIGATION_ENABLE int fota_verify_signature( @@ -127,11 +177,12 @@ int fota_verify_signature_prehashed( const uint8_t *sig, size_t sig_len ); +const unsigned char* fota_get_derivation_string(void); #ifdef __cplusplus } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif // __FOTA_CRYPTO_H_ diff --git a/fota/fota_crypto_asn_extra.c b/fota/fota_crypto_asn_extra.c index 92696eb..1e71acd 100644 --- a/fota/fota_crypto_asn_extra.c +++ b/fota/fota_crypto_asn_extra.c @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // diff --git a/fota/fota_crypto_asn_extra.h b/fota/fota_crypto_asn_extra.h index 39e1752..1b920e3 100644 --- a/fota/fota_crypto_asn_extra.h +++ b/fota/fota_crypto_asn_extra.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // diff --git a/fota/fota_crypto_defs.h b/fota/fota_crypto_defs.h index 78f4b9d..6fc4903 100644 --- a/fota/fota_crypto_defs.h +++ b/fota/fota_crypto_defs.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // diff --git a/fota/fota_curr_fw.h b/fota/fota_curr_fw.h index 193b9fb..3635416 100644 --- a/fota/fota_curr_fw.h +++ b/fota/fota_curr_fw.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -21,7 +21,7 @@ #include "fota/fota_base.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #include "fota_header_info.h" @@ -30,59 +30,41 @@ extern "C" { #endif /** - * Return a pointer to application start. - * - * \return Pointer to application start. - */ -uint8_t *fota_curr_fw_get_app_start_addr(void); - -/** - * Return a pointer to header start. - * - * \return Pointer to header start. + * @file fota_curr_fw.h + * \brief FOTA requires access to the currently installed firmware (FW) and the FW metadata header. + * Support code for each platform should implement the current FW interfaces described in this file. */ -uint8_t *fota_curr_fw_get_app_header_addr(void); -#if defined(FOTA_CUSTOM_CURR_FW_STRUCTURE) && (FOTA_CUSTOM_CURR_FW_STRUCTURE) /** - * Read header of current firmware. + * Reads the header of the current firmware. * * \param[in] header_info Header info structure. * \return FOTA_STATUS_SUCCESS on success. */ int fota_curr_fw_read_header(fota_header_info_t *header_info); -#else - -// Default read header implementation -static inline int fota_curr_fw_read_header(fota_header_info_t *header_info) -{ - uint8_t *header_in_curr_fw = (uint8_t *)fota_curr_fw_get_app_header_addr(); - return fota_deserialize_header(header_in_curr_fw, fota_get_header_size(), header_info); -} -#endif /** - * Read from current firmware. + * Read from the current firmware. * * \param[out] buf Buffer to read into. - * \param[in] offset Offset in firmware. - * \param[in] size Size to read (bytes). + * \param[in] offset Offset in the firmware. + * \param[in] size Size to read in bytes. * \param[out] num_read Number of read bytes. - * \return FOTA_STATUS_SUCCESS on success. + * \return ::FOTA_STATUS_SUCCESS on success. */ int fota_curr_fw_read(uint8_t *buf, size_t offset, size_t size, size_t *num_read); /** - * Read digest from current firmware. + * Read the digest from the current firmware. * * \param[out] buf Buffer to read into. - * \return FOTA_STATUS_SUCCESS on success. + * \return ::FOTA_STATUS_SUCCESS on success. */ int fota_curr_fw_get_digest(uint8_t *buf); #ifdef __cplusplus } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif // __FOTA_CURR_FW_H_ diff --git a/fota/fota_curr_fw_linux.cpp b/fota/fota_curr_fw_linux.cpp deleted file mode 100644 index c57eb0e..0000000 --- a/fota/fota_curr_fw_linux.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// ---------------------------------------------------------------------------- -// Copyright 2020 ARM Ltd. -// -// SPDX-License-Identifier: Apache-2.0 -// -// 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 "fota/fota_base.h" - -#ifdef MBED_CLOUD_CLIENT_FOTA_ENABLE -#if defined(TARGET_LIKE_LINUX) -#if defined(FOTA_CUSTOM_CURR_FW_STRUCTURE) || (FOTA_CUSTOM_CURR_FW_STRUCTURE) - -#define TRACE_GROUP "FOTA" - -#include -#include "fota/fota_curr_fw.h" -#include "fota/fota_status.h" - -#include - -extern char *program_invocation_name; - -int fota_curr_fw_read(uint8_t *buf, size_t offset, size_t size, size_t *num_read) -{ - int status = FOTA_STATUS_INTERNAL_ERROR; - size_t read_bytes; - FILE *fs = fopen(program_invocation_name, "r"); - if (!fs) { - FOTA_TRACE_ERROR("Failed to open program file"); - return status; - } - - if (fseek(fs, offset, SEEK_SET) == -1) { - goto cleanup; - } - - read_bytes = fread(buf, 1, size, fs); - if (read_bytes < 1) { - goto cleanup; - } - - *num_read = read_bytes; - status = FOTA_STATUS_SUCCESS; - -cleanup: - (void)fclose(fs); - return status; -} - -int fota_curr_fw_get_digest(uint8_t *buf) -{ - fota_header_info_t curr_fw_info; - int ret = fota_curr_fw_read_header(&curr_fw_info); - if (ret) { - FOTA_TRACE_ERROR("Failed to read current header"); - return ret; - } - memcpy(buf, curr_fw_info.digest, FOTA_CRYPTO_HASH_SIZE); - return FOTA_STATUS_SUCCESS; -} - -int fota_curr_fw_read_header(fota_header_info_t *header_info) -{ - int status = FOTA_STATUS_INTERNAL_ERROR; - size_t bytes_read = 0; - - FILE *fs = fopen(MBED_CLOUD_CLIENT_FOTA_LINUX_HEADER_FILENAME, "r"); - if (!fs) { - FOTA_TRACE_ERROR("Failed to open current header file"); - return status; - } - - const size_t header_size = fota_get_header_size(); - uint8_t *buf = (uint8_t *)malloc(header_size); - if (!buf) { - FOTA_TRACE_ERROR("Failed to allocate buffer for header reading"); - goto cleanup; - } - - bytes_read = fread(buf, 1, header_size, fs); - if (bytes_read != header_size) { - FOTA_TRACE_ERROR("fread failed: %s", strerror(errno)); - FOTA_TRACE_ERROR("Failed to read header file"); - goto cleanup; - } - - status = fota_deserialize_header(buf, header_size, header_info); - -cleanup: - (void)fclose(fs); - free(buf); - return status; -} - -uint8_t *fota_curr_fw_get_app_start_addr(void) -{ - return 0; -} - -uint8_t *fota_curr_fw_get_app_header_addr(void) -{ - return 0; -} - -#endif // defined(FOTA_CUSTOM_CURR_FW_STRUCTURE) || (FOTA_CUSTOM_CURR_FW_STRUCTURE) -#endif // defined(TARGET_LIKE_LINUX) -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE diff --git a/fota/fota_delta.c b/fota/fota_delta.c index 948bb9f..1af6aac 100644 --- a/fota/fota_delta.c +++ b/fota/fota_delta.c @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2016-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -27,8 +27,8 @@ #include "fota/fota_status.h" #include "fota/fota_header_info.h" -#include "bspatch.h" -#include "bspatch_private.h" //For bspatch_stream +#include "bspatch/bspatch.h" +#include "bspatch/bspatch_private.h" //For bspatch_stream #include #include @@ -120,11 +120,10 @@ int fota_delta_start(fota_delta_ctx_t **ctx, fota_component_curr_fw_read curr_fw { FOTA_DBG_ASSERT(ctx); - fota_delta_ctx_t *delta_ctx = (fota_delta_ctx_t *) malloc(sizeof(fota_delta_ctx_t)); + fota_delta_ctx_t *delta_ctx = (fota_delta_ctx_t *) calloc(1, sizeof(fota_delta_ctx_t)); if (!delta_ctx) { return FOTA_STATUS_OUT_OF_MEMORY; } - memset(delta_ctx, 0, sizeof(*delta_ctx)); delta_ctx->next_event_to_post = EBSAPI_START_PATCH_PROCESSING; delta_ctx->bspatch_read_patch_buffer_remaining = 0; delta_ctx->bspatch_read_patch_buffer_length = 0; diff --git a/fota/fota_delta.h b/fota/fota_delta.h index c42ead7..c69a6b9 100644 --- a/fota/fota_delta.h +++ b/fota/fota_delta.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -21,7 +21,7 @@ #include "fota/fota_base.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #if !defined(FOTA_DISABLE_DELTA) @@ -50,6 +50,6 @@ int fota_delta_finalize(fota_delta_ctx_t **ctx); #endif // !defined(FOTA_DISABLE_DELTA) -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif // __FOTA_DELTA_H_ diff --git a/fota/fota_device_key.cpp b/fota/fota_device_key.cpp new file mode 100644 index 0000000..018c177 --- /dev/null +++ b/fota/fota_device_key.cpp @@ -0,0 +1,87 @@ +// ---------------------------------------------------------------------------- +// Copyright 2021 Pelion Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// 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. +// ---------------------------------------------------------------------------- + +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) && !defined(FOTA_UNIT_TEST) +#include +#include +#include "fota_device_key.h" +#include "fota/fota_crypto_defs.h" +#include "fota/fota_status.h" + +#if ((MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION == 2) && (MBED_CLOUD_CLIENT_FOTA_FW_HEADER_EXTERNAL == 1)) || \ + (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) + +#if defined(MBED_CONF_MBED_CLOUD_CLIENT_EXTERNAL_SST_SUPPORT) + +#include "kv_config.h" +#include "KVMap.h" +#include "TDBStore.h" + +using namespace mbed; + +extern "C" int8_t fota_get_device_key_128bit(uint8_t *key, uint32_t keyLenBytes) +{ + KVMap &kv_map = KVMap::get_instance(); + KVStore *inner_store = kv_map.get_internal_kv_instance(NULL); + + //Check key buffer + if (key == NULL) { + return FOTA_STATUS_INTERNAL_ERROR; + } + //Check key buffer size + if (keyLenBytes != FOTA_ENCRYPT_KEY_SIZE) { + return FOTA_STATUS_INTERNAL_ERROR; + } + //Check internal instance + if (inner_store == NULL) { + return FOTA_STATUS_INTERNAL_ERROR; + } + + //Read ROT + int ret = ((TDBStore *)inner_store)->reserved_data_get(key, keyLenBytes); + if (ret == MBED_SUCCESS) { + return FOTA_STATUS_SUCCESS; + } else if (ret == MBED_ERROR_ITEM_NOT_FOUND) { + return FOTA_STATUS_NOT_FOUND; + } else { + return FOTA_STATUS_INTERNAL_ERROR; + } + +} + +#else + +#include "pal.h" + +int8_t fota_get_device_key_128bit(uint8_t *key, uint32_t keyLenBytes) +{ + palStatus_t ret = pal_osGetDeviceKey(palOsStorageEncryptionKey128Bit, key, keyLenBytes); + if (ret == PAL_SUCCESS) { + return FOTA_STATUS_SUCCESS; + } else if (ret == PAL_ERR_ITEM_NOT_EXIST) { + return FOTA_STATUS_NOT_FOUND; + } else { + return FOTA_STATUS_INTERNAL_ERROR; + } + + return FOTA_STATUS_SUCCESS; +} + +#endif // MBED_CONF_MBED_CLOUD_CLIENT_EXTERNAL_SST_SUPPORT +#endif +#endif //MBED_CLOUD_CLIENT_FOTA_ENABLE diff --git a/fota/fota_platform_linux.h b/fota/fota_device_key.h similarity index 69% rename from fota/fota_platform_linux.h rename to fota/fota_device_key.h index fbc4e99..e3d18e0 100644 --- a/fota/fota_platform_linux.h +++ b/fota/fota_device_key.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2020 ARM Ltd. +// Copyright 2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -16,24 +16,21 @@ // limitations under the License. // ---------------------------------------------------------------------------- -#ifndef __FOTA_PLATFORM_LINUX_H_ -#define __FOTA_PLATFORM_LINUX_H_ - -#include "fota/fota_base.h" - -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#ifndef FOTA_DEVICE_KEY +#define FOTA_DEVICE_KEY #ifdef __cplusplus extern "C" { #endif -#include "fota_candidate.h" +#if ((MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION == 2) && (MBED_CLOUD_CLIENT_FOTA_FW_HEADER_EXTERNAL == 1)) || \ + (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) +int8_t fota_get_device_key_128bit(uint8_t *key, uint32_t keyLenBytes); -int fota_linux_candidate_iterate(fota_candidate_iterate_callback_info *info); -int fota_linux_init(); +#endif #ifdef __cplusplus } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE -#endif // __FOTA_PLATFORM_LINUX_H_ + +#endif // FOTA_DEVICE_KEY diff --git a/fota/fota_event_handler.c b/fota/fota_event_handler.c index e215f4f..f457e92 100644 --- a/fota/fota_event_handler.c +++ b/fota/fota_event_handler.c @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -20,11 +20,15 @@ #ifdef MBED_CLOUD_CLIENT_FOTA_ENABLE +// TODO: Replace this with a proper define +#if !defined(FOTA_UNIT_TEST) + #define TRACE_GROUP "FOTA" #include #include "mbed-client-libservice/ns_types.h" #include "nanostack-event-loop/eventOS_event.h" +#include "nanostack-event-loop/eventOS_event_timer.h" #include "fota/fota_status.h" #include "fota/fota_event_handler.h" @@ -116,8 +120,8 @@ void fota_event_handler_deinit(void) //nothing to de-register - eventOS does not have a method for destroying handlers } -int fota_event_handler_defer_with_data( - fota_deferred_data_callabck_t cb, uint8_t *data, size_t size) +static int fota_event_handler_defer_with_data_( + fota_deferred_data_callabck_t cb, uint8_t *data, size_t size, size_t in_ms) { FOTA_ASSERT(!g_ctx.is_pending_event); g_ctx.is_pending_event = true; @@ -140,11 +144,33 @@ int fota_event_handler_defer_with_data( g_ctx.event_storage.data.event_type = FOTA_EVENT_EXECUTE_WITH_BUFFER; g_ctx.event_storage.data.data_ptr = (void *)&g_ctx; - eventOS_event_send_user_allocated(&g_ctx.event_storage); + if (in_ms) { + arm_event_t event = { 0 }; + event.data_ptr = g_ctx.event_storage.data.data_ptr; + event.sender = g_tasklet_id; + event.receiver = g_ctx.event_storage.data.receiver; + event.event_type = g_ctx.event_storage.data.event_type; + event.priority = g_ctx.event_storage.data.priority; + eventOS_event_timer_request_in(&event, eventOS_event_timer_ms_to_ticks(in_ms)); + } else { + eventOS_event_send_user_allocated(&g_ctx.event_storage); + } return FOTA_STATUS_SUCCESS; } +int fota_event_handler_defer_with_data( + fota_deferred_data_callabck_t cb, uint8_t *data, size_t size) +{ + return fota_event_handler_defer_with_data_(cb, data, size, 0); +} + +int fota_event_handler_defer_with_data_in_ms( + fota_deferred_data_callabck_t cb, uint8_t *data, size_t size, size_t in_ms) +{ + return fota_event_handler_defer_with_data_(cb, data, size, in_ms); +} + void fota_event_handler_defer_with_result( fota_deferred_result_callabck_t cb, int32_t status) { @@ -172,4 +198,5 @@ void fota_event_handler_defer_with_result_ignore_busy( } } +#endif // !defined(FOTA_UNIT_TEST) #endif // MBED_CLOUD_CLIENT_FOTA_ENABLE diff --git a/fota/fota_event_handler.h b/fota/fota_event_handler.h index 57ad8e0..4a4c296 100644 --- a/fota/fota_event_handler.h +++ b/fota/fota_event_handler.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -20,7 +20,7 @@ #include "fota_base.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #ifdef __cplusplus extern "C" { @@ -29,7 +29,7 @@ extern "C" { #include "fota_internal.h" typedef void (*fota_deferred_data_callabck_t)(uint8_t *data, size_t size); -typedef void (*fota_deferred_result_callabck_t)(int32_t status); +typedef void (*fota_deferred_result_callabck_t)(int32_t param); /* * Initialize event handler @@ -59,6 +59,20 @@ void fota_event_handler_deinit(void); int fota_event_handler_defer_with_data( fota_deferred_data_callabck_t cb, uint8_t *data, size_t size); +/* + * Defer execution of a FOTA callback with a data buffer after a given time + * + * The deferred callback will run in its own time slot + * + * /param cb[in] callback function pointer to be deferred + * /param data[in] deferred callback input data pointer + * /param size[in] deferred callback input data size + * /param in_ms[in] time to wait in milliseconds + * \return FOTA_STATUS_SUCCESS on success. + */ +int fota_event_handler_defer_with_data_in_ms( + fota_deferred_data_callabck_t cb, uint8_t *data, size_t size, size_t in_ms); + /* * Defer execution of a FOTA callback with error details * /param cb callback function pointer to be deferred @@ -80,6 +94,6 @@ void fota_event_handler_defer_with_result_ignore_busy( } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif // FOTA_FOTA_EVENT_HANDLER_H_ diff --git a/fota/fota_ext_downloader.h b/fota/fota_ext_downloader.h new file mode 100644 index 0000000..18a0830 --- /dev/null +++ b/fota/fota_ext_downloader.h @@ -0,0 +1,61 @@ +// ---------------------------------------------------------------------------- +// Copyright 2020 Pelion Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// 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 __FOTA_EXT_DOWNLOADER_H_ +#define __FOTA_EXT_DOWNLOADER_H_ + +#include "fota/fota_base.h" + +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) + +#ifdef __cplusplus +extern "C" { +#endif + +#if MBED_CLOUD_CLIENT_FOTA_EXTERNAL_DOWNLOADER + +/** + * Tell FOTA that image downloaded externally is ready. + * + * \return FOTA_STATUS_SUCCESS on success. + */ +int fota_ext_downloader_on_image_ready(void); + +/** + * Write a fragment to the image at a certain offset. + * Notes: + * - Writes can’t be done twice to the same offset. + * - Writes can only start after download has been authorized by app + * - Write offset should be a multiple of the candidate storage program size + * - Write size should be a multiple of the candidate storage program size (except for the last write) + * + * \param[in] buffer Buffer to write. + * \param[in] offset Offset of buffer in image. + * \param[in] size Buffer size + * \return FOTA_STATUS_SUCCESS on success + */ +int fota_ext_downloader_write_image_fragment(const void *buffer, size_t offset, size_t size); + +#endif // MBED_CLOUD_CLIENT_FOTA_EXTERNAL_DOWNLOADER + +#ifdef __cplusplus +} +#endif + +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) +#endif // __FOTA_MULTICAST_H_ diff --git a/fota/fota_fw_download.h b/fota/fota_fw_download.h index 651edf0..e6b8dd1 100644 --- a/fota/fota_fw_download.h +++ b/fota/fota_fw_download.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -21,7 +21,7 @@ #include "fota/fota_base.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #include "fota/fota_status.h" @@ -69,6 +69,6 @@ void fota_download_deinit(void **download_handle); } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif // __FOTA_FW_DOWNLAD_H_ diff --git a/fota/fota_fw_download_coap.c b/fota/fota_fw_download_coap.c index a128bcb..665d5f7 100644 --- a/fota/fota_fw_download_coap.c +++ b/fota/fota_fw_download_coap.c @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // diff --git a/fota/fota_fw_download_curl.c b/fota/fota_fw_download_curl.c index 251898a..371c32e 100644 --- a/fota/fota_fw_download_curl.c +++ b/fota/fota_fw_download_curl.c @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // diff --git a/fota/fota_header_info.h b/fota/fota_header_info.h index 1ef7e8c..57f6755 100644 --- a/fota/fota_header_info.h +++ b/fota/fota_header_info.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -21,7 +21,7 @@ #include "fota/fota_base.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #include "fota/fota_crypto_defs.h" #include "fota/fota_component_defs.h" @@ -32,7 +32,7 @@ extern "C" { #endif // These checks are only relevant when FOTA is enabled (unlike this header file) -#ifdef MBED_CLOUD_CLIENT_FOTA_ENABLE + #if !defined(MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION) #error MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION expected to be set in fota_config.h #endif @@ -40,7 +40,6 @@ extern "C" { #if !defined(FOTA_HEADER_HAS_CANDIDATE_READY) #error FOTA_HEADER_HAS_CANDIDATE_READY expected to be set in fota_config.h #endif -#endif #define FOTA_FW_HEADER_MAGIC ((uint32_t)(0x5c0253a3)) @@ -61,8 +60,9 @@ typedef struct { uint32_t footer; } fota_candidate_ready_header_t; -#define FOTA_HEADER_ENCRYPTED_FLAG 0x01 -#define FOTA_HEADER_SUPPORT_RESUME_FLAG 0x02 +#define FOTA_HEADER_ENCRYPTED_FLAG 0x01 +#define FOTA_HEADER_SUPPORT_RESUME_FLAG 0x02 +#define FOTA_INTERNAL_HEADER_RESERVED_FIELD_SIZE 0x40 /* * FW header as found in flash. @@ -72,22 +72,38 @@ typedef struct { * module for reporting current version details. */ typedef struct { - uint32_t magic; /*< Magic value */ - size_t fw_size; /*< FW size in bytes */ - uint64_t version; /*< FW version - timestamp */ + uint32_t magic; /*< Magic value */ + uint32_t fw_size; /*< FW size in bytes */ + uint64_t version; /*< FW version - timestamp */ #if defined(MBED_CLOUD_CLIENT_FOTA_SIGNED_IMAGE_SUPPORT) - uint8_t signature[FOTA_IMAGE_RAW_SIGNATURE_SIZE]; /*< RAW ECDSA signature */ + uint8_t signature[FOTA_IMAGE_RAW_SIGNATURE_SIZE]; /*< RAW ECDSA signature */ #endif // defined(MBED_CLOUD_CLIENT_FOTA_SIGNED_IMAGE_SUPPORT) - uint8_t digest[FOTA_CRYPTO_HASH_SIZE]; /*< FW image SHA256 digest */ + uint8_t digest[FOTA_CRYPTO_HASH_SIZE]; /*< FW image SHA256 digest */ + uint8_t reserved[FOTA_INTERNAL_HEADER_RESERVED_FIELD_SIZE]; /*< Reserved */ // From this point on, all fields are relevant to candidate only and // can be skipped by bootloader if it wishes not to save them internally + // !The size of the internal header can't be changed, different size of the header + // will break older version of the bootloader. + // reserved field should be used for additional internal header data. uint8_t internal_header_barrier; uint8_t flags; /*< Flags */ + uint16_t external_header_size; /*< Size of external header size */ uint16_t block_size; /*< Block size. Encryption block size if encrypted, validated block size if unencrypted and block validation turned on */ uint8_t precursor[FOTA_CRYPTO_HASH_SIZE]; /*< contains previously installed FW SHA256 digest */ + /*< Vendor custom data as received in Pelion FOTA manifest. */ + uint8_t vendor_data[FOTA_MANIFEST_VENDOR_DATA_SIZE]; +#if (MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION == FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY) + /*< Encrypted info to encrypt\decrypt payload Encryption key */ + uint8_t encrypted_fw_key[FOTA_ENCRYPT_KEY_SIZE]; + uint8_t encrypted_fw_key_tag[FOTA_ENCRYPT_TAG_SIZE]; + uint64_t encrypted_fw_key_iv; +#endif uint32_t footer; + // !New fields of the external header must me added in the end of the current structure, + // otherwise additional field will break older version of the bootloader. + } fota_header_info_t; static inline size_t fota_get_header_size(void) @@ -124,5 +140,5 @@ int fota_serialize_header(const fota_header_info_t *header_info, uint8_t *header } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif // __FOTA_HEADER_INFO_H_ diff --git a/fota/fota_header_info_v2.c b/fota/fota_header_info_v2.c index b23ca94..98e14c1 100644 --- a/fota/fota_header_info_v2.c +++ b/fota/fota_header_info_v2.c @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -24,15 +24,18 @@ #include #include #include +#include #if (MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION == 2) #include "fota/fota_status.h" #include "fota/fota_header_info.h" #include "fota/fota_crypto.h" +#include "fota/fota_device_key.h" #include "mbedtls/md.h" #include "CloudClientStorage.h" + #define ARM_UC_HEADER_VERSION_V2 (2) #define ARM_UC_HEADER_VERSION_OFFSET_V2 (4) #define ARM_UC_FIRMWARE_VERSION_OFFSET_V2 (8) @@ -60,7 +63,6 @@ #define ARM_UC_DEVICE_HMAC_KEY "StorageEnc256HMACSHA256SIGNATURE" #define ARM_UC_DEVICE_HMAC_KEY_SIZE (sizeof(ARM_UC_DEVICE_HMAC_KEY) - 1) - size_t arm_uc_crc32(const uint8_t *buffer, size_t length) { const uint8_t *current = buffer; @@ -150,12 +152,14 @@ static int fota_hmac_sha256(const uint8_t *key, size_t key_size, return ret; } -int fota_get_device_key_256Bit(uint8_t key_buf_hmac[ARM_UC_DEVICE_KEY_SIZE]) +#if (MBED_CLOUD_CLIENT_FOTA_FW_HEADER_EXTERNAL == 1) + +static int fota_get_device_key_256Bit(uint8_t key_buf_hmac[ARM_UC_DEVICE_KEY_SIZE]) { int ret = FOTA_STATUS_INTERNAL_ERROR; #ifdef __MBED__ - ret = mbed_cloud_client_get_rot_128bit(key_buf_hmac, ARM_UC_ROT_SIZE); + ret = fota_get_device_key_128bit((uint8_t*)key_buf_hmac, (uint32_t)ARM_UC_ROT_SIZE); #else ret = FOTA_STATUS_SUCCESS; memset(key_buf_hmac, 0x27, ARM_UC_ROT_SIZE); @@ -171,8 +175,6 @@ int fota_get_device_key_256Bit(uint8_t key_buf_hmac[ARM_UC_DEVICE_KEY_SIZE]) return ret; } -#if (MBED_CLOUD_CLIENT_FOTA_FW_HEADER_EXTERNAL == 1) - static int serialize_header_v2_external(const fota_header_info_t *header_info, uint8_t *header_buf, size_t header_buf_size) { memset(header_buf, 0, ARM_UC_HEADER_SIZE_V2_EXTERNAL); @@ -285,8 +287,6 @@ int fota_serialize_header(const fota_header_info_t *header_info, uint8_t *header #endif } -#ifdef FOTA_COMPLETE_TEST - static int deserialize_header_v2_external(const uint8_t *buffer, size_t buffer_size, fota_header_info_t *header_info) { FOTA_DBG_ASSERT(fota_get_header_size() <= buffer_size); @@ -308,8 +308,7 @@ static int deserialize_header_v2_external(const uint8_t *buffer, size_t buffer_s return FOTA_STATUS_SUCCESS; } -#endif - +#if (MBED_CLOUD_CLIENT_FOTA_FW_HEADER_EXTERNAL != 1) static int deserialize_header_v2_internal(const uint8_t *buffer, size_t buffer_size, fota_header_info_t *header_info) { FOTA_DBG_ASSERT(fota_get_header_size() <= buffer_size); @@ -341,24 +340,17 @@ static int deserialize_header_v2_internal(const uint8_t *buffer, size_t buffer_s return FOTA_STATUS_INTERNAL_CRYPTO_ERROR; } +#endif int fota_deserialize_header(const uint8_t *buffer, size_t buffer_size, fota_header_info_t *header_info) { -#ifdef FOTA_COMPLETE_TEST - /* for unitests*/ #if (MBED_CLOUD_CLIENT_FOTA_FW_HEADER_EXTERNAL == 1) return deserialize_header_v2_external(buffer, buffer_size, header_info); #else return deserialize_header_v2_internal(buffer, buffer_size, header_info); #endif - -#else - return deserialize_header_v2_internal(buffer, buffer_size, header_info); - -#endif - } -#endif +#endif //MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION == 2 -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE diff --git a/fota/fota_header_info_v3.c b/fota/fota_header_info_v3.c index fe895ce..3cba919 100644 --- a/fota/fota_header_info_v3.c +++ b/fota/fota_header_info_v3.c @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // diff --git a/fota/fota_internal.h b/fota/fota_internal.h index e1345fc..d90b546 100644 --- a/fota/fota_internal.h +++ b/fota/fota_internal.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -21,7 +21,7 @@ #include "fota/fota_base.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #include "fota/fota_manifest.h" #include "fota/fota_app_ifs.h" @@ -29,6 +29,7 @@ #include "fota/fota_header_info.h" #include "fota/fota_crypto.h" #include "fota/fota_component.h" +#include "fota/fota_multicast.h" #ifdef __cplusplus extern "C" { @@ -39,6 +40,7 @@ typedef enum { FOTA_STATE_AWAIT_DOWNLOAD_AUTHORIZATION, FOTA_STATE_DOWNLOADING, FOTA_STATE_AWAIT_INSTALL_AUTHORIZATION, + FOTA_STATE_INSTALLING, FOTA_STATE_INVALID = 255, } fota_state_e; @@ -48,6 +50,20 @@ typedef enum { FOTA_RESUME_STATE_ONGOING, } fota_resume_state_e; +typedef enum { + FOTA_INSTALL_STATE_IDLE, + FOTA_INSTALL_STATE_AUTHORIZE, + FOTA_INSTALL_STATE_DEFER, + FOTA_INSTALL_STATE_POSTPONE_REBOOT +} fota_install_state_e; + + +// The encryption block size used to encrypt payload by the cloud +#define FOTA_CLOUD_ENCRYPTION_BLOCK_SIZE 1024 + +// Internal component for BR downloader (must start with '%' as it's internal) +#define FOTA_MULTICAST_BR_INT_COMP_NAME "%MC_BR" + typedef struct { manifest_firmware_info_t *fw_info; size_t payload_offset; @@ -59,8 +75,14 @@ typedef struct { uint8_t *delta_buf; fota_delta_ctx_t *delta_ctx; #endif +#if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) fota_encrypt_context_t *enc_ctx; - fota_hash_context_t *curr_fw_hash_ctx; + uint8_t encryption_key[FOTA_ENCRYPT_KEY_SIZE]; +#endif + fota_hash_context_t *payload_hash_ctx; +#if !defined(FOTA_DISABLE_DELTA) + fota_hash_context_t *installed_hash_ctx; +#endif uint8_t *page_buf; uint32_t page_buf_offset; uint32_t page_buf_size; @@ -72,23 +94,48 @@ typedef struct { uint32_t candidate_header_size; fota_resume_state_e resume_state; void *download_handle; +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_BR_MODE) + // Tells that this is a Multicast BR mode update on a BR (unlike unicast update to the BR itself) + bool mc_br_update; + fota_multicast_br_post_action_callback_t mc_br_post_action_callback; +#endif +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_NODE_MODE) + bool mc_node_update; + bool mc_node_update_activated; + fota_multicast_node_post_action_callback_t mc_node_post_action_callback; + uint8_t *mc_node_frag_buf; + uint8_t mc_node_manifest_hash[FOTA_CRYPTO_HASH_SIZE]; +#endif } fota_context_t; + +typedef struct { + unsigned int comp_id; + fota_state_e state; +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_BR_MODE) + bool mc_br_update; + fota_multicast_br_post_action_callback_t mc_br_post_action_callback; +#elif (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_NODE_MODE) + bool mc_node_update; + fota_multicast_node_post_action_callback_t mc_node_post_action_callback; +#endif +} fota_persistent_context_t; + + fota_context_t *fota_get_context(void); -bool fota_is_active_update(void); int fota_is_ready(uint8_t *data, size_t size, fota_state_e *fota_state); void fota_on_manifest(uint8_t *data, size_t size); void fota_on_reject(int32_t status); -void fota_on_defer(int32_t status); +void fota_on_defer(int32_t param); void fota_on_authorize(int32_t status); void fota_on_fragment(uint8_t *buf, size_t size); void fota_on_fragment_failure(int32_t status); -void fota_on_resume(int32_t status); +void fota_on_resume(int32_t param); #ifdef __cplusplus } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif // __FOTA_INTERNAL_H_ diff --git a/fota/fota_internal_ifs.c b/fota/fota_internal_ifs.c new file mode 100644 index 0000000..73a2e53 --- /dev/null +++ b/fota/fota_internal_ifs.c @@ -0,0 +1,30 @@ +// ---------------------------------------------------------------------------- +// Copyright 2021 Pelion Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// 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 "fota_internal_ifs.h" + +#ifdef MBED_CLOUD_CLIENT_FOTA_ENABLE + +#include "fota/fota_event_handler.h" + +void fota_internal_resume() +{ + fota_event_handler_defer_with_result_ignore_busy(fota_on_resume, /*fota resume by internal flow */ 1); +} + +#endif diff --git a/fota/fota_internal_ifs.h b/fota/fota_internal_ifs.h new file mode 100644 index 0000000..e74479a --- /dev/null +++ b/fota/fota_internal_ifs.h @@ -0,0 +1,44 @@ +// ---------------------------------------------------------------------------- +// Copyright 2021 Pelion Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// 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 __FOTA_INTERNAL_IFS_H_ +#define __FOTA_INTERNAL_IFS_H_ + +#include "fota/fota_config.h" + +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Resume Pelion FOTA update - internal + * + * If the update process is interrupted, the interal flow can call this function to resume the process. + */ +void fota_internal_resume(void); + + +#ifdef __cplusplus +} +#endif + +#endif // (MBED_CLOUD_CLIENT_FOTA_ENABLE) + +#endif // __FOTA_INTERNAL_IFS_H_ diff --git a/fota/fota_manifest.h b/fota/fota_manifest.h index be3b2c8..d919bb0 100644 --- a/fota/fota_manifest.h +++ b/fota/fota_manifest.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -21,7 +21,7 @@ #include "fota/fota_base.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #include "fota/fota_crypto_defs.h" #include "fota/fota_component.h" @@ -36,8 +36,15 @@ extern "C" { #define FOTA_MANIFEST_TRACE_DEBUG(fmt, ...) #endif -#define FOTA_MANIFEST_PAYLOAD_FORMAT_RAW 1 -#define FOTA_MANIFEST_PAYLOAD_FORMAT_DELTA 5 +// Payload format types +#define FOTA_MANIFEST_PAYLOAD_FORMAT_RAW 0x0001 +#define FOTA_MANIFEST_PAYLOAD_FORMAT_DELTA 0x0005 +// V3 only +#define FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_RAW 0x0101 +#define FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_DELTA 0x0105 // not supported yet + +// Encryption key tags +#define FOTA_MANIFEST_ENCRYPTION_KEY_TAG_AES_128 0x1 /* * Update details as extracted from the Pelion FOTA manifest @@ -57,6 +64,9 @@ typedef struct { #if defined(MBED_CLOUD_CLIENT_FOTA_SIGNED_IMAGE_SUPPORT) uint8_t installed_signature[FOTA_IMAGE_RAW_SIGNATURE_SIZE]; /** Raw encoded signature over installed image */ #endif // defined(MBED_CLOUD_CLIENT_FOTA_SIGNED_IMAGE_SUPPORT) +#if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) + uint8_t encryption_key[FOTA_ENCRYPT_KEY_SIZE]; /*< Encryption key used to encrypt payload */ +#endif } manifest_firmware_info_t; @@ -80,6 +90,6 @@ int fota_manifest_parse( } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif // __FOTA_MANIFEST_H_ diff --git a/fota/fota_manifest_v1.c b/fota/fota_manifest_v1.c index 33e3245..f917a25 100644 --- a/fota/fota_manifest_v1.c +++ b/fota/fota_manifest_v1.c @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -28,6 +28,7 @@ #include "fota/fota_status.h" #include "fota/fota_manifest.h" +#include "fota/fota_component_defs.h" #include "fota/fota_crypto.h" #include "fota/fota_crypto_asn_extra.h" #include "fota/fota_nvm.h" @@ -501,6 +502,8 @@ int fota_manifest_parse( FOTA_DBG_ASSERT(fw_info); memset(fw_info, 0, sizeof(*fw_info)); + // In V1 manifest we always get the main component + strcpy(fw_info->component_name, FOTA_COMPONENT_MAIN_COMPONENT_NAME); int ret = FOTA_STATUS_MANIFEST_MALFORMED; // used by FOTA_FI_SAFE_COND int fota_sig_status = FOTA_STATUS_MANIFEST_MALFORMED; // must be set to error int tmp_status; // reusable status diff --git a/fota/fota_manifest_v3.c b/fota/fota_manifest_v3.c index edb1a5e..4dd4280 100644 --- a/fota/fota_manifest_v3.c +++ b/fota/fota_manifest_v3.c @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -38,13 +38,14 @@ #include "fota/fota_base.h" #include "fota/fota_nvm.h" #include "fota/fota_crypto.h" +#include "fota/fota_internal.h" #include "mbedtls/asn1.h" #include "mbedtls/asn1write.h" #include "mbedtls/x509_crt.h" #define FOTA_IMAGE_DER_SIGNATURE_SIZE 72 // DER encoded signature max size -#if defined(FOTA_USE_UPDATE_X509) +#if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT == FOTA_X509_PUBLIC_KEY_FORMAT) static inline int der_encode_signature_helper( const mbedtls_mpi *r, const mbedtls_mpi *s, uint8_t *buffer, size_t buffer_size, size_t *bytes_written) @@ -101,17 +102,23 @@ static inline int fota_der_encode_signature( return ret; } -#endif // FOTA_USE_UPDATE_X509 +#endif // #if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT==FOTA_X509_PUBLIC_KEY_FORMAT) /* - * DeltaMetadata ::= SEQUENCE { - * installed-size INTEGER, - * installed-digest OCTET STRING, - * precursor-digest OCTET STRING - * } + * -- Metadata for payload reconstruction + * PayloadMetadata ::= SEQUENCE { + * -- represents reconstructed payload size + * installed-size INTEGER, + * -- represents reconstructed payload digest + * installed-digest OCTET STRING, + * + * -- Used with 'arm-patch-stream' and 'encrypted-patch', + * -- never for other payload formats + * precursor-digest OCTET STRING OPTIONAL + * } */ -#if !defined(FOTA_DISABLE_DELTA) -static int parse_delta_metadata( +#if !defined(FOTA_DISABLE_DELTA) || (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) +static int parse_payload_metadata( const uint8_t *metadata, size_t metadata_size, manifest_firmware_info_t *fw_info, const uint8_t *input_data ) @@ -120,70 +127,93 @@ static int parse_delta_metadata( const unsigned char *metadata_end = metadata + metadata_size; size_t len; - FOTA_MANIFEST_TRACE_DEBUG("Parse DeltaMetadata:installed-size @%d", p - input_data); + FOTA_MANIFEST_TRACE_DEBUG("Parse PayloadMetadata:installed-size @%d", p - input_data); int tls_status = mbedtls_asn1_get_int(&p, metadata_end, (int *) &fw_info->installed_size); if (tls_status != 0) { - FOTA_TRACE_ERROR("Error reading DeltaMetadata:installed-size %d", tls_status); + FOTA_TRACE_ERROR("Error reading PayloadMetadata:installed-size %d", tls_status); return FOTA_STATUS_MANIFEST_MALFORMED; } - FOTA_MANIFEST_TRACE_DEBUG("DeltaMetadata:installed-size %" PRIu32, fw_info->installed_size); + FOTA_MANIFEST_TRACE_DEBUG("PayloadMetadata:installed-size %" PRIu32, fw_info->installed_size); - FOTA_MANIFEST_TRACE_DEBUG("Parse DeltaMetadata:installed-digest @%d", p - input_data); + FOTA_MANIFEST_TRACE_DEBUG("Parse PayloadMetadata:installed-digest @%d", p - input_data); tls_status = mbedtls_asn1_get_tag( &p, metadata_end, &len, MBEDTLS_ASN1_OCTET_STRING); if (tls_status != 0) { - FOTA_TRACE_ERROR("Error reading DeltaMetadata:installed-digest %d", tls_status); + FOTA_TRACE_ERROR("Error reading PayloadMetadata:installed-digest %d", tls_status); return FOTA_STATUS_MANIFEST_MALFORMED; } if (FOTA_CRYPTO_HASH_SIZE != len) { - FOTA_TRACE_ERROR("DeltaMetadata:installed-digest too long %zu", len); + FOTA_TRACE_ERROR("PayloadMetadata:installed-digest too long %zu", len); return FOTA_STATUS_INTERNAL_CRYPTO_ERROR; } memcpy(fw_info->installed_digest, p, len); p += len; - FOTA_MANIFEST_TRACE_DEBUG("Parse DeltaMetadata:precursor-digest @%d", p - input_data); - tls_status = mbedtls_asn1_get_tag( - &p, metadata_end, &len, - MBEDTLS_ASN1_OCTET_STRING); - if (tls_status != 0) { - FOTA_TRACE_ERROR("Error reading DeltaMetadata:precursor-digest %d", tls_status); - return FOTA_STATUS_MANIFEST_MALFORMED; - } +#if !defined(FOTA_DISABLE_DELTA) + if (fw_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_DELTA) { + FOTA_MANIFEST_TRACE_DEBUG("Parse PayloadMetadata:precursor-digest @%d", p - input_data); + tls_status = mbedtls_asn1_get_tag( + &p, metadata_end, &len, + MBEDTLS_ASN1_OCTET_STRING); + if (tls_status != 0) { + FOTA_TRACE_ERROR("Error reading PayloadMetadata:precursor-digest %d", tls_status); + return FOTA_STATUS_MANIFEST_MALFORMED; + } - if (FOTA_CRYPTO_HASH_SIZE != len) { - FOTA_TRACE_ERROR("DeltaMetadata:precursor-digest too long %zu", len); - return FOTA_STATUS_INTERNAL_CRYPTO_ERROR; + if (FOTA_CRYPTO_HASH_SIZE != len) { + FOTA_TRACE_ERROR("PayloadMetadata:precursor-digest too long %zu", len); + return FOTA_STATUS_INTERNAL_CRYPTO_ERROR; + } + memcpy(fw_info->precursor_digest, p, len); } - memcpy(fw_info->precursor_digest, p, len); +#endif // !FOTA_DISABLE_DELTA return FOTA_STATUS_SUCCESS; } -#endif // !FOTA_DISABLE_DELTA +#endif // !FOTA_DISABLE_DELTA || (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) /* - * Manifest ::= SEQUENCE { - * vendor-id OCTET STRING, - * class-id OCTET STRING, - * update-priority INTEGER, - * component-name UTF8String, - * payload-version UTF8String, - * payload-digest OCTET STRING, - * payload-size INTEGER, - * payload-uri UTF8String, - * payload-format ENUMERATED { - * raw-binary(1), - * arm-patch-stream(5) - * }, - * installed-signature OCTET STRING, - * delta-metadata DeltaMetadata OPTIONAL, - * vendor-data OCTET STRING OPTIONAL - * } + * Manifest ::= SEQUENCE { + * + * -- identifier fields + * vendor-id OCTET STRING, + * class-id OCTET STRING, + * + * -- update priority to be passed to an application callback + * update-priority INTEGER, + * + * -- component name + * component-name UTF8String, + * + * -- payload description -- + * payload-version UTF8String, + * payload-digest OCTET STRING, + * payload-size INTEGER, + * payload-uri UTF8String, + * payload-format ENUMERATED { + * -- xx01-xxFF describe payload-format + * -- 01xx-FFxx describe encrypted-format + * raw-binary(1), + * arm-patch-stream(5), + * encrypted-raw(257), -- 0x0101 + * encrypted-patch(261) -- 0x0105 + * }, + * + * -- raw ECDSA signature (r||s) over installed payload + * installed-signature OCTET STRING, + * + * -- Used with 'arm-patch-stream', 'encrypted-raw' and 'encrypted-patch' + * -- never for 'raw-binary' + * payload-metadata PayloadMetadata OPTIONAL, + * + * -- custom data to be passed to an endpoint device + * vendor-data OCTET STRING OPTIONAL + * } */ int parse_manifest_internal( const uint8_t *manifest, size_t manifest_size, @@ -192,9 +222,6 @@ int parse_manifest_internal( int fota_status = FOTA_STATUS_INTERNAL_ERROR; const unsigned char *manifest_end = manifest + manifest_size; unsigned char *p = (unsigned char *) manifest; -#if !defined(FOTA_DISABLE_DELTA) - bool is_delta = false; -#endif size_t len; FOTA_MANIFEST_TRACE_DEBUG("Parse Manifest:vendor-id @%d", p - input_data); @@ -330,23 +357,35 @@ int parse_manifest_internal( p += len; FOTA_MANIFEST_TRACE_DEBUG("Parse Manifest:payload-format @%d", p - input_data); - int payload_format_value = 0; - tls_status = mbedtls_asn1_get_enumerated_value(&p, manifest_end, &payload_format_value); + tls_status = mbedtls_asn1_get_enumerated_value(&p, manifest_end, &fw_info->payload_format); if (tls_status != 0) { FOTA_TRACE_ERROR("Error reading Manifest:payload-format %d", tls_status); return FOTA_STATUS_MANIFEST_MALFORMED; } - FOTA_MANIFEST_TRACE_DEBUG("Manifest:payload-format %d", payload_format_value); - fw_info->payload_format = payload_format_value; + FOTA_MANIFEST_TRACE_DEBUG("Manifest:payload-format %d", fw_info->payload_format); + switch (fw_info->payload_format) { + case FOTA_MANIFEST_PAYLOAD_FORMAT_RAW: + #if !defined(FOTA_DISABLE_DELTA) - if (payload_format_value == FOTA_MANIFEST_PAYLOAD_FORMAT_DELTA) { - is_delta = true; - } else + case FOTA_MANIFEST_PAYLOAD_FORMAT_DELTA: +#endif + break; +#if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) + case FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_RAW: +#if (MBED_CLOUD_CLIENT_FOTA_CANDIDATE_BLOCK_SIZE != FOTA_CLOUD_ENCRYPTION_BLOCK_SIZE) + // reject manifest because we can't guarantee proper operation + // when device's block size is different then payload's encryption size. + FOTA_TRACE_ERROR("error device doesn't support encrypted-raw payload's block size"); + return FOTA_STATUS_MANIFEST_PAYLOAD_UNSUPPORTED; #endif - if (payload_format_value != FOTA_MANIFEST_PAYLOAD_FORMAT_RAW) { - FOTA_TRACE_ERROR("error unsupported payload format %d - ", payload_format_value); - return FOTA_STATUS_MANIFEST_PAYLOAD_UNSUPPORTED; +#endif + break; + + // FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_DELTA not supported yet + default: + FOTA_TRACE_ERROR("error unsupported payload format %d - ", fw_info->payload_format); + return FOTA_STATUS_MANIFEST_PAYLOAD_UNSUPPORTED; } FOTA_MANIFEST_TRACE_DEBUG("Parse Manifest:installed-signature @%d", p - input_data); @@ -366,21 +405,21 @@ int parse_manifest_internal( FOTA_MANIFEST_TRACE_DEBUG("installed-signature not found ptr=%p", p); } -#if !defined(FOTA_DISABLE_DELTA) - if (is_delta) { - FOTA_MANIFEST_TRACE_DEBUG("Parse Manifest:delta-metadata @%d", p - input_data); +#if !defined(FOTA_DISABLE_DELTA) || (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) + if (fw_info->payload_format != FOTA_MANIFEST_PAYLOAD_FORMAT_RAW) { + FOTA_MANIFEST_TRACE_DEBUG("Parse Manifest:payload-metadata @%d", p - input_data); tls_status = mbedtls_asn1_get_tag( &p, manifest_end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); if (tls_status != 0) { - FOTA_TRACE_ERROR("Error reading Manifest:delta-metadata %d", tls_status); + FOTA_TRACE_ERROR("Error reading Manifest:payload-metadata %d", tls_status); return FOTA_STATUS_MANIFEST_MALFORMED; } - fota_status = parse_delta_metadata(p, len, fw_info, input_data); + fota_status = parse_payload_metadata(p, len, fw_info, input_data); if (fota_status != 0) { - FOTA_TRACE_ERROR("Error parse_delta_metadata %d", fota_status); + FOTA_TRACE_ERROR("Error parse_payload_metadata %d", fota_status); return fota_status; } @@ -389,7 +428,8 @@ int parse_manifest_internal( } else #endif { - /*for the ease of use we will fill in payload size and digest values */ + /* FOTA_MANIFEST_PAYLOAD_FORMAT_RAW */ + /* for the ease of use we will fill in payload size and digest values */ memcpy(fw_info->installed_digest, fw_info->payload_digest, FOTA_CRYPTO_HASH_SIZE); fw_info->installed_size = fw_info->payload_size; } @@ -415,14 +455,73 @@ int parse_manifest_internal( return FOTA_STATUS_SUCCESS; } +#if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) /* - * Assuming following ASN1 schema - * SignedResource ::= SEQUENCE { - * manifest-version ENUMERATED { - * v3(3) - * }, - * manifest Manifest, - * signature OCTET STRING + * -- Encryption Key Schema: + * -- the key used to encrypt the payload + * -- added by service after SignedResource + * EncryptionKeySchema DEFINITIONS IMPLICIT TAGS ::= BEGIN + * EncryptionKey ::= CHOICE { + * aes-128-bit [1] IMPLICIT OCTET STRING (SIZE(16)) + * } + */ +static int parse_encryption_key( + const uint8_t *int_key, size_t int_key_size, + uint8_t encryption_key[FOTA_ENCRYPT_KEY_SIZE], + const uint8_t *input_data +) +{ + size_t len = int_key_size; + unsigned char *p = (unsigned char *)int_key; + unsigned char *encryption_key_end = p + int_key_size; + + FOTA_MANIFEST_TRACE_DEBUG("Parse EncryptionKey @%d", p - input_data); + int ret = mbedtls_asn1_get_tag( + &p, encryption_key_end, &len, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | FOTA_MANIFEST_ENCRYPTION_KEY_TAG_AES_128); + if (ret != 0) { + FOTA_TRACE_ERROR("Error EncryptionKey tag %d", ret); + return FOTA_STATUS_MANIFEST_MALFORMED; + } + + if (len != FOTA_ENCRYPT_KEY_SIZE) { + FOTA_TRACE_ERROR("Unexpected EncryptionKey size %zu", len); + return FOTA_STATUS_MANIFEST_MALFORMED; + } + + if (p + len > encryption_key_end) { + FOTA_TRACE_ERROR("Error got truncated manifest"); + return FOTA_STATUS_MANIFEST_MALFORMED; + } + + fota_fi_memcpy(encryption_key, p, FOTA_ENCRYPT_KEY_SIZE); + p += len; + + return FOTA_STATUS_SUCCESS; +} +#endif // (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) + +/* + * Assuming SignedResource followed by EncryptionKeySchema + * when the payload is pre-encrypted + * + * SignedResource ::= SEQUENCE { + * manifest-version ENUMERATED { + * v3(3) + * }, + * manifest Manifest, + * + * -- raw ECDSA signature (r||s) over Manifest + * signature OCTET STRING + * } + * + * -- Encryption Key Schema: + * -- the key used to encrypt the payload + * -- added by service after SignedResource + * EncryptionKeySchema DEFINITIONS IMPLICIT TAGS ::= BEGIN + * EncryptionKey ::= CHOICE { + * aes-128-bit [1] IMPLICIT OCTET STRING (SIZE(16)) + * } */ int fota_manifest_parse( const uint8_t *input_data, size_t input_size, @@ -459,8 +558,9 @@ int fota_manifest_parse( return FOTA_STATUS_MANIFEST_MALFORMED; } - // input data size may be bigger than real manifest due to storage limitations. - // update real resource end + // input data size may be bigger than real SignedResource size + // due to storage limitations or EncryptionKey. + // set to exact SignedResource end signed_resource_end = p + len; int manifest_format_version = 0; @@ -506,7 +606,7 @@ int fota_manifest_parse( } #if !defined(FOTA_TEST_MANIFEST_BYPASS_VALIDATION) -#if defined(FOTA_USE_UPDATE_X509) +#if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT==FOTA_X509_PUBLIC_KEY_FORMAT) // signature in manifest schema v3 is a raw signature, // When using mbedtls_pk is used DER encoded signature is expected uint8_t der_encoded_sig[FOTA_IMAGE_DER_SIGNATURE_SIZE]; @@ -522,12 +622,14 @@ int fota_manifest_parse( fota_sig_status = fota_verify_signature( signed_data_ptr, signed_data_size, der_encoded_sig, der_encoded_sig_size); -#else // defined(FOTA_USE_UPDATE_X509) +#elif (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT==FOTA_RAW_PUBLIC_KEY_FORMAT) fota_sig_status = fota_verify_signature( signed_data_ptr, signed_data_size, p, len); -#endif // defined(FOTA_USE_UPDATE_X509) +#else +#error public key format not supported +#endif // MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT FOTA_FI_SAFE_COND( fota_sig_status == FOTA_STATUS_SUCCESS, fota_sig_status, @@ -545,6 +647,22 @@ int fota_manifest_parse( return tmp_status; } +#if (MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT == 1) + if (fw_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_RAW) { + + unsigned char *int_key = p; + size_t int_key_size = input_size - (size_t)(p - input_data); + tmp_status = parse_encryption_key( + int_key, int_key_size, + fw_info->encryption_key, input_data); + if (tmp_status != 0) { + FOTA_TRACE_ERROR("parse_manifest_internal failed %d", tmp_status); + return tmp_status; + } + + } +#endif + FOTA_MANIFEST_TRACE_DEBUG("status = %d", FOTA_STATUS_SUCCESS); return FOTA_STATUS_SUCCESS; fail: diff --git a/fota/fota_multicast.h b/fota/fota_multicast.h new file mode 100644 index 0000000..db57398 --- /dev/null +++ b/fota/fota_multicast.h @@ -0,0 +1,156 @@ +// ---------------------------------------------------------------------------- +// Copyright 2020 Pelion Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// 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 __FOTA_MULTICAST_H_ +#define __FOTA_MULTICAST_H_ + +#include "fota/fota_base.h" + +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) + +#ifdef __cplusplus +extern "C" { +#endif + +#if (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_NODE_MODE) + +// Post action callback - supplying success/error code +typedef void (*fota_multicast_node_post_action_callback_t)(int); + +/** + * Got a new manifest via Multicast. + * + * \param[in] data manifest data. + * \param[in] size manifest size. + * \param[in] on_manifest_cb callback whether OK to continue or error. + * \return FOTA_STATUS_SUCCESS on success. + */ +int fota_multicast_node_on_manifest(uint8_t *data, size_t size, + fota_multicast_node_post_action_callback_t on_manifest_cb); + +/** + * Got an image ready indication from Multicast. + * + * \return FOTA_STATUS_SUCCESS on success. + */ +int fota_multicast_node_on_image_ready(void); + +/** + * Got an activate update command from Multicast. + * + * \param[in] activate_in_sec Seconds to wait before activation. + * \param[in] finish_cb Callback triggered when finished. + * \return FOTA_STATUS_SUCCESS on success. + */ +int fota_multicast_node_on_activate(size_t activate_in_sec, + fota_multicast_node_post_action_callback_t activate_finish_cb); + +/** + * Got an abort update command from Multicast. + * + * \return FOTA_STATUS_SUCCESS on success. + */ +int fota_multicast_node_on_abort(void); + +/** + * Got a request from Multicast to get ready to store a new image. + * + * \param[in] image_size Image size + * \return FOTA_STATUS_SUCCESS on success + */ +int fota_multicast_node_get_ready_for_image(size_t image_size); + +/** + * Write a fragment to the image at a certain offset. + * Notes: + * - Writes can’t be done twice to the same offset. + * - Writes can only start after either ::fota_multicast_node_on_manifest or ::fota_multicast_node_get_ready_for_image were received + * - Write offset should be a multiple of the fragment size + * - Write size should be a multiple of the the fragment size (except for the last write) + * + * \param[in] buffer Buffer to write. + * \param[in] offset Offset of buffer in image. + * \param[in] size Buffer size + * \return FOTA_STATUS_SUCCESS on success + */ +int fota_multicast_node_write_image_fragment(const void *buffer, size_t offset, size_t size); + +/** + * Read a fragment from the image at a certain offset. + * Notes: + * - Reads can only start after either ::fota_multicast_node_on_manifest or ::fota_multicast_node_get_ready_for_image were received + * - Read offset should be a multiple of the candidate storage read size + * - Read size should be a multiple of the the candidate storage read + * + * \param[in] buffer Buffer to read. + * \param[in] offset Offset of buffer in image. + * \param[in] size Buffer size + * \return FOTA_STATUS_SUCCESS on success + */ +int fota_multicast_node_read_image_fragment(void *buffer, size_t offset, size_t size); + +/** + * Set multicast network fragment size. + * + * \param[in] frag_size Fragment size. + * \return FOTA_STATUS_SUCCESS on success + */ +int fota_multicast_node_set_fragment_size(size_t frag_size); + +#elif (MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT == FOTA_MULTICAST_BR_MODE) + +typedef struct { + char uri[FOTA_MANIFEST_URI_SIZE]; // URI for downloading the image + size_t payload_size; // Image size to be downloaded + uint8_t payload_digest[FOTA_CRYPTO_HASH_SIZE]; // Image SHA256 digest +} fota_multicast_br_image_params; + +// Post action callback - supplying success/error code +typedef void (*fota_multicast_br_post_action_callback_t)(int); + +/** + * Called when a new image is available from service to the BR + * + * \param[in] image_params Downloaded image parameters. + * \param[in] image_ready_cb Callback called when image download ends successfully or fails. + * \return FOTA_STATUS_SUCCESS on success + */ +int fota_multicast_br_on_image_request(const fota_multicast_br_image_params *image_params, + fota_multicast_br_post_action_callback_t image_ready_cb); + +/** + * Read a fragment from downloaded image at a certain offset. + * Notes: + * - Read offset should be a multiple of the candidate storage read size + * - Read size should be a multiple of the the candidate storage read + * + * \param[in] buffer Buffer to read. + * \param[in] offset Offset of buffer in image. + * \param[in] size Buffer size + * \return FOTA_STATUS_SUCCESS on success + */ +int fota_multicast_br_read_from_image(void *buffer, size_t offset, size_t size); + +#endif // FOTA_MULTICAST_BR_MODE + +#ifdef __cplusplus +} +#endif + +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) +#endif // __FOTA_MULTICAST_H_ diff --git a/fota/fota_nvm.c b/fota/fota_nvm.c index e653218..5d3e56f 100644 --- a/fota/fota_nvm.c +++ b/fota/fota_nvm.c @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -20,6 +20,9 @@ #ifdef MBED_CLOUD_CLIENT_FOTA_ENABLE +// TODO: Replace this with a proper define +#if !defined(FOTA_UNIT_TEST) + #define TRACE_GROUP "FOTA" #include "fota/fota_status.h" @@ -52,6 +55,7 @@ static fota_status_e map_store_result(int result) return res; } + #if (MBED_CLOUD_CLIENT_PROFILE == MBED_CLOUD_CLIENT_PROFILE_FULL) int fota_nvm_get(cloud_client_param key, uint8_t *buffer, size_t buffer_size, size_t *bytes_read, ccs_item_type_e item_type) { @@ -61,7 +65,17 @@ int fota_nvm_get(cloud_client_param key, uint8_t *buffer, size_t buffer_size, si int fota_nvm_set(cloud_client_param key, const uint8_t *buffer, size_t buffer_size, ccs_item_type_e item_type) { + // the ccs is wrapper over the KCM + // KCM returns an error if the same item is stored twice + // In case the item already exists, item must be deleted first. ccs_status_e result = ccs_set_item(key, buffer, buffer_size, item_type); + if (result == CCS_STATUS_KEY_EXISTS) { + result = ccs_delete_item(key, item_type); + if (result == CCS_STATUS_SUCCESS) { + result = ccs_set_item(key, buffer, buffer_size, item_type); + } + } + return map_store_result(result); } @@ -94,11 +108,9 @@ int fota_nvm_remove(cloud_client_param key, ccs_item_type_e item_type) return map_store_result(status); } - #endif // (MBED_CLOUD_CLIENT_PROFILE == MBED_CLOUD_CLIENT_PROFILE_FULL) - -#if !defined(FOTA_USE_EXTERNAL_FW_KEY) +#if !defined(FOTA_KEY_ENCRYPTION_EXTERNAL_STORAGE) int fota_nvm_fw_encryption_key_get(uint8_t buffer[FOTA_ENCRYPT_KEY_SIZE]) { size_t bytes_read; @@ -116,11 +128,11 @@ int fota_nvm_fw_encryption_key_delete(void) { return fota_nvm_remove(FOTA_ENCRYPT_KEY, CCS_SYMMETRIC_KEY_ITEM); } -#endif // !defined(FOTA_USE_EXTERNAL_FW_KEY) +#endif // !defined(FOTA_KEY_ENCRYPTION_EXTERNAL_STORAGE) /******************************************************************************************************/ /* Update x509 Certificate */ /******************************************************************************************************/ -#if defined(FOTA_USE_UPDATE_X509) +#if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT == FOTA_X509_PUBLIC_KEY_FORMAT) #if defined(MBED_CLOUD_DEV_UPDATE_CERT) @@ -182,12 +194,12 @@ int fota_nvm_get_update_certificate(uint8_t *buffer, size_t size, size_t *bytes_ } #endif // !defined(FOTA_USE_EXTERNAL_CERT) -#endif // defined(FOTA_USE_UPDATE_X509) +#endif // (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT==FOTA_X509_PUBLIC_KEY_FORMAT) /******************************************************************************************************/ /* Update public key */ /******************************************************************************************************/ -#if defined(FOTA_USE_UPDATE_RAW_PUBLIC_KEY) +#if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT == FOTA_RAW_PUBLIC_KEY_FORMAT) #if defined(MBED_CLOUD_DEV_UPDATE_RAW_PUBLIC_KEY) extern const uint8_t arm_uc_update_public_key[]; @@ -236,7 +248,7 @@ int fota_nvm_get_update_public_key(uint8_t buffer[FOTA_UPDATE_RAW_PUBLIC_KEY_SIZ #endif // !defined(FOTA_USE_EXTERNAL_UPDATE_RAW_PUBLIC_KEY) -#endif // defined(FOTA_USE_UPDATE_RAW_PUBLIC_KEY)§ +#endif // #if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT == FOTA_RAW_PUBLIC_KEY_FORMAT) /******************************************************************************************************/ /* VENDOR and CLASS IDs */ @@ -362,4 +374,5 @@ int fota_nvm_comp_version_get(const char *comp_name, fota_component_version_t *v return fota_nvm_get(key, (uint8_t *)version, sizeof(*version), &bytes_read, CCS_CONFIG_ITEM); } +#endif //!defined(FOTA_UNIT_TEST) #endif // MBED_CLOUD_CLIENT_FOTA_ENABLE diff --git a/fota/fota_nvm.h b/fota/fota_nvm.h index 8d8d87b..bfaa857 100644 --- a/fota/fota_nvm.h +++ b/fota/fota_nvm.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -20,7 +20,7 @@ #define __FOTA_NVM_H_ #include "fota/fota_base.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #ifdef __cplusplus extern "C" { @@ -33,8 +33,6 @@ extern "C" { #include #include - - #define FOTA_GUID_SIZE (128/8) /* @@ -55,7 +53,7 @@ int fota_nvm_get_class_id(uint8_t buffer[FOTA_GUID_SIZE]); */ int fota_nvm_get_vendor_id(uint8_t buffer[FOTA_GUID_SIZE]); -#if defined(FOTA_USE_UPDATE_X509) +#if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT==FOTA_X509_PUBLIC_KEY_FORMAT) /* * Get FOTA certificate from storage. * @@ -66,11 +64,11 @@ int fota_nvm_get_vendor_id(uint8_t buffer[FOTA_GUID_SIZE]); * \return FOTA_STATUS_SUCCESS on success. */ int fota_nvm_get_update_certificate(uint8_t *buffer, size_t size, size_t *bytes_read); -#endif // defined(FOTA_USE_UPDATE_X509) +#endif // (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT==FOTA_X509_PUBLIC_KEY_FORMAT) -#if defined(FOTA_USE_UPDATE_RAW_PUBLIC_KEY) +#if (MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT == FOTA_RAW_PUBLIC_KEY_FORMAT) int fota_nvm_get_update_public_key(uint8_t buffer[FOTA_UPDATE_RAW_PUBLIC_KEY_SIZE]); -#endif // defined(FOTA_USE_UPDATE_RAW_PUBLIC_KEY) +#endif /* * Get firmware encryption key from storage. @@ -165,5 +163,5 @@ int fota_nvm_set_update_public_key(void); #ifdef __cplusplus } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif //__FOTA_NVM_H_ diff --git a/fota/fota_nvm_int.h b/fota/fota_nvm_int.h index 6335e20..898d587 100644 --- a/fota/fota_nvm_int.h +++ b/fota/fota_nvm_int.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -21,7 +21,7 @@ #include "fota/fota_base.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #ifdef __cplusplus extern "C" { @@ -33,20 +33,22 @@ extern "C" { typedef uint8_t ccs_item_type_e; -#define CCS_PRIVATE_KEY_ITEM 0x1 -#define CCS_PUBLIC_KEY_ITEM 0x2 -#define CCS_SYMMETRIC_KEY_ITEM 0x4 -#define CCS_CERTIFICATE_ITEM 0x8 -#define CCS_CONFIG_ITEM 0x10 +#define CCS_PRIVATE_KEY_ITEM 0 +#define CCS_PUBLIC_KEY_ITEM 1 +#define CCS_SYMMETRIC_KEY_ITEM 2 +#define CCS_CERTIFICATE_ITEM 3 +#define CCS_CONFIG_ITEM 4 #else // (MBED_CLOUD_CLIENT_PROFILE == MBED_CLOUD_CLIENT_PROFILE_FULL) +#include "fcc_defs.h" + typedef const char * cloud_client_param; -#define UPDATE_VENDOR_ID "pelion_wCfgParam_mbed.VendorId" // "FWVendorId" -#define UPDATE_CLASS_ID "pelion_wCfgParam_mbed.ClassId" // "FWClassId" -#define UPDATE_CERTIFICATE "pelion_wCrtae_mbed.UpdateAuthCert" // "FWUpdateCert" -#define UPDATE_PUBKEY "pelion_wCrtae_mbed.UpdatePubKey" +#define UPDATE_VENDOR_ID g_fcc_vendor_id_name +#define UPDATE_CLASS_ID g_fcc_class_id_name +#define UPDATE_CERTIFICATE g_fcc_update_authentication_certificate_name +#define UPDATE_PUBKEY "FOTA_UPDATE_PUB_KEY" #define FOTA_ENCRYPT_KEY "FOTA_ENCRYPT_KEY" // "FTEncryptKey" #define FOTA_SALT_KEY "FOTA_SALT_KEY" // ""FTSaltKey" #define FOTA_MANIFEST_KEY "FOTA_MANIFEST_KEY" // ""FTManKey" @@ -63,5 +65,5 @@ int fota_nvm_remove(cloud_client_param key, ccs_item_type_e item_type); #ifdef __cplusplus } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif //__FOTA_NVM_INT_H_ diff --git a/fota/fota_platform.h b/fota/fota_platform_hooks.h similarity index 50% rename from fota/fota_platform.h rename to fota/fota_platform_hooks.h index b02d60f..eb64c4a 100644 --- a/fota/fota_platform.h +++ b/fota/fota_platform_hooks.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -21,82 +21,62 @@ #include "fota/fota_base.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #include "fota/fota_status.h" -#include "fota/fota_block_device.h" -#include "fota/fota_candidate.h" #ifdef __cplusplus extern "C" { #endif +/** + * @file fota_platform_hooks.h + * \brief Platform hooks that the platform can implement if the target requires more complex FOTA initialization and teardown steps. + * By default, Pelion FOTA provides an empty implementation for these hooks. + * An application developer can override these hooks by injecting the ::FOTA_CUSTOM_PLATFORM macro to the build and implementing all the callback functions listed below. + */ -#if defined(FOTA_CUSTOM_PLATFORM) && (FOTA_CUSTOM_PLATFORM) - -// Hooks that need to be supplied by platform specific code /** - * Platform init hook, called at FOTA module initialization. + * Platform init hook. + * Called when the FOTA module is initialized. * - * \param[in] after_upgrade Indicates that hook was called after an upgrade. - * \return FOTA_STATUS_SUCCESS on success. + * \param[in] after_upgrade Indicates whether the FOTA client is being initiated after a successful upgrade of the installed image, or as part of a regular boot. + * \return ::FOTA_STATUS_SUCCESS on success. */ int fota_platform_init_hook(bool after_upgrade); /** - * Platform start update hook, called when update is started. + * Platform start update hook. + * Called when the download of the update candidate begins. * * \param[in] comp_name Component name. - * \return FOTA_STATUS_SUCCESS on success. + * \return ::FOTA_STATUS_SUCCESS on success. */ int fota_platform_start_update_hook(const char *comp_name); /** - * Platform finish update hook, called when update is finished. + * Platform finish update hook. + * Called when the download of the update candidate ends. * * \param[in] comp_name Component name. - * \return FOTA_STATUS_SUCCESS on success. + * \return ::FOTA_STATUS_SUCCESS on success. */ int fota_platform_finish_update_hook(const char *comp_name); /** - * Platform start update hook, called when update is aborted. + * Platform abort update hook. + * Called when the download of the update candidate is aborted. * * \param[in] comp_name Component name. - * \param[in] abort_code Abort return code. - * \return FOTA_STATUS_SUCCESS on success. + * \return ::FOTA_STATUS_SUCCESS on success. */ int fota_platform_abort_update_hook(const char *comp_name); -#else - -// Default platform hooks -static inline int fota_platform_init_hook(bool after_upgrade) -{ - return FOTA_STATUS_SUCCESS; -} - -static inline int fota_platform_start_update_hook(const char *comp_name) -{ - return FOTA_STATUS_SUCCESS; -} - -static inline int fota_platform_finish_update_hook(const char *comp_name) -{ - return FOTA_STATUS_SUCCESS; -} - -static inline int fota_platform_abort_update_hook(const char *comp_name) -{ - return FOTA_STATUS_SUCCESS; -} - -#endif // !defined(FOTA_CUSTOM_PLATFORM) || (!FOTA_CUSTOM_PLATFORM) #ifdef __cplusplus } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif // __FOTA_PLATFORM_H_ diff --git a/fota/fota_platform_hooks_default.c b/fota/fota_platform_hooks_default.c new file mode 100644 index 0000000..e4d1400 --- /dev/null +++ b/fota/fota_platform_hooks_default.c @@ -0,0 +1,48 @@ +// ---------------------------------------------------------------------------- +// Copyright 2021 Pelion Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// 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 "fota_platform_hooks.h" + +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) + +#if !defined(FOTA_CUSTOM_PLATFORM) || (!FOTA_CUSTOM_PLATFORM) + +// Default platform hooks +int fota_platform_init_hook(bool after_upgrade) +{ + return FOTA_STATUS_SUCCESS; +} + +int fota_platform_start_update_hook(const char *comp_name) +{ + return FOTA_STATUS_SUCCESS; +} + +int fota_platform_finish_update_hook(const char *comp_name) +{ + return FOTA_STATUS_SUCCESS; +} + +int fota_platform_abort_update_hook(const char *comp_name) +{ + return FOTA_STATUS_SUCCESS; +} + +#endif // !defined(FOTA_CUSTOM_PLATFORM) || (!FOTA_CUSTOM_PLATFORM) + +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) + diff --git a/fota/fota_platform_linux.cpp b/fota/fota_platform_linux.cpp deleted file mode 100644 index da71dda..0000000 --- a/fota/fota_platform_linux.cpp +++ /dev/null @@ -1,182 +0,0 @@ -// ---------------------------------------------------------------------------- -// Copyright 2020 ARM Ltd. -// -// SPDX-License-Identifier: Apache-2.0 -// -// 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 "fota/fota_base.h" - -#ifdef MBED_CLOUD_CLIENT_FOTA_ENABLE -#if defined(TARGET_LIKE_LINUX) - -#include -#include -#include -#include -#include - -#include "fota_platform_linux.h" -#include "fota_crypto.h" -#include "fota_curr_fw.h" -#include "fota_component_internal.h" - -#define TRACE_GROUP "FOTA" - -#ifdef MBED_CLOUD_CLIENT_FOTA_INIT_MAIN_VERSION -#define __STRINGIFY(macro) #macro -#define STRINGIFY(macro) __STRINGIFY(macro) -#define INIT_MAIN_VERSION STRINGIFY(MBED_CLOUD_CLIENT_FOTA_INIT_MAIN_VERSION) -#else -#define INIT_MAIN_VERSION "0.0.0" -#endif - - -extern char *program_invocation_name; -static const int fw_buf_size = 2048; - -int fota_linux_candidate_iterate(fota_candidate_iterate_callback_info *info) -{ - switch (info->status) { - case FOTA_CANDIDATE_ITERATE_START: { - // open candidate file to write - info->user_ctx = (void *)fopen(MBED_CLOUD_CLIENT_FOTA_LINUX_CANDIDATE_FILENAME, "wb"); - if (info->user_ctx == NULL) { - FOTA_TRACE_ERROR("Failed opening file %s: %d", MBED_CLOUD_CLIENT_FOTA_LINUX_CANDIDATE_FILENAME, errno); - return FOTA_STATUS_STORAGE_WRITE_FAILED; - } - - return FOTA_STATUS_SUCCESS; - } - - case FOTA_CANDIDATE_ITERATE_FRAGMENT: - if (fseek((FILE *)info->user_ctx, info->frag_pos, SEEK_SET)) { - (void)fclose((FILE *)info->user_ctx); - return FOTA_STATUS_STORAGE_WRITE_FAILED; - } - if (fwrite(info->frag_buf, info->frag_size, 1, (FILE *)info->user_ctx) != 1) { - (void)fclose((FILE *)info->user_ctx); - return FOTA_STATUS_STORAGE_WRITE_FAILED; - } - - return FOTA_STATUS_SUCCESS; - - case FOTA_CANDIDATE_ITERATE_FINISH: { - (void)fclose((FILE *)info->user_ctx); - - // write current header - size_t size; - uint8_t header_buf[fw_buf_size]; - - if (fota_serialize_header(info->header_info, header_buf, fw_buf_size, &size) != FOTA_STATUS_SUCCESS) { - FOTA_TRACE_ERROR("Failed to serialize header"); - return FOTA_STATUS_INTERNAL_ERROR; - } - - FILE *fd = fopen(MBED_CLOUD_CLIENT_FOTA_LINUX_HEADER_FILENAME, "wb"); - if (!fd) { - FOTA_TRACE_ERROR("Failed to open header file for update"); - return FOTA_STATUS_INTERNAL_ERROR; - } - - if (fwrite(header_buf, size, 1, fd) != 1) { - FOTA_TRACE_ERROR("Failed to write header"); - (void)fclose(fd); - return FOTA_STATUS_INTERNAL_ERROR; - } - - (void)fclose(fd); - - return FOTA_STATUS_SUCCESS; - } - - default: - return FOTA_STATUS_INTERNAL_ERROR; - } - - return FOTA_STATUS_INTERNAL_ERROR; -} - -int fota_linux_init() -{ - int status = FOTA_STATUS_INTERNAL_ERROR; - - // Check first whether file exists, if exists skip header creation part - FILE *fs = fopen(MBED_CLOUD_CLIENT_FOTA_LINUX_HEADER_FILENAME, "r"); - if (!fs) { - fota_header_info_t header_info = {0}; - - status = fota_component_version_semver_to_int(INIT_MAIN_VERSION, &header_info.version); - if (status) { - FOTA_TRACE_ERROR("Invalid initial version " INIT_MAIN_VERSION); - FOTA_ASSERT(!status); - } - - // Write valid header information into file - fs = fopen(MBED_CLOUD_CLIENT_FOTA_LINUX_HEADER_FILENAME, "w"); - if (!fs) { - FOTA_TRACE_ERROR("Failed to create file for header information"); - return status; - } - - struct stat statbuf; - if (stat(program_invocation_name, &statbuf) != 0) { - FOTA_TRACE_ERROR("Failed to read program file params"); - goto cleanup; - } - - header_info.fw_size = statbuf.st_size; - fota_set_header_info_magic(&header_info); - uint8_t fw_buf[fw_buf_size]; - size_t actual_size; - - fota_hash_context_t *hash_ctx = NULL; - if (fota_hash_start(&hash_ctx) != FOTA_STATUS_SUCCESS) { - goto cleanup; - } - - for (size_t i = 0; i < statbuf.st_size; i += fw_buf_size) { - if (fota_curr_fw_read(fw_buf, i, fw_buf_size, &actual_size) == FOTA_STATUS_SUCCESS) { - if (fota_hash_update(hash_ctx, fw_buf, actual_size)) { - goto cleanup; - } - } else { - goto cleanup; - } - } - - if (fota_hash_result(hash_ctx, header_info.digest)) { - goto cleanup; - } - - fota_hash_finish(&hash_ctx); - - if (fota_serialize_header(&header_info, fw_buf, fw_buf_size, &actual_size)) { - goto cleanup; - } - - if (fwrite(fw_buf, 1, actual_size, fs) != actual_size) { - goto cleanup; - } - } // !fs - - status = FOTA_STATUS_SUCCESS; - -cleanup: - fclose(fs); - return status; -} - -#endif // defined(TARGET_LIKE_LINUX) -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE diff --git a/fota/fota_shim_layer.cpp b/fota/fota_shim_layer.cpp new file mode 100644 index 0000000..fbb077b --- /dev/null +++ b/fota/fota_shim_layer.cpp @@ -0,0 +1,164 @@ +// ---------------------------------------------------------------------------- +// Copyright 2019-2021 Pelion Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// 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 "fota/fota_shim_layer.h" + +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) && defined(FOTA_SHIM_LAYER) + +#define TRACE_GROUP "FOTA" + +#include "fota/fota_app_ifs.h" + +#include + +auth_handler_t auth_handler = NULL; +priority_auth_handler_t priority_auth_handler = NULL; +progress_handler_t progress_handler = NULL; + +void fota_shim_set_auth_handler(auth_handler_t handler) +{ + auth_handler = handler; +} + +void fota_shim_set_auth_handler(priority_auth_handler_t handler) +{ + priority_auth_handler = handler; +} + +void fota_shim_set_progress_handler(progress_handler_t handler) +{ + progress_handler = handler; +} + +void fota_app_on_download_progress(size_t downloaded_size, size_t current_chunk_size, size_t total_size) +{ + FOTA_ASSERT(total_size); + if (progress_handler) { + progress_handler(downloaded_size, total_size); + return; + } + + static const uint32_t print_range_percent = 5; + + total_size /= 100; + // In case total size is less then 100B return without printing progress + if (total_size == 0) { + return; + } + + uint32_t progress = (downloaded_size + current_chunk_size) / total_size; + uint32_t prev_progress = downloaded_size / total_size; + + if (downloaded_size == 0 || ((progress / print_range_percent) > (prev_progress / print_range_percent))) { + FOTA_APP_PRINT("Downloading firmware. %" PRIu32 "%c", progress, '%'); + } +} + +/* Pelion FOTA done or terminated. + * Application can restore performance sensitive tasks and + * dismiss any update running dialogs. + * +*/ +int fota_app_on_complete(int32_t status) +{ + return FOTA_STATUS_SUCCESS; +} + +/* Pelion FOTA Client wishes to reboot and apply the new firmware. + + The user application is supposed to save all current work + before rebooting. + + Note: the authorization call can be postponed and called later. + This doesn't affect the performance of the Cloud Client. +*/ +int fota_app_on_install_authorization() +{ + if (priority_auth_handler) { + priority_auth_handler(ARM_UCCC_REQUEST_INSTALL, 0); + } else if (auth_handler) { + auth_handler(ARM_UCCC_REQUEST_INSTALL); + } else { + fota_app_authorize(); + FOTA_APP_PRINT("Install authorization granted"); + } + return FOTA_STATUS_SUCCESS; +} + +/* Pelion FOTA Client wishes to download new firmware. + This can have a negative impact on the performance of the + rest of the system. + + The user application is supposed to pause performance + sensitive tasks before authorizing the download. + + Note: the authorization call can be postponed and called later. + This doesn't affect the performance of the Cloud Client. +*/ +int fota_app_on_download_authorization( + const manifest_firmware_info_t *candidate_info, + fota_component_version_t curr_fw_version +) +{ + if (priority_auth_handler) { + priority_auth_handler(ARM_UCCC_REQUEST_DOWNLOAD, candidate_info->priority); + } else if (auth_handler) { + auth_handler(ARM_UCCC_REQUEST_DOWNLOAD); + } else { + char curr_semver[FOTA_COMPONENT_MAX_SEMVER_STR_SIZE] = { 0 }; + char new_semver[FOTA_COMPONENT_MAX_SEMVER_STR_SIZE] = { 0 }; + fota_component_version_int_to_semver(curr_fw_version, curr_semver); + fota_component_version_int_to_semver(candidate_info->version, new_semver); + FOTA_APP_PRINT("---------------------------------------------------"); + FOTA_APP_PRINT( + "Updating component %s from version %s to %s", + candidate_info->component_name, + curr_semver, new_semver + ); + FOTA_APP_PRINT("Update priority %" PRIu32, candidate_info->priority); + + if (candidate_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_DELTA) { + FOTA_APP_PRINT( + "Delta update. Patch size %zuB full image size %zuÃ¥B", + candidate_info->payload_size, + candidate_info->installed_size + ); + } else if (candidate_info->payload_format == FOTA_MANIFEST_PAYLOAD_FORMAT_ENCRYPTED_RAW) { + FOTA_APP_PRINT("Update size %zuB (Encrypted image size %zuB)", + candidate_info->installed_size, + candidate_info->payload_size + ); + } else { + FOTA_APP_PRINT("Update size %zuB", candidate_info->payload_size); + } + FOTA_APP_PRINT("---------------------------------------------------"); + FOTA_APP_PRINT("Download authorization granted"); + fota_app_authorize(); + /* Application can reject an update in the following way + fota_app_reject(127); + Reason error code will be logged. + Alternatively application can defer the update by calling + fota_app_defer(); + Deferred update will be restarted on next boot or by calling fota_app_resume() API. + + */ + } + return FOTA_STATUS_SUCCESS; +} + +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) && defined(FOTA_SHIM_LAYER) diff --git a/fota/fota_shim_layer.h b/fota/fota_shim_layer.h new file mode 100644 index 0000000..752bc10 --- /dev/null +++ b/fota/fota_shim_layer.h @@ -0,0 +1,46 @@ +// ---------------------------------------------------------------------------- +// Copyright 2019-2021 Pelion Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// 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 __FOTA_SHIM_LAYER_H_ +#define __FOTA_SHIM_LAYER_H_ + +#include "fota/fota_base.h" + +#ifdef FOTA_SHIM_LAYER + +#define ARM_UCCC_REQUEST_INVALID 117 +#define ARM_UCCC_REQUEST_DOWNLOAD 118 +#define ARM_UCCC_REQUEST_INSTALL 119 + +#define ARM_UCCC_REJECT_REASON_UNAUTHORIZED 120 +#define ARM_UCCC_REJECT_REASON_UNAVAILABLE 121 + +#define ARM_UC_HUB_Uninitialize() + + +typedef void (*auth_handler_t)(int32_t request); +typedef void (*priority_auth_handler_t)(int32_t request, uint64_t priority); +typedef void (*progress_handler_t)(uint32_t progress, uint32_t total); + +void fota_shim_set_auth_handler(auth_handler_t handler); +void fota_shim_set_auth_handler(priority_auth_handler_t handler); +void fota_shim_set_progress_handler(progress_handler_t handler); + +#endif // FOTA_SHIM_LAYER + +#endif // __FOTA_SHIM_LAYER_H_ diff --git a/fota/fota_source.h b/fota/fota_source.h index 6f9ac46..78c60aa 100644 --- a/fota/fota_source.h +++ b/fota/fota_source.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -21,7 +21,7 @@ #include "fota/fota_base.h" -#if MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #ifdef __cplusplus extern "C" { @@ -60,11 +60,12 @@ typedef void (*report_sent_callback_t)(void); int fota_source_report_state(fota_source_state_e state, report_sent_callback_t on_sent, report_sent_callback_t on_failure); int fota_source_report_update_result(int result); void fota_source_send_manifest_received_ack(void); +void fota_source_enable_auto_observable_resources_reporting(bool enable); #ifdef __cplusplus } #endif -#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE +#endif // defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) #endif // __FOTA_SOURCE_H_ diff --git a/fota/fota_source_defs.h b/fota/fota_source_defs.h index f183ce3..9e35ee9 100644 --- a/fota/fota_source_defs.h +++ b/fota/fota_source_defs.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // diff --git a/fota/fota_source_profile_full.cpp b/fota/fota_source_profile_full.cpp index 3ea0a65..d25ad31 100644 --- a/fota/fota_source_profile_full.cpp +++ b/fota/fota_source_profile_full.cpp @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2019 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -19,7 +19,7 @@ #include "fota/fota_base.h" #ifdef MBED_CLOUD_CLIENT_FOTA_ENABLE -#if (MBED_CLOUD_CLIENT_PROFILE == MBED_CLOUD_CLIENT_PROFILE_FULL) +#if (MBED_CLOUD_CLIENT_PROFILE == MBED_CLOUD_CLIENT_PROFILE_FULL) && !defined(FOTA_UNIT_TEST) #define TRACE_GROUP "FOTA" @@ -28,6 +28,7 @@ #include "fota/fota_crypto_defs.h" #include "fota/fota_status.h" #include "fota/fota_internal.h" +#include "fota/fota.h" #include "fota/fota_event_handler.h" #include "fota/fota_component_defs.h" #include "fota/fota_component_internal.h" @@ -43,13 +44,15 @@ static M2MInterface *g_m2m = NULL; static M2MResource *g_manifest_resource = NULL; // /10252/0/1 static M2MResource *g_state_resource = NULL; // /10252/0/2 static M2MResource *g_update_result_resource = NULL; // /10252/0/3 - +static M2MObject *g_lwm2m_manifest_object = NULL; //10252 +static M2MObject *g_lwm2m_dev_metadata_object = NULL; //10255 #if (FOTA_SOURCE_LEGACY_OBJECTS_REPORT == 0) static M2MObject *g_component_lwm2m_object = NULL; // /14 #endif static report_sent_callback_t g_on_sent_callback = NULL; static report_sent_callback_t g_on_failure_callback = NULL; +static bool auto_observable_reporting_enabled; typedef struct { size_t max_frag_size; @@ -245,11 +248,11 @@ int fota_source_init( g_m2m = (M2MInterface *)m2m_interface; - M2MObject *lwm2m_object = M2MInterfaceFactory::create_object("10252"); - FOTA_ASSERT(lwm2m_object); + g_lwm2m_manifest_object = M2MInterfaceFactory::create_object("10252");//Manifest + FOTA_ASSERT(g_lwm2m_manifest_object); // Create first (and only) instance /10252/0 - M2MObjectInstance *lwm2m_object_instance = lwm2m_object->create_object_instance(); + M2MObjectInstance *lwm2m_object_instance = g_lwm2m_manifest_object->create_object_instance(); FOTA_ASSERT(lwm2m_object_instance); // Create package resource /10252/0/1 @@ -290,50 +293,49 @@ int fota_source_init( g_update_result_resource->set_operation(M2MBase::GET_ALLOWED); g_update_result_resource->set_message_delivery_status_cb(notification_status, NULL); g_update_result_resource->set_value(-1); - g_update_result_resource->publish_value_in_registration_msg(false); + g_update_result_resource->publish_value_in_registration_msg(true); g_update_result_resource->set_auto_observable(true); #if (FOTA_SOURCE_LEGACY_OBJECTS_REPORT == 1) - M2MResource *pkg_name_resource = NULL; // /10252/0/5 - M2MResource *pkg_version_resource = NULL; // /10252/0/6 - // Create package name resource /10252/0/5 FOTA_DBG_ASSERT(curr_fw_digest_size == FOTA_CRYPTO_HASH_SIZE); bin_to_hex_string(curr_fw_digest, FOTA_CRYPTO_HASH_SIZE, str_digest, FOTA_CRYPTO_HASH_SIZE * 2 + 1); - pkg_name_resource = lwm2m_object_instance->create_dynamic_resource( - "5", - "PkgName", - M2MResourceInstance::STRING, - false // observable - ); + M2MResource *pkg_name_resource = lwm2m_object_instance->create_dynamic_resource( + "5", + "PkgName", + M2MResourceInstance::STRING, + false // observable + ); FOTA_ASSERT(pkg_name_resource); pkg_name_resource->set_operation(M2MBase::GET_ALLOWED); pkg_name_resource->set_value(str_digest, curr_fw_digest_size); pkg_name_resource->publish_value_in_registration_msg(true); +#endif +#if (FOTA_SOURCE_LEGACY_OBJECTS_REPORT == 1 || MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION < 3) // Create package version resource /10252/0/6 FOTA_TRACE_DEBUG("Announcing version is %" PRIu64, curr_fw_version); - pkg_version_resource = lwm2m_object_instance->create_dynamic_resource( - "6", - "PkgVersion", - M2MResourceInstance::INTEGER, - false // observable - ); + M2MResource *pkg_version_resource = lwm2m_object_instance->create_dynamic_resource( + "6", + "PkgVersion", + M2MResourceInstance::INTEGER, + false // observable + ); FOTA_ASSERT(pkg_version_resource); pkg_version_resource->set_operation(M2MBase::GET_ALLOWED); pkg_version_resource->set_value(curr_fw_version); pkg_version_resource->publish_value_in_registration_msg(true); #endif - m2m_object_list->push_back(lwm2m_object); + m2m_object_list->push_back(g_lwm2m_manifest_object); - lwm2m_object = M2MInterfaceFactory::create_object("10255"); - FOTA_ASSERT(lwm2m_object); + g_lwm2m_dev_metadata_object = M2MInterfaceFactory::create_object("10255");//Device Metadata + FOTA_ASSERT(g_lwm2m_dev_metadata_object); // Create first (and only) instance /10255/0 - lwm2m_object_instance = lwm2m_object->create_object_instance(); + lwm2m_object_instance = g_lwm2m_dev_metadata_object->create_object_instance(); FOTA_ASSERT(lwm2m_object_instance); // Create protocol supported resource /10255/0/0 @@ -371,16 +373,17 @@ int fota_source_init( resource->set_value(class_id, class_id_size); resource->publish_value_in_registration_msg(true); - m2m_object_list->push_back(lwm2m_object); + m2m_object_list->push_back(g_lwm2m_dev_metadata_object); #if (FOTA_SOURCE_LEGACY_OBJECTS_REPORT == 0) // Create g_component_lwm2m_object = M2MInterfaceFactory::create_object("14"); - FOTA_ASSERT(lwm2m_object); + FOTA_ASSERT(g_component_lwm2m_object); m2m_object_list->push_back(g_component_lwm2m_object); #endif + fota_source_enable_auto_observable_resources_reporting(true); return FOTA_STATUS_SUCCESS; } @@ -418,7 +421,12 @@ int fota_source_deinit(void) g_manifest_resource = NULL; g_state_resource = NULL; g_update_result_resource = NULL; + delete g_lwm2m_manifest_object; + delete g_lwm2m_dev_metadata_object; + g_lwm2m_manifest_object = NULL; + g_lwm2m_dev_metadata_object = NULL; #if (FOTA_SOURCE_LEGACY_OBJECTS_REPORT == 0) + delete g_component_lwm2m_object; g_component_lwm2m_object = NULL; #endif g_on_sent_callback = NULL; @@ -430,8 +438,16 @@ int fota_source_deinit(void) static int report_int(M2MResource *resource, int value, report_sent_callback_t on_sent, report_sent_callback_t on_failure) { - FOTA_DBG_ASSERT(!g_on_sent_callback); - FOTA_DBG_ASSERT(!g_on_failure_callback); + // Auto observable resources reporting not enabled - call the on sent callback ourselves here + if (!auto_observable_reporting_enabled) { + if (on_sent) { + on_sent(); + } + return FOTA_STATUS_SUCCESS; + } + + FOTA_DBG_ASSERT(!(on_sent && g_on_sent_callback)); + FOTA_DBG_ASSERT(!(on_failure && g_on_failure_callback)); // must assign values before calling registry_set_value_int because of special way unit-tests are implemented g_on_sent_callback = on_sent; @@ -511,11 +527,16 @@ int fota_source_firmware_request_fragment(const char *uri, size_t offset) true, //async data_req_callback, // data_cb data_req_error_callback, // error_cb - NULL // context + (void *) offset // context ); return FOTA_STATUS_SUCCESS; } +void fota_source_enable_auto_observable_resources_reporting(bool enable) +{ + auto_observable_reporting_enabled = enable; +} + #endif // (MBED_CLOUD_CLIENT_PROFILE == MBED_CLOUD_CLIENT_PROFILE_FULL) #endif // MBED_CLOUD_CLIENT_FOTA_ENABLE diff --git a/fota/fota_source_profile_lite.c b/fota/fota_source_profile_lite.c index c024ec9..7dffe6b 100644 --- a/fota/fota_source_profile_lite.c +++ b/fota/fota_source_profile_lite.c @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -19,7 +19,7 @@ #include "fota/fota_base.h" #ifdef MBED_CLOUD_CLIENT_FOTA_ENABLE -#if (MBED_CLOUD_CLIENT_PROFILE == MBED_CLOUD_CLIENT_PROFILE_LITE) +#if (MBED_CLOUD_CLIENT_PROFILE == MBED_CLOUD_CLIENT_PROFILE_LITE) || defined(FOTA_UNIT_TEST) #define TRACE_GROUP "FOTA" @@ -28,33 +28,40 @@ #include "fota/fota_crypto_defs.h" #include "fota/fota_status.h" #include "fota/fota_internal.h" +#include "fota/fota.h" #include "fota/fota_event_handler.h" #include "fota/fota_component_defs.h" +#include #ifdef MBED_CLOUD_CLIENT_DISABLE_REGISTRY #include "fota/fota_nvm.h" #endif +#if (MBED_CLOUD_CLIENT_PROFILE == MBED_CLOUD_CLIENT_PROFILE_LITE) #include "mbed-client/lwm2m_endpoint.h" #include "mbed-client/lwm2m_req_handler.h" #include "device-management-client/lwm2m_registry_handler.h" +#else +#include "mbed-client/test/fota/unittest/common/fota_lwm2m_get_req_sim.h" +#endif #include +#define DEFAULT_INT_VAL -1 + static endpoint_t *endpoint = 0; #ifndef MBED_CLOUD_CLIENT_DISABLE_REGISTRY static registry_t *registry; - -const int64_t default_int_val = -1; #endif - static bool initialized = false; #ifndef MBED_CLOUD_CLIENT_DISABLE_REGISTRY static report_sent_callback_t g_on_sent_callback = NULL; static report_sent_callback_t g_on_failure_callback = NULL; +static bool auto_observable_reporting_enabled; #else static const char *manifest_res_id = "/10252/0/1"; static const char *state_res_id = "/10252/0/2"; +static const char *update_result_res_id = "/10252/0/3"; static const char *protocol_version_res_id = "/10255/0/0"; static const char *vendor_id_res_id = "/10255/0/3"; static const char *class_id_res_id = "/10255/0/4"; @@ -68,6 +75,12 @@ static uint32_t fota_class_id_size; static char main_comp_name[FOTA_COMPONENT_MAX_NAME_SIZE]; static char main_comp_sem_ver[FOTA_COMPONENT_MAX_SEMVER_STR_SIZE]; static int fota_state; +// Value for this resource crosses the init/deinit calls (so it can be resent in +// reg message after failures), hence initialized here. +static int fota_update_result = DEFAULT_INT_VAL; + +static uint16_t update_result_aobs_id; + #endif static registry_path_t execute_path = { 0 }; @@ -111,6 +124,24 @@ static registry_status_t got_manifest_callback(registry_callback_type_t type, #endif ) { +#ifdef MBED_CLOUD_CLIENT_DISABLE_REGISTRY + if (path->resource_id == FOTA_SOURCE_UPDATE_RESULT_RESOURCE_ID) { + if (status == NOTIFICATION_STATUS_DELIVERED) { + // Reset current value of update result (default value is ignored) + fota_update_result = DEFAULT_INT_VAL; + return REGISTRY_STATUS_OK; + } + // keep current value of update result for possible later sending, + // return an arbitrary failure (not checked anyway by upper level) + return REGISTRY_STATUS_NO_MEMORY; + } + // Ignore this for all other resources (non manifest or update result) + if (path->resource_id != FOTA_SOURCE_PACKAGE_RESOURCE_ID) { + // Return success - error code is ignored for these cases anyway + return REGISTRY_STATUS_OK; + } +#endif + registry_status_t callback_status = REGISTRY_STATUS_OK; sn_coap_msg_code_e response = COAP_MSG_CODE_RESPONSE_CHANGED; fota_state_e fota_state; @@ -130,7 +161,7 @@ static registry_status_t got_manifest_callback(registry_callback_type_t type, registry_set_path(&res_path, FOTA_SOURCE_PACKAGE_OBJECT_ID, 0, FOTA_SOURCE_STATE_RESOURCE_ID, 0, REGISTRY_PATH_RESOURCE); - if (REGISTRY_STATUS_OK != registry_set_value_int(registry, &res_path, default_int_val)) { + if (REGISTRY_STATUS_OK != registry_set_value_int(registry, &res_path, DEFAULT_INT_VAL)) { FOTA_DBG_ASSERT(!"registry_set_value_int failed"); } @@ -139,6 +170,8 @@ static registry_status_t got_manifest_callback(registry_callback_type_t type, if (REGISTRY_STATUS_OK != registry_set_value_empty(registry, &res_path, true)) { FOTA_DBG_ASSERT(!"registry_set_value_empty failed"); } +#else + fota_update_result = DEFAULT_INT_VAL; #endif memcpy(&execute_token, token, sizeof(execute_token)); @@ -275,6 +308,15 @@ static int get_fota_resources(endpoint_t *endpoint, register_resource_t **res) return -1; } + curr->next = endpoint_create_register_resource_int(endpoint, update_result_res_id, true, fota_update_result); + curr = curr->next; + if (!curr) { + return -1; + } + + // Need to save this for later for dynamic reporting of update result + update_result_aobs_id = curr->aobs_id; + curr->next = endpoint_create_register_resource_int(endpoint, protocol_version_res_id, true, FOTA_MCCP_PROTOCOL_VERSION); curr = curr->next; if (!curr) { @@ -426,8 +468,9 @@ int fota_source_init( // Create update result resource /10252/0/3 registry_set_path(&path, FOTA_SOURCE_PACKAGE_OBJECT_ID, 0, FOTA_SOURCE_UPDATE_RESULT_RESOURCE_ID, 0, REGISTRY_PATH_RESOURCE); - if (REGISTRY_STATUS_OK != registry_set_value_int(registry, &path, default_int_val) || - REGISTRY_STATUS_OK != registry_set_auto_observable_parameter(registry, &path, true)) { + if (REGISTRY_STATUS_OK != registry_set_value_int(registry, &path, DEFAULT_INT_VAL) || + REGISTRY_STATUS_OK != registry_set_auto_observable_parameter(registry, &path, true) || + REGISTRY_STATUS_OK != registry_set_resource_value_to_reg_msg(registry, &path, true)) { goto fail; } @@ -436,7 +479,6 @@ int fota_source_init( registry_set_path(&path, FOTA_SOURCE_UPDATE_OBJECT_ID, 0, FOTA_SOURCE_PROTOCOL_SUPP_RESOURCE_ID, 0, REGISTRY_PATH_RESOURCE); if (REGISTRY_STATUS_OK != registry_set_value_int(registry, &path, FOTA_MCCP_PROTOCOL_VERSION) || - REGISTRY_STATUS_OK != registry_set_auto_observable_parameter(registry, &path, true) || REGISTRY_STATUS_OK != registry_set_resource_value_to_reg_msg(registry, &path, true)) { goto fail; } @@ -446,7 +488,6 @@ int fota_source_init( 0, REGISTRY_PATH_RESOURCE); if (REGISTRY_STATUS_OK != registry_set_value_opaque_copy(registry, &path, vendor_id, vendor_id_size) || - REGISTRY_STATUS_OK != registry_set_auto_observable_parameter(registry, &path, true) || REGISTRY_STATUS_OK != registry_set_resource_value_to_reg_msg(registry, &path, true)) { goto fail; } @@ -456,11 +497,12 @@ int fota_source_init( 0, REGISTRY_PATH_RESOURCE); if (REGISTRY_STATUS_OK != registry_set_value_opaque_copy(registry, &path, class_id, class_id_size) || - REGISTRY_STATUS_OK != registry_set_auto_observable_parameter(registry, &path, true) || REGISTRY_STATUS_OK != registry_set_resource_value_to_reg_msg(registry, &path, true)) { goto fail; } + fota_source_enable_auto_observable_resources_reporting(true); + #if (FOTA_SOURCE_LEGACY_OBJECTS_REPORT == 1) // Create package name resource /10252/0/5 FOTA_DBG_ASSERT(curr_fw_digest_size == FOTA_CRYPTO_HASH_SIZE); @@ -470,10 +512,12 @@ int fota_source_init( registry_set_path(&path, FOTA_SOURCE_PACKAGE_OBJECT_ID, 0, FOTA_SOURCE_PKG_NAME_RESOURCE_ID, 0, REGISTRY_PATH_RESOURCE); if (REGISTRY_STATUS_OK != registry_set_value_string_copy(registry, &path, str_digest, FOTA_CRYPTO_HASH_SIZE * 2) || - REGISTRY_STATUS_OK != registry_set_auto_observable_parameter(registry, &path, false) || REGISTRY_STATUS_OK != registry_set_resource_value_to_reg_msg(registry, &path, true)) { goto fail; } +#endif + +#if (FOTA_SOURCE_LEGACY_OBJECTS_REPORT == 1 || MBED_CLOUD_CLIENT_FOTA_FW_HEADER_VERSION < 3) // Create package version resource /10252/0/6 FOTA_TRACE_DEBUG("Announcing version is %" PRIu64, curr_fw_version); @@ -481,11 +525,11 @@ int fota_source_init( 0, REGISTRY_PATH_RESOURCE); if (REGISTRY_STATUS_OK != registry_set_value_int(registry, &path, curr_fw_version) || - REGISTRY_STATUS_OK != registry_set_auto_observable_parameter(registry, &path, false) || REGISTRY_STATUS_OK != registry_set_resource_value_to_reg_msg(registry, &path, true)) { goto fail; } #endif + #else // MBED_CLOUD_CLIENT_DISABLE_REGISTRY memcpy(fota_vendor_id, vendor_id, vendor_id_size); fota_vendor_id_size = vendor_id_size; @@ -524,7 +568,6 @@ int fota_source_add_component(unsigned int comp_id, const char *name, const char registry_set_path(&path, FOTA_SOURCE_SW_COMPONENT_OBJECT_ID, comp_id, FOTA_SOURCE_COMP_NAME_RESOURCE_ID, 0, REGISTRY_PATH_RESOURCE); if (REGISTRY_STATUS_OK != registry_set_value_string_copy(registry, &path, (uint8_t *) name, strlen(name) + 1) || - REGISTRY_STATUS_OK != registry_set_auto_observable_parameter(registry, &path, true) || REGISTRY_STATUS_OK != registry_set_resource_value_to_reg_msg(registry, &path, true)) { return FOTA_STATUS_INTERNAL_ERROR; } @@ -533,7 +576,6 @@ int fota_source_add_component(unsigned int comp_id, const char *name, const char registry_set_path(&path, FOTA_SOURCE_SW_COMPONENT_OBJECT_ID, comp_id, FOTA_SOURCE_COMP_VERSION_RESOURCE_ID, 0, REGISTRY_PATH_RESOURCE); if (REGISTRY_STATUS_OK != registry_set_value_string_copy(registry, &path, (uint8_t *) sem_ver, strlen(sem_ver) + 1) || - REGISTRY_STATUS_OK != registry_set_auto_observable_parameter(registry, &path, true) || REGISTRY_STATUS_OK != registry_set_resource_value_to_reg_msg(registry, &path, true)) { return FOTA_STATUS_INTERNAL_ERROR; } @@ -569,8 +611,16 @@ int fota_source_deinit(void) #ifndef MBED_CLOUD_CLIENT_DISABLE_REGISTRY static int report_int(int value, int16_t resource_id, report_sent_callback_t on_sent, report_sent_callback_t on_failure) { - FOTA_DBG_ASSERT(!g_on_sent_callback); - FOTA_DBG_ASSERT(!g_on_failure_callback); + // Auto observable resources reporting not enabled - call the on sent callback ourselves here + if (!auto_observable_reporting_enabled) { + if (on_sent) { + on_sent(); + } + return FOTA_STATUS_SUCCESS; + } + + FOTA_DBG_ASSERT(!(on_sent && g_on_sent_callback)); + FOTA_DBG_ASSERT(!(on_failure && g_on_failure_callback)); // must assign values before calling registry_set_value_int because of special way unit-tests are implemented g_on_sent_callback = on_sent; @@ -609,7 +659,12 @@ int fota_source_report_update_result(int result) #ifndef MBED_CLOUD_CLIENT_DISABLE_REGISTRY return report_int(result, FOTA_SOURCE_UPDATE_RESULT_RESOURCE_ID, NULL, NULL); // 10252/0/3 #else - // result isn't actually being reported, so just pretend it was successful + fota_update_result = result; + registry_path_t path = {FOTA_SOURCE_PACKAGE_OBJECT_ID, 0, FOTA_SOURCE_UPDATE_RESULT_RESOURCE_ID, 0, REGISTRY_PATH_RESOURCE}; + int ret = endpoint_send_notification_int(endpoint, &path, update_result_aobs_id, result); + if (ret != NOTIFICATION_STATUS_SENT) { + return FOTA_STATUS_INTERNAL_ERROR; + } return FOTA_STATUS_SUCCESS; #endif } @@ -658,5 +713,13 @@ int fota_source_firmware_request_fragment(const char *uri, size_t offset) return FOTA_STATUS_SUCCESS; } + +void fota_source_enable_auto_observable_resources_reporting(bool enable) +{ +#ifndef MBED_CLOUD_CLIENT_DISABLE_REGISTRY + auto_observable_reporting_enabled = enable; +#endif +} + #endif // (MBED_CLOUD_CLIENT_PROFILE == MBED_CLOUD_CLIENT_PROFILE_LITE) #endif // MBED_CLOUD_CLIENT_FOTA_ENABLE diff --git a/fota/fota_status.h b/fota/fota_status.h index dac8d2b..e955ec3 100644 --- a/fota/fota_status.h +++ b/fota/fota_status.h @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -26,39 +26,45 @@ extern "C" { typedef enum { FOTA_STATUS_FW_UPDATE_OK = 18, /**< Asset update successfully completed */ - FOTA_STATUS_MANIFEST_INVALID_URI = -21, /**< FW payload URI in manifest is too long */ - FOTA_STATUS_MANIFEST_MALFORMED = -22, /**< Failure to parse an update manifest */ - FOTA_STATUS_MANIFEST_SIGNATURE_INVALID = -23, /**< Signature verification failed */ - FOTA_STATUS_DOWNLOAD_FRAGMENT_FAILED = -24, /**< Connection lost during download */ - FOTA_STATUS_MANIFEST_PAYLOAD_UNSUPPORTED = -25, /**< Payload format specified by manifest is unsupported */ - FOTA_STATUS_MANIFEST_PAYLOAD_CORRUPTED = -26, /**< Payload authenticity check failed */ - FOTA_STATUS_MANIFEST_VERSION_REJECTED = -27, /**< FW candidate version is rejected (is older or equals to installed one) */ - FOTA_STATUS_MANIFEST_SCHEMA_UNSUPPORTED = -28, /**< Manifest schema version unsupported (incompatible manifest-tool version) */ - FOTA_STATUS_MANIFEST_CUSTOM_DATA_TOO_BIG = -29, /**< Vendor data specified in a manifest is too big. */ - FOTA_STATUS_MANIFEST_WRONG_VENDOR_ID = -30, /**< Manifest with wrong vendor id */ - FOTA_STATUS_MANIFEST_WRONG_CLASS_ID = -31, /**< Manifest with wrong class id */ - FOTA_STATUS_MANIFEST_PRECURSOR_MISMATCH = -32, /**< Installed FW digest differs from the one specified in manifest (precursor) */ - FOTA_STATUS_INSUFFICIENT_STORAGE = -33, /**< Insufficient storage on a device for saving update candidate */ - FOTA_STATUS_OUT_OF_MEMORY = -34, /**< Not enough RAM */ - FOTA_STATUS_STORAGE_WRITE_FAILED = -35, /**< Storage write error */ - FOTA_STATUS_STORAGE_READ_FAILED = -36, /**< Storage read error */ - FOTA_STATUS_INSTALL_AUTH_NOT_GRANTED = -37, /**< Application rejected install authorization request */ - FOTA_STATUS_DOWNLOAD_AUTH_NOT_GRANTED = -38, /**< Application rejected download authorization request */ - FOTA_STATUS_UNEXPECTED_COMPONENT = -39, /**< Component name in manifest targets unknown component */ - FOTA_STATUS_MANIFEST_UNKNOWN_COMPONENT = FOTA_STATUS_UNEXPECTED_COMPONENT, // TODO: remove - FOTA_STATUS_FW_INSTALLATION_FAILED = -40, /**< Update failed at installation phase */ - FOTA_STATUS_INTERNAL_ERROR = -41, /**< Non-specific internal error */ - FOTA_STATUS_INTERNAL_DELTA_ERROR = -42, /**< Non-specific internal error - delta engine */ - FOTA_STATUS_INTERNAL_CRYPTO_ERROR = -43, /**< Non-specific internal error - crypto engine */ - FOTA_STATUS_NOT_FOUND = -44, /**< Expected asset is not found in NVM */ + FOTA_STATUS_MANIFEST_INVALID_URI = -21, /**< FW payload URI in manifest is too long */ + FOTA_STATUS_MANIFEST_MALFORMED = -22, /**< Failure to parse an update manifest */ + FOTA_STATUS_MANIFEST_SIGNATURE_INVALID = -23, /**< Signature verification failed */ + FOTA_STATUS_DOWNLOAD_FRAGMENT_FAILED = -24, /**< Connection lost during download */ + FOTA_STATUS_MANIFEST_PAYLOAD_UNSUPPORTED = -25, /**< Payload format specified by manifest is unsupported */ + FOTA_STATUS_MANIFEST_PAYLOAD_CORRUPTED = -26, /**< Payload authenticity check failed */ + FOTA_STATUS_MANIFEST_VERSION_REJECTED = -27, /**< FW candidate version is rejected (is older or equals to installed one) */ + FOTA_STATUS_MANIFEST_SCHEMA_UNSUPPORTED = -28, /**< Manifest schema version unsupported (incompatible manifest-tool version) */ + FOTA_STATUS_MANIFEST_CUSTOM_DATA_TOO_BIG = -29, /**< Vendor data specified in a manifest is too big. */ + FOTA_STATUS_MANIFEST_WRONG_VENDOR_ID = -30, /**< Manifest with wrong vendor id */ + FOTA_STATUS_MANIFEST_WRONG_CLASS_ID = -31, /**< Manifest with wrong class id */ + FOTA_STATUS_MANIFEST_PRECURSOR_MISMATCH = -32, /**< Installed FW digest differs from the one specified in manifest (precursor) */ + FOTA_STATUS_INSUFFICIENT_STORAGE = -33, /**< Insufficient storage on a device for saving update candidate */ + FOTA_STATUS_OUT_OF_MEMORY = -34, /**< Not enough RAM */ + FOTA_STATUS_STORAGE_WRITE_FAILED = -35, /**< Storage write error */ + FOTA_STATUS_STORAGE_READ_FAILED = -36, /**< Storage read error */ + FOTA_STATUS_INSTALL_AUTH_NOT_GRANTED = -37, /**< Application rejected install authorization request */ + FOTA_STATUS_DOWNLOAD_AUTH_NOT_GRANTED = -38, /**< Application rejected download authorization request */ + FOTA_STATUS_UNEXPECTED_COMPONENT = -39, /**< Component name in manifest targets unknown component */ + FOTA_STATUS_FW_INSTALLATION_FAILED = -40, /**< Update failed at installation phase */ + FOTA_STATUS_INTERNAL_ERROR = -41, /**< Non-specific internal error */ + FOTA_STATUS_INTERNAL_DELTA_ERROR = -42, /**< Non-specific internal error - delta engine */ + FOTA_STATUS_INTERNAL_CRYPTO_ERROR = -43, /**< Non-specific internal error - crypto engine */ + FOTA_STATUS_NOT_FOUND = -44, /**< Expected asset is not found in NVM */ + FOTA_STATUS_MULTICAST_UPDATE_ABORTED = -45, /**< Received abort request from Multicast */ // internal transient errors - should not be reported to service - FOTA_STATUS_SUCCESS = 0, /**< all good */ - FOTA_STATUS_FAIL_UPDATE_STATE = -80, /**< Failed to deliver FOTA state */ - FOTA_STATUS_UPDATE_DEFERRED = -81, /**< Application deferred the update */ - FOTA_STATUS_TRANSIENT_FAILURE = -82, /**< transient failure during update **/ - FOTA_STATUS_FW_DELTA_REQUIRED_MORE_DATA = -83, /**< Delta engine requires more data to proceed */ - FOTA_STATUS_FW_SIZE_MISMATCH = -84 /**< FW fetching returned more data than expected - should not happen */ + FOTA_STATUS_SUCCESS = 0, /**< all good */ + FOTA_STATUS_INVALID_ERR_CODE = -1, /**< Invalid error code, for internal purposes */ + FOTA_STATUS_INTERNAL_ERR_BASE = -80, + FOTA_STATUS_FAIL_UPDATE_STATE = FOTA_STATUS_INTERNAL_ERR_BASE, /**< Failed to deliver FOTA state */ + FOTA_STATUS_UPDATE_DEFERRED = -81, /**< Application deferred the update */ + FOTA_STATUS_TRANSIENT_FAILURE = -82, /**< transient failure during update **/ + FOTA_STATUS_FW_DELTA_REQUIRED_MORE_DATA = -83, /**< Delta engine requires more data to proceed */ + FOTA_STATUS_FW_SIZE_MISMATCH = -84, /**< FW fetching returned more data than expected - should not happen */ + FOTA_STATUS_RESOURCE_BUSY = -85, /**< Resource (typically storage) is busy */ + FOTA_STATUS_MULTICAST_UPDATE_ABORTED_INTERNAL = -86, /**< Abort Multicast not report to service*/ + FOTA_STATUS_MULTICAST_UPDATE_ACTIVATED = -87, /**< Received abort request or new manifest from Multicast, when previous one was activated*/ + FOTA_STATUS_INVALID_ARGUMENT = -88 /**< Invalid argument was received */ } fota_status_e; diff --git a/fota/import_ref.txt b/fota/import_ref.txt new file mode 100644 index 0000000..7b28701 --- /dev/null +++ b/fota/import_ref.txt @@ -0,0 +1 @@ +Imported from origin/master at hash: ee86f554cdca8f2a3470d4bf7d2cfc107ef08eaa diff --git a/fota/mbed_lib.json b/fota/mbed_lib.json index f129e0a..9f72760 100644 --- a/fota/mbed_lib.json +++ b/fota/mbed_lib.json @@ -8,9 +8,9 @@ "value": null }, "block-device-type": { - "help": "Type of block device: Internal flash, custom (supply a block device instance) or external (implement all FOTA block device C APIs)", + "help": "Type of block device: mbed-os internal flash, mbed-os default block device, custom (supply a block device instance) or external (implement all FOTA block device C APIs)", "macro_name": "MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE", - "accepted_values": ["FOTA_INTERNAL_FLASH_BD", "FOTA_CUSTOM_BD", "FOTA_EXTERNAL_BD"], + "accepted_values": ["FOTA_INTERNAL_FLASH_MBED_OS_BD", "FOTA_CUSTOM_MBED_OS_BD", "FOTA_EXTERNAL_BD", "FOTA_DEFAULT_MBED_OS_BD"], "value": null }, "storage-start-address": { @@ -28,6 +28,24 @@ "macro_name": "MBED_CLOUD_CLIENT_FOTA_ENCRYPTION_SUPPORT", "value": null }, + "key-encryption": { + "help": "Encryption key options", + "macro_name": "MBED_CLOUD_CLIENT_FOTA_KEY_ENCRYPTION", + "accepted_values": ["FOTA_USE_DEVICE_KEY", "FOTA_USE_ENCRYPTED_ONE_TIME_FW_KEY"], + "value": null + }, + "public-key-format": { + "help": "Public key elliptic curve point format", + "macro_name": "MBED_CLOUD_CLIENT_FOTA_PUBLIC_KEY_FORMAT", + "accepted_values": ["FOTA_PUBLIC_KEY_NOT_SUPPORTED_FORMAT", "FOTA_RAW_PUBLIC_KEY_FORMAT", "FOTA_X509_PUBLIC_KEY_FORMAT"], + "value": null + }, + "multicast-support": { + "help": "Support Multicast update on mesh networks", + "macro_name": "MBED_CLOUD_CLIENT_FOTA_MULTICAST_SUPPORT", + "accepted_values": ["FOTA_MULTICAST_UNSUPPORTED", "FOTA_MULTICAST_NODE_MODE", "FOTA_MULTICAST_BR_MODE"], + "value": null + }, "candidate-block-size": { "help": "FW candidate block size in storage - only relevant if encrypted or if resume is supported", "macro_name": "MBED_CLOUD_CLIENT_FOTA_CANDIDATE_BLOCK_SIZE", @@ -73,6 +91,11 @@ "help": "size of bsdiff blocks used to create delta-update", "macro_name": "MBED_CLOUD_CLIENT_FOTA_DELTA_BLOCK_SIZE", "value": 1024 + }, + "default-app-ifs": { + "help": " enable default fota implementation callbacks", + "macro_name": "FOTA_DEFAULT_APP_IFS", + "value": null } } } diff --git a/fota/fota_block_device.cpp b/fota/platform/mbed-os/fota_block_device_mbed_os.cpp similarity index 67% rename from fota/fota_block_device.cpp rename to fota/platform/mbed-os/fota_block_device_mbed_os.cpp index ff2bc42..cb88c33 100644 --- a/fota/fota_block_device.cpp +++ b/fota/platform/mbed-os/fota_block_device_mbed_os.cpp @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -24,37 +24,48 @@ #include "fota/fota_block_device.h" #include "fota/fota_status.h" +#include + +#if FOTA_BD_SIMULATE_ERASE +static const uint8_t sim_erase_val = 0xFF; +#endif // External BD should supply all these APIs #if (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE != FOTA_EXTERNAL_BD) +#if defined(__MBED__) static bool initialized = false; #include "BlockDevice.h" -#if (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_INTERNAL_FLASH_BD) +#if (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_INTERNAL_FLASH_MBED_OS_BD) #if COMPONENT_FLASHIAP #include "FlashIAPBlockDevice.h" #else #error FlashIAP component should be defined in case of an internal flash block device configuration #endif -#endif // (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_INTERNAL_FLASH_BD) +#endif // (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_INTERNAL_FLASH_MBED_OS_BD) #include static mbed::BlockDevice *bd = 0; -#if (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_CUSTOM_BD) -// In custom BD case, the user code should supply this function, returning the desired block device +#if (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_CUSTOM_MBED_OS_BD) +// Should be supplied by application mbed::BlockDevice *fota_bd_get_custom_bd(); -#elif (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_INTERNAL_FLASH_BD) +#else //FOTA_INTERNAL_FLASH_MBED_OS_BD or FOTA_DEFAULT_MBED_OS_BD mbed::BlockDevice *fota_bd_get_custom_bd() { +#if (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_INTERNAL_FLASH_MBED_OS_BD) if (!bd) { bd = new FlashIAPBlockDevice(MBED_ROM_START, MBED_ROM_SIZE); } return bd; + +#elif (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_DEFAULT_MBED_OS_BD) + return mbed::BlockDevice::get_default_instance(); +#endif } #endif @@ -84,6 +95,7 @@ int fota_bd_init(void) int ret = bd->init(); if (!ret) { + FOTA_TRACE_DEBUG("BlockDevice type %s", bd->get_type()); initialized = true; return FOTA_STATUS_SUCCESS; } @@ -98,7 +110,7 @@ int fota_bd_deinit(void) } int ret = bd->deinit(); -#if (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_INTERNAL_FLASH_BD) +#if (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_INTERNAL_FLASH_MBED_OS_BD) delete bd; #endif bd = 0; @@ -139,6 +151,46 @@ int fota_bd_erase(size_t addr, size_t size) int ret; FOTA_ASSERT(bd); +#if FOTA_BD_SIMULATE_ERASE + int erase_value = bd->get_erase_value(); + if (erase_value < 0) { + uint8_t *erase_buf = NULL; + while (size) { + size_t erase_size, prev_erase_size = 0; + if (fota_bd_get_erase_size(addr, &erase_size)) { + ret = FOTA_STATUS_STORAGE_WRITE_FAILED; + goto end; + } + if ((addr % erase_size) || (size < erase_size)) { + ret = FOTA_STATUS_STORAGE_WRITE_FAILED; + goto end; + } + + if (erase_size > prev_erase_size) { + free(erase_buf); + erase_buf = (uint8_t *) malloc(erase_size); + if (!erase_buf) { + ret = FOTA_STATUS_STORAGE_WRITE_FAILED; + goto end; + } + } + + memset(erase_buf, sim_erase_val, erase_size); + if (bd->program(erase_buf, addr, erase_size)) { + ret = FOTA_STATUS_STORAGE_WRITE_FAILED; + goto end; + } + prev_erase_size = erase_size; + addr += erase_size; + size -= erase_size; + } + ret = FOTA_STATUS_SUCCESS; +end: + free(erase_buf); + return ret; + } +#endif // FOTA_BD_SIMULATE_ERASE + ret = bd->erase(addr, size); if (ret) { return FOTA_STATUS_STORAGE_WRITE_FAILED; @@ -175,20 +227,21 @@ int fota_bd_get_erase_value(int *erase_value) FOTA_ASSERT(bd); *erase_value = bd->get_erase_value(); - return FOTA_STATUS_SUCCESS; -} -#ifdef __cplusplus -} +#if FOTA_BD_SIMULATE_ERASE + if (*erase_value < 0) { + *erase_value = sim_erase_val; + } #endif -#endif // (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE != FOTA_EXTERNAL_BD) + return FOTA_STATUS_SUCCESS; +} static bool is_internal_flash_bd() { -#if (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_INTERNAL_FLASH_BD) +#if (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_INTERNAL_FLASH_MBED_OS_BD) return true; -#elif (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_CUSTOM_BD) +#elif (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_CUSTOM_MBED_OS_BD) FOTA_ASSERT(bd); const char *bd_type = bd->get_type(); if (strcmp("FLASHIAP", bd_type) == 0) { @@ -200,7 +253,7 @@ static bool is_internal_flash_bd() #endif } -extern "C" size_t fota_bd_physical_addr_to_logical_addr(size_t phys_addr) +size_t fota_bd_physical_addr_to_logical_addr(size_t phys_addr) { #ifdef __MBED__ if (is_internal_flash_bd()) { @@ -210,4 +263,10 @@ extern "C" size_t fota_bd_physical_addr_to_logical_addr(size_t phys_addr) return phys_addr; } +#ifdef __cplusplus +} +#endif + +#endif // defined(__MBED__) +#endif // (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE != FOTA_EXTERNAL_BD) #endif // MBED_CLOUD_CLIENT_FOTA_ENABLE diff --git a/fota/fota_curr_fw.c b/fota/platform/mbed-os/fota_curr_fw_mbed_os.c similarity index 88% rename from fota/fota_curr_fw.c rename to fota/platform/mbed-os/fota_curr_fw_mbed_os.c index a0e5673..218d9b3 100644 --- a/fota/fota_curr_fw.c +++ b/fota/platform/mbed-os/fota_curr_fw_mbed_os.c @@ -1,5 +1,5 @@ // ---------------------------------------------------------------------------- -// Copyright 2018-2020 ARM Ltd. +// Copyright 2019-2021 Pelion Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -20,14 +20,14 @@ #ifdef MBED_CLOUD_CLIENT_FOTA_ENABLE +#if defined(__MBED__) + #define TRACE_GROUP "FOTA" #include "fota/fota_curr_fw.h" #include "fota/fota_status.h" #include -#if !defined(FOTA_CUSTOM_CURR_FW_STRUCTURE) || (!FOTA_CUSTOM_CURR_FW_STRUCTURE) -#if defined(__MBED__) // Bootloader and application have different defines #if !defined(APPLICATION_ADDR) #if defined(MBED_CONF_MBED_BOOTLOADER_APPLICATION_START_ADDRESS) @@ -49,9 +49,7 @@ #endif #endif // !defined(HEADER_ADDR) - -// The following two functions should be overridden in the non mbed-os cases. -uint8_t *fota_curr_fw_get_app_start_addr(void) +static uint8_t *fota_curr_fw_get_app_start_addr(void) { #ifdef APPLICATION_ADDR return (uint8_t *) APPLICATION_ADDR; @@ -62,7 +60,7 @@ uint8_t *fota_curr_fw_get_app_start_addr(void) #endif } -uint8_t *fota_curr_fw_get_app_header_addr(void) +static uint8_t *fota_curr_fw_get_app_header_addr(void) { #ifdef HEADER_ADDR return (uint8_t *) HEADER_ADDR; @@ -72,7 +70,12 @@ uint8_t *fota_curr_fw_get_app_header_addr(void) return NULL; #endif } -#endif // defined(__MBED__) + +int fota_curr_fw_read_header(fota_header_info_t *header_info) +{ + uint8_t *header_in_curr_fw = (uint8_t *)fota_curr_fw_get_app_header_addr(); + return fota_deserialize_header(header_in_curr_fw, fota_get_header_size(), header_info); +} int fota_curr_fw_read(uint8_t *buf, size_t offset, size_t size, size_t *num_read) { @@ -110,6 +113,6 @@ int fota_curr_fw_get_digest(uint8_t *buf) return FOTA_STATUS_SUCCESS; } -#endif // !defined(FOTA_CUSTOM_CURR_FW_STRUCTURE) || (!FOTA_CUSTOM_CURR_FW_STRUCTURE) +#endif // defined(__MBED__) #endif // MBED_CLOUD_CLIENT_FOTA_ENABLE diff --git a/fota/platform/nxp/fota_block_device_nxp_lpc.cpp b/fota/platform/nxp/fota_block_device_nxp_lpc.cpp new file mode 100644 index 0000000..505a02d --- /dev/null +++ b/fota/platform/nxp/fota_block_device_nxp_lpc.cpp @@ -0,0 +1,143 @@ +// ---------------------------------------------------------------------------- +// Copyright 2021 Pelion Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// 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 "fota/fota_base.h" + +#ifdef MBED_CLOUD_CLIENT_FOTA_ENABLE + +#define TRACE_GROUP "FOTA" + +#include "fota/fota_config.h" +#include "fota/fota_status.h" +#include "fota/fota_block_device.h" + +#if (MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_EXTERNAL_BD) +//Should be compiled only for nxp freertos and nxp bootloader +#if defined(__NXP_FREERTOS__) || defined(FLASH_W25Q) || defined(FLASH_MT25Q) || defined(FLASH_MX25R) +#include "ExternalBlockDevice.h" + +// This ifdef is here (always true) to prevent astyle from indenting enclosed functions +#ifdef __cplusplus +extern "C" { +#endif + + +/*Fota block device implementation for external bd of NXP LPC */ +using namespace mbed; +static bool initialized = false; +static ExternalBlockDevice *bd = 0; + +int fota_bd_init(void) +{ + if (initialized) { + return 0; + } + if (!bd) { + bd = new ExternalBlockDevice(); + } + + if (!bd) { + return FOTA_STATUS_INTERNAL_ERROR; + } + + int ret = bd->init(); + if (!ret) { + initialized = true; + } + return ret; +} + +int fota_bd_deinit(void) +{ + + if (!initialized) { + return 0; + } + + int ret = bd->deinit(); + + delete bd; + initialized = false; + + return ret; +} + +int fota_bd_size(size_t *size) +{ + FOTA_ASSERT(bd); + + *size = (size_t)bd->size(); + + return 0; +} + +int fota_bd_read(void *buffer, size_t addr, size_t size) +{ + FOTA_ASSERT(bd); + return bd->read(buffer,(bd_addr_t)addr, (bd_size_t)size); +} + +int fota_bd_program(const void *buffer, size_t addr, size_t size) +{ + FOTA_ASSERT(bd); + return bd->program(buffer, addr, size); +} + +int fota_bd_erase(size_t addr, size_t size) +{ + FOTA_ASSERT(bd); + return bd->erase((bd_addr_t)addr, (bd_size_t)size); +} + +int fota_bd_get_read_size(size_t *read_size) +{ + FOTA_ASSERT(bd); + *read_size = (size_t)bd->get_read_size(); + return 0; +} + +int fota_bd_get_program_size(size_t *prog_size) +{ + FOTA_ASSERT(bd); + *prog_size = (size_t)bd->get_program_size(); + return 0; +} + +int fota_bd_get_erase_size(size_t addr, size_t *erase_size) +{ + FOTA_ASSERT(bd); + *erase_size = (size_t)bd->get_erase_size(); + return 0; +} + +int fota_bd_get_erase_value(int *erase_value) +{ + FOTA_DBG_ASSERT(initialized); + *erase_value = (int)bd->get_erase_value(); + return 0; +} +size_t fota_bd_physical_addr_to_logical_addr(size_t phys_addr) +{ + return phys_addr; +} + +#ifdef __cplusplus +} +#endif +#endif // __NXP_FREERTOS__ +#endif // MBED_CLOUD_CLIENT_FOTA_BLOCK_DEVICE_TYPE == FOTA_EXTERNAL_BD) +#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE diff --git a/fota/platform/nxp/fota_curr_fw_nxp_lpc.c b/fota/platform/nxp/fota_curr_fw_nxp_lpc.c new file mode 100644 index 0000000..59f7f63 --- /dev/null +++ b/fota/platform/nxp/fota_curr_fw_nxp_lpc.c @@ -0,0 +1,122 @@ +// ---------------------------------------------------------------------------- +// Copyright 2021 Pelion Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// 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 "fota/fota_base.h" + +#ifdef MBED_CLOUD_CLIENT_FOTA_ENABLE + +#ifdef __NXP_FREERTOS__ + +#define TRACE_GROUP "FOTA" + +#include "fota/fota_curr_fw.h" +#include "fota/fota_status.h" +#include + +// Bootloader and application have different defines +#if !defined(APPLICATION_ADDR) +#if defined(MBED_CONF_MBED_BOOTLOADER_APPLICATION_START_ADDRESS) +#define APPLICATION_ADDR MBED_CONF_MBED_BOOTLOADER_APPLICATION_START_ADDRESS +#elif defined(MBED_CONF_TARGET_APP_OFFSET) +#define APPLICATION_ADDR MBED_CONF_TARGET_APP_OFFSET +#else +#error Application start address not defined +#endif +#endif // !defined(APPLICATION_ADDR) + +#if !defined(HEADER_ADDR) +#if defined(MBED_CONF_MBED_BOOTLOADER_APPLICATION_HEADER_ADDRESS) +#define HEADER_ADDR MBED_CONF_MBED_BOOTLOADER_APPLICATION_HEADER_ADDRESS +#elif defined(MBED_CONF_TARGET_HEADER_OFFSET) +#define HEADER_ADDR MBED_CONF_TARGET_HEADER_OFFSET +#else +#error Header start address not defined +#endif +#endif // !defined(HEADER_ADDR) + + +/*Custom fw implementation */ +#if defined(FOTA_CUSTOM_CURR_FW_STRUCTURE) && (FOTA_CUSTOM_CURR_FW_STRUCTURE == 0) + +static uint8_t *fota_curr_fw_get_app_start_addr(void) +{ +#ifdef APPLICATION_ADDR + return (uint8_t *) APPLICATION_ADDR; +#else +//#error No address was defined for application + FOTA_ASSERT(!"No app start address defined"); + return NULL; +#endif +} + +static uint8_t *fota_curr_fw_get_app_header_addr(void) +{ +#ifdef HEADER_ADDR + return (uint8_t *) HEADER_ADDR; +#else +//#error No address was defined for application header + FOTA_ASSERT(!"No app start address defined"); + return NULL; +#endif +} + +int fota_curr_fw_read_header(fota_header_info_t *header_info) +{ + uint8_t *header_in_curr_fw = (uint8_t *)fota_curr_fw_get_app_header_addr(); + return fota_deserialize_header(header_in_curr_fw, fota_get_header_size(), header_info); +} + +int fota_curr_fw_read(uint8_t *buf, size_t offset, size_t size, size_t *num_read) +{ + fota_header_info_t header_info; + int ret = FOTA_STATUS_INTERNAL_ERROR; + + ret = fota_curr_fw_read_header(&header_info); + if (ret) { + return ret; + } + + if (offset >= header_info.fw_size) { + return FOTA_STATUS_INTERNAL_ERROR; + } + + *num_read = header_info.fw_size - offset; + if (*num_read > size) { + *num_read = size; + } + + memcpy(buf, fota_curr_fw_get_app_start_addr() + offset, *num_read); + return FOTA_STATUS_SUCCESS; + +} + +int fota_curr_fw_get_digest(uint8_t *buf) +{ + fota_header_info_t curr_fw_info; + int ret = fota_curr_fw_read_header(&curr_fw_info); + if (ret) { + FOTA_TRACE_ERROR("Failed to read current header"); + return ret; + } + memcpy(buf, curr_fw_info.digest, FOTA_CRYPTO_HASH_SIZE); + return FOTA_STATUS_SUCCESS; +} + +#endif //defined(FOTA_CUSTOM_CURR_FW_STRUCTURE) && (FOTA_CUSTOM_CURR_FW_STRUCTURE == 0) + +#endif // __NXP_FREERTOS__ +#endif // MBED_CLOUD_CLIENT_FOTA_ENABLE diff --git a/mbed-client/mbed-client/lwm2m_config.h b/mbed-client/mbed-client/lwm2m_config.h index f77bf44..2c251c4 100644 --- a/mbed-client/mbed-client/lwm2m_config.h +++ b/mbed-client/mbed-client/lwm2m_config.h @@ -203,10 +203,6 @@ typedef struct mbedtls_entropy { int strong; }entropy_cb; -#ifdef MBED_CLIENT_USER_CONFIG_FILE -#include MBED_CLIENT_USER_CONFIG_FILE -#endif - #ifdef MBED_CLOUD_CLIENT_USER_CONFIG_FILE #include MBED_CLOUD_CLIENT_USER_CONFIG_FILE #endif diff --git a/mbed-client/mbed-client/lwm2m_connection.h b/mbed-client/mbed-client/lwm2m_connection.h index 7e74366..ece9ea0 100644 --- a/mbed-client/mbed-client/lwm2m_connection.h +++ b/mbed-client/mbed-client/lwm2m_connection.h @@ -87,6 +87,8 @@ typedef struct connection_s { int8_t event_handler_id; + bool initialized; + } connection_t; #define CONNECTION_STATUS_WOULD_BLOCK 1 ///< No more data available. @@ -128,6 +130,7 @@ typedef struct connection_s { * \param key Pointer to the certificate or PSK Key. * \param key_len Length of the `key`. * \param bootstrap Authenticating against BS or LwM2M + * \param ignore_session_resume if true, does not use stored ssl session * * \return CONNECTION_STATUS_OK Initialization done. * \return CONNECTION_STATUS_ERROR_GENERIC Initialization failed. @@ -137,6 +140,9 @@ int8_t connection_init(connection_t *connection, void(*event_handler)(connection const uint8_t *ca_cert, uint16_t ca_cert_len, const uint8_t *cert, uint16_t cert_len, const uint8_t *key, uint16_t key_len #if defined(PROTOMAN_USE_SSL_SESSION_RESUME) || defined(PROTOMAN_OFFLOAD_TLS) , bool bootstrap +#endif +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + , bool ignore_session_resume #endif ); @@ -155,7 +161,7 @@ void connection_destroy(connection_t *connection); * * \note After calling this function, other connection calls must not be done before calling `connection_init` again. * - * \param connection Pointer to the connection to be destroyed. + * \param connection Pointer to the connection to be closed. */ void connection_close(connection_t *connection); @@ -163,18 +169,25 @@ void connection_close(connection_t *connection); * \brief Try to establish connection using the parameters set with `connection_init`. * * \param connection Pointer to the connection to be started. + * + * \return false if connection is not initialized and protoman call is skipped. + * \return true if connection is initialized. */ -void connection_start(connection_t *connection); +bool connection_start(connection_t *connection); /** * \brief Close the connection. The connection can be opened again. * * \param connection Pointer to the connection to be closed. + * + * \return false if connection is not initialized and protoman call is skipped. + * \return true if connection is initialized. */ -void connection_stop(connection_t *connection); +bool connection_stop(connection_t *connection); -void connection_pause(connection_t *connection); -void connection_resume(connection_t *connection); +bool connection_pause(connection_t *connection); + +bool connection_resume(connection_t *connection); /** @@ -244,6 +257,35 @@ int8_t connection_protoman_layers_init(struct connection_s *connection, char *ho ,bool bootstrap #endif ); +/** + * \brief Internal test function. Set CID for current tls session. + * + * \param connection Secure connection. + * \param data_ptr CID + * \param data_len length of the CID + */ +void set_cid_value(struct connection_s *connection, const uint8_t *data_ptr, const size_t data_len); + +/** + * \brief Internal test function. Store CID to storage + * + * \param connection Secure connection. + */ +void store_connection_id(struct connection_s *connection); + +/** + * \brief Internal test function. Remove CID from storage and from RAM + * + * \param connection Secure connection. + */ +void remove_connection_id(struct connection_s *connection); + +/** + * \brief Check if tls connection id is available + * + * \return true if connection id is available + */ +bool is_connection_id_available(); #ifdef __cplusplus } diff --git a/mbed-client/mbed-client/lwm2m_constants.h b/mbed-client/mbed-client/lwm2m_constants.h index 4a8b64a..d060334 100644 --- a/mbed-client/mbed-client/lwm2m_constants.h +++ b/mbed-client/mbed-client/lwm2m_constants.h @@ -220,7 +220,7 @@ #define ERROR_MEMORY_FAIL "MemoryFail" #define ERROR_UNREGISTER_FAIL "UnregisterFail" -#define MAX_RECONNECT_TIMEOUT 604800 +#define MAX_RECONNECT_TIMEOUT 0x94000 // 1 week in seconds (24*7*60*60), rounded up to easier binary value, so 1 week and 23 minutes #define RECONNECT_INCREMENT_FACTOR 2 // TLV serializer / deserializer #define TYPE_RESOURCE 0xC0 diff --git a/mbed-client/mbed-client/lwm2m_endpoint.h b/mbed-client/mbed-client/lwm2m_endpoint.h index 937567b..095ce30 100644 --- a/mbed-client/mbed-client/lwm2m_endpoint.h +++ b/mbed-client/mbed-client/lwm2m_endpoint.h @@ -126,7 +126,7 @@ typedef struct endpoint_s { #if defined(MBED_CLOUD_CLIENT_TRANSPORT_MODE_TCP) || defined(MBED_CLOUD_CLIENT_TRANSPORT_MODE_TCP_QUEUE) bool coap_ping_request; ///< CoAP ping request is pending. #endif - + uint16_t tick_remainder; ///< tick-rate is EVENTOS_EVENT_TIMER_HZ/s, use it to update coap_time in case of starvation. send_queue_t send_queue; ///< Data allocated for send queue. struct connection_s *connection; ///< Pointer to connection. @@ -157,6 +157,7 @@ typedef struct endpoint_s { notifier_t notifier; ///< Data allocated for notifier. endpoint_confirmable_response_t confirmable_response; ///< Data allocated for storing a response. + uint32_t old_tick; ///< save value of eventOS_event_timer_ticks, at beetween endpoint_get_coap_time calls. } endpoint_t; @@ -498,7 +499,7 @@ coap_req_cb *endpoint_get_coap_request_callback(endpoint_t *endpoint, uint16_t o registry_callback_t endpoint_get_object_callback(endpoint_t *endpoint, uint16_t object_id); -int endpoint_send_notification_int(endpoint_t *endpoint, uint16_t object_id, uint16_t aobs_id, int64_t value); +int endpoint_send_notification_int(endpoint_t *endpoint, registry_path_t *path, uint16_t aobs_id, int64_t value); #endif diff --git a/mbed-client/mbed-client/lwm2m_interface.h b/mbed-client/mbed-client/lwm2m_interface.h index 2cb2df0..ac30c40 100644 --- a/mbed-client/mbed-client/lwm2m_interface.h +++ b/mbed-client/mbed-client/lwm2m_interface.h @@ -36,7 +36,12 @@ extern "C" { #define MAX_RECONNECT_ATTEMPT 2 ///< Number of reconnection attempts before giving an error to upper layers. +// PDMC_CONNECT_STARTUP_EVENT_TYPE event type used when creating event handler. +// 0xFF is safe to use. In error event lwm2m_interface_error_t values are used as event type and those start from 0->. +#define PDMC_CONNECT_STARTUP_EVENT_TYPE 0xFF + #ifdef MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP_QUEUE + /** * \brief Callback function definition. * @@ -164,10 +169,11 @@ typedef enum { * \brief Reconnection state. */ typedef enum { - LWM2M_INTERFACE_RECONNECTION_STATE_NONE, ///< Not reconnecting - LWM2M_INTERFACE_RECONNECTION_STATE_WITH_UPDATE, ///< Reconnecting using update register. + LWM2M_INTERFACE_RECONNECTION_STATE_NONE, ///< Not reconnecting + LWM2M_INTERFACE_RECONNECTION_STATE_WITH_UPDATE, ///< Reconnecting using update register. LWM2M_INTERFACE_RECONNECTION_STATE_FULL_REGISTRATION, ///< Reconnecting with full register. - LWM2M_INTERFACE_RECONNECTION_STATE_UNREGISTRATION ///< Try to reconnect to unregister. + LWM2M_INTERFACE_RECONNECTION_STATE_UNREGISTRATION, ///< Try to reconnect to unregister. + LWM2M_INTERFACE_RECONNECTION_STATE_CLIENT_PING ///< Ping for testing network connection } lwm2m_interface_reconnection_state_t; /** @@ -505,6 +511,15 @@ bool lwm2m_interface_queue_mode(const lwm2m_interface_t *interface); */ bool lwm2m_interface_send_update_registration(lwm2m_interface_t *interface); +/** + * \brief Internal test function. Set CID for current tls session. + * + * \param interface Context structure. + * \param data_ptr CID + * \param data_len length of the CID + */ +void lwm2m_interface_set_cid_value(lwm2m_interface_t *interface, const uint8_t *data_ptr, const size_t data_len); + #ifdef __cplusplus } #endif diff --git a/mbed-client/mbed-client/lwm2m_notifier.h b/mbed-client/mbed-client/lwm2m_notifier.h index 2095770..c8a46a6 100644 --- a/mbed-client/mbed-client/lwm2m_notifier.h +++ b/mbed-client/mbed-client/lwm2m_notifier.h @@ -36,11 +36,9 @@ extern "C" { */ typedef struct notifier_s { uint16_t message_id; ///< Message ID of the last notification or 0. -#ifdef MBED_CLOUD_CLIENT_DISABLE_REGISTRY - uint16_t last_notified; ///< Object ID of the last Resource notified if `message_id` is not 0. -#else registry_path_t last_notified; ///< Path of the last Resource notified if `message_id` is not 0. +#ifndef MBED_CLOUD_CLIENT_DISABLE_REGISTRY // Note: following bools were stored in a bitfields, but it wasted ~64B of ROM while saving only <2 bytes of RAM. bool block_notify; ///< Flag set if notification sent using block transfer. bool running; ///< Flag set if running. diff --git a/mbed-client/mbed-client/lwm2m_send_queue.h b/mbed-client/mbed-client/lwm2m_send_queue.h index 57e587b..8796486 100644 --- a/mbed-client/mbed-client/lwm2m_send_queue.h +++ b/mbed-client/mbed-client/lwm2m_send_queue.h @@ -54,9 +54,9 @@ typedef struct send_queue_s { timeout_t *timeout; ///< Used for adding delay to scheduling. send_queue_sender_t pending; ///< This field tracks what message types are currently pending for sending. - uint8_t last_sender; ///< Type of the last message sent, used for scheduling different types in turns. - ///< Does not include `SEND_QUEUE_ENDPOINT` as it always gets sent first if requested. - ///< `unsigned` instead of `send_queue_sender_t` as we are not using the `SEND_QUEUE_ENDPOINT` with this field. + send_queue_sender_t last_sender; ///< Type of the last message sent, used for scheduling different types in turns. + ///< Does not include `SEND_QUEUE_ENDPOINT` as it always gets sent first if requested. + ///< We are not using the `SEND_QUEUE_ENDPOINT` with this field. bool sending_in_progress; ///< True if message sending is in progress. } send_queue_t; diff --git a/mbed-client/mbed-protocol-manager/mbed-protocol-manager/protoman_config.h b/mbed-client/mbed-protocol-manager/mbed-protocol-manager/protoman_config.h index 8235058..45ebcc2 100644 --- a/mbed-client/mbed-protocol-manager/mbed-protocol-manager/protoman_config.h +++ b/mbed-client/mbed-protocol-manager/mbed-protocol-manager/protoman_config.h @@ -99,12 +99,6 @@ #define PROTOMAN_USE_SSL_SESSION_RESUME #endif - - -#ifdef MBED_CLIENT_USER_CONFIG_FILE -#include MBED_CLIENT_USER_CONFIG_FILE -#endif - #ifdef MBED_CLOUD_CLIENT_USER_CONFIG_FILE #include MBED_CLOUD_CLIENT_USER_CONFIG_FILE #endif diff --git a/mbed-client/mbed-protocol-manager/mbed-protocol-manager/protoman_layer_mbedtls.h b/mbed-client/mbed-protocol-manager/mbed-protocol-manager/protoman_layer_mbedtls.h index d67782b..60b0969 100644 --- a/mbed-client/mbed-protocol-manager/mbed-protocol-manager/protoman_layer_mbedtls.h +++ b/mbed-client/mbed-protocol-manager/mbed-protocol-manager/protoman_layer_mbedtls.h @@ -81,10 +81,27 @@ int wrapper_write(void *ctx, const uint8_t *buf, size_t len); int wrapper_read(void *ctx, uint8_t *buf, size_t len); int wrapper_recv_timeout(void *ctx, unsigned char *buf, size_t len, uint32_t timeout); -extern void protoman_add_layer_mbedtls(struct protoman_s *protoman, struct protoman_layer_s *layer); +extern void protoman_add_layer_mbedtls( + struct protoman_s *protoman, + struct protoman_layer_s *layer +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + , bool ignore_session_resume +#endif + ); + +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME +extern void protoman_set_cid_value(struct protoman_layer_s *layer, const uint8_t *data_ptr, const size_t data_len); +#endif +extern bool protoman_is_connection_id_available(); +extern void store_ssl_session_context_to_storage(struct protoman_layer_s *layer); +extern void remove_ssl_session(struct protoman_layer_s *layer); + struct protoman_layer_mbedtls_common_s { struct protoman_layer_s layer; +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + bool ignore_session_resume; +#endif uint8_t handshakes_failed; uint8_t handshakes_max; uint32_t handshakes_delay_ms; diff --git a/mbed-client/mbed-protocol-manager/source/include/protoman_trace.h b/mbed-client/mbed-protocol-manager/source/include/protoman_trace.h index 47bfad5..df12129 100644 --- a/mbed-client/mbed-protocol-manager/source/include/protoman_trace.h +++ b/mbed-client/mbed-protocol-manager/source/include/protoman_trace.h @@ -32,10 +32,30 @@ extern "C" { #else #define protoman_verbose(...) #endif + +#if MBED_TRACE_MAX_LEVEL >= TRACE_LEVEL_DEBUG #define protoman_debug(...) protoman_tracef(TRACE_LEVEL_DEBUG, __VA_ARGS__, "") +#else +#define protoman_debug(...) +#endif + +#if MBED_TRACE_MAX_LEVEL >= TRACE_LEVEL_WARN #define protoman_warn(...) protoman_tracef(TRACE_LEVEL_WARN, __VA_ARGS__, "") +#else +#define protoman_warn(...) +#endif + +#if MBED_TRACE_MAX_LEVEL >= TRACE_LEVEL_INFO #define protoman_info(...) protoman_tracef(TRACE_LEVEL_INFO, __VA_ARGS__, "") +#else +#define protoman_info(...) +#endif + +#if MBED_TRACE_MAX_LEVEL >= TRACE_LEVEL_ERROR #define protoman_err(...) protoman_tracef(TRACE_LEVEL_ERROR, __VA_ARGS__, "") +#else +#define protoman_err(...) +#endif #ifdef PROTOMAN_VERBOSE_POINTERS /* Verbose logs */ diff --git a/mbed-client/mbed-protocol-manager/source/platform/mbedos/protoman_layer_mbedos_socket.cpp b/mbed-client/mbed-protocol-manager/source/platform/mbedos/protoman_layer_mbedos_socket.cpp index 2ee8564..9e22b71 100644 --- a/mbed-client/mbed-protocol-manager/source/platform/mbedos/protoman_layer_mbedos_socket.cpp +++ b/mbed-client/mbed-protocol-manager/source/platform/mbedos/protoman_layer_mbedos_socket.cpp @@ -44,10 +44,6 @@ #include "AT_CellularStack.h" #endif // PROTOMAN_OFFLOAD_TLS -#ifdef MBED_HEAP_STATS_ENABLED -#include "memory_tests.h" -#endif - // This macro will enable the socket callback bouncing out of possible // interrupt context. // Note: the value of this macro could/should be hardware/application specific, @@ -337,14 +333,15 @@ static void _async_dns_callback(void *data, nsapi_error_t result, SocketAddress struct protoman_s *protoman = (struct protoman_s *)layer->protoman; struct protoman_layer_mbedos_socket_s *layer_mbedos_socket = (struct protoman_layer_mbedos_socket_s *)layer; - if (NSAPI_ERROR_OK == result) { + if (NSAPI_ERROR_OK <= result) { + // at least one address resolved layer_mbedos_socket->address = *address; + layer_mbedos_socket->async_dns_query = NSAPI_ERROR_OK; + } else { + layer_mbedos_socket->async_dns_query = result; } - layer_mbedos_socket->async_dns_query = result; - protoman_event(protoman, layer, PROTOMAN_EVENT_RUN, PROTOMAN_EVENT_PRIORITY_LOW, 0); - } static int _do_connect(struct protoman_layer_s *layer) diff --git a/mbed-client/mbed-protocol-manager/source/platform/mbedos/protoman_layer_mbedos_socket_error_parser.cpp b/mbed-client/mbed-protocol-manager/source/platform/mbedos/protoman_layer_mbedos_socket_error_parser.cpp index dd20f54..847e134 100644 --- a/mbed-client/mbed-protocol-manager/source/platform/mbedos/protoman_layer_mbedos_socket_error_parser.cpp +++ b/mbed-client/mbed-protocol-manager/source/platform/mbedos/protoman_layer_mbedos_socket_error_parser.cpp @@ -72,4 +72,4 @@ const char* protoman_str_nsapi_error(int state) } #endif // PROTOMAN_ERROR_STRING } -#endif //MBED_CONF_NSAPI_PRESENT \ No newline at end of file +#endif //MBED_CONF_NSAPI_PRESENT diff --git a/mbed-client/mbed-protocol-manager/source/protoman.c b/mbed-client/mbed-protocol-manager/source/protoman.c index ca19a41..c6e34fa 100644 --- a/mbed-client/mbed-protocol-manager/source/protoman.c +++ b/mbed-client/mbed-protocol-manager/source/protoman.c @@ -108,6 +108,9 @@ static bool _layers_in_state(struct protoman_s *protoman, int state) static bool _layer_exists(struct protoman_s *protoman, struct protoman_layer_s *layer_in) { + if(layer_in == NULL || layer_in->name == NULL) { + return false; + } ns_list_foreach(struct protoman_layer_s, layer, &protoman->layers) { if (layer == layer_in) { protoman_verbose("%s layer", layer->name); diff --git a/mbed-client/mbed-protocol-manager/source/protoman_layer.c b/mbed-client/mbed-protocol-manager/source/protoman_layer.c index 31262dc..55969a8 100644 --- a/mbed-client/mbed-protocol-manager/source/protoman_layer.c +++ b/mbed-client/mbed-protocol-manager/source/protoman_layer.c @@ -188,7 +188,7 @@ void protoman_generic_layer_run(struct protoman_layer_s *layer) case PROTOMAN_STATE_RETVAL_ERROR: default: - protoman_err("layer->callbacks->state_do_init() returned %s (%d)", protoman_strstateretval(retval), retval); + protoman_err("state_do_init() returned %s (%d)", protoman_strstateretval(retval), retval); protoman_layer_state_change(layer, PROTOMAN_STATE_ERRORING); return; } @@ -233,7 +233,7 @@ void protoman_generic_layer_run(struct protoman_layer_s *layer) case PROTOMAN_STATE_RETVAL_ERROR: default: - protoman_err("layer->callbacks->state_do_connect() returned %s (%d)", protoman_strstateretval(retval), retval); + protoman_err("state_do_connect() returned %s (%d)", protoman_strstateretval(retval), retval); protoman_layer_state_change(layer, PROTOMAN_STATE_ERRORING); return; } @@ -277,9 +277,9 @@ void protoman_generic_layer_run(struct protoman_layer_s *layer) case PROTOMAN_STATE_RETVAL_ERROR: default: if (PROTOMAN_ERR_CONNECTION_CLOSED == layer->protoman_error) { - protoman_info("layer->callbacks->state_do_read() connection closed."); + protoman_info("state_do_read() connection closed"); } else { - protoman_err("layer->callbacks->state_do_read() returned %s (%d)", protoman_strstateretval(retval), retval); + protoman_err("state_do_read() returned %s (%d)", protoman_strstateretval(retval), retval); } protoman_layer_state_change(layer, PROTOMAN_STATE_ERRORING); return; @@ -295,7 +295,8 @@ void protoman_generic_layer_run(struct protoman_layer_s *layer) break; case PROTOMAN_STATE_RETVAL_AGAIN: - protoman_verbose("layer->callbacks->state_do_write() returned PROTOMAN_STATE_RETVAL_AGAIN, call again in %d ms", + protoman_verbose("state_do_write() returned %s, call again in %d ms", + protoman_strstateretval(retval), (int)delays->do_write); delay = delays->do_write; goto do_event; @@ -307,7 +308,7 @@ void protoman_generic_layer_run(struct protoman_layer_s *layer) case PROTOMAN_STATE_RETVAL_ERROR: default: - protoman_err("layer->callbacks->state_do_write() returned %s (%d)", protoman_strstateretval(retval), retval); + protoman_err("state_do_write() returned %s (%d)", protoman_strstateretval(retval), retval); protoman_layer_state_change(layer, PROTOMAN_STATE_ERRORING); return; } @@ -331,7 +332,7 @@ void protoman_generic_layer_run(struct protoman_layer_s *layer) case PROTOMAN_STATE_RETVAL_ERROR: default: - protoman_err("layer->callbacks->state_do_disconnect() returned %s (%d)", protoman_strstateretval(retval), retval); + protoman_err("state_do_disconnect() returned %s (%d)", protoman_strstateretval(retval), retval); protoman_layer_state_change(layer, PROTOMAN_STATE_ERRORING); return; } @@ -358,7 +359,7 @@ void protoman_generic_layer_run(struct protoman_layer_s *layer) case PROTOMAN_STATE_RETVAL_ERROR: default: - protoman_err("layer->callbacks->state_do_pause() returned %s (%d)", protoman_strstateretval(retval), retval); + protoman_err("state_do_pause() returned %s (%d)", protoman_strstateretval(retval), retval); protoman_layer_state_change(layer, PROTOMAN_STATE_ERRORING); return; } @@ -398,7 +399,7 @@ void protoman_generic_layer_run(struct protoman_layer_s *layer) case PROTOMAN_STATE_RETVAL_ERROR: default: - protoman_err("layer->callbacks->state_do_resume() returned %s (%d)", protoman_strstateretval(retval), retval); + protoman_err("state_do_resume() returned %s (%d)", protoman_strstateretval(retval), retval); protoman_layer_state_change(layer, PROTOMAN_STATE_ERRORING); return; } @@ -431,9 +432,9 @@ void protoman_generic_layer_run(struct protoman_layer_s *layer) case PROTOMAN_STATE_RESUMED: /* Connecting when resuming from initialized or disconnected state */ - protoman_debug("force connecting on resuming from %s", protoman_strstate(protoman->current_state)); - protoman->target_state = PROTOMAN_STATE_CONNECTED; - protoman_layer_state_change(protoman, PROTOMAN_STATE_CONNECTING); + protoman_debug("force connecting on resuming from %s", protoman_strstate(layer->current_state)); + layer->target_state = PROTOMAN_STATE_CONNECTED; + protoman_layer_state_change(layer, PROTOMAN_STATE_CONNECTING); break; } break; @@ -446,7 +447,7 @@ void protoman_generic_layer_run(struct protoman_layer_s *layer) break; default: if (PROTOMAN_ERR_CONNECTION_CLOSED == layer->protoman_error) { - protoman_info("PROTOMAN_STATE_ERRORING, connection closed."); + protoman_info("PROTOMAN_STATE_ERRORING, connection closed"); } else { protoman_err("PROTOMAN_STATE_ERRORING %s (%d)", protoman_strstateretval(protoman_get_layer_error(protoman)), diff --git a/mbed-client/mbed-protocol-manager/source/protoman_layer_mbedtls.c b/mbed-client/mbed-protocol-manager/source/protoman_layer_mbedtls.c index d0bc472..94db159 100644 --- a/mbed-client/mbed-protocol-manager/source/protoman_layer_mbedtls.c +++ b/mbed-client/mbed-protocol-manager/source/protoman_layer_mbedtls.c @@ -22,6 +22,7 @@ #include #include /* offsetof() */ #include +#include #include "nanostack-event-loop/eventOS_event.h" #include "mbedtls/net.h" @@ -33,6 +34,10 @@ #include "mbed-protocol-manager/protoman_layer_mbedtls.h" +#if defined(MBEDTLS_SSL_CONF_RNG) +#include "shared_rng.h" +#endif + #define TRACE_GROUP "mTLS" #include "include/protoman_internal.h" #include "include/protoman_layer_mbedtls_timer.h" @@ -77,11 +82,29 @@ static const char _mbedtls_error[] = "mbedTLS passes read and write errors throu //unsigned char id[32]; /*!< session identifier */ //unsigned char master[48]; /*!< the master secret */ +#ifndef MBEDTLS_SSL_DTLS_CONNECTION_ID +#error "MBEDTLS_SSL_DTLS_CONNECTION_ID must be defined with PROTOMAN_USE_SSL_SESSION_RESUME" +#endif + +#ifndef MBEDTLS_SSL_CONTEXT_SERIALIZATION +#error "MBEDTLS_SSL_CONTEXT_SERIALIZATION must be defined with PROTOMAN_USE_SSL_SESSION_RESUME" +#endif + // Size of the session data static const int ssl_session_size = 92; +#define SSL_SESSION_CONTEXT_SIZE 1024 + +size_t session_context_length = 0; +uint8_t session_context[SSL_SESSION_CONTEXT_SIZE] = {0}; + +static bool store_ssl_session(struct protoman_layer_s *layer); +static bool load_ssl_session(struct protoman_layer_s *layer); +static void load_ssl_session_from_storage(struct protoman_layer_s *layer); +static void store_ssl_session_to_storage(struct protoman_layer_s *layer); +static bool store_ssl_session_context(struct protoman_layer_s *layer); +static bool load_ssl_session_context(struct protoman_layer_s *layer); +static void load_ssl_session_context_from_storage(struct protoman_layer_s *layer); -static void store_ssl_session(struct protoman_layer_s *layer); -static void load_ssl_session(struct protoman_layer_s *layer); #endif //PROTOMAN_USE_SSL_SESSION_RESUME static const struct protoman_layer_callbacks_s callbacks = { @@ -99,7 +122,13 @@ static const struct protoman_layer_callbacks_s callbacks = { _do_resume // &_do_resume }; -void protoman_add_layer_mbedtls(struct protoman_s *protoman, struct protoman_layer_s *layer) +void protoman_add_layer_mbedtls( + struct protoman_s *protoman, + struct protoman_layer_s *layer +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + , bool ignore_session_resume +#endif + ) { struct protoman_layer_mbedtls_common_s *layer_mbedtls_common = (struct protoman_layer_mbedtls_common_s *)layer; @@ -122,6 +151,9 @@ void protoman_add_layer_mbedtls(struct protoman_s *protoman, struct protoman_lay layer_mbedtls_common->handshakes_failed = 0; layer_mbedtls_common->handshakes_max = 10; layer_mbedtls_common->handshakes_delay_ms = 5000; +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + layer_mbedtls_common->ignore_session_resume = ignore_session_resume; +#endif protoman_add_layer(protoman, layer); } @@ -366,6 +398,11 @@ static int _do_configuration(struct protoman_layer_s *layer) protoman_debug("Setting ssl mtu: %d", PROTOMAN_MTU); #endif // MBEDTLS_SSL_PROTO_DTLS +#if defined(PROTOMAN_MBEDTLS_DISABLE_DATAGRAM_PACKING) + mbedtls_ssl_set_datagram_packing(&layer_mbedtls_common->ssl, 0); + protoman_debug("Disabling datagram packing"); +#endif + #if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) protoman_debug("Setting ssl max_content_len: %d, max_frag_len: %d", MBEDTLS_SSL_MAX_CONTENT_LEN, PROTOMAN_LAYER_MBEDTLS_MAX_FRAG_LEN); @@ -385,7 +422,7 @@ static int _do_configuration(struct protoman_layer_s *layer) return PROTOMAN_STATE_RETVAL_ERROR; } -#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) && defined(MBEDTLS_SSL_CID_ENABLED) +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) && (MBEDTLS_SSL_CID_ENABLED) if (protoman->config.is_dgram) { protoman_debug("Setting cid enabled"); retval = mbedtls_ssl_set_cid(&layer_mbedtls_common->ssl, MBEDTLS_SSL_CID_ENABLED, NULL, 0); @@ -397,15 +434,6 @@ static int _do_configuration(struct protoman_layer_s *layer) } #endif -#ifdef PROTOMAN_USE_SSL_SESSION_RESUME - struct protoman_config_tls_certificate_s *config_cert = (struct protoman_config_tls_certificate_s *)layer->config; - if (!config_cert->bootstrap) { - load_ssl_session(layer); - } else { - protoman_info("Do not try to load ssl session in bootstrap mode"); - } -#endif - if (layer_mbedtls_common->conf.mfl_code == PROTOMAN_LAYER_MBEDTLS_MAX_FRAG_LEN) { // CFI (Control Flow Integrity) check to confirm basic initialization before returning success protoman_info("Configuration succesfull"); @@ -674,9 +702,10 @@ static int _do_certificates(struct protoman_layer_s *layer) static int _do_connect(struct protoman_layer_s *layer) { struct protoman_layer_mbedtls_common_s *layer_mbedtls_common = (struct protoman_layer_mbedtls_common_s *)layer; - int retval; - - retval = mbedtls_ssl_handshake_step(&layer_mbedtls_common->ssl); + int retval = 0; + if (layer_mbedtls_common->ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER) { + retval = mbedtls_ssl_handshake_step(&layer_mbedtls_common->ssl); + } protoman_verbose("mbedtls_ssl_handshake_step()"); switch (retval) { @@ -696,10 +725,14 @@ static int _do_connect(struct protoman_layer_s *layer) #ifdef PROTOMAN_USE_SSL_SESSION_RESUME struct protoman_config_tls_certificate_s *config_cert = (struct protoman_config_tls_certificate_s *)layer->config; - if (!config_cert->bootstrap) { - store_ssl_session(layer); + if (!layer_mbedtls_common->ignore_session_resume) { + if (!config_cert->bootstrap) { + store_ssl_session_to_storage(layer); + } else { + protoman_debug("Do not store ssl session in bootstrap mode"); + } } else { - protoman_info("Do not store ssl session in bootstrap mode"); + protoman_debug("Do not load store ssl session. CID might be incorrect"); } #endif @@ -715,7 +748,12 @@ static int _do_connect(struct protoman_layer_s *layer) /* No PROTOMAN_EVENT_RUN event scheduled here because there * will be PROTOMAN_DATA_AVAIL or PROTOMAN_DATA_WRITTEN event from below */ return PROTOMAN_STATE_RETVAL_WAIT; - + case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: + case MBEDTLS_ERR_SSL_UNEXPECTED_CID: +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + remove_ssl_session(layer); +#endif + break; case MBEDTLS_ERR_SSL_BAD_INPUT_DATA: default: protoman_err("mbedtls_ssl_handshake_step() returned %s (%d)", protoman_strmbedtls(retval), retval); @@ -742,7 +780,7 @@ static int _do_write(struct protoman_layer_s *layer) retval = mbedtls_ssl_write(&layer_mbedtls_common->ssl, layer->tx_buf + layer->tx_offset, layer->tx_len - layer->tx_offset); if (retval < 0 && retval != MBEDTLS_ERR_SSL_WANT_READ && retval != MBEDTLS_ERR_SSL_WANT_WRITE) { - protoman_warn("mbedtls_ssl_write() returned %s (%X)", protoman_strmbedtls(retval), retval); + protoman_warn("mbedtls_ssl_write() returned %s (%d)", protoman_strmbedtls(retval), retval); } /* Capture master secret for TLS decryption in Wireshark */ @@ -783,6 +821,13 @@ static int _do_write(struct protoman_layer_s *layer) } } +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + // check if is dtls mode + if (!layer_mbedtls_common->ignore_session_resume && protoman->config.is_dgram) { + store_ssl_session_to_storage(layer); + } +#endif + exit: return state_retval; } @@ -836,7 +881,7 @@ static int _do_read(struct protoman_layer_s *layer) protoman_layer_record_error(layer, PROTOMAN_ERR_CONNECTION_CLOSED, retval, "EOF"); state_retval = PROTOMAN_STATE_RETVAL_ERROR; } - protoman_info("mbedtls_ssl_read() returned %s (%X)", protoman_strmbedtls(retval), retval); + protoman_info("mbedtls_ssl_read() returned %s (%d)", protoman_strmbedtls(retval), retval); goto cleanup; default: @@ -854,12 +899,20 @@ static int _do_read(struct protoman_layer_s *layer) goto exit; } print_as_error: - protoman_err("mbedtls_ssl_read() returned %s (%X)", protoman_strmbedtls(retval), retval); + protoman_err("mbedtls_ssl_read() returned %s (%d)", protoman_strmbedtls(retval), retval); cleanup: PROTOMAN_DEBUG_PRINT_FREE(layer->name, layer->rx_buf); protoman_rx_free(protoman, layer->rx_buf); layer->rx_buf = NULL; exit: +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + // If received data, connection is ok + if (layer_mbedtls_common->ignore_session_resume) { + // Ping success + protoman_info("Ping success"); + layer_mbedtls_common->ignore_session_resume = false; + } +#endif return state_retval; } @@ -887,9 +940,23 @@ static int _do_disconnect(struct protoman_layer_s *layer) return PROTOMAN_STATE_RETVAL_WAIT; /* stay in disconnectin state and try again */ default: - protoman_warn("mbedtls_ssl_close_notify() failed with %X", retval); + protoman_warn("mbedtls_ssl_close_notify() failed with %d", retval); + } + } +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + /* store ssl session to storage */ + struct protoman_config_tls_certificate_s *config_cert = (struct protoman_config_tls_certificate_s *)layer->config; + if (!config_cert->bootstrap && !layer_mbedtls_common->ignore_session_resume) { + // check if is dtls mode + if (!protoman->config.is_dgram) { + store_ssl_session(layer); + } else { + if (store_ssl_session_context(layer)) { + store_ssl_session_context_to_storage(layer); + } } } +#endif /* Reset mbed TLS session * https://tls.mbed.org/api/ssl_8h.html#a21432367cbce428f10dcb62d9456fa7e */ @@ -901,7 +968,7 @@ static int _do_disconnect(struct protoman_layer_s *layer) case MBEDTLS_ERR_SSL_ALLOC_FAILED: default: - protoman_err("mbedtls_ssl_session_reset(), failed with %s (%X)", protoman_strmbedtls(retval), retval); + protoman_err("mbedtls_ssl_session_reset(), failed with %s (%d)", protoman_strmbedtls(retval), retval); protoman_layer_record_error(layer, PROTOMAN_ERR_NOMEM, retval, protoman_strmbedtls(retval)); return PROTOMAN_STATE_RETVAL_ERROR; } @@ -918,7 +985,6 @@ static int _do_init(struct protoman_layer_s *layer) { struct protoman_config_tls_common_s *config_common = (struct protoman_config_tls_common_s *)layer->config; int retval; - protoman_verbose(""); /* Do configuration */ @@ -927,7 +993,19 @@ static int _do_init(struct protoman_layer_s *layer) protoman_err("_do_configuration() failed with %s", protoman_strstateretval(retval)); return PROTOMAN_STATE_RETVAL_ERROR; } - +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + struct protoman_layer_mbedtls_common_s *layer_mbedtls_common = (struct protoman_layer_mbedtls_common_s *)layer; + struct protoman_config_tls_certificate_s *config_cert = (struct protoman_config_tls_certificate_s *)layer->config; + if (!layer_mbedtls_common->ignore_session_resume) { + if (!config_cert->bootstrap) { + load_ssl_session_from_storage(layer); + } else { + protoman_debug("Do not try to load ssl session in bootstrap mode"); + } + } else { + protoman_debug("Do not to load ssl session. Ignoring session resume"); + } +#endif /* Do security configuration */ if (PROTOMAN_SECURITY_MODE_CERTIFICATE == config_common->security_mode) { #ifdef PROTOMAN_SECURITY_ENABLE_CERTIFICATE @@ -1002,31 +1080,27 @@ static void layer_free(struct protoman_layer_s *layer) mbedtls_ssl_config_free(&layer_mbedtls_common->conf); mbedtls_ssl_free(&layer_mbedtls_common->ssl); } -#endif #ifdef PROTOMAN_USE_SSL_SESSION_RESUME -static void store_ssl_session(struct protoman_layer_s *layer) +static bool store_ssl_session(struct protoman_layer_s *layer) { protoman_debug(""); struct protoman_layer_mbedtls_common_s *layer_mbedtls_common = (struct protoman_layer_mbedtls_common_s *)layer; mbedtls_ssl_session ssl_session = {0}; int session_status = mbedtls_ssl_get_session(&layer_mbedtls_common->ssl, &ssl_session); - + bool success = false; if (session_status != 0) { protoman_debug("mbedtls_ssl_get_session failed: %d", session_status); - return; + return success; } uint8_t session_buffer[ssl_session_size]; PROTOMAN_MEMCPY(session_buffer, (uint8_t*)&ssl_session.id_len, sizeof(ssl_session.id_len)); - PROTOMAN_MEMCPY(session_buffer + sizeof(ssl_session.id_len), - (uint8_t*)&ssl_session.id, sizeof(ssl_session.id)); - PROTOMAN_MEMCPY(session_buffer + sizeof(ssl_session.id_len) + sizeof(ssl_session.id), - (uint8_t*)&ssl_session.master, sizeof(ssl_session.master)); + PROTOMAN_MEMCPY(session_buffer + sizeof(ssl_session.id_len), (uint8_t*)&ssl_session.id, sizeof(ssl_session.id)); + PROTOMAN_MEMCPY(session_buffer + sizeof(ssl_session.id_len) + sizeof(ssl_session.id), (uint8_t*)&ssl_session.master, sizeof(ssl_session.master)); #if !defined(MBEDTLS_SSL_CONF_SINGLE_CIPHERSUITE) - PROTOMAN_MEMCPY(session_buffer + sizeof(ssl_session.id_len) + sizeof(ssl_session.id) + sizeof(ssl_session.master), - (uint8_t*)&ssl_session.ciphersuite, sizeof(ssl_session.ciphersuite)); + PROTOMAN_MEMCPY(session_buffer + sizeof(ssl_session.id_len) + sizeof(ssl_session.id) + sizeof(ssl_session.master), (uint8_t*)&ssl_session.ciphersuite, sizeof(ssl_session.ciphersuite)); #endif bool replace = false; @@ -1047,6 +1121,7 @@ static void store_ssl_session(struct protoman_layer_s *layer) // Session data not changed, use existing one if (status == CCS_STATUS_SUCCESS && memcmp(session_buffer, existing_session, ssl_session_size) == 0) { replace = false; + success = true; } } @@ -1057,25 +1132,70 @@ static void store_ssl_session(struct protoman_layer_s *layer) status = set_config_parameter(SSL_SESSION_DATA, session_buffer, ssl_session_size); if (status != CCS_STATUS_SUCCESS) { protoman_err("failed to store new session: %d", status); + } else { + success = true; } } else { protoman_info("keep old session"); } mbedtls_ssl_session_free(&ssl_session); + return success; } -void load_ssl_session(struct protoman_layer_s *layer) +static void load_ssl_session_from_storage(struct protoman_layer_s *layer) +{ + protoman_debug(""); + struct protoman_config_tls_certificate_s *config_cert = (struct protoman_config_tls_certificate_s *)layer->config; + if (config_cert->bootstrap) { + protoman_debug("do not load session in bootstrap mode"); + return; + } + // check if is dtls mode + if (!layer->protoman->config.is_dgram) { + load_ssl_session(layer); + } else { + if (session_context_length != 0) { + load_ssl_session_context(layer); + } else { + load_ssl_session_context_from_storage(layer); + if (session_context_length != 0) { + load_ssl_session_context(layer); + } + } + } +} + +static void store_ssl_session_to_storage(struct protoman_layer_s *layer) +{ + protoman_debug(""); + struct protoman_config_tls_certificate_s *config_cert = (struct protoman_config_tls_certificate_s *)layer->config; + // do nothing if bootstrap + if (config_cert->bootstrap) { + return; + } + // check if is dtls mode + if (!layer->protoman->config.is_dgram) { + store_ssl_session(layer); + } else { + if (store_ssl_session_context(layer)) { + load_ssl_session_from_storage(layer); + } + } +} + +static bool load_ssl_session(struct protoman_layer_s *layer) { protoman_debug(""); struct protoman_layer_mbedtls_common_s *layer_mbedtls_common = (struct protoman_layer_mbedtls_common_s *)layer; size_t data_size = 0; + bool success = false; uint8_t ssl_session_buffer[ssl_session_size]; ccs_status_e status = get_config_parameter(SSL_SESSION_DATA, ssl_session_buffer, ssl_session_size, &data_size); if (status != CCS_STATUS_SUCCESS) { protoman_info("failed to read session data info from storage: %d", status); - return; + return success; } mbedtls_ssl_session ssl_session = {0}; @@ -1088,10 +1208,94 @@ void load_ssl_session(struct protoman_layer_s *layer) if (mbedtls_ssl_set_session(&layer_mbedtls_common->ssl, &ssl_session) != 0) { protoman_err("mbedtls_ssl_set_session - failed!"); + } else { + success = true; + } + return success; +} + +static bool store_ssl_session_context(struct protoman_layer_s *layer) +{ + protoman_debug(""); + struct protoman_layer_mbedtls_common_s *layer_mbedtls_common = (struct protoman_layer_mbedtls_common_s *)layer; + int32_t ssl_status = 0; + PROTOMAN_MEMSET(session_context, 0, SSL_SESSION_CONTEXT_SIZE); + ssl_status = mbedtls_ssl_context_save(&layer_mbedtls_common->ssl, session_context, SSL_SESSION_CONTEXT_SIZE, &session_context_length); + if (ssl_status != 0) { + protoman_err("mbedtls_ssl_context_save failed! -0x%" PRIx32 ".", ssl_status); + } + return ssl_status == 0; +} + +void store_ssl_session_context_to_storage(struct protoman_layer_s *layer) +{ + protoman_debug(""); + (void) layer; // quiet compiler + ccs_status_e status = set_config_parameter(SSL_SESSION_DATA, session_context, session_context_length); + if (status != CCS_STATUS_SUCCESS) { + protoman_err("failed to store session context: %d", status); } } + +static bool load_ssl_session_context(struct protoman_layer_s *layer) +{ + protoman_debug(""); + struct protoman_layer_mbedtls_common_s *layer_mbedtls_common = (struct protoman_layer_mbedtls_common_s *)layer; + int32_t ssl_status = 0; + ssl_status = mbedtls_ssl_context_load( &layer_mbedtls_common->ssl, session_context, session_context_length ); + if (ssl_status != 0) { + protoman_err("mbedtls_ssl_context_load failed!-0x%" PRIx32 ".",ssl_status); + return false; + } + return true; +} + +static void load_ssl_session_context_from_storage(struct protoman_layer_s *layer) +{ + protoman_debug(""); + (void) layer; // quiet compiler + ccs_status_e status = get_config_parameter(SSL_SESSION_DATA, session_context, SSL_SESSION_CONTEXT_SIZE, &session_context_length); + if (status != CCS_STATUS_SUCCESS) { + protoman_info("failed to read session context from storage: %d", status); + } +} + +void remove_ssl_session(struct protoman_layer_s *layer) +{ + protoman_debug(""); + (void) layer; // quiet compiler + + // check if is dtls mode + if (layer->protoman->config.is_dgram) { + PROTOMAN_MEMSET(session_context, 0, SSL_SESSION_CONTEXT_SIZE); + session_context_length = 0; + } else { + remove_config_parameter(SSL_SESSION_DATA); + } +} + +void protoman_set_cid_value(struct protoman_layer_s *layer, const uint8_t *data_ptr, const size_t data_len) +{ + protoman_debug(""); + assert(data_len <= MBEDTLS_SSL_CID_OUT_LEN_MAX); + struct protoman_layer_mbedtls_common_s *layer_mbedtls_common = (struct protoman_layer_mbedtls_common_s *)layer; +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) && defined(MBEDTLS_SSL_CID_ENABLED) + memcpy(layer_mbedtls_common->ssl.transform_out->out_cid, data_ptr, data_len); + layer_mbedtls_common->ssl.transform_out->out_cid_len = data_len; + store_ssl_session_context(layer); +#endif +} #endif //PROTOMAN_USE_SSL_SESSION_RESUME +bool protoman_is_connection_id_available() +{ +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + return session_context_length > 0; +#else + return false; +#endif +} + static void print_cid(struct protoman_layer_s *layer, const char* prefix) { (void) layer; // quiet compiler @@ -1115,3 +1319,4 @@ static void print_cid(struct protoman_layer_s *layer, const char* prefix) #endif #endif } +#endif diff --git a/mbed-client/mbed-protocol-manager/source/protoman_layer_mbedtls_error_parser.c b/mbed-client/mbed-protocol-manager/source/protoman_layer_mbedtls_error_parser.c index 3295014..60a6458 100644 --- a/mbed-client/mbed-protocol-manager/source/protoman_layer_mbedtls_error_parser.c +++ b/mbed-client/mbed-protocol-manager/source/protoman_layer_mbedtls_error_parser.c @@ -221,6 +221,11 @@ const char *protoman_strmbedtls(int errcode) case MBEDTLS_ERR_PLATFORM_FAULT_DETECTED: return "MBEDTLS_ERR_PLATFORM_FAULT_DETECTED"; #endif +#if (PROTOMAN_USE_SSL_SESSION_RESUME == 1) + case MBEDTLS_ERR_SSL_VERSION_MISMATCH: + return "MBEDTLS_ERR_SSL_VERSION_MISMATCH"; +#endif + default: return "UNKNOWN"; } diff --git a/mbed-client/source/lwm2m_connection.c b/mbed-client/source/lwm2m_connection.c index 64473c1..e1ea9b4 100644 --- a/mbed-client/source/lwm2m_connection.c +++ b/mbed-client/source/lwm2m_connection.c @@ -82,10 +82,13 @@ int8_t connection_init(connection_t *connection, void(*event_handler)(connection const uint8_t *ca_cert, uint16_t ca_cert_len, const uint8_t *cert, uint16_t cert_len, const uint8_t *key, uint16_t key_len #if defined(PROTOMAN_USE_SSL_SESSION_RESUME) || defined(PROTOMAN_OFFLOAD_TLS) , bool bootstrap +#endif +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + , bool ignore_session_resume #endif ) { - + connection->initialized = true; connection->event_handler = event_handler; connection->event_handler_id = event_handler_id; @@ -106,7 +109,13 @@ int8_t connection_init(connection_t *connection, void(*event_handler)(connection #if defined (PROTOMAN_SECURITY_ENABLE_CERTIFICATE) struct protoman_config_tls_certificate_s *tls_configuration; memset(&connection->protoman_layer_mbedtls, 0, sizeof(struct protoman_layer_mbedtls_certificate_s)); - protoman_add_layer_mbedtls(&connection->protoman, (protoman_layer_id_t)&connection->protoman_layer_mbedtls); + protoman_add_layer_mbedtls( + &connection->protoman, + (protoman_layer_id_t)&connection->protoman_layer_mbedtls +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + , ignore_session_resume +#endif + ); tls_configuration = protoman_get_config(&connection->protoman, (protoman_layer_id_t)&connection->protoman_layer_mbedtls); @@ -131,7 +140,13 @@ int8_t connection_init(connection_t *connection, void(*event_handler)(connection #elif defined (PROTOMAN_SECURITY_ENABLE_PSK) struct protoman_config_tls_psk_s *tls_configuration; memset(&connection->protoman_layer_mbedtls, 0, sizeof(struct protoman_layer_mbedtls_psk_s)); - protoman_add_layer_mbedtls(&connection->protoman, (protoman_layer_id_t)&connection->protoman_layer_mbedtls); + protoman_add_layer_mbedtls( + &connection->protoman, + (protoman_layer_id_t)&connection->protoman_layer_mbedtls, +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + , false +#endif + ); tls_configuration = protoman_get_config(&connection->protoman, (protoman_layer_id_t)&connection->protoman_layer_mbedtls); tls_configuration->common.security_mode = PROTOMAN_SECURITY_MODE_PSK; @@ -184,27 +199,48 @@ void connection_destroy(connection_t *connection) void connection_close(connection_t *connection) { + connection->initialized = false; protoman_close(&connection->protoman); } -void connection_start(connection_t *connection) +bool connection_start(connection_t *connection) { - protoman_connect(&connection->protoman); + if (connection->initialized) { + protoman_connect(&connection->protoman); + return true; + } + + return false; } -void connection_stop(connection_t *connection) +bool connection_stop(connection_t *connection) { - protoman_disconnect(&connection->protoman); + if (connection->initialized) { + protoman_disconnect(&connection->protoman); + return true; + } + + return false; } -void connection_pause(connection_t *connection) +bool connection_pause(connection_t *connection) { - protoman_pause(&connection->protoman); + if (connection->initialized) { + protoman_pause(&connection->protoman); + return true; + } + + return false; } -void connection_resume(connection_t *connection) +bool connection_resume(connection_t *connection) { - protoman_resume(&connection->protoman); + if (connection->initialized) { + protoman_resume(&connection->protoman); + return true; + } + + return false; } @@ -339,14 +375,14 @@ int8_t connection_set_entropy_callback(connection_t *connection, entropy_cb call } #endif // #ifndef PROTOMAN_OFFLOAD_TLS -static void connection_send_event(connection_t *connection, uint8_t type, uint32_t status) +static void connection_send_event(connection_t *connection, connection_event_t type, uint32_t status) { arm_event_t event; event.data_ptr = connection->context; event.event_data = status; event.event_id = CONNECTION_EVENT_ID; - event.event_type = type; + event.event_type = (uint8_t)type; event.priority = ARM_LIB_LOW_PRIORITY_EVENT; event.receiver = connection->event_handler_id; event.sender = 0; @@ -361,3 +397,38 @@ void connection_interface_status(connection_t *connection, bool up) { connection_send_event(connection, CONNECTION_EVENT_INTERFACE_STATUS, !up); } + +void set_cid_value(struct connection_s *connection, const uint8_t *data_ptr, const size_t data_len) +{ +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + struct protoman_layer_s *layer = (struct protoman_layer_s *)&connection->protoman_layer_mbedtls; + protoman_set_cid_value(layer, data_ptr, data_len); +#endif +} + + +void store_connection_id(struct connection_s *connection) +{ +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + struct protoman_layer_s *layer = (struct protoman_layer_s *)&connection->protoman_layer_mbedtls; + store_ssl_session_context_to_storage(layer); +#endif +} + +void remove_connection_id(struct connection_s *connection) +{ +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + struct protoman_layer_s *layer = (struct protoman_layer_s *)&connection->protoman_layer_mbedtls; + remove_ssl_session(layer); +#endif +} + +bool is_connection_id_available() +{ +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + return protoman_is_connection_id_available(); +#else + return true; +#endif + +} \ No newline at end of file diff --git a/mbed-client/source/lwm2m_endpoint.c b/mbed-client/source/lwm2m_endpoint.c index 7f03a4f..69fa625 100644 --- a/mbed-client/source/lwm2m_endpoint.c +++ b/mbed-client/source/lwm2m_endpoint.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020 ARM Limited. All rights reserved. + * Copyright (c) 2017-2021 Pelion. All rights reserved. * SPDX-License-Identifier: Apache-2.0 * Licensed under the Apache License, Version 2.0 (the License); you may * not use this file except in compliance with the License. @@ -44,7 +44,7 @@ #define TRACE_GROUP "lwEP" -static const char MCC_VERSION[] = "mccv=1.3.0-lite"; +static const char MCC_VERSION[] = "mccv=1.4.0-lite"; #ifndef MBED_CONF_MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE static const char iep_name_parameter[] ="iep="; /* Internal endpoint name*/ #endif //MBED_CONF_MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE @@ -60,7 +60,9 @@ static const char bs_ep_name[] = "ep="; // same as normal ep-name?! static const char ep_lifetime_parameter[] = "lt="; /* Lifetime. Number of seconds that this registration will be valid for. Must be updated within this time, or will be removed. */ static const char et_parameter[] = "et="; /* Endpoint type */ static const char bs_queue_mode[] = "b="; +#ifndef MBED_CLOUD_CLIENT_DISABLE_REGISTRY static const char obs_parameter[] = {'o', 'b', 's'}; /* Observable */ +#endif #if MBED_CLIENT_ENABLE_AUTO_OBSERVATION static const char auto_obs_parameter[] = {'a', 'o', 'b', 's', '='}; /* Auto observable */ #endif @@ -134,6 +136,8 @@ static uint8_t coap_tx_callback(uint8_t *data_ptr, uint16_t data_len, sn_nsdl_ad static int8_t endpoint_rx_function(sn_coap_hdr_s *coap_packet_ptr, sn_nsdl_addr_s *address_ptr, void* param); static void* lwm2m_alloc_uint16(uint16_t size); + +static uint32_t endpoint_get_coap_time(endpoint_t *ep); static void endpoint_coap_timer(void *ep); #ifndef MBED_CLOUD_CLIENT_DISABLE_REGISTRY @@ -151,10 +155,28 @@ static void calculate_new_coap_ping_send_time(endpoint_t *endpoint); static bool endpoint_command(endpoint_t *endpoint, unsigned message_type); +static uint32_t endpoint_get_coap_time(endpoint_t *ep) +{ + uint32_t old_tick = ep->old_tick; // initialized to value of eventOS_event_timer_ticks() + uint_fast16_t remainder = ep->tick_remainder; // initialized to 50 + uint32_t current_tick = eventOS_event_timer_ticks(); + remainder += current_tick - old_tick; + // If multiple seconds has passed between calls this loop will fast-forward to catch up. + // nanostack-eventloop tick-rate is EVENTOS_EVENT_TIMER_HZ/s, so EVENTOS_EVENT_TIMER_HZ ticks elapsed == 1 second elapsed. + while (remainder >= EVENTOS_EVENT_TIMER_HZ) { + remainder -= EVENTOS_EVENT_TIMER_HZ; + ep->coap_time++; + } + ep->old_tick = current_tick; + ep->tick_remainder = remainder; + + return ep->coap_time; +} + static void endpoint_coap_timer(void *ep) { endpoint_t *endpoint = ep; - sn_coap_protocol_exec(endpoint->coap, endpoint->coap_time++); + sn_coap_protocol_exec(endpoint->coap, endpoint_get_coap_time(endpoint)); #if defined(MBED_CLOUD_CLIENT_TRANSPORT_MODE_TCP) || defined(MBED_CLOUD_CLIENT_TRANSPORT_MODE_TCP_QUEUE) endpoint_request_coap_ping(endpoint); #endif @@ -221,7 +243,14 @@ int endpoint_setup(endpoint_t *endpoint, int8_t event_handler_id) endpoint->event_handler_id = event_handler_id; + endpoint->old_tick = eventOS_event_timer_ticks(); + // To reduce the effect of jitter remainder is originally initialized to 50. + // Time is incremented if remainder is in range 50-150, i.e. "in the middle of a second". + // This allows up to 0.5s jitter before gaps or clumping in the tick values coap library sees. + endpoint->tick_remainder = 50; + endpoint->coap_time = 0; + endpoint->coap_timeout = eventOS_timeout_every_ms(&endpoint_coap_timer, 1000, endpoint); if (!endpoint->coap_timeout) { return ENDPOINT_STATUS_ERROR; @@ -369,7 +398,7 @@ int endpoint_reset_binding_mode(registry_t *registry, const oma_lwm2m_binding_an registry_status_t status = registry_is_value_empty(registry, &binding_path, &empty); if (status != REGISTRY_STATUS_OK && status != REGISTRY_STATUS_NO_DATA) { - tr_error("endpoint_reset_binding_mode() could not check resource emptyness from registry"); + tr_error("reset_binding_mode() could not check resource emptyness from registry"); return ENDPOINT_STATUS_ERROR; } @@ -400,7 +429,7 @@ int endpoint_reset_binding_mode(registry_t *registry, const oma_lwm2m_binding_an } if (registry_set_value_string(registry, &binding_path, (char*)binding_mode, 0) != REGISTRY_STATUS_OK) { - tr_error("endpoint_set_binding_mode() setting mode to registry failed!"); + tr_error("set_binding_mode() setting mode to registry failed"); return ENDPOINT_STATUS_ERROR; } } @@ -540,7 +569,7 @@ void endpoint_send_message(endpoint_t *endpoint) endpoint->registered = false; } - tr_info("endpoint_send_message, type: %d", endpoint->message_type); + tr_info("send_message, type: %d", endpoint->message_type); if (ENDPOINT_STATUS_OK == endpoint_send_pending_message(endpoint)) { endpoint->last_message_type = endpoint->message_type; @@ -610,7 +639,7 @@ int endpoint_send_event(endpoint_t *endpoint, uint8_t type, uint32_t coap_msg_st event.sender = 0; if (0 > eventOS_event_send(&event)) { - tr_error("eventOS_event_send failed."); + tr_error("eventOS_event_send failed"); return ENDPOINT_STATUS_ERROR; } @@ -697,7 +726,7 @@ int endpoint_process_coap(endpoint_t *endpoint, uint8_t *packet_ptr, uint16_t pa #if SN_COAP_DUPLICATION_MAX_MSGS_COUNT if (coap_packet_ptr->coap_status == COAP_STATUS_PARSER_DUPLICATED_MSG) { - tr_info("endpoint_process_coap, received duplicate message, ignore"); + tr_info("process_coap, received duplicate message, ignore"); sn_coap_parser_release_allocated_coap_msg_mem(endpoint->coap, coap_packet_ptr); return ENDPOINT_STATUS_OK; } @@ -707,7 +736,7 @@ int endpoint_process_coap(endpoint_t *endpoint, uint8_t *packet_ptr, uint16_t pa if (coap_packet_ptr->options_list_ptr) { if (coap_packet_ptr->options_list_ptr->proxy_uri_len) { - tr_warn("endpoint_process_coap, proxy option found, not supported."); + tr_warn("process_coap, proxy option found, not supported"); coap_response_ptr = sn_coap_build_response(endpoint->coap, coap_packet_ptr, COAP_MSG_CODE_RESPONSE_PROXYING_NOT_SUPPORTED); @@ -767,7 +796,7 @@ int endpoint_send_coap_message(endpoint_t *endpoint, sn_nsdl_addr_s *address_ptr if (!address_ptr) { if (get_nsdl_address(endpoint, &address) != ENDPOINT_STATUS_OK) { - tr_error("endpoint_send_coap_message get_nsdl_address failed."); + tr_error("send_coap_message get_nsdl_address failed"); return ENDPOINT_STATUS_ERROR; } address_ptr = &address; @@ -775,26 +804,27 @@ int endpoint_send_coap_message(endpoint_t *endpoint, sn_nsdl_addr_s *address_ptr #if SN_COAP_MAX_BLOCKWISE_PAYLOAD_SIZE /* If Message blockwising is not used at all, this part of code will not be compiled */ ret_val = prepare_blockwise_message(endpoint->coap, coap_hdr_ptr); - if( 0 != ret_val ) { - tr_error("endpoint_send_coap_message prepare_blockwise_message failed."); + if (0 != ret_val) { + tr_error("send_coap_message prepare_blockwise failed err: %d", ret_val); return ENDPOINT_STATUS_ERROR_MEMORY_FAILED; } #endif /* Calculate message length */ message_len = sn_coap_builder_calc_needed_packet_data_size_2(coap_hdr_ptr, endpoint->coap->sn_coap_block_data_size); - tr_debug("sn_nsdl_send_coap_message - msg len after calc: [%d]", message_len); - tr_debug("sn_send_send_coap_message - msg id: [%d]", coap_hdr_ptr->msg_id); + tr_debug("send_coap_message - msg len after calc: [%d]", message_len); + tr_debug("send_coap_message - msg id: [%d]", coap_hdr_ptr->msg_id); /* Allocate memory for message and check was allocating successfully */ message_ptr = lwm2m_alloc(message_len); if (message_ptr == NULL) { - tr_error("endpoint_send_coap_message lwm2m_alloc failed."); + tr_error("send_coap_message lwm2m_alloc(%d) failed", message_len); return ENDPOINT_STATUS_ERROR_MEMORY_FAILED; } /* Build CoAP message */ + endpoint->coap->system_time = endpoint_get_coap_time(endpoint); int coap_length = sn_coap_protocol_build(endpoint->coap, address_ptr, message_ptr, coap_hdr_ptr, (void *)endpoint); int return_value = ENDPOINT_STATUS_ERROR; if (coap_length == -2) { @@ -803,7 +833,7 @@ int endpoint_send_coap_message(endpoint_t *endpoint, sn_nsdl_addr_s *address_ptr if ( coap_length < 0) { lwm2m_free(message_ptr); message_ptr = 0; - tr_error("endpoint_send_coap_message sn_coap_protocol_build failed. Failure reason %d", return_value); + tr_error("send_coap_message protocol_build err: %d", return_value); return return_value; } @@ -829,9 +859,9 @@ int endpoint_send_coap_message(endpoint_t *endpoint, sn_nsdl_addr_s *address_ptr ret_val = connection_send_data(endpoint->connection, message_ptr, coap_length, true); if (ret_val == CONNECTION_STATUS_WOULD_BLOCK) { - tr_warn("endpoint_send_coap_message connection_send_data, would block"); + tr_warn("send_coap_message send_data, would block"); } else if (ret_val != CONNECTION_STATUS_OK) { - tr_err("endpoint_send_coap_message connection_send_data failed, ret_val: %d", ret_val); + tr_err("send_coap_message send_data failed, err: %d", ret_val); } // No need to fail the call at this point as the CoAP library takes care of re-sending if needed now. @@ -839,7 +869,7 @@ int endpoint_send_coap_message(endpoint_t *endpoint, sn_nsdl_addr_s *address_ptr calculate_new_coap_ping_send_time(endpoint); #endif - tr_info("endpoint_send_coap_message connection_send_data OK."); + tr_info("send_coap_message send_data OK"); return ENDPOINT_STATUS_OK; } @@ -966,7 +996,7 @@ static int endpoint_update_or_unregister_endpoint(endpoint_t *endpoint, sn_nsdl_ } else { // According to OMA LWM2M spec 8.2.3, the server MUST return the location on register, // so this branch is not necessarily even needed. - tr_error("endpoint_update_endpoint_registration: location not specified"); + tr_error("update_endpoint_registration: location not specified"); status = ENDPOINT_STATUS_ERROR; } @@ -1073,7 +1103,7 @@ static int endpoint_internal_coap_send(endpoint_t *endpoint, sn_coap_hdr_s *coap int status; - tr_debug("endpoint_internal_coap_send"); + tr_debug("internal_coap_send"); endpoint->message_type = message_description; endpoint->message_token = generate_token(); @@ -1138,7 +1168,7 @@ static bool endpoint_handle_endpoint_response(endpoint_t *endpoint, sn_coap_hdr_ break; case ENDPOINT_MSG_UNDEFINED: - tr_warn("endpoint_handle_endpoint_response() unhandled CoAP error"); + tr_warn("handle_response() unhandled CoAP error"); return true; } } else { @@ -1169,11 +1199,10 @@ static bool endpoint_handle_endpoint_response(endpoint_t *endpoint, sn_coap_hdr_ break; case ENDPOINT_MSG_UNDEFINED: - tr_warn("endpoint_handle_response() unhandled CoAP response"); + tr_warn("handle_response() unhandled CoAP response"); return true; } - } endpoint->message_type = ENDPOINT_MSG_UNDEFINED; @@ -1181,7 +1210,6 @@ static bool endpoint_handle_endpoint_response(endpoint_t *endpoint, sn_coap_hdr_ send_queue_sent(endpoint, true); return true; - } static void endpoint_handle_response(endpoint_t *endpoint, sn_coap_hdr_s *coap_header) @@ -1213,7 +1241,7 @@ static void endpoint_handle_response(endpoint_t *endpoint, sn_coap_hdr_s *coap_h // Response was handled. if (coap_header->coap_status == COAP_STATUS_BUILDER_MESSAGE_SENDING_FAILED || coap_header->coap_status == COAP_STATUS_BUILDER_BLOCK_SENDING_FAILED) { - tr_warn("endpoint_handle_response CoAP message sending failed status: %d", coap_header->coap_status); + tr_warn("handle_response CoAP message sending failed status: %d", coap_header->coap_status); endpoint->registered = false; } } @@ -1244,7 +1272,7 @@ static int endpoint_handle_registration_response(endpoint_t *endpoint, const sn_ // would likely not include an explicit Max-Age option, in which case we'd see the default 60 seconds. if (max_time >= MINIMUM_REGISTRATION_TIME) { - tr_debug("endpoint_handle_registration_response() setting lifetime from registration response: %"PRId32, max_time); + tr_debug("handle_registration_response() setting lifetime from registration response: %"PRId32, max_time); return endpoint_set_lifetime(endpoint, max_time); } @@ -1261,7 +1289,7 @@ int endpoint_set_retransmission_parameters(endpoint_t *endpoint) { // past the client lifetime. the last attempt needs to end before 75% of client lifetime has passed. uint32_t lifetime = 0; if (ENDPOINT_STATUS_OK != endpoint_get_lifetime(endpoint, &lifetime)) { - tr_error("endpoint_set_retransmission_parameters() could not read endpoint lifetime!"); + tr_error("set_retransmission_parameters() could not read endpoint lifetime"); return ENDPOINT_STATUS_ERROR; } @@ -1281,7 +1309,7 @@ int endpoint_set_retransmission_parameters(endpoint_t *endpoint) { reconnection_total_time = MBED_CLIENT_RECONNECTION_INTERVAL + (MBED_CLIENT_RECONNECTION_INTERVAL * (reconnection_count * (reconnection_count + 1))); reconnection_total_time += MAXIMUM_RECONNECTION_TIME_INTERVAL; } - tr_debug("endpoint_set_retransmission_parameters() setting max resend count to %"PRIu32 " with total time: %"PRIu32, reconnection_count, reconnection_total_time); + tr_debug("set_retransmission_parameters() setting max resend count to %"PRIu32 " with total time: %"PRIu32, reconnection_count, reconnection_total_time); if (sn_coap_protocol_set_retransmission_parameters(endpoint->coap, reconnection_count, MBED_CLIENT_RECONNECTION_INTERVAL) < 0) { return ENDPOINT_STATUS_ERROR; @@ -1293,12 +1321,12 @@ int endpoint_set_retransmission_parameters(endpoint_t *endpoint) { int endpoint_set_lifetime(endpoint_t *endpoint, uint32_t lifetime) { - tr_debug("endpoint_set_lifetime() lifetime: %"PRIu32, lifetime); + tr_debug("set_lifetime() lifetime: %"PRIu32, lifetime); // doing some gatekeeping here if (lifetime > 0 && lifetime < MINIMUM_REGISTRATION_TIME) { lifetime = MINIMUM_REGISTRATION_TIME; - tr_debug("endpoint_set_lifetime() setting default value (minimum): %"PRIu32, lifetime); + tr_debug("set_lifetime() setting default value (minimum): %"PRIu32, lifetime); } #ifndef MBED_CLOUD_CLIENT_DISABLE_REGISTRY @@ -1313,7 +1341,7 @@ int endpoint_set_lifetime(endpoint_t *endpoint, uint32_t lifetime) #ifndef SN_COAP_DISABLE_RESENDINGS // If the mode is UDP or Queue mode then reconfigure the retransmission count to avoid full registration cycle. - if ((endpoint->mode | BINDING_MODE_U) || (endpoint->mode | BINDING_MODE_Q)) { + if ((endpoint->mode & BINDING_MODE_U) || (endpoint->mode & BINDING_MODE_Q)) { if (endpoint_set_retransmission_parameters(endpoint) != ENDPOINT_STATUS_OK) { return ENDPOINT_STATUS_ERROR; } @@ -1331,7 +1359,7 @@ int endpoint_get_lifetime(const endpoint_t *endpoint, uint32_t *lifetime) { #endif if (!lifetime || !endpoint) { - tr_error("endpoint_get_lifetime() invalid params"); + tr_error("get_lifetime() invalid params"); assert(0); return ENDPOINT_STATUS_ERROR; } @@ -1339,7 +1367,7 @@ int endpoint_get_lifetime(const endpoint_t *endpoint, uint32_t *lifetime) { #ifndef MBED_CLOUD_CLIENT_DISABLE_REGISTRY registry_set_path(&path, M2M_SERVER_ID, 0, SERVER_LIFETIME, 0, REGISTRY_PATH_RESOURCE); if (registry_get_value_int(&endpoint->registry, &path, &tmp_lifetime) != REGISTRY_STATUS_OK) { - tr_error("endpoint_get_lifetime() reading from registry failed!"); + tr_error("get_lifetime() reading from registry failed"); return ENDPOINT_STATUS_ERROR; } *lifetime = (uint32_t) tmp_lifetime; @@ -1550,14 +1578,13 @@ static bool is_observable(const registry_path_t *path) } return registry_meta_is_resource_observable(resource_def); - } #endif static int endpoint_build_registration_body(endpoint_t *endpoint, sn_coap_hdr_s *message_ptr, uint8_t updating_registeration, int32_t *len) { #ifndef MBED_CLOUD_CLIENT_DISABLE_REGISTRY - tr_debug("endpoint_build_registration_body"); + tr_debug("build_registration_body"); /* Local variables */ uint8_t *data; int32_t data_len; @@ -1583,7 +1610,7 @@ static int endpoint_build_registration_body(endpoint_t *endpoint, sn_coap_hdr_s return ENDPOINT_STATUS_ERROR; } - tr_debug("endpoint_build_registration_body - body size: [%d]", message_ptr->payload_len); + tr_debug("build_registration_body - body size: [%d]", message_ptr->payload_len); message_ptr->payload_ptr = lwm2m_alloc(message_ptr->payload_len); if (!message_ptr->payload_ptr) { return ENDPOINT_STATUS_ERROR; @@ -1714,7 +1741,7 @@ static int endpoint_build_registration_body(endpoint_t *endpoint, sn_coap_hdr_s t->next = resources; } if (ret) { - tr_error("endpoint_build_registration_body - out of memory"); + tr_error("build_registration_body - out of memory"); break; } } @@ -1755,7 +1782,7 @@ static int endpoint_build_registration_body(endpoint_t *endpoint, sn_coap_hdr_s if (leng < 0 || leng > UINT16_MAX) { return ENDPOINT_STATUS_ERROR; } - tr_debug("endpoint_build_registration_body - body size: [%d]", message_ptr->payload_len); + tr_debug("build_registration_body - body size: [%d]", message_ptr->payload_len); message_ptr->payload_ptr = lwm2m_alloc(message_ptr->payload_len); if (!message_ptr->payload_ptr) { return ENDPOINT_STATUS_ERROR; @@ -1932,18 +1959,14 @@ static void endpoint_print_coap_data(const sn_coap_hdr_s *coap_header_ptr, bool return; } - if (outgoing) { - tr_info("======== Outgoing CoAP package ========"); - } else { - tr_info("======== Incoming CoAP package ========"); - } + tr_info("======== %sing CoAP package ========", (outgoing ? "Outgo" : "Incom")); if (coap_header_ptr->uri_path_len > 0 && coap_header_ptr->uri_path_ptr) { tr_info("Uri-Path:\t\t%.*s", coap_header_ptr->uri_path_len, coap_header_ptr->uri_path_ptr); } - tr_info("Status:\t\t%s", endpoint_coap_status_description(coap_header_ptr->coap_status)); - tr_info("Code:\t\t%s", endpoint_coap_message_code_desc(coap_header_ptr->msg_code)); - tr_info("Type:\t\t%s", endpoint_coap_message_type_desc(coap_header_ptr->msg_type)); + tr_info("Status:\t\tCOAP_STATUS_%s", endpoint_coap_status_description(coap_header_ptr->coap_status)); + tr_info("Code:\t\tCOAP_MSG_CODE_%s", endpoint_coap_message_code_desc(coap_header_ptr->msg_code)); + tr_info("Type:\t\tCOAP_MSG_TYPE_%s", endpoint_coap_message_type_desc(coap_header_ptr->msg_type)); tr_info("Id:\t\t%d", coap_header_ptr->msg_id); if (coap_header_ptr->token_ptr && coap_header_ptr->token_len > 0) { tr_info("Token:\t\t%s", tr_array(coap_header_ptr->token_ptr, coap_header_ptr->token_len)); @@ -1964,8 +1987,9 @@ static void endpoint_print_coap_data(const sn_coap_hdr_s *coap_header_ptr, bool tr_info("PL:\t\t%s", tr_array( coap_header_ptr->payload_ptr + i, row_len)); i += row_len; } - if (i >= max_length) - tr_info("PL:\t\t....."); + if (i >= max_length) { + tr_info("PL:\t\t..."); + } } #endif @@ -2032,107 +2056,110 @@ static void endpoint_print_coap_data(const sn_coap_hdr_s *coap_header_ptr, bool } #if defined(FEA_TRACE_SUPPORT) || MBED_CONF_MBED_TRACE_ENABLE || YOTTA_CFG_MBED_TRACE || (defined(YOTTA_CFG) && !defined(NDEBUG)) +// return human readable mapping of status to sn_coap_status_e, without "COAP_STATUS_" prefix static const char *endpoint_coap_status_description(sn_coap_status_e status) { switch(status) { case COAP_STATUS_OK: - return "COAP_STATUS_OK"; + return "OK"; case COAP_STATUS_PARSER_ERROR_IN_HEADER: - return "COAP_STATUS_PARSER_ERROR_IN_HEADER"; + return "PARSER_ERROR_IN_HEADER"; case COAP_STATUS_PARSER_DUPLICATED_MSG: - return "COAP_STATUS_PARSER_DUPLICATED_MSG"; + return "PARSER_DUPLICATED_MSG"; case COAP_STATUS_PARSER_BLOCKWISE_MSG_RECEIVING: - return "COAP_STATUS_PARSER_BLOCKWISE_MSG_RECEIVING"; + return "PARSER_BLOCKWISE_MSG_RECEIVING"; case COAP_STATUS_PARSER_BLOCKWISE_ACK: - return "COAP_STATUS_PARSER_BLOCKWISE_ACK"; + return "PARSER_BLOCKWISE_ACK"; case COAP_STATUS_PARSER_BLOCKWISE_MSG_REJECTED: - return "COAP_STATUS_PARSER_BLOCKWISE_MSG_REJECTED"; + return "PARSER_BLOCKWISE_MSG_REJECTED"; case COAP_STATUS_PARSER_BLOCKWISE_MSG_RECEIVED: - return "COAP_STATUS_PARSER_BLOCKWISE_MSG_RECEIVED"; + return "PARSER_BLOCKWISE_MSG_RECEIVED"; case COAP_STATUS_BUILDER_MESSAGE_SENDING_FAILED: - return "COAP_STATUS_BUILDER_MESSAGE_SENDING_FAILED"; + return "BUILDER_MESSAGE_SENDING_FAILED"; default: - return ""; + return "UNKNOWN"; } } +// return human readable mapping of msg_code to sn_coap_msg_code_e, without "COAP_MSG_CODE_" prefix static const char *endpoint_coap_message_code_desc(int msg_code) { switch(msg_code) { case COAP_MSG_CODE_EMPTY: - return "COAP_MSG_CODE_EMPTY"; + return "EMPTY"; case COAP_MSG_CODE_REQUEST_GET: - return "COAP_MSG_CODE_REQUEST_GET"; + return "REQUEST_GET"; case COAP_MSG_CODE_REQUEST_POST: - return "COAP_MSG_CODE_REQUEST_POST"; + return "REQUEST_POST"; case COAP_MSG_CODE_REQUEST_PUT: - return "COAP_MSG_CODE_REQUEST_PUT"; + return "REQUEST_PUT"; case COAP_MSG_CODE_REQUEST_DELETE: - return "COAP_MSG_CODE_REQUEST_DELETE"; + return "REQUEST_DELETE"; case COAP_MSG_CODE_RESPONSE_CREATED: - return "COAP_MSG_CODE_RESPONSE_CREATED"; + return "RESPONSE_CREATED"; case COAP_MSG_CODE_RESPONSE_DELETED: - return "COAP_MSG_CODE_RESPONSE_DELETED"; + return "RESPONSE_DELETED"; case COAP_MSG_CODE_RESPONSE_VALID: - return "COAP_MSG_CODE_RESPONSE_VALID"; + return "RESPONSE_VALID"; case COAP_MSG_CODE_RESPONSE_CHANGED: - return "COAP_MSG_CODE_RESPONSE_CHANGED"; + return "RESPONSE_CHANGED"; case COAP_MSG_CODE_RESPONSE_CONTENT: - return "COAP_MSG_CODE_RESPONSE_CONTENT"; + return "RESPONSE_CONTENT"; case COAP_MSG_CODE_RESPONSE_CONTINUE: - return "COAP_MSG_CODE_RESPONSE_CONTINUE"; + return "RESPONSE_CONTINUE"; case COAP_MSG_CODE_RESPONSE_BAD_REQUEST: - return "COAP_MSG_CODE_RESPONSE_BAD_REQUEST"; + return "RESPONSE_BAD_REQUEST"; case COAP_MSG_CODE_RESPONSE_UNAUTHORIZED: - return "COAP_MSG_CODE_RESPONSE_UNAUTHORIZED"; + return "RESPONSE_UNAUTHORIZED"; case COAP_MSG_CODE_RESPONSE_BAD_OPTION: - return "COAP_MSG_CODE_RESPONSE_BAD_OPTION"; + return "RESPONSE_BAD_OPTION"; case COAP_MSG_CODE_RESPONSE_FORBIDDEN: - return "COAP_MSG_CODE_RESPONSE_FORBIDDEN"; + return "RESPONSE_FORBIDDEN"; case COAP_MSG_CODE_RESPONSE_NOT_FOUND: - return "COAP_MSG_CODE_RESPONSE_NOT_FOUND"; + return "RESPONSE_NOT_FOUND"; case COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED: - return "COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED"; + return "RESPONSE_METHOD_NOT_ALLOWED"; case COAP_MSG_CODE_RESPONSE_NOT_ACCEPTABLE: - return "COAP_MSG_CODE_RESPONSE_NOT_ACCEPTABLE"; + return "RESPONSE_NOT_ACCEPTABLE"; case COAP_MSG_CODE_RESPONSE_REQUEST_ENTITY_INCOMPLETE: - return "COAP_MSG_CODE_RESPONSE_REQUEST_ENTITY_INCOMPLETE"; + return "RESPONSE_REQUEST_ENTITY_INCOMPLETE"; case COAP_MSG_CODE_RESPONSE_PRECONDITION_FAILED: - return "COAP_MSG_CODE_RESPONSE_PRECONDITION_FAILED"; + return "RESPONSE_PRECONDITION_FAILED"; case COAP_MSG_CODE_RESPONSE_REQUEST_ENTITY_TOO_LARGE: - return "COAP_MSG_CODE_RESPONSE_REQUEST_ENTITY_TOO_LARGE"; + return "RESPONSE_REQUEST_ENTITY_TOO_LARGE"; case COAP_MSG_CODE_RESPONSE_UNSUPPORTED_CONTENT_FORMAT: - return "COAP_MSG_CODE_RESPONSE_UNSUPPORTED_CONTENT_FORMAT"; + return "RESPONSE_UNSUPPORTED_CONTENT_FORMAT"; case COAP_MSG_CODE_RESPONSE_INTERNAL_SERVER_ERROR: - return "COAP_MSG_CODE_RESPONSE_INTERNAL_SERVER_ERROR"; + return "RESPONSE_INTERNAL_SERVER_ERROR"; case COAP_MSG_CODE_RESPONSE_NOT_IMPLEMENTED: - return "COAP_MSG_CODE_RESPONSE_NOT_IMPLEMENTED"; + return "RESPONSE_NOT_IMPLEMENTED"; case COAP_MSG_CODE_RESPONSE_BAD_GATEWAY: - return "COAP_MSG_CODE_RESPONSE_BAD_GATEWAY"; + return "RESPONSE_BAD_GATEWAY"; case COAP_MSG_CODE_RESPONSE_SERVICE_UNAVAILABLE: - return "COAP_MSG_CODE_RESPONSE_SERVICE_UNAVAILABLE"; + return "RESPONSE_SERVICE_UNAVAILABLE"; case COAP_MSG_CODE_RESPONSE_GATEWAY_TIMEOUT: - return "COAP_MSG_CODE_RESPONSE_GATEWAY_TIMEOUT"; + return "RESPONSE_GATEWAY_TIMEOUT"; case COAP_MSG_CODE_RESPONSE_PROXYING_NOT_SUPPORTED: - return "COAP_MSG_CODE_RESPONSE_PROXYING_NOT_SUPPORTED"; + return "RESPONSE_PROXYING_NOT_SUPPORTED"; default: - return ""; + return "UNKNOWN"; } } +// return human readable mapping of msg_type to sn_coap_msg_type_e, without "COAP_MSG_TYPE_" prefix static const char *endpoint_coap_message_type_desc(int msg_type) { switch(msg_type) { case COAP_MSG_TYPE_CONFIRMABLE: - return "COAP_MSG_TYPE_CONFIRMABLE"; + return "CONFIRMABLE"; case COAP_MSG_TYPE_NON_CONFIRMABLE: - return "COAP_MSG_TYPE_NON_CONFIRMABLE"; + return "NON_CONFIRMABLE"; case COAP_MSG_TYPE_ACKNOWLEDGEMENT: - return "COAP_MSG_TYPE_ACKNOWLEDGEMENT"; + return "ACKNOWLEDGEMENT"; case COAP_MSG_TYPE_RESET: - return "COAP_MSG_TYPE_RESET"; + return "RESET"; default: - return ""; + return "UNKNOWN"; } } #endif @@ -2262,17 +2289,19 @@ unsigned int endpoint_last_message_sent(const endpoint_t *endpoint) bool endpoint_set_uri_query_parameters(endpoint_t *endpoint, const char *uri_query_params) { - tr_debug("endpoint_set_uri_query_parameters"); + tr_debug("set_uri_query_parameters"); + + assert(endpoint); + assert(uri_query_params); + size_t query_len = strlens(uri_query_params); size_t current_len = strlens(endpoint->custom_uri_query_params); size_t new_size = query_len + current_len; - if (uri_query_params == NULL || - endpoint == NULL || - query_len == 0 || + if (query_len == 0 || query_len > MAX_ALLOWED_STRING_LENGTH || new_size > MAX_ALLOWED_STRING_LENGTH) { - tr_error("endpoint_set_uri_query_parameters - invalid params!"); + tr_error("set_uri_query_parameters - invalid size"); return false; } @@ -2304,7 +2333,7 @@ bool endpoint_set_uri_query_parameters(endpoint_t *endpoint, const char *uri_que endpoint->custom_uri_query_params[query_len] = '\0'; } - tr_info("endpoint_set_uri_query_parameters - custom string %s", endpoint->custom_uri_query_params); + tr_info("set_uri_query_parameters - custom string %s", endpoint->custom_uri_query_params); return true; } @@ -2315,7 +2344,7 @@ static void endpoint_request_coap_ping(endpoint_t *endpoint) // Send only in TCP mode if (endpoint->mode != BINDING_MODE_T || MBED_CLIENT_TCP_KEEPALIVE_INTERVAL == 0 || - endpoint->coap_time != endpoint->next_coap_ping_send_time || + endpoint->coap_time < endpoint->next_coap_ping_send_time || endpoint->coap_ping_id) { return; } @@ -2340,7 +2369,7 @@ void endpoint_send_coap_ping(endpoint_t *endpoint) /* Send message */ if (ENDPOINT_STATUS_OK != endpoint_send_coap_message(endpoint, NULL, &coap_ping)) { - tr_error("endpoint_send_coap_ping - endpoint_send_coap_message failed!"); + tr_error("send_coap_ping - endpoint_send_coap_message failed"); endpoint_send_event(endpoint, ENDPOINT_EVENT_ERROR_REREGISTER, ENDPOINT_EVENT_STATUS_NO_MEMORY); send_queue_sent(endpoint, true); } else { @@ -2358,10 +2387,10 @@ static void calculate_new_coap_ping_send_time(endpoint_t *endpoint) static bool endpoint_command(endpoint_t *endpoint, unsigned message_type) { if (endpoint->message_type != ENDPOINT_MSG_UNDEFINED) { - tr_warn("endpoint_command(), %d in progress.", endpoint->message_type); + tr_warn("command %d in progress", endpoint->message_type); return ENDPOINT_STATUS_ERROR; } - tr_info("endpoint_command()"); + tr_info("command"); endpoint->message_type = message_type; send_queue_request(endpoint, SEND_QUEUE_ENDPOINT); return ENDPOINT_STATUS_OK; @@ -2376,6 +2405,12 @@ void endpoint_stop_coap_exec_timer(endpoint_t *endpoint) void endpoint_start_coap_exec_timer(endpoint_t *endpoint) { endpoint_stop_coap_exec_timer(endpoint); + endpoint->old_tick = eventOS_event_timer_ticks(); + // To reduce the effect of jitter remainder is originally initialized to 50. + // Time is incremented if remainder is in range 50-150, i.e. "in the middle of a second". + // This allows up to 0.5s jitter before gaps or clumping in the tick values coap library sees. + endpoint->tick_remainder = 50; + endpoint->coap_timeout = eventOS_timeout_every_ms(&endpoint_coap_timer, 1000, endpoint); } @@ -2476,7 +2511,7 @@ uint8_t* write_resource_value(uint8_t *packet, const registry_path_t *path, int3 packet_len); } else { - tr_error("write_resource_value - error in Base64 encoding error %d or olen is 0", ret_val); + tr_error("write_resource_value - Base64 encoding err: %d, olen: %lu", ret_val, (unsigned long)olen); } lwm2m_free(dst); } @@ -2629,7 +2664,7 @@ register_resource_t *endpoint_create_register_resource_opaque(endpoint_t *endpoi dst_size = (((len + 2) / 3) << 2) + 1; dst = (uint8_t*) lwm2m_alloc(dst_size); if (!dst) { - tr_error("endpoint_create_register_resource_opaque - failed to allocate buffer"); + tr_error("create_register_resource_opaque - failed to allocate buffer"); return NULL; } @@ -2638,7 +2673,7 @@ register_resource_t *endpoint_create_register_resource_opaque(endpoint_t *endpoi if (ret == 0 && olen > 0) { res = endpoint_create_register_resource_str(endpoint, id, auto_obs, dst, olen); } else { - tr_error("endpoint_create_register_resource_opaque - error in Base64 encoding. Error %d or olen is 0", ret); + tr_error("create_register_resource_opaque - Base64 encoding err: %d, olen: %d", ret, olen); res = NULL; } lwm2m_free(dst); @@ -2681,16 +2716,21 @@ registry_callback_t endpoint_get_object_callback(endpoint_t *endpoint, uint16_t return NULL; } -int endpoint_send_notification_int(endpoint_t *endpoint, uint16_t object_id, uint16_t aobs_id, int64_t value) +int endpoint_send_notification_int(endpoint_t *endpoint, registry_path_t *path, uint16_t aobs_id, int64_t value) { int ret = -1; uint16_t token; uint8_t int_buf[REGISTRY_INT64_STRING_MAX_LEN]; size_t len = sizeof(int_buf); - tr_debug("endpoint_send_notification(), object id: %d", object_id); + tr_debug("send_notification(), object id: %d, resource id %d", path->object_id, path->resource_id); assert(endpoint); + assert(path); + + if (!endpoint->registered) { + return NOTIFICATION_STATUS_NOT_REGISTERED; + } if (endpoint->notifier.notifying) { tr_debug("a notification is already pending"); @@ -2704,7 +2744,7 @@ int endpoint_send_notification_int(endpoint_t *endpoint, uint16_t object_id, uin ret = notifier_send_observation_notification(endpoint, endpoint->lifetime, (uint8_t*)&token, sizeof(token), int_buf, len, COAP_CT_TEXT_PLAIN); if (ret == NOTIFICATION_STATUS_SENT) { - endpoint->notifier.last_notified = object_id; + endpoint->notifier.last_notified = *path; endpoint->notifier.notifying = true; } diff --git a/mbed-client/source/lwm2m_est_client.c b/mbed-client/source/lwm2m_est_client.c index 37c3525..50ea043 100644 --- a/mbed-client/source/lwm2m_est_client.c +++ b/mbed-client/source/lwm2m_est_client.c @@ -14,18 +14,22 @@ * limitations under the License. */ -#include -#include -#include -#include #include "est_defs.h" #include "dmc_connect_api.h" #include "lwm2m_req_handler.h" #include "common_functions.h" +#include +#include +#include +#include +#include + + #ifdef MBED_CLIENT_DISABLE_EST_FEATURE #undef MBED_CLIENT_DISABLE_EST_FEATURE // I need the prototypes anyway #endif + #include "lwm2m_est_client.h" /** @@ -127,12 +131,17 @@ static char* make_est_uri(const char *cert_name) char *uri = NULL; size_t uri_len = 0; size_t name_len = strlen(cert_name); - // User certificate - uri_len = snprintf(NULL, 0, EST_SEN_URI_FORMAT, name_len, cert_name); - uri_len++; // For null terminator - uri = calloc(uri_len, sizeof(char)); - if (uri != NULL) { - snprintf(uri, uri_len, EST_SEN_URI_FORMAT, name_len, cert_name); + + // The ".*s" needs a int argument, which likely has smaller max value than size_t, so let's check + // value before casting to avoid theoretical integer wrapping and undefined behavior in snprintf(). + if (name_len <= INT_MAX) { + // User certificate + uri_len = snprintf(NULL, 0, EST_SEN_URI_FORMAT, (int)name_len, cert_name); + uri_len++; // For null terminator + uri = calloc(uri_len, sizeof(char)); + if (uri != NULL) { + (void)snprintf(uri, uri_len, EST_SEN_URI_FORMAT, (int)name_len, cert_name); + } } return uri; } diff --git a/mbed-client/source/lwm2m_interface.c b/mbed-client/source/lwm2m_interface.c index 1eaedda..79ad5d1 100644 --- a/mbed-client/source/lwm2m_interface.c +++ b/mbed-client/source/lwm2m_interface.c @@ -28,7 +28,7 @@ #include "randLIB.h" #ifdef MBED_CLOUD_CLIENT_FOTA_ENABLE -#include "fota/fota_app_ifs.h" +#include "fota/fota_internal_ifs.h" #endif // Needed for PRIu64 on FreeRTOS @@ -94,6 +94,9 @@ static void lwm2m_interface_bootstrap_wait(lwm2m_interface_t *interface); static void lwm2m_interface_client_registered(lwm2m_interface_t *interface); static void lwm2m_interface_registration_updated(lwm2m_interface_t *interface); static void lwm2m_interface_client_unregistered(lwm2m_interface_t *interface); +#if defined (MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP_QUEUE) || defined(MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP) +static void lwm2m_interface_client_ping(lwm2m_interface_t *interface); +#endif static void lwm2m_interface_connection_handler(lwm2m_interface_t *interface, lwm2m_interface_error_t error_code); static void lwm2m_interface_registration_error(lwm2m_interface_t *interface, lwm2m_interface_error_t error_code); static void lwm2m_interface_state_bootstrap_or_register(lwm2m_interface_t *interface, lwm2m_interface_event_data_u *data, bool bootstrap); @@ -296,7 +299,7 @@ enum { static void lwm2m_interface_event_handler(arm_event_t *event) { lwm2m_interface_t *interface = (lwm2m_interface_t*)event->data_ptr; - tr_info("lwm2m_interface_event_handler() event id: %" PRId8 " event_type: %" PRId8, event->event_id, event->event_type); + tr_info("event_handler() event id: %" PRId8 " event_type: %" PRId8, event->event_id, event->event_type); if (event->event_id == ENDPOINT_EVENT_ID) { lwm2m_interface_endpoint_event_handler(event); @@ -305,11 +308,11 @@ static void lwm2m_interface_event_handler(arm_event_t *event) } else if (event->event_id == LWM2M_INTERFACE_EXTERNAL_EVENT) { lwm2m_interface_internal_event(interface, (uint8_t) event->event_data, &interface->external_event_data); } else if (event->event_id == CONNECTION_EVENT_ID) { - lwm2m_interface_connection_event_handler(event->event_type, interface, event->event_data); + lwm2m_interface_connection_event_handler((connection_event_t)event->event_type, interface, event->event_data); } else if (event->event_type == LWM2M_INTERFACE_EVENT_INIT) { // No need to do anything here. } else { - tr_debug("lwm2m_interface_event_handler() unknown event id"); + tr_debug("event_handler() unknown event id"); } } @@ -431,7 +434,7 @@ static void lwm2m_interface_connection_event_handler(connection_event_t event, v case CONNECTION_EVENT_DATA: if (CONNECTION_STATUS_OK != connection_read_data(&interface->connection, &data, &data_len, (uint8_t **) &address.address, &address.length, &address.port)) { - tr_warn("connection_event_handler connection_read_data failed."); + tr_warn("connection_event_handler connection_read_data failed"); return; } address.stack = address.length == 4 ? LWM2M_INTERFACE_NETWORK_STACK_LWIP_IPV4 : LWM2M_INTERFACE_NETWORK_STACK_LWIP_IPV6; @@ -442,14 +445,14 @@ static void lwm2m_interface_connection_event_handler(connection_event_t event, v #ifndef MBED_CONF_MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE if (!lwm2m_interface_is_bootstrap_done(interface)) { - tr_info("connection_event_handler - STATE_BOOTSTRAP_ADDRESS_RESOLVED."); + tr_info("connection_event_handler - STATE_BOOTSTRAP_ADDRESS_RESOLVED"); lwm2m_interface_internal_event(interface, STATE_BOOTSTRAP_ADDRESS_RESOLVED, NULL); } else { - tr_info("connection_event_handler - STATE_REGISTER_ADDRESS_RESOLVED."); + tr_info("connection_event_handler - STATE_REGISTER_ADDRESS_RESOLVED"); lwm2m_interface_internal_event(interface, STATE_REGISTER_ADDRESS_RESOLVED, NULL); } #else - tr_info("connection_event_handler - STATE_REGISTER_ADDRESS_RESOLVED."); + tr_info("connection_event_handler - STATE_REGISTER_ADDRESS_RESOLVED"); lwm2m_interface_internal_event(interface, STATE_REGISTER_ADDRESS_RESOLVED, NULL); #endif // MBED_CONF_MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE break; @@ -459,7 +462,7 @@ static void lwm2m_interface_connection_event_handler(connection_event_t event, v tr_info("CONNECTION_EVENT_DISCONNECTED"); #ifndef MBED_CONF_MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE if (lwm2m_interface_is_bootstrap_waiting(interface)) { - tr_info("connection_event_handler - CONNECTION_EVENT_DISCONNECTED."); + tr_info("connection_event_handler - CONNECTION_EVENT_DISCONNECTED"); lwm2m_interface_bootstrap_done(interface); } else { lwm2m_interface_connection_handler(interface, lwm2m_interface_convert_protoman_to_client_error(error_code)); @@ -492,11 +495,11 @@ static void lwm2m_interface_connection_event_handler(connection_event_t event, v // Resume is used here for restarting the reconnection pattern. // This is only done when reconnection time is on average shorter than it would be without this call. - tr_info("Calling lwm2m_interface_resume to reconnect."); + tr_info("Calling resume to reconnect"); lwm2m_interface_resume(interface); } else { - tr_info("Status change ignored."); + tr_info("Status change ignored"); } break; @@ -548,7 +551,7 @@ void lwm2m_interface_init(lwm2m_interface_t *interface, { MBED_ASSERT(interface != NULL); - tr_debug("lwm2m_interface_init() -IN"); + tr_debug("init() -IN"); #ifdef MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP_QUEUE interface->callback_handler = NULL; #endif // MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP_QUEUE @@ -591,7 +594,7 @@ void lwm2m_interface_init(lwm2m_interface_t *interface, // The server object won't handle any coap requests, but needs to be registered because of the res_cb object_handler_t *handler = endpoint_allocate_object_handler(M2M_SERVER_ID, get_server_object_resources, NULL, NULL); if (!handler) { - tr_error("lwm2m_interface_init() failed to allocate object handler"); + tr_error("init() failed to allocate object handler"); assert(handler); // if this happens it's a fatal error return; } @@ -608,7 +611,7 @@ bool lwm2m_interface_setup(lwm2m_interface_t *interface, { MBED_ASSERT(interface != NULL); - tr_debug("lwm2m_interface_setup()"); + tr_debug("setup()"); if (event_handler_id < 0) { event_handler_id = eventOS_event_handler_create(lwm2m_interface_event_handler, LWM2M_INTERFACE_EVENT_INIT); @@ -617,7 +620,7 @@ bool lwm2m_interface_setup(lwm2m_interface_t *interface, interface->event_handler_id = event_handler_id; if (interface->event_handler_id < 0) { - tr_error("lwm2m_interface_setup() eventOS_event_handler_create failed!"); + tr_error("setup() eventOS_event_handler_create failed"); return false; } @@ -630,7 +633,7 @@ bool lwm2m_interface_setup(lwm2m_interface_t *interface, // Range is from 2 to 10 interface->initial_reconnection_time = randLIB_get_random_in_range(2, 10); - tr_info("lwm2m_interface_init() initial random time %d\n", interface->initial_reconnection_time); + tr_info("init() initial random time %d\n", interface->initial_reconnection_time); interface->reconnection_time = interface->initial_reconnection_time; initialize_storage(); @@ -644,7 +647,7 @@ bool lwm2m_interface_setup(lwm2m_interface_t *interface, life_time, interface->event_handler_id)) { - tr_error("lwm2m_interface_setup() create_endpoint failed!"); + tr_error("setup() create_endpoint failed"); lwm2m_interface_clean(interface); return false; @@ -664,7 +667,7 @@ static void lwm2m_interface_initialize_root_of_trust(void) bool success = false; if (CCS_STATUS_SUCCESS == get_config_parameter(ROOT_OF_TRUST, buffer, max_size, &real_size)) { - tr_info("lwm2m_interface_initialize_root_of_trust() - read RoT from configuration, size %d", (int)real_size); + tr_info("initialize_root_of_trust() - read RoT from configuration, size %d", (int)real_size); success = true; } @@ -677,7 +680,7 @@ static void lwm2m_interface_initialize_root_of_trust(void) #elif MBED_CONF_MBED_CLOUD_CLIENT_USE_INSECURE_ROT #warning "You are using an insecure Root Of Trust implementation, DO NOT USE IN PRODUCTION ENVIRONMENTS. REPLACE WITH A PROPER IMPLEMENTATION BEFORE USE" - tr_warn("lwm2m_interface_initialize_root_of_trust() Using insecure implementation to generate Root of Trust."); + tr_warn("initialize_root_of_trust() Using insecure implementation to generate Root of Trust"); randLIB_get_n_bytes_random(&buffer, max_size); len = max_size; #else @@ -686,10 +689,10 @@ static void lwm2m_interface_initialize_root_of_trust(void) if (len == max_size) { if (CCS_STATUS_SUCCESS != set_config_parameter(ROOT_OF_TRUST, buffer, max_size)) { - tr_error("lwm2m_interface_initialize_root_of_trust() failed to store new RoT"); + tr_error("initialize_root_of_trust() failed to store new RoT"); } } else { - tr_error("lwm2m_interface_initialize_root_of_trust() failed to generate new RoT"); + tr_error("initialize_root_of_trust() failed to generate new RoT"); } } @@ -705,7 +708,7 @@ bool lwm2m_interface_create_endpoint(lwm2m_interface_t *interface, { MBED_ASSERT(interface != NULL); - tr_info("lwm2m_interface_create_endpoint(type %s, lifetime %" PRId32 ", event_handler_id %" PRId8 ")", + tr_info("create_endpoint(type %s, lifetime %" PRId32 ", event_handler_id %" PRId8 ")", type, life_time, event_handler_id); if (endpoint_setup(&interface->endpoint, event_handler_id) != ENDPOINT_STATUS_OK) { @@ -721,7 +724,7 @@ bool lwm2m_interface_create_endpoint(lwm2m_interface_t *interface, bool endpoint_correct = endpoint_set_parameters(&interface->endpoint, type, life_time); if (!endpoint_correct) { - tr_error("lwm2m_interface_create_endpoint() endpoint params are not valid"); + tr_error("create_endpoint() endpoint params are not valid"); } return endpoint_correct; @@ -735,6 +738,12 @@ bool lwm2m_interface_send_update_registration(lwm2m_interface_t *interface) return (ENDPOINT_STATUS_OK == endpoint_update_registration(&interface->endpoint)); } +void lwm2m_interface_set_cid_value(lwm2m_interface_t *interface, const uint8_t *data_ptr, const size_t data_len) +{ + MBED_ASSERT(interface != NULL); + set_cid_value(&interface->connection, data_ptr, data_len); +} + #ifndef MBED_CLOUD_CLIENT_DISABLE_REGISTRY static registry_status_t lwm2m_interface_registration_update_cb(registry_callback_type_t cb_type, const registry_path_t *path, @@ -750,7 +759,7 @@ static registry_status_t lwm2m_interface_registration_update_cb(registry_callbac if (REGISTRY_CALLBACK_EXECUTE == cb_type) { if (ENDPOINT_STATUS_OK != endpoint_update_registration(endpoint)) { - tr_error("lwm2m_interface_registration_update_cb() failed to send update registration"); + tr_error("registration_update_cb() failed to send update registration"); return REGISTRY_STATUS_NO_MEMORY; } @@ -798,14 +807,14 @@ void lwm2m_interface_stop(lwm2m_interface_t *interface) { MBED_ASSERT(interface != NULL); - tr_debug("lwm2m_interface_stop() - IN"); + tr_debug("stop() IN"); lwm2m_interface_stop_timers(interface->event_handler_id); endpoint_stop(&interface->endpoint); connection_close(&interface->connection); - tr_debug("lwm2m_interface_stop() - OUT"); + tr_debug("stop() OUT"); } void lwm2m_interface_pause(lwm2m_interface_t *interface) @@ -815,7 +824,7 @@ void lwm2m_interface_pause(lwm2m_interface_t *interface) return; } - tr_debug("lwm2m_interface_pause() - IN"); + tr_debug("pause() IN"); lwm2m_interface_stop_timers(interface->event_handler_id); @@ -823,7 +832,7 @@ void lwm2m_interface_pause(lwm2m_interface_t *interface) connection_pause(&interface->connection); - tr_debug("lwm2m_interface_pause() - OUT"); + tr_debug("pause() OUT"); } bool lwm2m_interface_resume(lwm2m_interface_t *interface) @@ -831,22 +840,31 @@ bool lwm2m_interface_resume(lwm2m_interface_t *interface) MBED_ASSERT(interface != NULL); #ifdef MBED_CONF_CLOUD_CLIENT_USE_SOFT_PAUSE_RESUME - tr_debug("lwm2m_interface_resume() - soft, IN"); - connection_resume(&interface->connection); -#else - tr_debug("lwm2m_interface_resume() - IN"); + tr_debug("resume() - soft, IN"); + if (connection_resume(&interface->connection)) { + return true; + } else { + tr_debug("Couldn't soft resume, lwm2m_interface_resume() - IN"); + } +#endif + + // Trigger full reconnect lwm2m_interface_internal_event(interface, STATE_IDLE, NULL); interface->reconnecting = false; interface->is_registered = false; interface->retry_timer_expired = false; interface->reconnection_time = interface->initial_reconnection_time; interface->reconnection_state = LWM2M_INTERFACE_RECONNECTION_STATE_WITH_UPDATE; +#ifdef MBED_CONF_CLOUD_CLIENT_USE_SOFT_PAUSE_RESUME + if (!lwm2m_interface_reset_timer(interface, LWM2M_INTERFACE_TIMER_RETRY, interface->event_handler_id, 0)) { +#else if (!lwm2m_interface_reset_timer(interface, LWM2M_INTERFACE_TIMER_RETRY, interface->event_handler_id, interface->reconnection_time * 1000)) { +#endif tr_error("lwm2m_interface_resume() lwm2m_interface_reset_timer Failed"); return false; } -#endif - tr_debug("lwm2m_interface_resume() - OUT"); + + tr_debug("resume() OUT"); return true; @@ -856,7 +874,7 @@ void lwm2m_interface_clean(lwm2m_interface_t *interface) { MBED_ASSERT(interface != NULL); - tr_debug("lwm2m_interface_clean() - IN"); + tr_debug("clean() IN"); lwm2m_interface_stop_timers(interface->event_handler_id); @@ -868,7 +886,7 @@ void lwm2m_interface_clean(lwm2m_interface_t *interface) uninitialize_storage(); - tr_debug("lwm2m_interface_clean() - OUT"); + tr_debug("clean() OUT"); } void lwm2m_interface_bootstrap(lwm2m_interface_t *interface) @@ -876,7 +894,7 @@ void lwm2m_interface_bootstrap(lwm2m_interface_t *interface) MBED_ASSERT(interface != NULL); #ifndef MBED_CONF_MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE - tr_debug("lwm2m_interface_bootstrap() - IN"); + tr_debug("bootstrap() IN"); eventOS_event_timer_cancel(LWM2M_INTERFACE_TIMER_RETRY, interface->event_handler_id); @@ -910,7 +928,7 @@ void lwm2m_interface_bootstrap(lwm2m_interface_t *interface) lwm2m_interface_set_error_description(interface, ERROR_REASON_2); lwm2m_interface_notify_observer(interface, LWM2M_INTERFACE_OBSERVER_EVENT_ERROR, LWM2M_INTERFACE_ERROR_NOT_ALLOWED); } - tr_debug("lwm2m_interface_bootstrap() - OUT"); + tr_debug("bootstrap() OUT"); #else lwm2m_interface_set_error_description(interface, ERROR_REASON_3); lwm2m_interface_notify_observer(interface, LWM2M_INTERFACE_OBSERVER_EVENT_ERROR, LWM2M_INTERFACE_ERROR_NOT_ALLOWED); @@ -928,7 +946,7 @@ void lwm2m_interface_register_object(lwm2m_interface_t *interface, uint16_t secu { MBED_ASSERT(interface != NULL); - tr_debug("lwm2m_interface_register_object - IN"); + tr_debug("register_object IN"); // TODO: check that the security object instance exists if(false) { lwm2m_interface_set_error_description(interface, ERROR_REASON_4); @@ -966,14 +984,14 @@ void lwm2m_interface_register_object(lwm2m_interface_t *interface, uint16_t secu lwm2m_interface_set_error_description(interface, ERROR_REASON_5); lwm2m_interface_notify_observer(interface, LWM2M_INTERFACE_OBSERVER_EVENT_ERROR, LWM2M_INTERFACE_ERROR_NOT_ALLOWED); } - tr_debug("lwm2m_interface_register_object - OUT"); + tr_debug("register_object OUT"); } void lwm2m_interface_update_registration(lwm2m_interface_t *interface, uint16_t security_instance, const uint32_t lifetime) { MBED_ASSERT(interface != NULL); - tr_debug("lwm2m_interface_update_registration()"); + tr_debug("update_registration()"); lwm2m_interface_event_data_u data; data.update_register_data.security_instance = security_instance; data.update_register_data.lifetime = lifetime; @@ -984,7 +1002,7 @@ void lwm2m_interface_unregister_object(lwm2m_interface_t *interface) { MBED_ASSERT(interface != NULL); - tr_debug("lwm2m_interface_unregister_object - current state %d", interface->current_state); + tr_debug("unregister_object - current state %d", interface->current_state); // Transition to a new state based upon // the current state of the state machine BEGIN_TRANSITION_MAP // - Current State - @@ -1013,7 +1031,7 @@ void lwm2m_interface_unregister_object(lwm2m_interface_t *interface) lwm2m_interface_set_error_description(interface, ERROR_REASON_6); lwm2m_interface_notify_observer(interface, LWM2M_INTERFACE_OBSERVER_EVENT_ERROR, LWM2M_INTERFACE_ERROR_NOT_ALLOWED); } - tr_debug("lwm2m_interface_unregister_object - OUT"); + tr_debug("unregister_object OUT"); } #ifdef MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP_QUEUE @@ -1034,7 +1052,7 @@ void lwm2m_interface_set_entropy_callback(lwm2m_interface_t *interface, entropy_ MBED_ASSERT(interface != NULL); if (connection_set_entropy_callback(&interface->connection, callback) < 0) { - tr_error("lwm2m_interface_set_entropy_callback() failed to add entropy source"); + tr_error("set_entropy_callback() failed to add entropy source"); } } #endif // PROTOMAN_OFFLOAD_TLS @@ -1048,12 +1066,12 @@ void lwm2m_interface_set_platform_network_handler(lwm2m_interface_t *interface, static void lwm2m_interface_client_registered(lwm2m_interface_t *interface) { - tr_info("lwm2m_interface_client_registered"); + tr_info("client_registered"); interface->is_registered = true; lwm2m_interface_internal_event(interface, STATE_REGISTERED, NULL); #if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) && !defined(MBED_CLOUD_FOTA_CUSTOM_RESUME) - fota_app_resume(); + fota_internal_resume(); #endif // Inform client is registered. @@ -1064,11 +1082,11 @@ static void lwm2m_interface_client_registered(lwm2m_interface_t *interface) static void lwm2m_interface_registration_updated(lwm2m_interface_t *interface) { - tr_info("lwm2m_interface_registration_updated"); + tr_info("registration_updated"); interface->is_registered = true; lwm2m_interface_internal_event(interface, STATE_REGISTERED, NULL); #if defined(MBED_CLOUD_CLIENT_FOTA_ENABLE) && !defined(MBED_CLOUD_FOTA_CUSTOM_RESUME) - fota_app_resume(); + fota_internal_resume(); #endif // TODO: use server id as parameter? lwm2m_interface_notify_observer(interface, LWM2M_INTERFACE_OBSERVER_EVENT_REGISTRATION_UPDATED, LWM2M_INTERFACE_ERROR_NONE); @@ -1076,7 +1094,7 @@ static void lwm2m_interface_registration_updated(lwm2m_interface_t *interface) static void lwm2m_interface_registration_error(lwm2m_interface_t *interface, lwm2m_interface_error_t error_code) { - tr_error("lwm2m_interface_registration_error code [%d]", error_code); + tr_error("registration_error code [%d]", error_code); interface->is_registered = false; endpoint_stop(&interface->endpoint); @@ -1098,11 +1116,11 @@ else if (error_code == LWM2M_INTERFACE_ERROR_INVALID_PARAMETERS) { else if (interface->reconnection_state == LWM2M_INTERFACE_RECONNECTION_STATE_WITH_UPDATE) { // If update registration failed again try full registration right away - tr_info("Update registration retry failed, try full registration right away."); + tr_info("Update registration retry failed, try full registration right away"); interface->reconnection_state = LWM2M_INTERFACE_RECONNECTION_STATE_FULL_REGISTRATION; if(ENDPOINT_STATUS_OK != endpoint_register(&interface->endpoint)) { // If resource creation fails then inform error to application - tr_error("lwm2m_interface_registration_error endpoint_register: LWM2M_INTERFACE_ERROR_NOT_ALLOWED"); + tr_error("registration_error endpoint_register: LWM2M_INTERFACE_ERROR_NOT_ALLOWED"); error_code = LWM2M_INTERFACE_ERROR_NOT_ALLOWED; } else { return; @@ -1114,20 +1132,27 @@ else if (error_code == LWM2M_INTERFACE_ERROR_INVALID_PARAMETERS) { static void lwm2m_interface_client_unregistered(lwm2m_interface_t *interface) { - tr_info("lwm2m_interface_client_unregistered()"); + tr_info("client_unregistered()"); interface->unregister_ongoing = false; interface->is_registered = false; lwm2m_interface_internal_event(interface, STATE_UNREGISTERED, NULL); lwm2m_interface_notify_observer(interface, LWM2M_INTERFACE_OBSERVER_EVENT_OBJECT_UNREGISTERED, LWM2M_INTERFACE_ERROR_NONE); } +#if defined (MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP_QUEUE) || defined(MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP) +static void lwm2m_interface_client_ping(lwm2m_interface_t *interface) +{ + interface->reconnection_state = LWM2M_INTERFACE_RECONNECTION_STATE_CLIENT_PING; +} +#endif + #ifndef MBED_CONF_MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE static void lwm2m_interface_bootstrap_done(lwm2m_interface_t *interface) { registry_path_t path; bool bootstrap; - tr_info("lwm2m_interface_bootstrap_done"); + tr_info("bootstrap_done"); registry_set_path(&path, M2M_SECURITY_ID, 0, SECURITY_BOOTSTRAP_SERVER, 0, REGISTRY_PATH_RESOURCE); if (REGISTRY_STATUS_OK != registry_get_value_boolean(&interface->endpoint.registry, &path, &bootstrap)) { @@ -1165,14 +1190,14 @@ static void lwm2m_interface_bootstrap_done(lwm2m_interface_t *interface) static void lwm2m_interface_bootstrap_wait(lwm2m_interface_t *interface) { - tr_info("lwm2m_interface_bootstrap_wait"); + tr_info("bootstrap_wait"); lwm2m_interface_internal_event(interface, STATE_BOOTSTRAP_WAIT, NULL); } static void lwm2m_interface_bootstrap_error(lwm2m_interface_t *interface, const char *reason) { - tr_error("lwm2m_interface_bootstrap_error(%s)", reason); + tr_error("bootstrap_error(%s)", reason); interface->bootstrapped = false; eventOS_event_timer_cancel(LWM2M_INTERFACE_TIMER_BOOTSTRAP_FLOW, interface->event_handler_id); @@ -1193,7 +1218,7 @@ static void lwm2m_interface_bootstrap_error(lwm2m_interface_t *interface, const interface->event_handler_id, interface->reconnection_time * 1000); - tr_info("lwm2m_interface_bootstrap_error - reconnecting in %" PRIu32 "(s)", interface->reconnection_time); + tr_info("bootstrap_error - reconnecting in %" PRIu32 "(s)", interface->reconnection_time); interface->reconnection_time = interface->reconnection_time * RECONNECT_INCREMENT_FACTOR; if(interface->reconnection_time >= MAX_RECONNECT_TIMEOUT) { interface->reconnection_time = MAX_RECONNECT_TIMEOUT; @@ -1207,7 +1232,7 @@ static void lwm2m_interface_data_available(lwm2m_interface_t *interface, uint16_t data_size, const lwm2m_interface_socketaddress_t *address) { - tr_debug("lwm2m_interface_data_available"); + tr_debug("data_available"); lwm2m_interface_event_data_u event; event.received_data.data = data; event.received_data.size = data_size; @@ -1221,22 +1246,24 @@ static void lwm2m_interface_connection_handler(lwm2m_interface_t *interface, lwm endpoint_stop(&interface->endpoint); connection_close(&interface->connection); if (interface->current_state == STATE_BOOTSTRAP_WAIT) { - tr_debug("lwm2m_interface_connection_handler() bootstrap completed"); + tr_debug("connection_handler() bootstrap completed"); // Bootstrap completed once PEER CLOSE notify received from the server. return; } - tr_error("lwm2m_interface_connection_handler: (%d), reconnecting (%d), reconnection_state (%d)", + tr_error("connection_handler: (%d), reconnecting (%d), reconnection_state (%d)", error_code, interface->reconnecting, (int)interface->reconnection_state); -#ifdef MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP_QUEUE - // Ignore errors while client is sleeping - if (lwm2m_interface_queue_mode(interface)) { - if (interface->callback_handler && interface->queue_mode_timer_ongoing && error_code != LWM2M_INTERFACE_ERROR_TIMEOUT) { - tr_info("lwm2m_interface_connection_handler - Queue Mode - don't try to reconnect while in QueueMode"); - return; - } - eventOS_event_timer_cancel(LWM2M_INTERFACE_TIMER_QUEUE_SLEEP, interface->event_handler_id); +#if defined (MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP_QUEUE) || defined(MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP) + if ((interface->binding_mode == BINDING_MODE_U || interface->binding_mode == BINDING_MODE_Q) && + error_code == LWM2M_INTERFACE_ERROR_NETWORK_ERROR && is_connection_id_available()) { + // Check if we can ping LWm2m server (send it immediately and lets have timeout of 60 seconds) + // if(server responds) + // CID has expired, delete CID do handshake + // else + // Network issue, do not delete CID but continue reconnection logic (99%) + lwm2m_interface_client_ping(interface); + return; } #endif //MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP_QUEUE @@ -1251,7 +1278,7 @@ static void lwm2m_interface_connection_handler(lwm2m_interface_t *interface, lwm if (interface->reconnection_state == LWM2M_INTERFACE_RECONNECTION_STATE_FULL_REGISTRATION) { interface->reconnection_state = LWM2M_INTERFACE_RECONNECTION_STATE_NONE; } else if (interface->reconnection_state == LWM2M_INTERFACE_RECONNECTION_STATE_UNREGISTRATION) { - tr_info("lwm2m_interface_connection_handler - unreg failed again just call client_unregistered"); + tr_info("connection_handler - unreg failed again just call client_unregistered"); lwm2m_interface_client_unregistered(interface); return; } @@ -1263,7 +1290,7 @@ static void lwm2m_interface_connection_handler(lwm2m_interface_t *interface, lwm interface->event_handler_id, interface->reconnection_time * 1000); - tr_info("lwm2m_interface_connection_handler - reconnecting in %" PRIu32 "(s)", interface->reconnection_time); + tr_info("connection_handler - reconnecting in %" PRIu32 "(s)", interface->reconnection_time); interface->reconnection_time = interface->reconnection_time * RECONNECT_INCREMENT_FACTOR; if (interface->reconnection_time >= MAX_RECONNECT_TIMEOUT) { interface->reconnection_time = MAX_RECONNECT_TIMEOUT; @@ -1281,7 +1308,7 @@ void lwm2m_interface_data_sent(lwm2m_interface_t *interface) { MBED_ASSERT(interface != NULL); - tr_debug("lwm2m_interface_data_sent()"); + tr_debug("data_sent()"); #ifdef MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP_QUEUE if (lwm2m_interface_queue_mode(interface) && interface->callback_handler) { @@ -1316,7 +1343,7 @@ static void lwm2m_interface_timer_expired(lwm2m_interface_t *interface, lwm2m_in else if (LWM2M_INTERFACE_TIMER_RETRY == type) { if (interface->reconnecting) { - tr_debug("lwm2m_interface_timer_expired() - retry"); + tr_debug("timer_expired() - retry"); interface->reconnect_attempt++; interface->retry_timer_expired = true; } @@ -1331,14 +1358,14 @@ static void lwm2m_interface_timer_expired(lwm2m_interface_t *interface, lwm2m_in } else if (LWM2M_INTERFACE_TIMER_BOOTSTRAP_FLOW == type) { #ifndef MBED_CONF_MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE - tr_debug("lwm2m_interface_timer_expired() - bootstrap"); + tr_debug("timer_expired() - bootstrap"); interface->bootstrapped = false; eventOS_event_timer_cancel(LWM2M_INTERFACE_TIMER_BOOTSTRAP_FLOW, interface->event_handler_id); lwm2m_interface_bootstrap_error(interface, ERROR_REASON_23); #endif //MBED_CONF_MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE } else if (LWM2M_INTERFACE_TIMER_REGISTRATION_FLOW == type) { - tr_debug("lwm2m_interface_timer_expired() - register"); + tr_debug("timer_expired() - register"); lwm2m_interface_registration_error(interface, LWM2M_INTERFACE_ERROR_TIMEOUT); } #ifdef MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP_QUEUE @@ -1349,14 +1376,14 @@ static void lwm2m_interface_timer_expired(lwm2m_interface_t *interface, lwm2m_in #endif if (queue_size > 0 || !interface->is_registered || interface->reconnecting || !interface->bootstrapped) { - tr_debug("lwm2m_interface_timer_expired() - RESEND queue not empty, or not registered, or reconnection or bootstrap ongoing, continue sleep timer"); + tr_debug("timer_expired() - RESEND queue not empty, or not registered, or reconnection or bootstrap ongoing, continue sleep timer"); lwm2m_interface_reset_timer(interface, LWM2M_INTERFACE_TIMER_QUEUE_SLEEP, interface->event_handler_id, MBED_CLIENT_RECONNECTION_COUNT * MBED_CLIENT_RECONNECTION_INTERVAL * 1000); } else { - tr_debug("lwm2m_interface_timer_expired() - sleep"); + tr_debug("timer_expired() - sleep"); //TODO: Check if more actions needed here? interface->queue_mode_timer_ongoing = true; if(interface->callback_handler) { @@ -1370,7 +1397,7 @@ static void lwm2m_interface_timer_expired(lwm2m_interface_t *interface, lwm2m_in // state machine sits here. static void lwm2m_interface_state_idle(lwm2m_interface_t *interface, lwm2m_interface_event_data_u *data) { - tr_debug("lwm2m_interface_state_idle"); + tr_debug("state_idle"); interface->unregister_ongoing = false; //TODO Check if this set should be done here? eventOS_event_timer_cancel(LWM2M_INTERFACE_TIMER_QUEUE_SLEEP, interface->event_handler_id); } @@ -1398,16 +1425,25 @@ static void lwm2m_interface_state_bootstrap_or_register(lwm2m_interface_t *inter const void *ca_certificate = NULL; size_t ca_certificate_size = 0; interface->is_registered = false; - tr_debug("lwm2m_interface_state_bootstrap_or_register() bootstrap: %d", bootstrap); +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + bool ignore_session_resume = interface->reconnection_state == LWM2M_INTERFACE_RECONNECTION_STATE_CLIENT_PING; + if (ignore_session_resume) { + if (interface->is_registered) { + interface->reconnection_state = LWM2M_INTERFACE_RECONNECTION_STATE_WITH_UPDATE; + } else { + interface->reconnection_state = LWM2M_INTERFACE_RECONNECTION_STATE_NONE; + } + } +#endif + + tr_debug("state_bootstrap_or_register() in %s sequence", (bootstrap ? "bootstrap" : "register")); #ifndef MBED_CONF_MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE if (bootstrap) { - tr_debug("lwm2m_interface_state_bootstrap_or_register() in bootstrap sequence"); interface->bootstrapped = false; } else #endif { - tr_debug("lwm2m_interface_state_bootstrap_or_register() in register sequence"); interface->listen_port = 0; #ifndef MBED_CLOUD_CLIENT_DISABLE_REGISTRY storage_set_credentials(&interface->endpoint.registry); @@ -1418,7 +1454,7 @@ static void lwm2m_interface_state_bootstrap_or_register(lwm2m_interface_t *inter *full_server_address_end = '\0'; if (strlen(full_server_address)) { - tr_debug("lwm2m_interface_state_bootstrap_or_register - server_address %s", full_server_address); + tr_debug("state_bootstrap_or_register - server_address %s", full_server_address); #ifndef MBED_CLIENT_ENABLE_MINIMAL_SERVER_URL_PROCESSING if (strstr(full_server_address, COAP)) { @@ -1432,6 +1468,10 @@ static void lwm2m_interface_state_bootstrap_or_register(lwm2m_interface_t *inter // Don't strip protocol string server_address = full_server_address; #endif + if (server_address == NULL) { + tr_error("No valid server address available"); + goto finish; + } lwm2m_interface_process_address(interface, server_address); uint8_t connection_mode = CONNECTION_MODE_UDP; @@ -1441,7 +1481,7 @@ static void lwm2m_interface_state_bootstrap_or_register(lwm2m_interface_t *inter } #endif - tr_debug("lwm2m_interface_state_bootstrap_or_register - IP address %s , Port %d", interface->server_ip_address, interface->server_port); + tr_debug("state_bootstrap_or_register - IP address %s , Port %d", interface->server_ip_address, interface->server_port); // If bind and resolving server address succeed then proceed else // return error to the application and go to Idle state. if(strlen(interface->server_ip_address) != 0) { @@ -1503,6 +1543,9 @@ static void lwm2m_interface_state_bootstrap_or_register(lwm2m_interface_t *inter certificate_size, key, key_size #if defined(PROTOMAN_USE_SSL_SESSION_RESUME) || defined(PROTOMAN_OFFLOAD_TLS) , bootstrap +#endif +#ifdef PROTOMAN_USE_SSL_SESSION_RESUME + , ignore_session_resume #endif )) { error = LWM2M_INTERFACE_ERROR_NONE; @@ -1514,9 +1557,10 @@ static void lwm2m_interface_state_bootstrap_or_register(lwm2m_interface_t *inter } } +finish: // XXX: still needed? fast fail could be easier to pull off if this were a function. if (error != LWM2M_INTERFACE_ERROR_NONE) { - tr_error("lwm2m_interface_state_bootstrap_or_register - set error as LWM2M_INTERFACE_INVALID_PARAMETERS"); + tr_error("state_bootstrap_or_register : INVALID_PARAMETERS"); lwm2m_interface_internal_event(interface, STATE_IDLE, NULL); lwm2m_interface_set_error_description(interface, ERROR_REASON_11); lwm2m_interface_notify_observer(interface, LWM2M_INTERFACE_OBSERVER_EVENT_ERROR, error); @@ -1529,13 +1573,13 @@ static void lwm2m_interface_state_bootstrap_address_resolved(lwm2m_interface_t * { (void)data; - tr_debug("lwm2m_interface_state_bootstrap_address_resolved()"); + tr_debug("state_bootstrap_address_resolved()"); if(ENDPOINT_STATUS_OK == endpoint_bootstrap(&interface->endpoint)) { lwm2m_interface_internal_event(interface, STATE_BOOTSTRAP_RESOURCE_CREATED, NULL); } else{ // If resource creation fails then inform error to application - tr_error("lwm2m_interface_state_bootstrap_address_resolved : LWM2M_INTERFACE_ERROR_NOT_ALLOWED"); + tr_error("state_bootstrap_address_resolved : ERROR_NOT_ALLOWED"); lwm2m_interface_internal_event(interface, STATE_IDLE, NULL); lwm2m_interface_set_error_description(interface, ERROR_REASON_12); lwm2m_interface_notify_observer(interface, LWM2M_INTERFACE_OBSERVER_EVENT_ERROR, LWM2M_INTERFACE_ERROR_NOT_ALLOWED); @@ -1544,19 +1588,19 @@ static void lwm2m_interface_state_bootstrap_address_resolved(lwm2m_interface_t * static void lwm2m_interface_state_bootstrap_resource_created(lwm2m_interface_t *interface, lwm2m_interface_event_data_u *data) { - tr_debug("lwm2m_interface_state_bootstrap_resource_created"); + tr_debug("state_bootstrap_resource_created"); } static void lwm2m_interface_state_bootstrapped(lwm2m_interface_t *interface, lwm2m_interface_event_data_u *data) { - tr_debug("lwm2m_interface_state_bootstrapped"); + tr_debug("state_bootstrapped"); connection_close(&interface->connection); } #endif //MBED_CONF_MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE static void lwm2m_interface_state_register(lwm2m_interface_t *interface, lwm2m_interface_event_data_u *data) { - tr_debug("lwm2m_interface_state_register"); + tr_debug("state_register"); lwm2m_interface_state_bootstrap_or_register(interface, data, false); } @@ -1630,28 +1674,25 @@ static void lwm2m_interface_state_register_address_resolved(lwm2m_interface_t *i (void)data; - tr_debug("lwm2m_interface_state_register_address_resolved"); + tr_debug("state_register_address_resolved"); connection_get_server_address(&interface->connection, &address, &address_len, &port); - if(address_len == 4) { - tr_info("lwm2m_interface_state_register_address_resolved : IPv4 address"); - - } else { - tr_info("lwm2m_interface_state_register_address_resolved : IPv6 address"); - } + tr_info("state_register_address_resolved : IPv%d address", ((address_len == 4) ? 4 : 6)); switch (interface->reconnection_state) { case LWM2M_INTERFACE_RECONNECTION_STATE_NONE: case LWM2M_INTERFACE_RECONNECTION_STATE_FULL_REGISTRATION: if(ENDPOINT_STATUS_OK != endpoint_register(&interface->endpoint)) { // If resource creation fails then inform error to application - tr_error("lwm2m_interface_state_register_address_resolved : LWM2M_INTERFACE_ERROR_NOT_ALLOWED"); + tr_error("state_register_address_resolved : LWM2M_INTERFACE_ERROR_NOT_ALLOWED"); lwm2m_interface_internal_event(interface, STATE_IDLE, NULL); lwm2m_interface_set_error_description(interface, ERROR_REASON_25); lwm2m_interface_notify_observer(interface, LWM2M_INTERFACE_OBSERVER_EVENT_ERROR, LWM2M_INTERFACE_ERROR_NOT_ALLOWED); } break; + case LWM2M_INTERFACE_RECONNECTION_STATE_CLIENT_PING: + break; case LWM2M_INTERFACE_RECONNECTION_STATE_WITH_UPDATE: // Start registration update in case it is reconnection logic because of network issue. lwm2m_interface_internal_event(interface, STATE_UPDATE_REGISTRATION, NULL); @@ -1668,7 +1709,7 @@ static void lwm2m_interface_state_registered(lwm2m_interface_t *interface, lwm2m uint32_t lifetime = MINIMUM_REGISTRATION_TIME; uint16_t registration_percent = REREGISTRATION_INTERVAL; - tr_info("lwm2m_interface_state_registered"); + tr_info("state_registered"); eventOS_event_timer_cancel(LWM2M_INTERFACE_TIMER_REGISTRATION_FLOW, interface->event_handler_id); @@ -1702,7 +1743,7 @@ static void lwm2m_interface_state_registered(lwm2m_interface_t *interface, lwm2m // Find the string following the second "/" character in the endpoint location-path // The format should be "rd//" - while ((iep = strstr(iep, "/"))) { + while ((iep = strstr(iep, "/")) != NULL) { iep++; if (++count == 2) { break; @@ -1711,15 +1752,15 @@ static void lwm2m_interface_state_registered(lwm2m_interface_t *interface, lwm2m if (iep && strlen(iep) > 0) { if (!storage_set_internal_endpoint_name(iep)) { - tr_error("lwm2m_interface_state_registered() error storing internal endpoint name"); + tr_error("state_registered() error storing internal endpoint name"); } } else { - tr_error("lwm2m_interface_state_registered() failed to parse internal endpoint name"); + tr_error("state_registered() failed to parse internal endpoint name"); } #endif } else { - tr_error("lwm2m_interface_state_registered() error reading endpoint lifetime"); + tr_error("state_registered() error reading endpoint lifetime"); } lwm2m_interface_reset_timer(interface, @@ -1740,7 +1781,7 @@ static void lwm2m_interface_state_registered(lwm2m_interface_t *interface, lwm2m static void lwm2m_interface_state_update_registration(lwm2m_interface_t *interface, lwm2m_interface_event_data_u *data) { - tr_debug("lwm2m_interface_state_update_registration"); + tr_debug("state_update_registration"); #ifdef MBED_CLOUD_CLIENT_TRANSPORT_MODE_UDP_QUEUE // Set to false to allow reconnection to work. interface->queue_mode_timer_ongoing = false; @@ -1753,12 +1794,12 @@ static void lwm2m_interface_state_update_registration(lwm2m_interface_t *interfa endpoint_set_lifetime(&interface->endpoint, new_lifetime); } else { - tr_debug("lwm2m_interface_state_update_registration() no event data found"); + tr_debug("state_update_registration no event data found"); } if (ENDPOINT_STATUS_OK != endpoint_update_registration(&interface->endpoint)) { - tr_error("lwm2m_interface_state_update_registration : LWM2M_INTERFACE_ERROR_MEMORY_FAIL"); + tr_error("state_update_registration : LWM2M_INTERFACE_ERROR_MEMORY_FAIL"); lwm2m_interface_internal_event(interface, STATE_IDLE, NULL); lwm2m_interface_set_error_description(interface, ERROR_REASON_25); lwm2m_interface_notify_observer(interface, LWM2M_INTERFACE_OBSERVER_EVENT_ERROR, LWM2M_INTERFACE_ERROR_NOT_ALLOWED); @@ -1768,14 +1809,14 @@ static void lwm2m_interface_state_update_registration(lwm2m_interface_t *interfa static void lwm2m_interface_state_unregister(lwm2m_interface_t *interface, lwm2m_interface_event_data_u *data) { - tr_debug("lwm2m_interface_state_unregister"); + tr_debug("state_unregister"); if (interface->unregister_ongoing) { - tr_warn("lwm2m_interface_state_unregister : _unregister_ongoing"); + tr_warn("state_unregister : _unregister_ongoing"); return; } lwm2m_interface_internal_event(interface, STATE_SENDING_COAP_DATA, NULL); if (ENDPOINT_STATUS_OK != endpoint_unregister(&interface->endpoint)) { - tr_error("lwm2m_interface_state_unregister : LWM2M_INTERFACE_ERROR_NOT_ALLOWED"); + tr_error("state_unregister : LWM2M_INTERFACE_ERROR_NOT_ALLOWED"); lwm2m_interface_internal_event(interface, STATE_IDLE, NULL); lwm2m_interface_set_error_description(interface, ERROR_REASON_5); lwm2m_interface_notify_observer(interface, LWM2M_INTERFACE_OBSERVER_EVENT_ERROR, LWM2M_INTERFACE_ERROR_NOT_ALLOWED); @@ -1786,7 +1827,7 @@ static void lwm2m_interface_state_unregister(lwm2m_interface_t *interface, lwm2m static void lwm2m_interface_state_unregistered(lwm2m_interface_t *interface, lwm2m_interface_event_data_u *data) { - tr_info("lwm2m_interface_state_unregistered"); + tr_info("state_unregistered"); interface->reconnection_time = interface->initial_reconnection_time; interface->reconnection_state = LWM2M_INTERFACE_RECONNECTION_STATE_NONE; connection_close(&interface->connection); @@ -1797,20 +1838,20 @@ static void lwm2m_interface_state_unregistered(lwm2m_interface_t *interface, lwm static void lwm2m_interface_state_sending_coap_data(lwm2m_interface_t *interface, lwm2m_interface_event_data_u *data) { //NOTE: This function is no-longer used. - tr_debug("lwm2m_interface_state_sending_coap_data"); + tr_debug("state_sending_coap_data"); lwm2m_interface_internal_event(interface, STATE_WAITING, NULL); } static void lwm2m_interface_state_coap_data_sent(lwm2m_interface_t *interface, lwm2m_interface_event_data_u *data) { - tr_debug("lwm2m_interface_state_coap_data_sent"); + tr_debug("state_coap_data_sent"); lwm2m_interface_internal_event(interface, STATE_WAITING, NULL); } static void lwm2m_interface_state_coap_data_received(lwm2m_interface_t *interface, lwm2m_interface_event_data_u *data) { - tr_debug("lwm2m_interface_state_coap_data_received"); + tr_debug("state_coap_data_received"); if(data) { lwm2m_interface_received_data_t event = data->received_data; @@ -1834,7 +1875,7 @@ static void lwm2m_interface_state_coap_data_received(lwm2m_interface_t *interfac // Process received data lwm2m_interface_internal_event(interface, STATE_PROCESSING_COAP_DATA, NULL); if(ENDPOINT_STATUS_OK != endpoint_process_coap(&interface->endpoint, event.data, event.size, &address)) { - tr_error("lwm2m_interface_state_coap_data_received : LWM2M_INTERFACE_ERROR_RESPONSE_PARSE_FAILED"); + tr_error("state_coap_data_received : LWM2M_INTERFACE_ERROR_RESPONSE_PARSE_FAILED"); lwm2m_interface_set_error_description(interface, ERROR_REASON_17); lwm2m_interface_notify_observer(interface, LWM2M_INTERFACE_OBSERVER_EVENT_ERROR, LWM2M_INTERFACE_ERROR_RESPONSE_PARSE_FAILED); @@ -1851,19 +1892,19 @@ static void lwm2m_interface_state_coap_data_received(lwm2m_interface_t *interfac static void lwm2m_interface_state_processing_coap_data(lwm2m_interface_t *interface, lwm2m_interface_event_data_u *data) { - tr_debug("lwm2m_interface_state_processing_coap_data"); + tr_debug("state_processing_coap_data"); lwm2m_interface_internal_event(interface, STATE_WAITING, NULL); } static void lwm2m_interface_state_coap_data_processed(lwm2m_interface_t *interface, lwm2m_interface_event_data_u *data) { - tr_debug("lwm2m_interface_state_coap_data_processed"); + tr_debug("state_coap_data_processed"); lwm2m_interface_internal_event(interface, STATE_WAITING, NULL); } static void lwm2m_interface_state_waiting(lwm2m_interface_t *interface, lwm2m_interface_event_data_u *data) { - tr_debug("lwm2m_interface_state_waiting"); + tr_debug("state_waiting"); } // generates an external event. called once per external event @@ -1872,14 +1913,14 @@ static void lwm2m_interface_external_event(lwm2m_interface_t *interface, uint8_t new_state, lwm2m_interface_event_data_u* p_data) { - tr_debug("lwm2m_interface_external_event : new state %d", new_state); + tr_debug("external_event : new state %d", new_state); // if we are supposed to ignore this event if (new_state == EVENT_IGNORED) { - tr_debug("lwm2m_interface_external_event : new state is EVENT_IGNORED"); + tr_debug("external_event : new state is EVENT_IGNORED"); interface->event_ignored = true; } else { - tr_debug("lwm2m_interface_external_event : handle new state"); + tr_debug("external_event : handle new state"); eventOS_cancel(&interface->external_event); @@ -1905,7 +1946,7 @@ static void lwm2m_interface_internal_event(lwm2m_interface_t *interface, uint8_t new_state, lwm2m_interface_event_data_u* p_data) { - tr_debug("lwm2m_interface_internal_event : new state %d", new_state); + tr_debug("internal_event : new state %d", new_state); interface->event_data = p_data; interface->event_generated = true; interface->current_state = new_state; @@ -1915,7 +1956,7 @@ static void lwm2m_interface_internal_event(lwm2m_interface_t *interface, // the state engine executes the state machine states static void lwm2m_interface_state_engine (lwm2m_interface_t *interface) { - tr_debug("lwm2m_interface_state_engine"); + tr_debug("state_engine"); lwm2m_interface_event_data_u* p_data_temp = NULL; // while events are being generated keep executing states @@ -2002,7 +2043,7 @@ static void lwm2m_interface_state_function(lwm2m_interface_t *interface, lwm2m_i } void lwm2m_interface_start_register_update(lwm2m_interface_t *interface, lwm2m_interface_event_data_u *data) { - tr_debug("lwm2m_interface_start_register_update()"); + tr_debug("start_register_update()"); MBED_ASSERT(interface != NULL); @@ -2170,7 +2211,7 @@ void lwm2m_interface_get_data_request(lwm2m_interface_t *interface, MBED_ASSERT(interface != NULL); if (interface->unregister_ongoing) { - tr_error("lwm2m_interface_get_data_request - unregister_ongoing!"); + tr_error("get_data_request - unregister_ongoing"); error_cb(ERROR_NOT_REGISTERED, context); return; } @@ -2192,7 +2233,7 @@ bool lwm2m_interface_set_uri_query_parameters(lwm2m_interface_t *interface, cons static void lwm2m_interface_notify_observer(lwm2m_interface_t *interface, lwm2m_interface_observer_event_t event_id, lwm2m_interface_error_t event_type) { if (interface->observer_id < 0 || !interface->observer) { - tr_error("lwm2m_interface_notify_observer() no observer set"); + tr_error("notify_observer() no observer set"); return; } @@ -2224,7 +2265,7 @@ static bool lwm2m_interface_reset_timer(lwm2m_interface_t *interface, int8_t tim eventOS_event_timer_cancel(timer, tasklet_id); if (!eventOS_event_timer_request_in(&event, eventOS_event_timer_ms_to_ticks(time_ms))) { - tr_error("lwm2m_interface_reset_timer() call to eventOS_event_timer_request_in() failed!"); + tr_error("reset_timer() call to eventOS_event_timer_request_in() failed"); return false; } @@ -2232,7 +2273,7 @@ static bool lwm2m_interface_reset_timer(lwm2m_interface_t *interface, int8_t tim } static lwm2m_interface_error_t lwm2m_interface_convert_protoman_to_client_error(int protoman_error) { - tr_error("lwm2m_interface_convert_protoman_to_client_error - protoman error %d", protoman_error); + tr_error("convert_protoman_to_client_error - protoman error %d", protoman_error); lwm2m_interface_error_t error = LWM2M_INTERFACE_ERROR_NETWORK_ERROR; switch(protoman_error) { case PROTOMAN_ERR_DNS_RESOLVING_FAILED: diff --git a/mbed-client/source/lwm2m_notifier.c b/mbed-client/source/lwm2m_notifier.c index 94921f5..9442722 100644 --- a/mbed-client/source/lwm2m_notifier.c +++ b/mbed-client/source/lwm2m_notifier.c @@ -119,7 +119,7 @@ static void notifier_event_handler(arm_event_t *event) void notifier_init(notifier_t *notifier, endpoint_t *endpoint) { - tr_info("notifier_init"); + tr_info("init"); notifier->endpoint = endpoint; @@ -143,7 +143,7 @@ bool notifier_setup(notifier_t *notifier) if (notifier_event_handler_id < 0) { notifier_event_handler_id = eventOS_event_handler_create(¬ifier_event_handler, NOTIFIER_EVENT_INIT); if (notifier_event_handler_id < 0) { - tr_error("notifier_setup() eventOS_event_handler_create failed!"); + tr_error("setup() eventOS_event_handler_create failed"); assert(0); } } @@ -193,7 +193,6 @@ void notifier_continue(notifier_t *notifier) static uint32_t notifier_get_notify_option_number(notifier_t *notifier) { - notifier->notify_option_number++; if (notifier->notify_option_number > NOTIFIER_UINT24_MAX) { @@ -474,7 +473,6 @@ static void notifier_schedule_notification(notifier_t *notifier, uint32_t curren assert(0); } } - } static uint8_t notifier_set_parameters(notifier_t *notifier, registry_observation_parameters_t *parameters, const registry_path_t *path, @@ -519,18 +517,15 @@ static uint8_t notifier_set_parameters(notifier_t *notifier, registry_observatio } return 0; - } static void notifier_callback(endpoint_t *endpoint, const registry_path_t *path, registry_notification_status_t status) { - registry_callback_t callback; if (registry_get_callback(&endpoint->registry, path, &callback) == REGISTRY_STATUS_OK) { callback(REGISTRY_CALLBACK_NOTIFICATION_STATUS, path, NULL, NULL, status, &endpoint->registry); } - } static bool notifier_send_observation_notification_with_path(endpoint_t *endpoint, const registry_path_t *path, uint8_t *token_ptr, uint8_t token_len, @@ -540,9 +535,10 @@ static bool notifier_send_observation_notification_with_path(endpoint_t *endpoin uint32_t max_age; /* Check parameters */ - if (endpoint == NULL || endpoint->coap == NULL || endpoint->connection == NULL) { - tr_error("notifier_send_observation_notification invalid parameters."); - notifier_callback(&endpoint->notifier, path, NOTIFICATION_STATUS_BUILD_ERROR); + assert(endpoint); + if (endpoint->coap == NULL || endpoint->connection == NULL) { + tr_error("send_observation_notification invalid parameters"); + notifier_callback(endpoint, path, NOTIFICATION_STATUS_BUILD_ERROR); return false; } @@ -550,7 +546,7 @@ static bool notifier_send_observation_notification_with_path(endpoint_t *endpoin if (registry_get_max_age(&endpoint->registry, path, &max_age) == REGISTRY_STATUS_OK) { status = notifier_send_observation_notification(endpoint, max_age, token_ptr, token_len, payload_ptr, payload_len, content_format); } else { - tr_error("notifier_send_observation_notification() could not read max_age from registry!"); + tr_error("send_observation_notification() could not read max_age from registry"); status = NOTIFICATION_STATUS_BUILD_ERROR; } @@ -571,12 +567,12 @@ int notifier_send_observation_notification(struct endpoint_s *endpoint, uint32_t /* Allocate and initialize memory for header struct */ notification_message_ptr = sn_coap_parser_alloc_message(endpoint->coap); if (notification_message_ptr == NULL) { - tr_error("notifier_send_observation_notification alloc_message failed."); + tr_error("send_observation_notification alloc_message failed"); goto exit_error; } if (sn_coap_parser_alloc_options(endpoint->coap, notification_message_ptr) == NULL) { - tr_error("notifier_send_observation_notification alloc_options failed."); + tr_error("send_observation_notification alloc_options failed"); goto exit_error; } @@ -646,7 +642,7 @@ static bool notifier_send_notification(notifier_t *notifier, const registry_path registry_tlv_serialize_status_t serializer_status; const lwm2m_resource_meta_definition_t* meta_data = NULL; - tr_info("notifier_send_notification()"); + tr_info("send_notification()"); if(path->path_type == REGISTRY_PATH_RESOURCE) { @@ -714,12 +710,10 @@ static bool notifier_send_notification(notifier_t *notifier, const registry_path } return success; - } static void notifier_clear_dirty_children(notifier_t *notifier, const registry_path_t *path) { - registry_listing_t listing; registry_observation_parameters_t parameters; int previous_level = path->path_type; @@ -751,18 +745,16 @@ static void notifier_clear_dirty_children(notifier_t *notifier, const registry_p parameters.dirty = false; parameters.sent = false; - print_registry_path("notifier_clear_dirty_children() clear:", &listing.path); + print_registry_path("clear_dirty_children() clear:", &listing.path); registry_set_observation_parameters(¬ifier->endpoint->registry, &listing.path, ¶meters); } } - } static void notifier_clear_dirty(notifier_t *notifier, const registry_path_t *path) { - registry_observation_parameters_t parameters; registry_path_t current_path = *path; @@ -783,7 +775,7 @@ static void notifier_clear_dirty(notifier_t *notifier, const registry_path_t *pa parameters.dirty = false; parameters.sent = false; - print_registry_path("notifier_clear_dirty() clear:", ¤t_path); + print_registry_path("clear_dirty() clear:", ¤t_path); registry_set_observation_parameters(¬ifier->endpoint->registry, ¤t_path, ¶meters); } @@ -794,12 +786,10 @@ static void notifier_clear_dirty(notifier_t *notifier, const registry_path_t *pa notifier_clear_dirty_children(notifier, path); - } void notifier_notification_sent(notifier_t *notifier, bool success, const registry_path_t *path) { - registry_observation_parameters_t parameters; notifier->notifying = false; @@ -824,7 +814,6 @@ void notifier_notification_sent(notifier_t *notifier, bool success, const regist send_queue_sent(notifier->endpoint, true); send_queue_request(notifier->endpoint, SEND_QUEUE_NOTIFIER); - } static void notifier_notify(notifier_t *notifier, const registry_path_t *path, registry_observation_parameters_t *parameters, notifier_observation_value_t *value, bool *notified) @@ -855,7 +844,7 @@ static void notifier_notify(notifier_t *notifier, const registry_path_t *path, r if (!time_to_pmax) { if (!*notified) { - tr_debug("notifier_notify() !time_to_pmax"); + tr_debug("notify() !time_to_pmax"); *notified = notifier_send_notification(notifier, path, parameters, current_time, observation_value, false); } else { notifier_schedule_notification(notifier, current_time, time_to_pmax); @@ -874,7 +863,7 @@ static void notifier_notify(notifier_t *notifier, const registry_path_t *path, r time_to_pmin = notifier_time_to_pmin(notifier, parameters, current_time); if (!time_to_pmin && !*notified) { - tr_debug("notifier_notify() !time_to_pmin"); + tr_debug("notify() !time_to_pmin"); *notified = notifier_send_notification(notifier, path, parameters, current_time, observation_value, true); } else { notifier_schedule_notification(notifier, current_time, time_to_pmin); @@ -885,8 +874,6 @@ static void notifier_notify(notifier_t *notifier, const registry_path_t *path, r } notifier_schedule_notification(notifier, current_time, time_to_pmax); - - } static uint8_t notifier_get_observation_parameters(notifier_t *notifier, registry_path_t *path, registry_observation_parameters_t *parameters) @@ -925,7 +912,6 @@ static uint8_t notifier_get_observation_parameters(notifier_t *notifier, registr static void notifier_init_parameters(notifier_t *notifier, const registry_path_t *path) { - registry_listing_t listing; registry_observation_parameters_t parameters; @@ -941,11 +927,8 @@ static void notifier_init_parameters(notifier_t *notifier, const registry_path_t parameters = (registry_observation_parameters_t){0}; registry_set_observation_parameters(¬ifier->endpoint->registry, &listing.path, ¶meters); - } - } - } int32_t notifier_start_observation(notifier_t *notifier, const registry_path_t *path, const uint8_t *token, const uint8_t token_len, const sn_coap_content_format_e content_type) @@ -987,7 +970,6 @@ int32_t notifier_start_observation(notifier_t *notifier, const registry_path_t * notifier_schedule_notification(notifier, current_time, pmax); return notifier_get_notify_option_number(notifier); - } void notifier_stop_observation(notifier_t *notifier, const registry_path_t *path) @@ -1018,7 +1000,7 @@ void notifier_stop_observation(notifier_t *notifier, const registry_path_t *path if (!registry_is_auto_observable(¬ifier->endpoint->registry, ¬ifier->last_notified)) { #endif - print_registry_path("notifier_stop_observation() ", path); + print_registry_path("stop_observation() ", path); parameters.observed = 0; parameters.token_size = 0; @@ -1031,7 +1013,7 @@ void notifier_stop_observation(notifier_t *notifier, const registry_path_t *path #if MBED_CLIENT_ENABLE_AUTO_OBSERVATION } else { - tr_warn("notifier_stop_observation() Auto-observation may not be stopped."); + tr_warn("stop_observation() Auto-observation may not be stopped"); } #endif @@ -1041,7 +1023,6 @@ void notifier_stop_observation(notifier_t *notifier, const registry_path_t *path send_queue_request(notifier->endpoint, SEND_QUEUE_NOTIFIER); send_queue_sent(notifier->endpoint, true); } - } static void notifier_notify_resource(notifier_t *notifier, const registry_path_t *path, registry_object_value_t *resource_value, bool *notified) @@ -1093,8 +1074,6 @@ static void notifier_notify_resource(notifier_t *notifier, const registry_path_t notifier_notify(notifier, ¤t_path, &resource_parameters, value_read, notified); } while (current_path.path_type++ != path->path_type); - - } void notifier_set_dirty(registry_t *registry, const registry_path_t *path) @@ -1147,7 +1126,6 @@ void notifier_set_dirty(registry_t *registry, const registry_path_t *path) registry_set_observation_parameters(registry, ¤t_path, ¶meters); } while (current_path.path_type++ != path->path_type); - } static void notifier_check_all_resources(notifier_t *notifier, bool *notified) @@ -1189,9 +1167,7 @@ static void notifier_check_all_resources(notifier_t *notifier, bool *notified) notifier_notify_resource(notifier, &listing.path, &value, notified); } - } - } static void notifier_value_changed(notifier_t *notifier) @@ -1199,7 +1175,6 @@ static void notifier_value_changed(notifier_t *notifier) bool notified = NOTIFTER_USE_INITIAL_DELAY; notifier_check_all_resources(notifier, ¬ified); - } static void notifier_timer_expired(notifier_t *notifier) @@ -1249,7 +1224,6 @@ static void notifier_notify_next(notifier_t *notifier, const registry_path_t *pa skip = false; } while (!*notified && round++ == 1); - } void notifier_send_now(notifier_t *notifier) @@ -1264,7 +1238,6 @@ void notifier_send_now(notifier_t *notifier) if (!notified) { send_queue_sent(notifier->endpoint, true); } - } void notifier_parameters_changed(notifier_t *notifier, const registry_path_t *path) @@ -1296,9 +1269,7 @@ void notifier_clear_notifications(notifier_t *notifier) parameters.observed = false; parameters.token_size = 0; registry_set_observation_parameters(¬ifier->endpoint->registry, &listing.path, ¶meters); - } - } } diff --git a/mbed-client/source/lwm2m_registry.c b/mbed-client/source/lwm2m_registry.c index a657f5a..d0cafec 100644 --- a/mbed-client/source/lwm2m_registry.c +++ b/mbed-client/source/lwm2m_registry.c @@ -978,15 +978,14 @@ registry_status_t registry_is_value_empty(const registry_t *registry, const regi static registry_status_t registry_set_object_data(registry_t *registry, const registry_path_t *path, registry_object_value_t value, registry_data_type_t data_type) { - - print_registry_path("registry_set_object_data() path: ", path); - registry_object_t *object; if (!registry || !path) { return REGISTRY_STATUS_INVALID_INPUT; } + print_registry_path("registry_set_object_data() path: ", path); + if (path->path_type < REGISTRY_PATH_RESOURCE) { return REGISTRY_STATUS_NO_DATA; } diff --git a/mbed-client/source/lwm2m_registry_handler.c b/mbed-client/source/lwm2m_registry_handler.c index 4ff57b5..c4f2a42 100644 --- a/mbed-client/source/lwm2m_registry_handler.c +++ b/mbed-client/source/lwm2m_registry_handler.c @@ -37,8 +37,10 @@ #define TRACE_GROUP "RegH" static uint8_t parse_registry_path(const uint8_t* buf, size_t len, registry_path_t* path); -#ifndef MBED_CLOUD_CLIENT_DISABLE_REGISTRY +#ifdef MBED_CLIENT_ENABLE_DYNAMIC_CREATION static char *registry_path_to_string(const registry_path_t* path); +#endif +#ifndef MBED_CLOUD_CLIENT_DISABLE_REGISTRY static bool send_callback_data(const registry_path_t *path, const sn_coap_hdr_s* header, const uint8_t type); static sn_coap_hdr_s* handle_get_request(const registry_path_t* path, endpoint_t* endpoint, sn_coap_hdr_s* received_coap_header); static sn_coap_hdr_s* handle_put_request(const registry_path_t* path, endpoint_t *endpoint, sn_coap_hdr_s* received_coap_header); @@ -145,7 +147,7 @@ static uint8_t parse_registry_path(const uint8_t* buf, size_t len, registry_path return pathpart; } -#ifndef MBED_CLOUD_CLIENT_DISABLE_REGISTRY +#ifdef MBED_CLIENT_ENABLE_DYNAMIC_CREATION static char *registry_path_to_string(const registry_path_t* path) { /* four uint16_t fields, three slashes and terminating null byte */ @@ -161,7 +163,7 @@ static char *registry_path_to_string(const registry_path_t* path) len = snprintf(pathstr, PATH_MAX_LENGTH, "/%u/%u", path->object_id, path->object_instance_id); break; default: - tr_error("registry_path_to_string - unsupported path!"); + tr_error("unsupported path type: %d", path->path_type); assert(0); break; } @@ -174,7 +176,9 @@ static char *registry_path_to_string(const registry_path_t* path) return pathstr; } +#endif +#ifndef MBED_CLOUD_CLIENT_DISABLE_REGISTRY static sn_coap_hdr_s* handle_get_request(const registry_path_t* path, endpoint_t* endpoint, sn_coap_hdr_s* received_coap_header) { @@ -326,7 +330,7 @@ static sn_coap_hdr_s* handle_get_request(const registry_path_t* path, endpoint_t } if (registry_get_max_age(&endpoint->registry, path, &coap_response->options_list_ptr->max_age) != REGISTRY_STATUS_OK) { - tr_error("handle_get_request() could not read max_age from registry!"); + tr_error("handle_get_request() could not read max_age from registry"); // XXX: setting the default here could be masking an actual error in registry coap_response->options_list_ptr->max_age = LWM2M_VALUE_CACHE_MAX_AGE; } @@ -1102,6 +1106,8 @@ bool handle_coap_response(endpoint_t* endpoint, sn_coap_hdr_s *received_coap_hea registry_notification_status_t notification_status; if (received_coap_header->msg_type == COAP_MSG_TYPE_ACKNOWLEDGEMENT) { notification_status = NOTIFICATION_STATUS_DELIVERED; + } else if (received_coap_header->msg_type == COAP_MSG_TYPE_RESET) { + notification_status = NOTIFICATION_STATUS_UNSUBSCRIBED; } else if (received_coap_header->coap_status == COAP_STATUS_BUILDER_MESSAGE_SENDING_FAILED) { notification_status = NOTIFICATION_STATUS_SEND_FAILED; } else { @@ -1111,7 +1117,7 @@ bool handle_coap_response(endpoint_t* endpoint, sn_coap_hdr_s *received_coap_hea endpoint->notifier.notifying = false; endpoint->notifier.message_id = 0; - callback = endpoint_get_object_callback(endpoint, endpoint->notifier.last_notified); + callback = endpoint_get_object_callback(endpoint, endpoint->notifier.last_notified.object_id); if (callback) { // Call callback with NULL path. It's up to the sender of the notification to keep track of what notification is being sent callback(REGISTRY_CALLBACK_NOTIFICATION_STATUS, NULL, NULL, NULL, notification_status, endpoint); diff --git a/mbed-client/source/lwm2m_req_handler.c b/mbed-client/source/lwm2m_req_handler.c index 0a7b8e4..0e935d4 100644 --- a/mbed-client/source/lwm2m_req_handler.c +++ b/mbed-client/source/lwm2m_req_handler.c @@ -80,7 +80,7 @@ void req_handler_send_data_request(endpoint_t *endpoint, uint8_t *payload, uint16_t payload_len) { - tr_debug("req_handler_send_data_request - uri: %s, offset: %lu", uri, (unsigned long)offset); + tr_debug("send_data_request - uri: %s, offset: %lu", uri, (unsigned long)offset); get_data_request_t *data_request = NULL; @@ -174,7 +174,7 @@ void req_handler_send_message(endpoint_t *endpoint) req_message.options_list_ptr = NULL; if (sn_coap_parser_alloc_options(endpoint->coap, &req_message) == NULL) { - tr_error("req_handler_send_message - sn_coap_parser_alloc_options Allocation failed, return retry later"); + tr_error("send_message - sn_coap_parser_alloc_options failed, return retry later"); endpoint->coap->sn_coap_protocol_free(req_message.options_list_ptr); send_queue_sent(endpoint, true); return; @@ -266,7 +266,7 @@ bool req_handler_handle_response(endpoint_t *endpoint, const sn_coap_hdr_s *coap return false; } - tr_debug("req_handler_handle_response - msg code %d", coap_header->msg_code); + tr_debug("handle_response - msg code %d", coap_header->msg_code); send_queue_sent(endpoint, true); @@ -337,7 +337,7 @@ bool req_handler_handle_response(endpoint_t *endpoint, const sn_coap_hdr_s *coap return true; } else if (coap_header->msg_code == COAP_MSG_CODE_RESPONSE_SERVICE_UNAVAILABLE) { - tr_debug("req_handler_handle_response - msg code COAP_MSG_CODE_RESPONSE_SERVICE_UNAVAILABLE"); + tr_debug("handle_response - RESPONSE_SERVICE_UNAVAILABLE"); bool retry = true; if (!download_retry_time) { @@ -347,7 +347,7 @@ bool req_handler_handle_response(endpoint_t *endpoint, const sn_coap_hdr_s *coap download_retry_time *= 2; if (download_retry_time > MAX_RECONNECT_TIMEOUT) { - tr_error("req_handler_handle_response - file download failed, retry completed"); + tr_error("handle_response - file download failed, retry completed"); req_handler_free_request_list(coap_header, true, FAILED_TO_SEND_MSG); retry = false; } @@ -355,11 +355,11 @@ bool req_handler_handle_response(endpoint_t *endpoint, const sn_coap_hdr_s *coap if (retry) { if (eventOS_timeout_ms(timer_cb, download_retry_time * 1000, (void*)endpoint) == NULL) { - tr_error("req_handler_handle_response - failed to create a timer"); + tr_error("handle_response - failed to create a timer"); req_handler_free_request_list(coap_header, true, FAILED_TO_SEND_MSG); } else { get_data_req->resend = true; - tr_debug("req_handler_handle_response - continue file download after % "PRId32" (s)", download_retry_time); + tr_debug("handle_response - continue file download after %" PRId32 "s", download_retry_time); } } diff --git a/mbed-client/source/lwm2m_send_queue.c b/mbed-client/source/lwm2m_send_queue.c index 5cb3e32..35d28fe 100644 --- a/mbed-client/source/lwm2m_send_queue.c +++ b/mbed-client/source/lwm2m_send_queue.c @@ -73,7 +73,9 @@ void send_queue_sent(struct endpoint_s *endpoint, const bool confirmable) delay = SEND_QUEUE_DELAY_NON_CONFIRMABLE_MS; } - if ( (endpoint->send_queue.timeout = eventOS_timeout_ms(&timed_send_next, delay, endpoint)) ) { + endpoint->send_queue.timeout = eventOS_timeout_ms(&timed_send_next, delay, endpoint); + + if (endpoint->send_queue.timeout) { tr_debug("send_queue_sent() use %d ms delay", delay); return; } diff --git a/mbed-client/source/lwm2m_storage.c b/mbed-client/source/lwm2m_storage.c index f991468..bf95b59 100644 --- a/mbed-client/source/lwm2m_storage.c +++ b/mbed-client/source/lwm2m_storage.c @@ -249,7 +249,7 @@ const void *storage_read_certificate(size_t *buffer_size, bool bootstrap) temp_p = allocate_and_read_connectivity_parameter(LWM2M_DEVICE_CERTIFICATE, &lwm2m_certificate, buffer_size, bootstrap); } if (!temp_p) { - tr_error("storage_read_certificate() failed"); + tr_error("read_certificate() failed"); } return temp_p; } @@ -266,7 +266,7 @@ const void *storage_read_certificate_key(size_t *buffer_size, bool bootstrap) temp_p = allocate_and_read_connectivity_parameter(LWM2M_DEVICE_PRIVATE_KEY, &lwm2m_certificate_key, buffer_size, bootstrap); } if (!temp_p) { - tr_error("storage_read_certificate_key() failed"); + tr_error("read_certificate_key() failed"); } return temp_p; } @@ -285,7 +285,7 @@ const void *storage_read_ca_certificate(size_t *buffer_size, bool bootstrap) } if (!temp_p) { - tr_error("storage_read_ca_certificate() failed"); + tr_error("read_ca_certificate() failed"); } return temp_p; } @@ -304,17 +304,17 @@ bool storage_set_credentials(registry_t *registry) #endif //defined(PROTOMAN_SECURITY_ENABLE_CERTIFICATE) const char *server_uri; int64_t security; - tr_debug("storage_set_credentials()"); + tr_debug("set_credentials()"); registry_set_path(&path, M2M_SECURITY_ID, 0, SECURITY_M2M_SERVER_URI, 0, REGISTRY_PATH_OBJECT_INSTANCE); if (REGISTRY_STATUS_OK != registry_path_exists(registry, &path)) { - tr_info("storage_set_credentials() No credentials available."); + tr_info("set_credentials() No credentials available"); return false; } registry_set_path(&path, M2M_SECURITY_ID, 0, SECURITY_SECURITY_MODE, 0, REGISTRY_PATH_RESOURCE); if (REGISTRY_STATUS_OK != registry_get_value_int(registry, &path, &security)) { - tr_error("storage_set_credentials() No security mode set."); + tr_error("set_credentials() No security mode set"); return false; } @@ -326,13 +326,13 @@ bool storage_set_credentials(registry_t *registry) #ifndef PROTOMAN_OFFLOAD_TLS registry_set_path(&path, M2M_SECURITY_ID, 0, SECURITY_PUBLIC_KEY, 0, REGISTRY_PATH_RESOURCE); if (REGISTRY_STATUS_OK != registry_get_value_opaque(registry, &path, &public_key)) { - tr_error("storage_set_credentials() registry_get_value_opaque public_key failed"); + tr_error("set_credentials() registry_get_value_opaque public_key failed"); return false; } registry_set_path(&path, M2M_SECURITY_ID, 0, SECURITY_SECRET_KEY, 0, REGISTRY_PATH_RESOURCE); if (REGISTRY_STATUS_OK != registry_get_value_opaque(registry, &path, &sec_key)) { - tr_error("storage_set_credentials() registry_get_value_opaque sec_key failed"); + tr_error("set_credentials() registry_get_value_opaque sec_key failed"); return false; } #endif @@ -344,7 +344,7 @@ bool storage_set_credentials(registry_t *registry) #ifndef PROTOMAN_OFFLOAD_TLS registry_set_path(&path, M2M_SECURITY_ID, 0, SECURITY_SERVER_PUBLIC_KEY, 0, REGISTRY_PATH_RESOURCE); if (REGISTRY_STATUS_OK != registry_get_value_opaque(registry, &path, &ca_cert)) { - tr_error("storage_set_credentials() registry_get_value_opaque sec_key failed"); + tr_error("set_credentials() registry_get_value_opaque sec_key failed"); return false; } #endif @@ -362,12 +362,12 @@ bool storage_set_credentials(registry_t *registry) #if defined(PROTOMAN_SECURITY_ENABLE_PSK) if (security == 0) { if (set_config_parameter(LWM2M_SERVER_PSK_IDENTITY, public_key->data, public_key->size) != CCS_STATUS_SUCCESS) { - tr_error("storage_set_credentials() set_config_parameter public_key failed"); + tr_error("set_credentials() set_config_parameter public_key failed"); return false; } if (set_config_parameter(LWM2M_SERVER_PSK_SECRET, sec_key->data, sec_key->size) != CCS_STATUS_SUCCESS) { - tr_error("storage_set_credentials() set_config_parameter sec_key failed"); + tr_error("set_credentials() set_config_parameter sec_key failed"); return false; } } @@ -376,17 +376,17 @@ bool storage_set_credentials(registry_t *registry) if (security == 2) { #ifndef PROTOMAN_OFFLOAD_TLS if (set_config_parameter(LWM2M_DEVICE_CERTIFICATE, public_key->data, public_key->size) != CCS_STATUS_SUCCESS) { - tr_error("storage_set_credentials() set_config_parameter public_key failed"); + tr_error("set_credentials() set_config_parameter public_key failed"); return false; } if (set_config_parameter(LWM2M_SERVER_ROOT_CA_CERTIFICATE, ca_cert->data, ca_cert->size) != CCS_STATUS_SUCCESS) { - tr_error("storage_set_credentials() set_config_parameter ca_cert failed"); + tr_error("set_credentials() set_config_parameter ca_cert failed"); return false; } if (set_config_parameter(LWM2M_DEVICE_PRIVATE_KEY, sec_key->data, sec_key->size) != CCS_STATUS_SUCCESS) { - tr_error("storage_set_credentials() set_config_parameter sec_key failed"); + tr_error("set_credentials() set_config_parameter sec_key failed"); return false; } #endif @@ -394,13 +394,13 @@ bool storage_set_credentials(registry_t *registry) #endif //defined(PROTOMAN_SECURITY_ENABLE_CERTIFICATE) if (set_config_parameter(LWM2M_SERVER_URI, (const uint8_t*)server_uri, strlen(server_uri)) != CCS_STATUS_SUCCESS) { - tr_error("storage_set_credentials() set_config_parameter server_uri failed"); + tr_error("set_credentials() set_config_parameter server_uri failed"); return false; } #ifndef MBED_CONF_MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE if (security == 0 || security == 2) { - tr_info("storage_set_credentials() set_config_parameter ok"); + tr_info("set_credentials() set_config_parameter ok"); const char *iep_ptr = NULL; const int iep_len = parse_query_parameter_value_from_query(server_uri, "iep", &iep_ptr); if (iep_ptr && iep_len > 0) { @@ -426,17 +426,17 @@ bool storage_clear_credentials(registry_t *registry) int64_t security; registry_set_path(&path, M2M_SECURITY_ID, 0, SECURITY_SECURITY_MODE, 0, REGISTRY_PATH_RESOURCE); if (REGISTRY_STATUS_OK != registry_get_value_int(registry, &path, &security)) { - tr_error("storage_clear_credentials() No security mode set."); + tr_error("clear_credentials() No security mode set"); return false; } #if defined(PROTOMAN_SECURITY_ENABLE_PSK) if (security == 0) { if (remove_config_parameter(LWM2M_SERVER_PSK_IDENTITY) != CCS_STATUS_SUCCESS) { - tr_error("storage_clear_credentials() remove_config_parameter public_key failed"); + tr_error("clear_credentials() remove_config_parameter public_key failed"); } if (remove_config_parameter(LWM2M_SERVER_PSK_SECRET, sec_key->data, sec_key->size) != CCS_STATUS_SUCCESS) { - tr_error("storage_clear_credentials() remove_config_parameter sec_key failed"); + tr_error("clear_credentials() remove_config_parameter sec_key failed"); } } #endif //defined(PROTOMAN_SECURITY_ENABLE_PSK) @@ -444,20 +444,20 @@ bool storage_clear_credentials(registry_t *registry) if (security == 2) { #ifndef PROTOMAN_OFFLOAD_TLS if (remove_config_parameter(LWM2M_DEVICE_CERTIFICATE) != CCS_STATUS_SUCCESS) { - tr_error("storage_clear_credentials() remove_config_parameter public_key failed"); + tr_error("clear_credentials() remove_config_parameter public_key failed"); } if (remove_config_parameter(LWM2M_SERVER_ROOT_CA_CERTIFICATE) != CCS_STATUS_SUCCESS) { - tr_error("storage_clear_credentials() remove_config_parameter ca_cert failed"); + tr_error("clear_credentials() remove_config_parameter ca_cert failed"); } if (remove_config_parameter(LWM2M_DEVICE_PRIVATE_KEY) != CCS_STATUS_SUCCESS) { - tr_error("storage_clear_credentials() remove_config_parameter sec_key failed"); + tr_error("clear_credentials() remove_config_parameter sec_key failed"); } #endif } #endif //defined(PROTOMAN_SECURITY_ENABLE_CERTIFICATE) if (remove_config_parameter(LWM2M_SERVER_URI) != CCS_STATUS_SUCCESS) { - tr_error("storage_clear_credentials() remove_config_parameter server_uri failed"); + tr_error("clear_credentials() remove_config_parameter server_uri failed"); } return true; } @@ -508,7 +508,7 @@ bool storage_set_bootstrap_credentials(registry_t *registry) return true; #else - tr_error("set_bootstrap_credentials() Redirecting bootstrap not supported."); + tr_error("set_bootstrap_credentials() Redirecting bootstrap not supported"); return false; #endif //defined(PROTOMAN_SECURITY_ENABLE_PSK) } @@ -517,7 +517,7 @@ bool storage_set_bootstrap_credentials(registry_t *registry) bool storage_set_internal_endpoint_name(const char *iep) { if (!iep || (set_config_parameter(INTERNAL_ENDPOINT, (const uint8_t*) iep, strlen(iep)) != CCS_STATUS_SUCCESS)) { - tr_error("storage_set_internal_endpoint_name() setting iep failed"); + tr_error("set_internal_endpoint_name() setting iep failed"); return false; } diff --git a/mbed-client/source/tlvserializer.c b/mbed-client/source/tlvserializer.c index ad4fcbe..8f49c75 100644 --- a/mbed-client/source/tlvserializer.c +++ b/mbed-client/source/tlvserializer.c @@ -1018,14 +1018,14 @@ static registry_tlv_serialize_status_t registry_tlv_deserialize_length(registry_ if (stlv->offset >= stlv->tlv_size) { return REGISTRY_TLV_SERIALIZE_STATUS_INVALID_INPUT; } - stlv->length = (stlv->length << 8) + stlv->tlv[stlv->offset++]; + stlv->length = (stlv->length << 8) | stlv->tlv[stlv->offset++]; } if (lengthType > LENGTH16) { if (stlv->offset >= stlv->tlv_size) { return REGISTRY_TLV_SERIALIZE_STATUS_INVALID_INPUT; } - stlv->length = (stlv->length << 8) + stlv->tlv[stlv->offset++]; + stlv->length = (stlv->length << 8) | stlv->tlv[stlv->offset++]; } return REGISTRY_TLV_SERIALIZE_STATUS_OK; diff --git a/porting/device_management_client_mbed_tls_config_linux_default.h b/porting/device_management_client_mbed_tls_config_linux_default.h index db441c9..8ad3f9a 100644 --- a/porting/device_management_client_mbed_tls_config_linux_default.h +++ b/porting/device_management_client_mbed_tls_config_linux_default.h @@ -1306,6 +1306,33 @@ */ //#define MBEDTLS_SSL_ASYNC_PRIVATE +/** + * \def MBEDTLS_SSL_CONTEXT_SERIALIZATION + * + * Enable serialization of the TLS context structures, through use of the + * functions mbedtls_ssl_context_save() and mbedtls_ssl_context_load(). + * + * This pair of functions allows one side of a connection to serialize the + * context associated with the connection, then free or re-use that context + * while the serialized state is persisted elsewhere, and finally deserialize + * that state to a live context for resuming read/write operations on the + * connection. From a protocol perspective, the state of the connection is + * unaffected, in particular this is entirely transparent to the peer. + * + * Note: this is distinct from TLS session resumption, which is part of the + * protocol and fully visible by the peer. TLS session resumption enables + * establishing new connections associated to a saved session with shorter, + * lighter handshakes, while context serialization is a local optimization in + * handling a single, potentially long-lived connection. + * + * Enabling these APIs makes some SSL structures larger, as 64 extra bytes are + * saved after the handshake to allow for more efficient serialization, so if + * you don't need this feature you'll save RAM by disabling it. + * + * Comment to disable the context serialization APIs. + */ +#define MBEDTLS_SSL_CONTEXT_SERIALIZATION + /** * \def MBEDTLS_SSL_DEBUG_ALL * @@ -3411,6 +3438,9 @@ * Allow user to override any previous default. * */ + +#define MBEDTLS_SSL_DTLS_CONNECTION_ID + #if defined(MBEDTLS_USER_CONFIG_FILE) #include MBEDTLS_USER_CONFIG_FILE #endif diff --git a/source/client_mandatory_oma_lwm2m_object_defs.c b/source/client_mandatory_oma_lwm2m_object_defs.c index c7b5964..acf6b9e 100644 --- a/source/client_mandatory_oma_lwm2m_object_defs.c +++ b/source/client_mandatory_oma_lwm2m_object_defs.c @@ -19,7 +19,9 @@ #include #ifdef USER_OMA_OBJECT_FILE +#if !defined(__CC_ARM) #pragma message ( "using user OMA object config file" ) +#endif #include USER_OMA_OBJECT_FILE #endif diff --git a/source/dmc_connect_api.c b/source/dmc_connect_api.c index 9391a83..28ae208 100644 --- a/source/dmc_connect_api.c +++ b/source/dmc_connect_api.c @@ -17,16 +17,10 @@ // ---------------------------------------------------------------------------- -#ifdef MBED_CLIENT_USER_CONFIG_FILE -#include MBED_CLIENT_USER_CONFIG_FILE -#endif - #ifdef MBED_CLOUD_CLIENT_USER_CONFIG_FILE #include MBED_CLOUD_CLIENT_USER_CONFIG_FILE #endif -#define PDMC_CONNECT_STARTUP_EVENT_TYPE 6 - #include "mbed-trace/mbed_trace.h" #include "mbed-client/lwm2m_endpoint.h" #include "mbed-client/lwm2m_interface.h" @@ -80,7 +74,8 @@ static void fota_update_init(void); #define TRACE_GROUP "pdmc" -typedef enum { // note these are encapsulated to u8 type, so remember limit 255 if more events are created +#define PDMC_APPLICATION_EVENT_TYPE 0 +typedef enum { // note these are encapsulated to u8 type, so remember limit 255 if more events are created. APPLICATION_EVENT_HANDLER_UPDATE_INIT = 200, APPLICATION_EVENT_START_BOOTSTRAP, APPLICATION_EVENT_REGISTER, @@ -168,7 +163,7 @@ static int pdmc_connect_set_observable_and_callback(registry_t *registry, const bool auto_observable, registry_callback_t callback); #endif -static void send_event(uint8_t event_type); +static void send_event(uint8_t event_id); static void forward_event_to_external_interface(arm_event_t *orig_event); static oma_lwm2m_binding_and_mode_t get_binding_mode(void); static registry_status_t reboot_callback(registry_callback_type_t type, @@ -189,7 +184,9 @@ static endpoint_t *pdmc_connect_get_endpoint(void); static registry_t *pdmc_connect_get_registry(void); #endif +#ifndef NDEBUG static bool interface_is_initialized(void); +#endif /** * \brief initialises update @@ -213,14 +210,14 @@ static void pdmc_connect_event_handler(arm_event_t *event) { event_in_flight = false; - if (event->event_type == PDMC_CONNECT_STARTUP_EVENT_TYPE && event->event_id == 0) { + if (event->event_type == PDMC_CONNECT_STARTUP_EVENT_TYPE) { return; } if (event->event_id > LWM2M_INTERFACE_FIRST_EVENT_ID && event->event_id < LWM2M_INTERFACE_LAST_EVENT_ID) { if (event->event_id == LWM2M_INTERFACE_OBSERVER_EVENT_BOOTSTRAP_DONE) { - tr_debug("pdmc_connect_event_handler - LWM2M_INTERFACE_OBSERVER_EVENT_BOOTSTRAP_DONE"); + tr_debug("event_handler - LWM2M_INTERFACE_OBSERVER_EVENT_BOOTSTRAP_DONE"); pdmc_connect_init_update(); // moves to the registration } @@ -276,7 +273,7 @@ static void pdmc_connect_event_handler(arm_event_t *event) break; #endif default: - tr_error("pdmc_connect_event_handler - unhandled event: %" PRId8 " sender %x", event->event_id, event->sender); + tr_error("event_handler - unhandled event: %" PRId8 " sender %x", event->event_id, event->sender); break; } } @@ -587,7 +584,7 @@ static int get_device_object_resources(endpoint_t *endpoint, register_resource_t assert(value_available); curr->next = endpoint_create_register_resource_str(endpoint, hardware_version_res_id, false, - value_str, strlen(value_str)); + (const uint8_t*)value_str, strlen(value_str)); curr = curr->next; if (!curr) { return -1; @@ -606,6 +603,15 @@ static int get_device_object_resources(endpoint_t *endpoint, register_resource_t } #endif +#if MBED_CLOUD_CLIENT_ENABLE_DEVICE_OBJECT_RESOURCE_6 || MBED_CLOUD_CLIENT_ENABLE_DEVICE_OBJECT_RESOURCE_7 \ + || MBED_CLOUD_CLIENT_ENABLE_DEVICE_OBJECT_RESOURCE_8 || MBED_CLOUD_CLIENT_ENABLE_DEVICE_OBJECT_RESOURCE_9 \ + || MBED_CLOUD_CLIENT_ENABLE_DEVICE_OBJECT_RESOURCE_13 || MBED_CLOUD_CLIENT_ENABLE_DEVICE_OBJECT_RESOURCE_18 \ + || MBED_CLOUD_CLIENT_ENABLE_DEVICE_OBJECT_RESOURCE_20 + + // silence warning for unused variable on release builds + (void)value_available; +#endif + return 0; } @@ -617,7 +623,7 @@ static sn_coap_hdr_s *on_device_object_coap_request(const registry_path_t* path, int *acked) { - tr_debug("dmc_connect_api - on_device_object_coap_request()"); + tr_debug("on_device_object_coap_request()"); if (memcmp("3/0/4", (char*)request->uri_path_ptr, request->uri_path_len) == 0) { tr_debug("on_device_object_coap_request() - response code: %d", response->msg_code); @@ -717,7 +723,7 @@ static oma_lwm2m_binding_and_mode_t get_binding_mode(void) #endif } -static void send_event(uint8_t event_type) +static void send_event(uint8_t event_id) { assert(interface_is_initialized()); @@ -729,9 +735,9 @@ static void send_event(uint8_t event_type) user_allocated_event.data.data_ptr = NULL; user_allocated_event.data.event_data = 0; user_allocated_event.data.sender = 0; - user_allocated_event.data.event_type = 0; + user_allocated_event.data.event_type = PDMC_APPLICATION_EVENT_TYPE; user_allocated_event.data.receiver = internal_event_handler_id; - user_allocated_event.data.event_id = event_type; + user_allocated_event.data.event_id = event_id; user_allocated_event.data.priority = ARM_LIB_LOW_PRIORITY_EVENT; eventOS_event_send_user_allocated(&user_allocated_event); @@ -779,6 +785,7 @@ static registry_t *pdmc_connect_get_registry(void) } #endif +#ifndef NDEBUG static bool interface_is_initialized(void) { #if MBED_CLOUD_CLIENT_DYNAMIC_INTERFACE_ALLOC @@ -787,7 +794,7 @@ static bool interface_is_initialized(void) return (internal_event_handler_id != -1); #endif } - +#endif void pdmc_connect_init(uint8_t event_handler_id) { @@ -824,7 +831,7 @@ void pdmc_connect_init(uint8_t event_handler_id) free(interface); interface = NULL; #endif - tr_error("pdmc_connect_init - failed to create event handler"); + tr_error("init - failed to create event handler"); assert(false); return; } @@ -845,7 +852,7 @@ void pdmc_connect_init(uint8_t event_handler_id) evt.priority = ARM_LIB_LOW_PRIORITY_EVENT; if (eventOS_event_send(&evt) < 0) { - tr_error("pdmc_connect_init - failed to send event"); + tr_error("init - failed to send event"); assert(false); } @@ -855,7 +862,7 @@ void pdmc_connect_init(uint8_t event_handler_id) #else object_handler_t *handler = endpoint_allocate_object_handler(M2M_DEVICE_ID, get_device_object_resources, on_device_object_coap_request, reboot_callback); if (!handler) { - tr_error("pdmc_connect_init() failed to allocate object handler"); + tr_error("init - failed to allocate object handler"); assert(handler); // if this happens it's a fatal error return; } @@ -1086,7 +1093,7 @@ static void init_update_event(arm_event_s *ev, void *cb, uintptr_t param) static bool schedule_update_event(arm_event_storage_t *ev, void *cb, uintptr_t param) { if (ev == NULL) { - tr_info("schedule_update_event - event is null!"); + tr_info("schedule_update_event - event is null"); return false; } diff --git a/source/storage/include/CloudClientStorage.h b/source/storage/include/CloudClientStorage.h index f8abf3f..92a51e5 100644 --- a/source/storage/include/CloudClientStorage.h +++ b/source/storage/include/CloudClientStorage.h @@ -104,7 +104,8 @@ typedef enum { CCS_STATUS_MEMORY_ERROR = 1, CCS_STATUS_VALIDATION_FAIL = 2, CCS_STATUS_KEY_DOESNT_EXIST = 3, - CCS_STATUS_ERROR = 4 + CCS_STATUS_ERROR = 4, + CCS_STATUS_FI_ATTACK = 5 } ccs_status_e; /** diff --git a/tools/.baremetal_mbedignore b/tools/.baremetal_mbedignore index d9ccb87..2750706 100644 --- a/tools/.baremetal_mbedignore +++ b/tools/.baremetal_mbedignore @@ -1,8 +1,14 @@ -mbed-os/features/mbedtls/crypto/* -mbed-os/features/mbedtls/targets/* -mbed-os/features/mbedtls/inc/* -mbed-os/features/mbedtls/src/* -mbed-os/features/mbedtls/mbed-crypto/* +mbed-os/connectivity/mbedtls/include/* +mbed-os/connectivity/mbedtls/source/* +mbed-os/connectivity/mbedtls/tests/* +mbed-os/connectivity/mbedtls/tools/* + +mbed-os/connectivity/nanostack/mbed_lib.json +mbed-os/connectivity/nanostack/source/* +mbed-os/connectivity/nanostack/include/* +mbed-os/connectivity/nanostack/mbed-mesh-api/* +mbed-os/connectivity/drivers/802.15.4_RF/* + mbed-cloud-client/mbedtls/src/x509_crl.c mbed-cloud-client/mbedtls/src/x509_crt.c mbed-cloud-client/mbedtls/src/x509_csr.c diff --git a/tools/importer/Makefile b/tools/importer/Makefile index 0c1bf66..8ac5cf8 100644 --- a/tools/importer/Makefile +++ b/tools/importer/Makefile @@ -35,7 +35,7 @@ # Set the mbed TLS release to import (this can/should be edited before import) MBED_TLS_RELEASE ?= baremetal -MBED_TLS_REPO_URL ?= git@github.com:ARMmbed/mbedtls.git +MBED_TLS_REPO_URL ?= git@github.com:PelionIoT/pelion-crypto.git # Translate between mbed TLS namespace and mbed namespace TARGET_PREFIX:=../ diff --git a/tools/setup_optimized_mbedtls.sh b/tools/setup_optimized_mbedtls.sh index 70a16b8..2cd9011 100755 --- a/tools/setup_optimized_mbedtls.sh +++ b/tools/setup_optimized_mbedtls.sh @@ -18,8 +18,8 @@ if [ ! -d "../mbedtls" ] then cd .. - MBEDTLS_HASH="858e4325d2eb55274950e9335d0c08a1326069c2" - git clone https://github.com/ARMmbed/mbedtls.git + MBEDTLS_HASH="bb9d720b21230664a122e386092935e775e3a422" + git clone https://github.com/PelionIOT/pelion-crypto.git mbedtls cd mbedtls git checkout "$MBEDTLS_HASH" cp -r ../tools/importer/ . @@ -29,6 +29,7 @@ then cd .. rm -rf library include programs cd .. + echo "Making a backup of application .mbedignore file for cleanup" cp ../.mbedignore ../.mbedignore-application-backup-baremetal echo "Appending application .mbedignore file with baremetal TLS configurations"