From f77831d2ba97395c460bba1c432a988b9ddb08da Mon Sep 17 00:00:00 2001 From: Tomasz Chyrowicz Date: Tue, 14 Jan 2025 16:55:03 +0100 Subject: [PATCH] suit: Split fetch tests into app and SDFW Add tests that verify fetching into MEM component from both Application and SDFW contexts. Add smoke tests for IPUCs as MEM driver as well as cache backend. Ref: NCSDK-30809 Signed-off-by: Tomasz Chyrowicz --- tests/subsys/suit/fetch/CMakeLists.txt | 10 +- tests/subsys/suit/fetch/Kconfig | 35 ++ .../suit/fetch/boards/native_sim.overlay | 28 +- .../suit/fetch/boards/native_sim_64.overlay | 28 +- .../subsys/suit/fetch/mocks/mock_suit_ipuc.h | 29 ++ tests/subsys/suit/fetch/prj.conf | 9 +- tests/subsys/suit/fetch/src/main.c | 262 +--------- tests/subsys/suit/fetch/src/test_app_ipuc.c | 484 ++++++++++++++++++ tests/subsys/suit/fetch/src/test_sdfw_mem.c | 276 ++++++++++ tests/subsys/suit/fetch/testcase.yaml | 15 + 10 files changed, 898 insertions(+), 278 deletions(-) create mode 100644 tests/subsys/suit/fetch/mocks/mock_suit_ipuc.h create mode 100644 tests/subsys/suit/fetch/src/test_app_ipuc.c create mode 100644 tests/subsys/suit/fetch/src/test_sdfw_mem.c diff --git a/tests/subsys/suit/fetch/CMakeLists.txt b/tests/subsys/suit/fetch/CMakeLists.txt index a89f4f225a89..774c2027a99c 100644 --- a/tests/subsys/suit/fetch/CMakeLists.txt +++ b/tests/subsys/suit/fetch/CMakeLists.txt @@ -11,8 +11,14 @@ find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(integration_test_fetch) include(../cmake/test_template.cmake) +target_include_directories(app PRIVATE mocks) + zephyr_library_link_libraries(suit_memptr_storage_interface) zephyr_library_link_libraries(suit_platform_interface) zephyr_library_link_libraries(suit_ipuc) -zephyr_library_link_libraries(suit_stream_filters_interface) -zephyr_library_link_libraries(suit_decrypt_test_utils) + +zephyr_library_link_libraries_ifdef(CONFIG_TEST_SUIT_PLATFORM_FETCH_VARIANT_APP suit_memory_layout_interface) +zephyr_library_link_libraries_ifdef(CONFIG_TEST_SUIT_PLATFORM_FETCH_VARIANT_SDFW suit_decrypt_test_utils) +zephyr_library_link_libraries_ifdef(CONFIG_TEST_SUIT_PLATFORM_FETCH_VARIANT_SDFW suit_stream_filters_interface) + +zephyr_compile_definitions(CONFIG_SOC_NRF54H20_CPUAPP) diff --git a/tests/subsys/suit/fetch/Kconfig b/tests/subsys/suit/fetch/Kconfig index 8edc11bd7cde..2a0a29ea192e 100644 --- a/tests/subsys/suit/fetch/Kconfig +++ b/tests/subsys/suit/fetch/Kconfig @@ -8,4 +8,39 @@ rsource "../mocks/Kconfig" rsource "../tests/Kconfig" +choice TEST_SUIT_PLATFORM_FETCH_VARIANT + prompt "SUIT platform implementation variant to test" + default TEST_SUIT_PLATFORM_FETCH_VARIANT_SDFW + +config TEST_SUIT_PLATFORM_FETCH_VARIANT_APP + bool "Local or application" + depends on SUIT_PLATFORM_VARIANT_APP + select FLASH_IPUC + select SUIT_ENVELOPE_INFO + select SUIT_CACHE_RW + select SUIT_STREAM_SINK_CACHE + select SUIT_STREAM_FETCH_SOURCE_MGR + +config TEST_SUIT_PLATFORM_FETCH_VARIANT_SDFW + bool "Bootloader or SDFW" + depends on SUIT_PLATFORM_VARIANT_SDFW + select MOCK_SDFW_ARBITER + select SUIT_EXECUTION_MODE + select SUIT_MCI + select SUIT_STREAM_FILTER_DECRYPT + select SUIT_CRYPTO + select SUIT_STORAGE + +endchoice # TEST_SUIT_PLATFORM_FETCH_VARIANT + +if TEST_SUIT_PLATFORM_FETCH_VARIANT_APP + +config SUIT_CACHE_SDFW_IPUC_ID + default 254 + +config SUIT_CACHE_APP_IPUC_ID + default 255 + +endif # TEST_SUIT_PLATFORM_FETCH_VARIANT_APP + source "Kconfig.zephyr" diff --git a/tests/subsys/suit/fetch/boards/native_sim.overlay b/tests/subsys/suit/fetch/boards/native_sim.overlay index ac659f573154..369c537bd235 100644 --- a/tests/subsys/suit/fetch/boards/native_sim.overlay +++ b/tests/subsys/suit/fetch/boards/native_sim.overlay @@ -7,12 +7,30 @@ &flash0 { partitions { compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; + #address-cells = < 0x1 >; + #size-cells = < 0x1 >; - /* Use the last 48KB of NVM as suit storage. */ - suit_storage: partition@f4000 { - reg = <0xf4000 DT_SIZE_K(48)>; + suit_storage: partition@e9000 { + reg = <0xe9000 DT_SIZE_K(48)>; }; + + dfu_partition: partition@f5000 { + reg = < 0xf5000 DT_SIZE_K(4) >; + }; + + dfu_cache_partition_1: partition@f6000 { + reg = <0xf6000 DT_SIZE_K(4)>; + }; + + dfu_cache_partition_3: partition@f7000 { + reg = <0xf7000 DT_SIZE_K(4)>; + }; + }; +}; + +/ { + sram0: memory@20000000 { + compatible = "mmio-sram"; + reg = <0x20000000 DT_SIZE_K(256)>; }; }; diff --git a/tests/subsys/suit/fetch/boards/native_sim_64.overlay b/tests/subsys/suit/fetch/boards/native_sim_64.overlay index ac659f573154..369c537bd235 100644 --- a/tests/subsys/suit/fetch/boards/native_sim_64.overlay +++ b/tests/subsys/suit/fetch/boards/native_sim_64.overlay @@ -7,12 +7,30 @@ &flash0 { partitions { compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; + #address-cells = < 0x1 >; + #size-cells = < 0x1 >; - /* Use the last 48KB of NVM as suit storage. */ - suit_storage: partition@f4000 { - reg = <0xf4000 DT_SIZE_K(48)>; + suit_storage: partition@e9000 { + reg = <0xe9000 DT_SIZE_K(48)>; }; + + dfu_partition: partition@f5000 { + reg = < 0xf5000 DT_SIZE_K(4) >; + }; + + dfu_cache_partition_1: partition@f6000 { + reg = <0xf6000 DT_SIZE_K(4)>; + }; + + dfu_cache_partition_3: partition@f7000 { + reg = <0xf7000 DT_SIZE_K(4)>; + }; + }; +}; + +/ { + sram0: memory@20000000 { + compatible = "mmio-sram"; + reg = <0x20000000 DT_SIZE_K(256)>; }; }; diff --git a/tests/subsys/suit/fetch/mocks/mock_suit_ipuc.h b/tests/subsys/suit/fetch/mocks/mock_suit_ipuc.h new file mode 100644 index 000000000000..2405f8139b53 --- /dev/null +++ b/tests/subsys/suit/fetch/mocks/mock_suit_ipuc.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef MOCK_SUIT_IPUC_H__ +#define MOCK_SUIT_IPUC_H__ + +#include +#include + +DEFINE_FFF_GLOBALS; + +FAKE_VALUE_FUNC(int, suit_ipuc_get_count, size_t *); +FAKE_VALUE_FUNC(int, suit_ipuc_get_info, size_t, struct zcbor_string *, suit_manifest_role_t *); +FAKE_VALUE_FUNC(int, suit_ipuc_write_setup, struct zcbor_string *, struct zcbor_string *, + struct zcbor_string *); +FAKE_VALUE_FUNC(int, suit_ipuc_write, struct zcbor_string *, size_t, uintptr_t, size_t, bool); + +static inline void mock_suit_ipuc_reset(void) +{ + RESET_FAKE(suit_ipuc_get_count); + RESET_FAKE(suit_ipuc_get_info); + RESET_FAKE(suit_ipuc_write_setup); + RESET_FAKE(suit_ipuc_write); +} + +#endif /* MOCK_SUIT_IPUC_H__ */ diff --git a/tests/subsys/suit/fetch/prj.conf b/tests/subsys/suit/fetch/prj.conf index 39dfd781706c..7b8b600e6ccd 100644 --- a/tests/subsys/suit/fetch/prj.conf +++ b/tests/subsys/suit/fetch/prj.conf @@ -22,6 +22,8 @@ CONFIG_SUIT_UTILS=y CONFIG_SUIT_CACHE=y CONFIG_SUIT_MEMPTR_STORAGE=y +CONFIG_SUIT_LOG_LEVEL_DBG=y + CONFIG_ZCBOR=y CONFIG_ZCBOR_CANONICAL=y @@ -29,10 +31,3 @@ CONFIG_FLASH=y CONFIG_FLASH_MAP=y CONFIG_SUIT_IPUC=y -CONFIG_MOCK_SDFW_ARBITER=y -CONFIG_SUIT_STORAGE=y - -CONFIG_SUIT_EXECUTION_MODE=y -CONFIG_SUIT_MCI=y -CONFIG_SUIT_STREAM_FILTER_DECRYPT=y -CONFIG_SUIT_CRYPTO=y diff --git a/tests/subsys/suit/fetch/src/main.c b/tests/subsys/suit/fetch/src/main.c index 547c080ae02a..1f491589db22 100644 --- a/tests/subsys/suit/fetch/src/main.c +++ b/tests/subsys/suit/fetch/src/main.c @@ -8,15 +8,7 @@ #include #include #include -#include #include -#include -#include -#include -#include - -#define TEST_DATA_SIZE 64 -#define WRITE_ADDR 0x1A00080000 static uint8_t test_data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, @@ -91,58 +83,14 @@ static void setup_cache_with_sample_entries(void) dfu_caches.pools[1].size = (size_t)cache2_len; dfu_caches.pools_count = 2; + suit_dfu_cache_deinitialize(); + suit_plat_err_t rc = suit_dfu_cache_initialize(&dfu_caches); zassert_equal(rc, SUIT_PLAT_SUCCESS, "Failed to initialize cache: %i", rc); } -static void test_before(void *data) -{ - /* Reset mocks */ - mocks_sdfw_reset(); - - /* Reset common FFF internal structures */ - FFF_RESET_HISTORY(); -} - -ZTEST_SUITE(fetch_tests, NULL, NULL, test_before, NULL, NULL); - -ZTEST(fetch_tests, test_integrated_fetch_to_msink_OK) -{ - struct zcbor_string source = {.value = test_data, .len = sizeof(test_data)}; - /* Create handle that will be used as destination */ - suit_component_t dst_handle; - /* [h'MEM', h'02', h'1A00080000', h'191000'] */ - uint8_t valid_dst_value[] = {0x84, 0x44, 0x63, 'M', 'E', 'M', 0x41, 0x02, 0x45, - 0x1A, 0x00, 0x08, 0x00, 0x00, 0x43, 0x19, 0x10, 0x00}; - - struct zcbor_string valid_dst_component_id = { - .value = valid_dst_value, - .len = sizeof(valid_dst_value), - }; - - int ipc_client_id = 1234; - int ret = suit_plat_create_component_handle(&valid_dst_component_id, false, &dst_handle); - - zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); - ret = suit_ipuc_sdfw_write_setup(ipc_client_id, &valid_dst_component_id, NULL, NULL); - zassert_equal(ret, SUIT_PLAT_ERR_NOT_FOUND, "in-place updateable component found"); - - ret = suit_ipuc_sdfw_declare(dst_handle, SUIT_MANIFEST_UNKNOWN); - zassert_equal(ret, SUIT_PLAT_SUCCESS, "suit_ipuc_sdfw_declare failed - error %i", ret); - - ret = suit_ipuc_sdfw_write_setup(ipc_client_id, &valid_dst_component_id, NULL, NULL); - zassert_equal(ret, SUIT_PLAT_SUCCESS, "suit_ipuc_sdfw_write_setup failed - error %i", ret); - - ret = suit_plat_fetch_integrated(dst_handle, &source, &valid_dst_component_id, NULL); - zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); - - ret = suit_ipuc_sdfw_write_setup(ipc_client_id, &valid_dst_component_id, NULL, NULL); - zassert_equal(ret, SUIT_PLAT_ERR_NOT_FOUND, "in-place updateable component found"); - - ret = suit_plat_release_component_handle(dst_handle); - zassert_equal(ret, SUIT_SUCCESS, "dst_handle release failed - error %i", ret); -} +ZTEST_SUITE(fetch_tests, NULL, NULL, NULL, NULL, NULL); ZTEST(fetch_tests, test_integrated_fetch_to_memptr_OK) { @@ -356,207 +304,3 @@ ZTEST(fetch_tests, test_integrated_fetch_to_memptr_NOK_handle_NULL) ret = suit_plat_release_component_handle(component_handle); zassert_equal(ret, SUIT_SUCCESS, "Handle release failed - error %i", ret); } - -ZTEST(fetch_tests, test_integrated_fetch_to_msink_encrypted_OK) -{ - suit_component_t component_handle; - memptr_storage_handle_t handle = NULL; - struct zcbor_string source = { - .value = decrypt_test_ciphertext_direct, - .len = sizeof(decrypt_test_ciphertext_direct) - }; - - /* [h'MEM', h'02', h'1A00080000', h'191000'] */ - uint8_t valid_value[] = {0x84, 0x44, 0x63, 'M', 'E', 'M', 0x41, 0x02, 0x45, - 0x1A, 0x00, 0x08, 0x00, 0x00, 0x43, 0x19, 0x10, 0x00}; - - struct zcbor_string valid_component_id = { - .value = valid_value, - .len = sizeof(valid_value), - }; - - psa_key_id_t cek_key_id; - uint8_t cek_key_id_cbor[] = { - 0x1A, 0x00, 0x00, 0x00, 0x00, - }; - - psa_status_t status = psa_crypto_init(); - - zassert_equal(status, PSA_SUCCESS, "Failed to init psa crypto"); - - status = decrypt_test_init_encryption_key(decrypt_test_key_data, - sizeof(decrypt_test_key_data), &cek_key_id, - PSA_ALG_GCM, cek_key_id_cbor); - zassert_equal(status, PSA_SUCCESS, "Failed to import key"); - - struct suit_encryption_info enc_info = - DECRYPT_TEST_ENC_INFO_DEFAULT_INIT(cek_key_id_cbor); - - int ret = suit_plat_create_component_handle(&valid_component_id, false, &component_handle); - - zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); - - ret = suit_plat_fetch_integrated(component_handle, &source, - &valid_manifest_component_id, &enc_info); - zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); - - ret = suit_plat_component_impl_data_get(component_handle, &handle); - zassert_equal(ret, SUIT_SUCCESS, "suit_plat_component_impl_data_get failed - error %i", - ret); - - const uint8_t *payload; - size_t payload_size = 0; - - - ret = suit_memptr_storage_ptr_get(handle, &payload, &payload_size); - - zassert_equal(ret, SUIT_PLAT_SUCCESS, "storage.get failed - error %i", ret); - zassert_equal(memcmp(decrypt_test_plaintext, payload, - strlen(decrypt_test_plaintext)), 0, - "Retrieved decrypted payload doesn't mach decrypt_test_plaintext"); - zassert_equal(sizeof(decrypt_test_plaintext), payload_size, - "Retrieved payload_size doesn't mach size of decrypt_test_plaintext"); - - ret = suit_plat_release_component_handle(component_handle); - zassert_equal(ret, SUIT_SUCCESS, "Handle release failed - error %i", ret); - - psa_destroy_key(cek_key_id); -} - -ZTEST(fetch_tests, test_integrated_fetch_to_memptr_encrypted_NOK) -{ - suit_component_t component_handle; - memptr_storage_handle_t handle = NULL; - struct zcbor_string source = { - .value = decrypt_test_ciphertext_direct, - .len = sizeof(decrypt_test_ciphertext_direct) - }; - - /* [h'CAND_IMG', h'02'] */ - uint8_t valid_value[] = {0x82, 0x49, 0x68, 'C', 'A', 'N', 'D', - '_', 'I', 'M', 'G', 0x41, 0x02}; - - struct zcbor_string valid_component_id = { - .value = valid_value, - .len = sizeof(valid_value), - }; - - psa_key_id_t cek_key_id; - uint8_t cek_key_id_cbor[] = { - 0x1A, 0x00, 0x00, 0x00, 0x00, - }; - - psa_status_t status = psa_crypto_init(); - - zassert_equal(status, PSA_SUCCESS, "Failed to init psa crypto"); - - status = decrypt_test_init_encryption_key(decrypt_test_key_data, - sizeof(decrypt_test_key_data), &cek_key_id, - PSA_ALG_GCM, cek_key_id_cbor); - zassert_equal(status, PSA_SUCCESS, "Failed to import key"); - - struct suit_encryption_info enc_info = - DECRYPT_TEST_ENC_INFO_DEFAULT_INIT(cek_key_id_cbor); - - int ret = suit_plat_create_component_handle(&valid_component_id, false, &component_handle); - - zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); - - ret = suit_plat_fetch_integrated(component_handle, &source, - &valid_manifest_component_id, &enc_info); - zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); - - ret = suit_plat_component_impl_data_get(component_handle, &handle); - zassert_equal(ret, SUIT_SUCCESS, "suit_plat_component_impl_data_get failed - error %i", - ret); - - const uint8_t *payload; - size_t payload_size = 0; - - - ret = suit_memptr_storage_ptr_get(handle, &payload, &payload_size); - - zassert_equal(ret, SUIT_PLAT_SUCCESS, "storage.get failed - error %i", ret); - zassert_not_equal(memcmp(decrypt_test_plaintext, payload, - strlen(decrypt_test_plaintext)), 0, - "Retrieved decrypted payload should not mach decrypt_test_plaintext"); - zassert_equal(sizeof(decrypt_test_plaintext), payload_size, - "Retrieved payload_size doesn't mach size of decrypt_test_plaintext"); - - ret = suit_plat_release_component_handle(component_handle); - zassert_equal(ret, SUIT_SUCCESS, "Handle release failed - error %i", ret); - - psa_destroy_key(cek_key_id); -} - -ZTEST(fetch_tests, test_integrated_fetch_to_msink_encrypted_wrong_key_NOK) -{ - suit_component_t component_handle; - memptr_storage_handle_t handle = NULL; - struct zcbor_string source = { - .value = decrypt_test_ciphertext_direct, - .len = sizeof(decrypt_test_ciphertext_direct) - }; - - /* [h'MEM', h'02', h'1A00080000', h'191000'] */ - uint8_t valid_value[] = {0x84, 0x44, 0x63, 'M', 'E', 'M', 0x41, 0x02, 0x45, - 0x1A, 0x00, 0x08, 0x00, 0x00, 0x43, 0x19, 0x10, 0x00}; - - struct zcbor_string valid_component_id = { - .value = valid_value, - .len = sizeof(valid_value), - }; - - psa_key_id_t cek_key_id; - uint8_t cek_key_id_cbor[] = { - 0x1A, 0x00, 0x00, 0x00, 0x00, - }; - - psa_status_t status = psa_crypto_init(); - - zassert_equal(status, PSA_SUCCESS, "Failed to init psa crypto"); - - uint8_t wrong_test_key_data[sizeof(decrypt_test_key_data)]; - - memcpy(wrong_test_key_data, decrypt_test_key_data, sizeof(wrong_test_key_data)); - /* Corrupt CEK that we store.*/ - wrong_test_key_data[10] = 0x00; - - status = decrypt_test_init_encryption_key(wrong_test_key_data, - sizeof(wrong_test_key_data), &cek_key_id, - PSA_ALG_GCM, cek_key_id_cbor); - zassert_equal(status, PSA_SUCCESS, "Failed to import key"); - - struct suit_encryption_info enc_info = - DECRYPT_TEST_ENC_INFO_DEFAULT_INIT(cek_key_id_cbor); - - int ret = suit_plat_create_component_handle(&valid_component_id, false, &component_handle); - - zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); - - ret = suit_plat_fetch_integrated(component_handle, &source, - &valid_manifest_component_id, &enc_info); - zassert_equal(ret, SUIT_ERR_AUTHENTICATION, - "suit_plat_fetch should have failed with SUIT_ERR_AUTHENTICATION"); - - ret = suit_plat_component_impl_data_get(component_handle, &handle); - zassert_equal(ret, SUIT_SUCCESS, "suit_plat_component_impl_data_get failed - error %i", - ret); - - const uint8_t *payload; - size_t payload_size = 0; - - ret = suit_memptr_storage_ptr_get(handle, &payload, &payload_size); - - zassert_equal(ret, SUIT_PLAT_SUCCESS, "storage.get failed - error %i", ret); - zassert_not_equal(memcmp(decrypt_test_plaintext, payload, - strlen(decrypt_test_plaintext)), 0, - "Retrieved decrypted payload should not mach decrypt_test_plaintext"); - zassert_equal(payload_size, 0, - "Retrieved payload_size is not 0"); - - ret = suit_plat_release_component_handle(component_handle); - zassert_equal(ret, SUIT_SUCCESS, "Handle release failed - error %i", ret); - - psa_destroy_key(cek_key_id); -} diff --git a/tests/subsys/suit/fetch/src/test_app_ipuc.c b/tests/subsys/suit/fetch/src/test_app_ipuc.c new file mode 100644 index 000000000000..a09b656d249b --- /dev/null +++ b/tests/subsys/suit/fetch/src/test_app_ipuc.c @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifdef CONFIG_TEST_SUIT_PLATFORM_FETCH_VARIANT_APP +#include +#include +#include +#include +#include +#include +#include +#include + +#if IS_ENABLED(CONFIG_FLASH) +#if (DT_NODE_EXISTS(DT_CHOSEN(zephyr_flash_controller))) +#define SUIT_PLAT_INTERNAL_NVM_DEV DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller)) +#else +#define SUIT_PLAT_INTERNAL_NVM_DEV DEVICE_DT_GET(DT_CHOSEN(zephyr_flash)) +#endif +#else +#define SUIT_PLAT_INTERNAL_NVM_DEV NULL +#endif + +static const uint8_t test_data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; +static const uint8_t test_data_sdfw[] = {'S', 'D', 'F', 'W', '.', 'b', 'i', 'n'}; +static const uint8_t test_data_scfw[] = {'S', 'y', 's', 'C', 't', 'r', 'l', '.', 'b', 'i', 'n'}; + +/* clang-format off */ +static const uint8_t component_rad_0x80000_0x1000[] = { + 0x84, /* array(4) */ + 0x44, /* bstr(4) */ + 0x63, /* text(3) */ + 'M', 'E', 'M', + 0x41, /* bstr(1) */ + 0x20, /* Data */ + 0x45, /* bstr(5) */ + 0x1a, /* uint32 */ + 0x00, 0x08, 0x00, 0x00, + 0x43, /* bstr(3) */ + 0x19, /* uint16 */ + 0x10, 0x00, +}; +static const uint8_t component_app_0x90000_0x1000[] = { + 0x84, /* array(4) */ + 0x44, /* bstr(4) */ + 0x63, /* text(3) */ + 'M', 'E', 'M', + 0x41, /* bstr(1) */ + 0x20, /* Data */ + 0x45, /* bstr(5) */ + 0x1a, /* uint32 */ + 0x00, 0x09, 0x00, 0x00, + 0x43, /* bstr(3) */ + 0x19, /* uint16 */ + 0x10, 0x00, +}; +/* clang-format on */ + +static suit_plat_err_t suit_ipuc_get_count_single_fake_func(size_t *count) +{ + zassert_not_null(count, "Caller must provide non-Null pointer"); + + *count = 1; + + return SUIT_PLAT_SUCCESS; +} + +static suit_plat_err_t +suit_ipuc_get_info_0x80000_0x1000_fake_func(size_t idx, struct zcbor_string *component_id, + suit_manifest_role_t *role) +{ + zassert_not_null(component_id, "Caller must provide non-Null pointer to read component_id"); + zassert_not_null(role, "Caller must provide non-Null pointer to read role"); + zassert_equal(idx, 0, "Unexpected idx value"); + + component_id->value = component_rad_0x80000_0x1000; + component_id->len = sizeof(component_rad_0x80000_0x1000); + *role = SUIT_MANIFEST_RAD_LOCAL_1; + + return SUIT_PLAT_SUCCESS; +} + +static suit_plat_err_t suit_ipuc_write_rad_fake_func(struct zcbor_string *component_id, + size_t offset, uintptr_t buffer, + size_t chunk_size, bool last_chunk) +{ + zassert_not_null(component_id, "Caller must provide non-Null pointer to read component_id"); + zassert_equal(component_id->len, sizeof(component_rad_0x80000_0x1000), + "Invalid component ID length passed"); + zassert_equal(memcmp(component_id->value, component_rad_0x80000_0x1000, component_id->len), + 0, "Invalid component ID value passed"); + + return SUIT_PLAT_SUCCESS; +} + +static suit_plat_err_t suit_ipuc_get_count_fake_func(size_t *count) +{ + zassert_not_null(count, "Caller must provide non-Null pointer"); + + *count = 2; + + return SUIT_PLAT_SUCCESS; +} + +static suit_plat_err_t suit_ipuc_get_info_fake_func(size_t idx, struct zcbor_string *component_id, + suit_manifest_role_t *role) +{ + zassert_not_null(component_id, "Caller must provide non-Null pointer to read component_id"); + zassert_not_null(role, "Caller must provide non-Null pointer to read role"); + zassert_true(idx < 2, "Unexpected idx value"); + + if (idx == 0) { + component_id->value = component_rad_0x80000_0x1000; + component_id->len = sizeof(component_rad_0x80000_0x1000); + *role = SUIT_MANIFEST_RAD_LOCAL_1; + } else if (idx == 1) { + component_id->value = component_app_0x90000_0x1000; + component_id->len = sizeof(component_app_0x90000_0x1000); + *role = SUIT_MANIFEST_APP_LOCAL_1; + } + + return SUIT_PLAT_SUCCESS; +} + +static suit_plat_err_t suit_ipuc_write_fake_func(struct zcbor_string *component_id, size_t offset, + uintptr_t buffer, size_t chunk_size, + bool last_chunk) +{ + zassert_not_null(component_id, "Caller must provide non-Null pointer to read component_id"); + zassert_equal(component_id->len, sizeof(component_app_0x90000_0x1000), + "Invalid component ID length passed"); + zassert_equal(memcmp(component_id->value, component_app_0x90000_0x1000, component_id->len), + 0, "Invalid component ID value passed"); + + zassert_equal(flash_write(SUIT_PLAT_INTERNAL_NVM_DEV, 0x90000 + offset, (uint8_t *)buffer, + chunk_size), + 0, "Unable to modify NVM to match requested changes"); + + return SUIT_PLAT_SUCCESS; +} + +static int fetch_request_fn(const uint8_t *uri, size_t uri_length, uint32_t session_id) +{ + int rc = 0; + + if (((uri_length == sizeof("http://databucket.com")) && + memcmp(uri, "http://databucket.com", uri_length) == 0)) { + zassert_equal(uri_length, sizeof("http://databucket.com"), + "Unexpected URI length: %d", uri_length); + zassert_equal(memcmp(uri, "http://databucket.com", uri_length), 0, + "Unexpected URI value"); + rc = suit_dfu_fetch_source_write_fetched_data(session_id, test_data, + sizeof(test_data)); + } else if (((uri_length == sizeof("http://databucket.com/sdfw.bin")) && + memcmp(uri, "http://databucket.com/sdfw.bin", uri_length) == 0)) { + zassert_equal(uri_length, sizeof("http://databucket.com/sdfw.bin"), + "Unexpected URI length: %d", uri_length); + zassert_equal(memcmp(uri, "http://databucket.com/sdfw.bin", uri_length), 0, + "Unexpected URI value"); + rc = suit_dfu_fetch_source_write_fetched_data(session_id, test_data_sdfw, + sizeof(test_data_sdfw)); + } else if (((uri_length == sizeof("http://databucket.com/scfw.bin")) && + memcmp(uri, "http://databucket.com/scfw.bin", uri_length) == 0)) { + zassert_equal(uri_length, sizeof("http://databucket.com/scfw.bin"), + "Unexpected URI length: %d", uri_length); + zassert_equal(memcmp(uri, "http://databucket.com/scfw.bin", uri_length), 0, + "Unexpected URI value"); + rc = suit_dfu_fetch_source_write_fetched_data(session_id, test_data_scfw, + sizeof(test_data_scfw)); + } else { + zassert_true(false, "Unsupporte URI value"); + } + + if (rc != 0) { + return SUIT_PLAT_ERR_CRASH; + } + + return SUIT_PLAT_SUCCESS; +} + +static void test_before(void *data) +{ + /* Reset mocks */ + mock_suit_ipuc_reset(); + + /* Reset common FFF internal structures */ + FFF_RESET_HISTORY(); + + /* Erase NVM area */ + zassert_equal(flash_erase(SUIT_PLAT_INTERNAL_NVM_DEV, 0x90000, 0x1000), 0, + "Unable to erase NVM before test execution"); + + /* Load IPUCs as SUIT caches */ + suit_ipuc_get_count_fake.custom_fake = suit_ipuc_get_count_fake_func; + suit_ipuc_get_info_fake.custom_fake = suit_ipuc_get_info_fake_func; + + suit_dfu_cache_rw_init(); + zassert_equal(suit_dfu_cache_0_resize(), SUIT_PLAT_SUCCESS, + "Failed to initialize SUIT cache before test execution."); + zassert_equal(suit_ipuc_get_count_fake.call_count, 2, + "Incorrect number of suit_ipuc_get_count() calls (%d)", + suit_ipuc_get_count_fake.call_count); + /* 2x ((count: search for max) + (info of the max))*/ + zassert_equal(suit_ipuc_get_info_fake.call_count, 6, + "Incorrect number of suit_ipuc_get_info() calls (%d)", + suit_ipuc_get_info_fake.call_count); + + /* Reset mocks */ + mock_suit_ipuc_reset(); + + /* Reset common FFF internal structures */ + FFF_RESET_HISTORY(); +} + +static void test_after(void *data) +{ + uintptr_t cache_ipuc_address = (uintptr_t)suit_plat_mem_ptr_get(0x90000); + size_t cache_ipuc_size = 0x1000; + struct device *flash_ipuc = NULL; + uintptr_t ipuc_address = 0; + size_t ipuc_size = 0; + + /* SUIT cache IPUC is not released automatically as it is possible to populate cache + * with miltiple payloads. + * Relase them manually to preserve constant number of IPUCs for all test cases. + */ + flash_ipuc = + flash_ipuc_find(cache_ipuc_address, cache_ipuc_size, &ipuc_address, &ipuc_size); + if (flash_ipuc != NULL) { + flash_ipuc_release(flash_ipuc); + } +} + +ZTEST_SUITE(fetch_app_mem_tests, NULL, NULL, test_before, NULL, NULL); + +ZTEST(fetch_app_mem_tests, test_fetch_to_ipuc_mem_OK) +{ + struct zcbor_string uri = {.value = "http://databucket.com", + .len = sizeof("http://databucket.com")}; + /* Create handle that will be used as destination */ + suit_component_t dst_handle; + /* [h'MEM', h'20', h'1A00080000', h'191000'] */ + uint8_t valid_dst_value[] = {0x84, 0x44, 0x63, 'M', 'E', 'M', 0x41, 0x20, 0x45, + 0x1A, 0x00, 0x08, 0x00, 0x00, 0x43, 0x19, 0x10, 0x00}; + + struct zcbor_string valid_dst_component_id = { + .value = valid_dst_value, + .len = sizeof(valid_dst_value), + }; + + suit_ipuc_get_count_fake.custom_fake = suit_ipuc_get_count_single_fake_func; + suit_ipuc_get_info_fake.custom_fake = suit_ipuc_get_info_0x80000_0x1000_fake_func; + suit_ipuc_write_setup_fake.return_val = SUIT_PLAT_SUCCESS; + suit_ipuc_write_fake.custom_fake = suit_ipuc_write_rad_fake_func; + + zassert_equal(suit_dfu_fetch_source_register(fetch_request_fn), SUIT_PLAT_SUCCESS, + "Failed to register fetch source mock"); + + int ret = suit_plat_create_component_handle(&valid_dst_component_id, false, &dst_handle); + + zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); + + ret = suit_plat_fetch(dst_handle, &uri, &valid_dst_component_id, NULL); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); + + zassert_equal(suit_ipuc_get_count_fake.call_count, 2, + "Incorrect number of suit_ipuc_get_count() calls (%d)", + suit_ipuc_get_count_fake.call_count); + zassert_equal(suit_ipuc_get_info_fake.call_count, 2, + "Incorrect number of suit_ipuc_get_info() calls (%d)", + suit_ipuc_get_info_fake.call_count); + zassert_equal(suit_ipuc_write_setup_fake.call_count, 1, + "Incorrect number of suit_ipuc_write_setup() calls (%d)", + suit_ipuc_write_setup_fake.call_count); + zassert_equal(suit_ipuc_write_fake.call_count, 2, + "Incorrect number of suit_ipuc_write() calls (%d)", + suit_ipuc_write_fake.call_count); + + zassert_equal(suit_ipuc_write_fake.arg1_history[0], 0, + "Unexpected offset in the first suit_ipuc_write() call (0x%x)", + suit_ipuc_write_fake.arg1_history[0]); + zassert_equal_ptr(suit_ipuc_write_fake.arg2_history[0], test_data, + "Unexpected data pointer in the first suit_ipuc_write() call (0x%x)", + suit_ipuc_write_fake.arg2_history[0]); + zassert_equal(suit_ipuc_write_fake.arg3_history[0], sizeof(test_data), + "Unexpected data size in the first suit_ipuc_write() call (0x%x)", + suit_ipuc_write_fake.arg3_history[0]); + zassert_equal(suit_ipuc_write_fake.arg4_history[0], false, + "Write closed on the first suit_ipuc_write() call"); + + zassert_equal(suit_ipuc_write_fake.arg1_history[1], sizeof(test_data), + "Unexpected offset in the second suit_ipuc_write() call (0x%x)", + suit_ipuc_write_fake.arg1_history[1]); + zassert_equal_ptr(suit_ipuc_write_fake.arg2_history[1], NULL, + "Unexpected data pointer in the second suit_ipuc_write() call (0x%x)", + suit_ipuc_write_fake.arg2_history[1]); + zassert_equal(suit_ipuc_write_fake.arg3_history[1], 0, + "Unexpected data size in the second suit_ipuc_write() call (0x%x)", + suit_ipuc_write_fake.arg3_history[1]); + zassert_equal(suit_ipuc_write_fake.arg4_history[1], true, + "Write was not closed on the second suit_ipuc_write() call"); + + ret = suit_plat_release_component_handle(dst_handle); + zassert_equal(ret, SUIT_SUCCESS, "dst_handle release failed - error %i", ret); +} + +ZTEST_SUITE(fetch_app_cache_tests, NULL, NULL, test_before, test_after, NULL); + +ZTEST(fetch_app_cache_tests, test_fetch_to_ipuc_cache_OK) +{ + struct zcbor_string uri = {.value = "http://databucket.com", + .len = sizeof("http://databucket.com")}; + /* Create handle that will be used as destination */ + suit_component_t dst_handle; + /* clang-format off */ + uint8_t valid_dst_value[] = { + 0x82, /* array(2) */ + 0x4B, /* bytes(11) */ + 0x6A, /* text(10) */ + 'C', 'A', 'C', 'H', 'E', '_', 'P', 'O', 'O', 'L', + 0x42, /* bytes(2) */ + 0x18, /* uint8_t */ + CONFIG_SUIT_CACHE_APP_IPUC_ID, + }; + /* clang-format on */ + struct zcbor_string valid_dst_component_id = { + .value = valid_dst_value, + .len = sizeof(valid_dst_value), + }; + size_t cached_size = 0; + const uint8_t *cached_payload = NULL; + + suit_ipuc_get_count_fake.custom_fake = suit_ipuc_get_count_fake_func; + suit_ipuc_get_info_fake.custom_fake = suit_ipuc_get_info_fake_func; + suit_ipuc_write_setup_fake.return_val = SUIT_PLAT_SUCCESS; + suit_ipuc_write_fake.custom_fake = suit_ipuc_write_fake_func; + + zassert_equal(suit_dfu_fetch_source_register(fetch_request_fn), SUIT_PLAT_SUCCESS, + "Failed to register fetch source mock"); + + int ret = suit_plat_create_component_handle(&valid_dst_component_id, false, &dst_handle); + + zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); + + ret = suit_plat_fetch(dst_handle, &uri, &valid_dst_component_id, NULL); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); + + zassert_equal(suit_ipuc_write_setup_fake.call_count, 1, + "Incorrect number of suit_ipuc_write_setup() calls (%d)", + suit_ipuc_write_setup_fake.call_count); + zassert_equal(suit_ipuc_get_count_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_count() calls (%d)", + suit_ipuc_get_count_fake.call_count); + zassert_equal(suit_ipuc_get_info_fake.call_count, 3, + "Incorrect number of suit_ipuc_get_info() calls (%d)", + suit_ipuc_get_info_fake.call_count); + + zassert_true(suit_ipuc_write_fake.call_count >= 4, + "Too small number of suit_ipuc_write() calls (%d)", + suit_ipuc_write_fake.call_count); + + size_t last_write = suit_ipuc_write_fake.call_count - 1; + + for (size_t i = 0; i < suit_ipuc_write_fake.call_count; i++) { + printk("\t%d\n", suit_ipuc_write_fake.arg3_history[i]); + zassert_equal(suit_ipuc_write_fake.arg4_history[i], false, + "Write closed too early in suit_ipuc_write() call"); + } + + /* partition header + slot header + URI + (data length) */ + /* data */ + + /* slot length */ + zassert_equal(suit_ipuc_write_fake.arg3_history[last_write - 1], 4, + "Unexpected data size in the third suit_ipuc_write() call (0x%x)", + suit_ipuc_write_fake.arg3_history[last_write - 1]); + + /* slot end marker */ + zassert_equal(suit_ipuc_write_fake.arg3_history[last_write], 1, + "Unexpected data size in the fourth suit_ipuc_write() call (0x%x)", + suit_ipuc_write_fake.arg3_history[last_write]); + + ret = suit_plat_release_component_handle(dst_handle); + zassert_equal(ret, SUIT_SUCCESS, "dst_handle release failed - error %i", ret); + + /* Verify the cache contents. */ + zassert_equal(suit_dfu_cache_search(uri.value, uri.len, &cached_payload, &cached_size), + SUIT_PLAT_SUCCESS, "Failed to find cached entry inside the SUIT cache"); + zassert_equal(cached_size, sizeof(test_data), "Invalid size of the cached data (%d)", + cached_size); + zassert_equal(memcmp(cached_payload, test_data, sizeof(test_data)), 0, + "Invalid cached payload"); +} + +ZTEST(fetch_app_cache_tests, test_fetch_to_ipuc_cache_sdfw_scfw_OK) +{ + struct zcbor_string uri_sdfw = {.value = "http://databucket.com/sdfw.bin", + .len = sizeof("http://databucket.com/sdfw.bin")}; + struct zcbor_string uri_scfw = {.value = "http://databucket.com/scfw.bin", + .len = sizeof("http://databucket.com/scfw.bin")}; + /* Create handle that will be used as destination */ + suit_component_t dst_handle; + /* clang-format off */ + uint8_t valid_dst_value[] = { + 0x82, /* array(2) */ + 0x4B, /* bytes(11) */ + 0x6A, /* text(10) */ + 'C', 'A', 'C', 'H', 'E', '_', 'P', 'O', 'O', 'L', + 0x42, /* bytes(2) */ + 0x18, /* uint8_t */ + CONFIG_SUIT_CACHE_SDFW_IPUC_ID, + }; + /* clang-format on */ + struct zcbor_string valid_dst_component_id = { + .value = valid_dst_value, + .len = sizeof(valid_dst_value), + }; + size_t cached_size = 0; + const uint8_t *cached_payload = NULL; + + suit_ipuc_get_count_fake.custom_fake = suit_ipuc_get_count_fake_func; + suit_ipuc_get_info_fake.custom_fake = suit_ipuc_get_info_fake_func; + suit_ipuc_write_setup_fake.return_val = SUIT_PLAT_SUCCESS; + suit_ipuc_write_fake.custom_fake = suit_ipuc_write_fake_func; + + zassert_equal(suit_dfu_fetch_source_register(fetch_request_fn), SUIT_PLAT_SUCCESS, + "Failed to register fetch source mock"); + + int ret = suit_plat_create_component_handle(&valid_dst_component_id, false, &dst_handle); + + zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); + + /* Fetch SDFW binary */ + ret = suit_plat_fetch(dst_handle, &uri_sdfw, &valid_dst_component_id, NULL); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); + + zassert_equal(suit_ipuc_write_setup_fake.call_count, 1, + "Incorrect number of suit_ipuc_write_setup() calls (%d)", + suit_ipuc_write_setup_fake.call_count); + zassert_equal(suit_ipuc_get_count_fake.call_count, 1, + "Incorrect number of suit_ipuc_get_count() calls (%d)", + suit_ipuc_get_count_fake.call_count); + zassert_equal(suit_ipuc_get_info_fake.call_count, 3, + "Incorrect number of suit_ipuc_get_info() calls (%d)", + suit_ipuc_get_info_fake.call_count); + + /* Fetch SCFW binary using the same IPUC and component context */ + ret = suit_plat_fetch(dst_handle, &uri_scfw, &valid_dst_component_id, NULL); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); + + /* The write_setup call count should remain the same - IPUC must not be erased at this + * point. + */ + zassert_equal(suit_ipuc_write_setup_fake.call_count, 1, + "Incorrect number of suit_ipuc_write_setup() calls (%d)", + suit_ipuc_write_setup_fake.call_count); + + ret = suit_plat_release_component_handle(dst_handle); + zassert_equal(ret, SUIT_SUCCESS, "dst_handle release failed - error %i", ret); + + /* Verify the cache contents. */ + zassert_equal( + suit_dfu_cache_search(uri_sdfw.value, uri_sdfw.len, &cached_payload, &cached_size), + SUIT_PLAT_SUCCESS, "Failed to find SDFW entry inside the SUIT cache"); + zassert_equal(cached_size, sizeof(test_data_sdfw), "Invalid SDFW size in cache (%d)", + cached_size); + zassert_equal(memcmp(cached_payload, test_data_sdfw, sizeof(test_data_sdfw)), 0, + "Invalid SDFW payload"); + + zassert_equal( + suit_dfu_cache_search(uri_scfw.value, uri_scfw.len, &cached_payload, &cached_size), + SUIT_PLAT_SUCCESS, "Failed to find SCFW entry inside the SUIT cache"); + zassert_equal(cached_size, sizeof(test_data_scfw), "Invalid SCFW size in cache (%d)", + cached_size); + zassert_equal(memcmp(cached_payload, test_data_scfw, sizeof(test_data_scfw)), 0, + "Invalid SCFW payload"); +} +#endif /* CONFIG_TEST_SUIT_PLATFORM_FETCH_VARIANT_APP */ diff --git a/tests/subsys/suit/fetch/src/test_sdfw_mem.c b/tests/subsys/suit/fetch/src/test_sdfw_mem.c new file mode 100644 index 000000000000..bb89723a0dd4 --- /dev/null +++ b/tests/subsys/suit/fetch/src/test_sdfw_mem.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifdef CONFIG_TEST_SUIT_PLATFORM_FETCH_VARIANT_SDFW +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static uint8_t test_data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}; + +/* clang-format off */ +static const uint8_t valid_manifest_component[] = { + 0x82, /* array: 2 elements */ + 0x4c, /* byte string: 12 bytes */ + 0x6b, /* string: 11 characters */ + 'I', 'N', 'S', 'T', 'L', 'D', '_', 'M', 'F', 'S', 'T', + 0x50, /* byte string: 16 bytes */ + 0x5b, 0x46, 0x9f, 0xd1, 0x90, 0xee, 0x53, 0x9c, + 0xa3, 0x18, 0x68, 0x1b, 0x03, 0x69, 0x5e, 0x36 +}; +/* clang-format on */ + +static struct zcbor_string valid_manifest_component_id = { + .value = valid_manifest_component, + .len = sizeof(valid_manifest_component), +}; + +static void test_before(void *data) +{ + /* Reset mocks */ + mocks_sdfw_reset(); + + /* Reset common FFF internal structures */ + FFF_RESET_HISTORY(); +} + +ZTEST_SUITE(fetch_sdfw_mem_tests, NULL, NULL, test_before, NULL, NULL); + +ZTEST(fetch_sdfw_mem_tests, test_integrated_fetch_to_msink_OK) +{ + struct zcbor_string source = {.value = test_data, .len = sizeof(test_data)}; + /* Create handle that will be used as destination */ + suit_component_t dst_handle; + /* [h'MEM', h'02', h'1A00080000', h'191000'] */ + uint8_t valid_dst_value[] = {0x84, 0x44, 0x63, 'M', 'E', 'M', 0x41, 0x02, 0x45, + 0x1A, 0x00, 0x08, 0x00, 0x00, 0x43, 0x19, 0x10, 0x00}; + + struct zcbor_string valid_dst_component_id = { + .value = valid_dst_value, + .len = sizeof(valid_dst_value), + }; + + int ipc_client_id = 1234; + int ret = suit_plat_create_component_handle(&valid_dst_component_id, false, &dst_handle); + + zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); + ret = suit_ipuc_sdfw_write_setup(ipc_client_id, &valid_dst_component_id, NULL, NULL); + zassert_equal(ret, SUIT_PLAT_ERR_NOT_FOUND, "in-place updateable component found"); + + ret = suit_ipuc_sdfw_declare(dst_handle, SUIT_MANIFEST_UNKNOWN); + zassert_equal(ret, SUIT_PLAT_SUCCESS, "suit_ipuc_sdfw_declare failed - error %i", ret); + + ret = suit_ipuc_sdfw_write_setup(ipc_client_id, &valid_dst_component_id, NULL, NULL); + zassert_equal(ret, SUIT_PLAT_SUCCESS, "suit_ipuc_sdfw_write_setup failed - error %i", ret); + + ret = suit_plat_fetch_integrated(dst_handle, &source, &valid_dst_component_id, NULL); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); + + ret = suit_ipuc_sdfw_write_setup(ipc_client_id, &valid_dst_component_id, NULL, NULL); + zassert_equal(ret, SUIT_PLAT_ERR_NOT_FOUND, "in-place updateable component found"); + + ret = suit_plat_release_component_handle(dst_handle); + zassert_equal(ret, SUIT_SUCCESS, "dst_handle release failed - error %i", ret); +} + +ZTEST(fetch_tests, test_integrated_fetch_to_msink_encrypted_OK) +{ + suit_component_t component_handle; + memptr_storage_handle_t handle = NULL; + struct zcbor_string source = {.value = decrypt_test_ciphertext_direct, + .len = sizeof(decrypt_test_ciphertext_direct)}; + + /* [h'MEM', h'02', h'1A00080000', h'191000'] */ + uint8_t valid_value[] = {0x84, 0x44, 0x63, 'M', 'E', 'M', 0x41, 0x02, 0x45, + 0x1A, 0x00, 0x08, 0x00, 0x00, 0x43, 0x19, 0x10, 0x00}; + + struct zcbor_string valid_component_id = { + .value = valid_value, + .len = sizeof(valid_value), + }; + + psa_key_id_t cek_key_id; + uint8_t cek_key_id_cbor[] = { + 0x1A, 0x00, 0x00, 0x00, 0x00, + }; + + psa_status_t status = psa_crypto_init(); + + zassert_equal(status, PSA_SUCCESS, "Failed to init psa crypto"); + + status = decrypt_test_init_encryption_key(decrypt_test_key_data, + sizeof(decrypt_test_key_data), &cek_key_id, + PSA_ALG_GCM, cek_key_id_cbor); + zassert_equal(status, PSA_SUCCESS, "Failed to import key"); + + struct suit_encryption_info enc_info = DECRYPT_TEST_ENC_INFO_DEFAULT_INIT(cek_key_id_cbor); + + int ret = suit_plat_create_component_handle(&valid_component_id, false, &component_handle); + + zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); + + ret = suit_plat_fetch_integrated(component_handle, &source, &valid_manifest_component_id, + &enc_info); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); + + ret = suit_plat_component_impl_data_get(component_handle, &handle); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_component_impl_data_get failed - error %i", + ret); + + const uint8_t *payload; + size_t payload_size = 0; + + ret = suit_memptr_storage_ptr_get(handle, &payload, &payload_size); + + zassert_equal(ret, SUIT_PLAT_SUCCESS, "storage.get failed - error %i", ret); + zassert_equal(memcmp(decrypt_test_plaintext, payload, strlen(decrypt_test_plaintext)), 0, + "Retrieved decrypted payload doesn't mach decrypt_test_plaintext"); + zassert_equal(sizeof(decrypt_test_plaintext), payload_size, + "Retrieved payload_size doesn't mach size of decrypt_test_plaintext"); + + ret = suit_plat_release_component_handle(component_handle); + zassert_equal(ret, SUIT_SUCCESS, "Handle release failed - error %i", ret); + + psa_destroy_key(cek_key_id); +} + +ZTEST(fetch_tests, test_integrated_fetch_to_memptr_encrypted_NOK) +{ + suit_component_t component_handle; + memptr_storage_handle_t handle = NULL; + struct zcbor_string source = {.value = decrypt_test_ciphertext_direct, + .len = sizeof(decrypt_test_ciphertext_direct)}; + + /* [h'CAND_IMG', h'02'] */ + uint8_t valid_value[] = {0x82, 0x49, 0x68, 'C', 'A', 'N', 'D', + '_', 'I', 'M', 'G', 0x41, 0x02}; + + struct zcbor_string valid_component_id = { + .value = valid_value, + .len = sizeof(valid_value), + }; + + psa_key_id_t cek_key_id; + uint8_t cek_key_id_cbor[] = { + 0x1A, 0x00, 0x00, 0x00, 0x00, + }; + + psa_status_t status = psa_crypto_init(); + + zassert_equal(status, PSA_SUCCESS, "Failed to init psa crypto"); + + status = decrypt_test_init_encryption_key(decrypt_test_key_data, + sizeof(decrypt_test_key_data), &cek_key_id, + PSA_ALG_GCM, cek_key_id_cbor); + zassert_equal(status, PSA_SUCCESS, "Failed to import key"); + + struct suit_encryption_info enc_info = DECRYPT_TEST_ENC_INFO_DEFAULT_INIT(cek_key_id_cbor); + + int ret = suit_plat_create_component_handle(&valid_component_id, false, &component_handle); + + zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); + + ret = suit_plat_fetch_integrated(component_handle, &source, &valid_manifest_component_id, + &enc_info); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret); + + ret = suit_plat_component_impl_data_get(component_handle, &handle); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_component_impl_data_get failed - error %i", + ret); + + const uint8_t *payload; + size_t payload_size = 0; + + ret = suit_memptr_storage_ptr_get(handle, &payload, &payload_size); + + zassert_equal(ret, SUIT_PLAT_SUCCESS, "storage.get failed - error %i", ret); + zassert_not_equal(memcmp(decrypt_test_plaintext, payload, strlen(decrypt_test_plaintext)), + 0, "Retrieved decrypted payload should not mach decrypt_test_plaintext"); + zassert_equal(sizeof(decrypt_test_plaintext), payload_size, + "Retrieved payload_size doesn't mach size of decrypt_test_plaintext"); + + ret = suit_plat_release_component_handle(component_handle); + zassert_equal(ret, SUIT_SUCCESS, "Handle release failed - error %i", ret); + + psa_destroy_key(cek_key_id); +} + +ZTEST(fetch_tests, test_integrated_fetch_to_msink_encrypted_wrong_key_NOK) +{ + suit_component_t component_handle; + memptr_storage_handle_t handle = NULL; + struct zcbor_string source = {.value = decrypt_test_ciphertext_direct, + .len = sizeof(decrypt_test_ciphertext_direct)}; + + /* [h'MEM', h'02', h'1A00080000', h'191000'] */ + uint8_t valid_value[] = {0x84, 0x44, 0x63, 'M', 'E', 'M', 0x41, 0x02, 0x45, + 0x1A, 0x00, 0x08, 0x00, 0x00, 0x43, 0x19, 0x10, 0x00}; + + struct zcbor_string valid_component_id = { + .value = valid_value, + .len = sizeof(valid_value), + }; + + psa_key_id_t cek_key_id; + uint8_t cek_key_id_cbor[] = { + 0x1A, 0x00, 0x00, 0x00, 0x00, + }; + + psa_status_t status = psa_crypto_init(); + + zassert_equal(status, PSA_SUCCESS, "Failed to init psa crypto"); + + uint8_t wrong_test_key_data[sizeof(decrypt_test_key_data)]; + + memcpy(wrong_test_key_data, decrypt_test_key_data, sizeof(wrong_test_key_data)); + /* Corrupt CEK that we store.*/ + wrong_test_key_data[10] = 0x00; + + status = decrypt_test_init_encryption_key(wrong_test_key_data, sizeof(wrong_test_key_data), + &cek_key_id, PSA_ALG_GCM, cek_key_id_cbor); + zassert_equal(status, PSA_SUCCESS, "Failed to import key"); + + struct suit_encryption_info enc_info = DECRYPT_TEST_ENC_INFO_DEFAULT_INIT(cek_key_id_cbor); + + int ret = suit_plat_create_component_handle(&valid_component_id, false, &component_handle); + + zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret); + + ret = suit_plat_fetch_integrated(component_handle, &source, &valid_manifest_component_id, + &enc_info); + zassert_equal(ret, SUIT_ERR_AUTHENTICATION, + "suit_plat_fetch should have failed with SUIT_ERR_AUTHENTICATION"); + + ret = suit_plat_component_impl_data_get(component_handle, &handle); + zassert_equal(ret, SUIT_SUCCESS, "suit_plat_component_impl_data_get failed - error %i", + ret); + + const uint8_t *payload; + size_t payload_size = 0; + + ret = suit_memptr_storage_ptr_get(handle, &payload, &payload_size); + + zassert_equal(ret, SUIT_PLAT_SUCCESS, "storage.get failed - error %i", ret); + zassert_not_equal(memcmp(decrypt_test_plaintext, payload, strlen(decrypt_test_plaintext)), + 0, "Retrieved decrypted payload should not mach decrypt_test_plaintext"); + zassert_equal(payload_size, 0, "Retrieved payload_size is not 0"); + + ret = suit_plat_release_component_handle(component_handle); + zassert_equal(ret, SUIT_SUCCESS, "Handle release failed - error %i", ret); + + psa_destroy_key(cek_key_id); +} +#endif /* CONFIG_TEST_SUIT_PLATFORM_FETCH_VARIANT_SDFW */ diff --git a/tests/subsys/suit/fetch/testcase.yaml b/tests/subsys/suit/fetch/testcase.yaml index 3cea0c3522ae..293e472e1b92 100644 --- a/tests/subsys/suit/fetch/testcase.yaml +++ b/tests/subsys/suit/fetch/testcase.yaml @@ -13,3 +13,18 @@ tests: - nrf52840dk/nrf52840 - native_sim - native_sim/native/64 + + suit-platform.integration.fetch.app: + platform_allow: + - native_sim + - native_sim/native/64 + tags: + - suit-processor + - suit_platform + - suit + - ci_tests_subsys_suit + extra_configs: + - CONFIG_SUIT_PLATFORM_VARIANT_APP=y + integration_platforms: + - native_sim + - native_sim/native/64