diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst index ec098c14a3c5..9d4b1b060fc2 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -728,6 +728,10 @@ Libraries for networking * Added support for using a password when connecting to a broker. +* :ref:`lib_fota_download` library: + + * Added the functions :c:func:`fota_download` and :c:func:`fota_download_any` that can accept a security tag list and security tag count as arguments instead of a single security tag. + Libraries for NFC ----------------- diff --git a/include/net/fota_download.h b/include/net/fota_download.h index 2eceeba7e8d5..a08a08dbff78 100644 --- a/include/net/fota_download.h +++ b/include/net/fota_download.h @@ -112,11 +112,45 @@ typedef void (*fota_download_callback_t)(const struct fota_download_evt *evt); */ int fota_download_init(fota_download_callback_t client_callback); -/**@brief Start downloading the given file from the given host. +/**@brief Download the given file with the specified image type from the given host. * + * Validate that the file type matches the expected type before proceeding with the download. * When the download is complete, the secondary slot of MCUboot is tagged as having * valid firmware inside it. The completion is reported through an event. * + * URI parameters (host and file) are not copied, so pointers must stay valid + * until download is finished. + * + * @param host Name of host to start downloading from. Can include scheme + * and port number, for example https://google.com:443 + * @param file Path to the file you wish to download. See fota_download_any() + * for details on expected format. + * @param sec_tag_list Security tags that you want to use with HTTPS. Pass NULL to disable TLS. + * @param sec_tag_count Number of TLS security tags in list. Pass 0 to disable TLS. + * @param pdn_id Packet Data Network ID to use for the download, or 0 to use the default. + * @param fragment_size Fragment size to be used for the download. + * If 0, @kconfig{CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE} is used. + * @param expected_type Type of firmware file to be downloaded and installed. + * + * @retval 0 If download has started successfully. + * @retval -EALREADY If download is already ongoing. + * @retval -E2BIG If sec_tag_count is larger than + * @kconfig{CONFIG_FOTA_DOWNLOAD_SEC_TAG_LIST_SIZE_MAX} + * Otherwise, a negative value is returned. + */ +int fota_download(const char *host, const char *file, const int *sec_tag_list, + uint8_t sec_tag_count, uint8_t pdn_id, size_t fragment_size, + const enum dfu_target_image_type expected_type); + + +/**@brief Start downloading the given file of any image type from the given host. + * + * When the download is complete, the secondary slot of MCUboot is tagged as having + * valid firmware inside it. The completion is reported through an event. + * + * URI parameters (host and file) are not copied, so pointers must stay valid + * until download is finished. + * * @param host Name of host to start downloading from. Can include scheme * and port number, e.g. https://google.com:443 * @param file Path to the file you wish to download. May be either a single @@ -130,7 +164,34 @@ int fota_download_init(fota_download_callback_t client_callback); * See * Secure Bootloader Chain Docs for details regarding the upgradable * bootloader slots. - * @param sec_tag Security tag you want to use with HTTPS set to -1 to Disable. + * @param sec_tag_list Security tags that you want to use with HTTPS. Pass NULL to disable TLS. + * @param sec_tag_count Number of TLS security tags in list. Pass 0 to disable TLS. + * @param pdn_id Packet Data Network ID to use for the download, or 0 to use the default. + * @param fragment_size Fragment size to be used for the download. + * If 0, @kconfig{CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE} is used. + * + * @retval 0 If download has started successfully. + * @retval -EALREADY If download is already ongoing. + * @retval -E2BIG If sec_tag_count is larger than + * @kconfig{CONFIG_FOTA_DOWNLOAD_SEC_TAG_LIST_SIZE_MAX} + * Otherwise, a negative value is returned. + */ +int fota_download_any(const char *host, const char *file, const int *sec_tag_list, + uint8_t sec_tag_count, uint8_t pdn_id, size_t fragment_size); + +/**@brief Start downloading the given file of any image type from the given host. + * + * When the download is complete, the secondary slot of MCUboot is tagged as having + * valid firmware inside it. The completion is reported through an event. + * + * URI parameters (host and file) are not copied, so pointers must stay valid + * until download is finished. + * + * @param host Name of host to start downloading from. Can include scheme + * and port number, e.g. https://google.com:443 + * @param file Path to the file you wish to download. See fota_download_any() + * for details on expected format. + * @param sec_tag Security tag you want to use with HTTPS. Pass -1 to disable TLS. * @param pdn_id Packet Data Network ID to use for the download, or 0 to use the default. * @param fragment_size Fragment size to be used for the download. * If 0, @kconfig{CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE} is used. @@ -142,9 +203,9 @@ int fota_download_init(fota_download_callback_t client_callback); int fota_download_start(const char *host, const char *file, int sec_tag, uint8_t pdn_id, size_t fragment_size); -/**@brief Start downloading the given file from the given host. Validate that the - * file type matches the expected type before starting the installation. +/**@brief Download the given file with the specified image type from the given host. * + * Validate that the file type matches the expected type before proceeding with the download. * When the download is complete, the secondary slot of MCUboot is tagged as having * valid firmware inside it. The completion is reported through an event. * @@ -153,9 +214,9 @@ int fota_download_start(const char *host, const char *file, int sec_tag, * * @param host Name of host to start downloading from. Can include scheme * and port number, for example https://google.com:443 - * @param file Path to the file you wish to download. See fota_download_start() + * @param file Path to the file you wish to download. See fota_download_any() * for details on expected format. - * @param sec_tag Security tag you want to use with HTTPS set to -1 to Disable. + * @param sec_tag Security tag you want to use with HTTPS. Pass -1 to disable TLS. * @param pdn_id Packet Data Network ID to use for the download, or 0 to use the default. * @param fragment_size Fragment size to be used for the download. * If 0, @kconfig{CONFIG_DOWNLOAD_CLIENT_HTTP_FRAG_SIZE} is used. diff --git a/samples/cellular/http_update/modem_delta_update/src/main.c b/samples/cellular/http_update/modem_delta_update/src/main.c index ee26795ee77c..5a5b27274e44 100644 --- a/samples/cellular/http_update/modem_delta_update/src/main.c +++ b/samples/cellular/http_update/modem_delta_update/src/main.c @@ -286,6 +286,8 @@ static int update_download(void) { int err; const char *file; + int sec_tag = SEC_TAG; + uint8_t sec_tag_count = sec_tag < 0 ? 0 : 1; err = modem_info_string_get(MODEM_INFO_FW_VERSION, modem_version, MAX_MODEM_VERSION_LEN); @@ -307,9 +309,10 @@ static int update_download(void) } /* Functions for getting the host and file */ - err = fota_download_start(CONFIG_DOWNLOAD_HOST, file, SEC_TAG, 0, 0); + err = fota_download(CONFIG_DOWNLOAD_HOST, file, &sec_tag, sec_tag_count, 0, 0, + DFU_TARGET_IMAGE_TYPE_MODEM_DELTA); if (err) { - printk("fota_download_start() failed, err %d\n", err); + printk("fota_download_any() failed, err %d\n", err); return err; } diff --git a/samples/cellular/http_update/modem_full_update/src/main.c b/samples/cellular/http_update/modem_full_update/src/main.c index e1cefa45bb4f..f026181879b5 100644 --- a/samples/cellular/http_update/modem_full_update/src/main.c +++ b/samples/cellular/http_update/modem_full_update/src/main.c @@ -420,6 +420,8 @@ static int update_download(void) { int err; const char *file; + int sec_tag = SEC_TAG; + uint8_t sec_tag_count = sec_tag < 0 ? 0 : 1; const struct dfu_target_full_modem_params params = { .buf = fmfu_buf, .len = sizeof(fmfu_buf), @@ -456,9 +458,10 @@ static int update_download(void) } /* Functions for getting the host and file */ - err = fota_download_start(CONFIG_DOWNLOAD_HOST, file, SEC_TAG, 0, 0); + err = fota_download(CONFIG_DOWNLOAD_HOST, file, &sec_tag, sec_tag_count, 0, 0, + DFU_TARGET_IMAGE_TYPE_FULL_MODEM); if (err != 0) { - printk("fota_download_start() failed, err %d\n", err); + printk("fota_download() failed, err %d\n", err); return err; } diff --git a/subsys/net/lib/fota_download/Kconfig b/subsys/net/lib/fota_download/Kconfig index 271ddac86a6f..03f278b89708 100644 --- a/subsys/net/lib/fota_download/Kconfig +++ b/subsys/net/lib/fota_download/Kconfig @@ -56,6 +56,12 @@ config FOTA_DOWNLOAD_RESOURCE_LOCATOR_LENGTH int "Size of buffer used for Resource locator" default 512 +config FOTA_DOWNLOAD_SEC_TAG_LIST_SIZE_MAX + int "Size of security tag list" + default 5 + help + Maximum size of the list of security tags used to store TLS credentials. + module=FOTA_DOWNLOAD module-dep=LOG module-str=Firmware Over the Air Download diff --git a/subsys/net/lib/fota_download/src/fota_download.c b/subsys/net/lib/fota_download/src/fota_download.c index 17978bd61d72..b032d54a876d 100644 --- a/subsys/net/lib/fota_download/src/fota_download.c +++ b/subsys/net/lib/fota_download/src/fota_download.c @@ -348,13 +348,6 @@ static void download_with_offset(struct k_work *unused) return; } -int fota_download_start(const char *host, const char *file, int sec_tag, - uint8_t pdn_id, size_t fragment_size) -{ - return fota_download_start_with_image_type(host, file, sec_tag, pdn_id, - fragment_size, DFU_TARGET_IMAGE_TYPE_ANY); -} - static bool is_ip_address(const char *host) { struct sockaddr sa; @@ -419,14 +412,22 @@ int fota_download_s0_active_get(bool *const s0_active) #endif /* PM_S1_ADDRESS */ } -int fota_download_start_with_image_type(const char *host, const char *file, - int sec_tag, uint8_t pdn_id, size_t fragment_size, + +int fota_download_any(const char *host, const char *file, const int *sec_tag_list, + uint8_t sec_tag_count, uint8_t pdn_id, size_t fragment_size) +{ + return fota_download(host, file, sec_tag_list, sec_tag_count, pdn_id, + fragment_size, DFU_TARGET_IMAGE_TYPE_ANY); +} + +int fota_download(const char *host, const char *file, + const int *sec_tag_list, uint8_t sec_tag_count, uint8_t pdn_id, size_t fragment_size, const enum dfu_target_image_type expected_type) { - static int sec_tag_list[1]; uint32_t host_hash = 0; uint32_t file_hash = 0; int err = -1; + static int sec_tag_list_copy[CONFIG_FOTA_DOWNLOAD_SEC_TAG_LIST_SIZE_MAX]; struct download_client_cfg config = { .pdn_id = pdn_id, @@ -441,6 +442,10 @@ int fota_download_start_with_image_type(const char *host, const char *file, return -EALREADY; } + if (sec_tag_count > ARRAY_SIZE(sec_tag_list_copy)) { + return -E2BIG; + } + atomic_clear_bit(&flags, FLAG_CLOSED); atomic_clear_bit(&flags, FLAG_RESUME); set_error_state(FOTA_DOWNLOAD_ERROR_CAUSE_NO_ERROR); @@ -456,19 +461,21 @@ int fota_download_start_with_image_type(const char *host, const char *file, } else { atomic_clear_bit(&flags, FLAG_NEW_URI); } + dl_host_hash = host_hash; dl_file_hash = file_hash; dl_host = host; dl_file = file; - if (sec_tag != -1 && !is_ip_address(host)) { - config.set_tls_hostname = true; - } + if ((sec_tag_list != NULL) && (sec_tag_count > 0)) { + memcpy(sec_tag_list_copy, sec_tag_list, sec_tag_count * sizeof(sec_tag_list[0])); - if (sec_tag != -1) { - sec_tag_list[0] = sec_tag; - config.sec_tag_list = sec_tag_list; - config.sec_tag_count = 1; + config.sec_tag_count = sec_tag_count; + config.sec_tag_list = sec_tag_list_copy; + + if (!is_ip_address(host)) { + config.set_tls_hostname = true; + } } socket_retries_left = CONFIG_FOTA_SOCKET_RETRIES; @@ -519,6 +526,26 @@ int fota_download_start_with_image_type(const char *host, const char *file, return 0; } +int fota_download_start(const char *host, const char *file, int sec_tag, + uint8_t pdn_id, size_t fragment_size) +{ + int sec_tag_list[1] = { sec_tag }; + uint8_t sec_tag_count = sec_tag < 0 ? 0 : 1; + + return fota_download_any(host, file, sec_tag_list, sec_tag_count, pdn_id, fragment_size); +} + +int fota_download_start_with_image_type(const char *host, const char *file, + int sec_tag, uint8_t pdn_id, size_t fragment_size, + const enum dfu_target_image_type expected_type) +{ + int sec_tag_list[1] = { sec_tag }; + uint8_t sec_tag_count = sec_tag < 0 ? 0 : 1; + + return fota_download(host, file, sec_tag_list, sec_tag_count, pdn_id, + fragment_size, expected_type); +} + static int fota_download_object_init(void) { int err; diff --git a/tests/subsys/net/lib/fota_download/CMakeLists.txt b/tests/subsys/net/lib/fota_download/CMakeLists.txt index 311c43da4dc2..c2c0e0d43d43 100644 --- a/tests/subsys/net/lib/fota_download/CMakeLists.txt +++ b/tests/subsys/net/lib/fota_download/CMakeLists.txt @@ -42,6 +42,7 @@ target_compile_options(app -DCONFIG_FOTA_DOWNLOAD_RESOURCE_LOCATOR_LENGTH=512 -DCONFIG_FOTA_DOWNLOAD_FILE_NAME_LENGTH=128 -DCONFIG_FOTA_DOWNLOAD_HOST_NAME_LENGTH=128 + -DCONFIG_FOTA_DOWNLOAD_SEC_TAG_LIST_SIZE_MAX=5 ${info_magic} ${ext_api_magic} ) diff --git a/tests/subsys/net/lib/fota_download/src/test_fota_download.c b/tests/subsys/net/lib/fota_download/src/test_fota_download.c index f181860217cd..b69571f42c5f 100644 --- a/tests/subsys/net/lib/fota_download/src/test_fota_download.c +++ b/tests/subsys/net/lib/fota_download/src/test_fota_download.c @@ -20,7 +20,7 @@ static char buf[1024]; #define BASE_DOMAIN "something.com" -#define NO_TLS -1 +#define NO_TLS NULL #define ARBITRARY_IMAGE_OFFSET 512 /* Stubs and mocks */ @@ -285,7 +285,7 @@ static void init(void) * @param expected_selection - The resource locator that fota_download_start is expected to select * @param s0_active - If true, pretend that s0 is active. Otherwise, pretend that s1 is active. */ -static void test_fota_download_start_generic(const char * const resource_locator, +static void test_fota_download_any_generic(const char * const resource_locator, const char * const expected_selection, bool s0_active) { int err; @@ -297,7 +297,7 @@ static void test_fota_download_start_generic(const char * const resource_locator /* Start the download, check that it succeeds */ strcpy(buf, resource_locator); - err = fota_download_start(BASE_DOMAIN, buf, NO_TLS, 0, 0); + err = fota_download_any(BASE_DOMAIN, buf, NO_TLS, 0, 0, 0); zassert_equal(err, 0, NULL); /* Verify that the correct resource was selected */ @@ -323,17 +323,17 @@ ZTEST(fota_download_tests, test_fota_download_cancel_before_init) ZTEST(fota_download_tests, test_download_single) { - test_fota_download_start_generic(S0_A, S0_A, S1_ACTIVE); + test_fota_download_any_generic(S0_A, S0_A, S1_ACTIVE); } ZTEST(fota_download_tests, test_download_dual_s0_active) { - test_fota_download_start_generic(S0_B " " S1_B, S1_B, S0_ACTIVE); + test_fota_download_any_generic(S0_B " " S1_B, S1_B, S0_ACTIVE); } ZTEST(fota_download_tests, test_download_dual_s1_active) { - test_fota_download_start_generic(S0_C " " S1_C, S0_C, S1_ACTIVE); + test_fota_download_any_generic(S0_C " " S1_C, S0_C, S1_ACTIVE); } ZTEST(fota_download_tests, test_download_with_offset) @@ -359,7 +359,7 @@ ZTEST(fota_download_tests, test_download_with_offset) /* Check that application is being notified when * download_with_offset fails to get fw image offset */ - err = fota_download_start(BASE_DOMAIN, buf, NO_TLS, 0, 0); + err = fota_download_any(BASE_DOMAIN, buf, NO_TLS, 0, 0, 0); zassert_ok(err, NULL); err = download_client_event_handler(&evt); @@ -378,7 +378,7 @@ ZTEST(fota_download_tests, test_download_with_offset) /* Check that application is being notified when * download_with_offset fails to connect */ - err = fota_download_start(BASE_DOMAIN, buf, NO_TLS, 0, 0); + err = fota_download_any(BASE_DOMAIN, buf, NO_TLS, 0, 0, 0); zassert_ok(err, NULL); err = download_client_event_handler(&evt); @@ -396,7 +396,7 @@ ZTEST(fota_download_tests, test_download_with_offset) /* Check that application is being notified when * download_with_offset fails to start download */ - err = fota_download_start(BASE_DOMAIN, buf, NO_TLS, 0, 0); + err = fota_download_any(BASE_DOMAIN, buf, NO_TLS, 0, 0, 0); zassert_ok(err, NULL); err = download_client_event_handler(&evt); @@ -412,7 +412,7 @@ ZTEST(fota_download_tests, test_download_with_offset) /* Successfully restart download with offset */ - err = fota_download_start(BASE_DOMAIN, buf, NO_TLS, 0, 0); + err = fota_download_any(BASE_DOMAIN, buf, NO_TLS, 0, 0, 0); zassert_ok(err, NULL); err = download_client_event_handler(&evt);