Skip to content

Commit

Permalink
tests: Extend SUIT tests for fetch, copy and write with decryption
Browse files Browse the repository at this point in the history
This commit extends integration tests for fetch, copy and write
SUIT directives with test cases for payload decryption.
Common test module decrypt_test_utils.c was added with default
constants and utilities for decryption tests to use.
Refactor of decrypt_filter/src/main.c was made because of this
common module.

Ref: NCSDK-31276

Signed-off-by: Michal Kozikowski <[email protected]>
  • Loading branch information
nordic-mik7 committed Jan 17, 2025
1 parent 35facd1 commit 83fecc4
Show file tree
Hide file tree
Showing 15 changed files with 673 additions and 174 deletions.
2 changes: 2 additions & 0 deletions tests/subsys/suit/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

add_subdirectory(mci_test)
add_subdirectory(validator_test)
add_subdirectory(decrypt_utils)
zephyr_include_directories(${CMAKE_CURRENT_LIST_DIR}/include)

if (CONFIG_MBEDTLS)
Expand All @@ -20,6 +21,7 @@ endif()

if (CONFIG_SUIT_STREAM_FILTER_DECRYPT)
target_link_libraries(suit_stream_filters_interface INTERFACE mbedTLS)
target_link_libraries(suit_decrypt_test_utils INTERFACE mbedTLS)
endif()

if (CONFIG_SUIT_STORAGE_LAYOUT_NRF54H20)
Expand Down
11 changes: 11 additions & 0 deletions tests/subsys/suit/common/decrypt_utils/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Copyright (c) 2025 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

if(CONFIG_SUIT_STREAM_FILTER_DECRYPT)
zephyr_library_named(suit_decrypt_test_utils)
zephyr_include_directories(.)
zephyr_library_sources(decrypt_test_utils.c)
zephyr_library_link_libraries(suit_metadata)
endif()
132 changes: 132 additions & 0 deletions tests/subsys/suit/common/decrypt_utils/decrypt_test_utils.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/

#include "decrypt_test_utils.h"

/* This module holds common utilities for SUIT decrypt filter tests.
* It defines default encryption key data, plaintext and ciphertext along with other
* suit_encryption_info struct members that can be used while testing.
*
* WARNING: All of the const values defined in this module CAN be changed freely.
* This also means that any test depending on them should expect it.
*/

/**
* The master key used by these tests can be imported into the local KMS backend by running:
*
* nrfkms import_keyvalue -k TEST_AES_KEY -t aes -v aHWJdIkl5hdXw4SS1nTdVYE/q7ycMOZm2mR6qx/KvKw=
*
* The KEK below is derived from context "test"
* To acquire it run:
* nrfkms export_derived -k TEST_AES_KEY -c test --format native
* hexdump -e '16/1 "0x%02x, " "\n"' kms_output/derived_key_native_test_from_TEST_AES_KEY.bin
*/
const uint8_t decrypt_test_key_data[] = {
0xf8, 0xfa, 0x8e, 0x7b, 0xed, 0x32, 0xd0, 0xc7, 0x15, 0x1f, 0xd9, 0xab, 0x0d,
0x8d, 0xed, 0x95, 0x26, 0xa8, 0x6a, 0x15, 0x34, 0x16, 0x01, 0xcf, 0x9c, 0x6b,
0xba, 0x00, 0x6a, 0xab, 0xaa, 0x9a,
};

const uint8_t decrypt_test_plaintext[] = {
"This is a sample plaintext for testing the decryption filter",
};

const uint8_t decrypt_test_aad[] = {
"sample aad"
};

/**
* Encryption and using wrapped CEK achieved by running:
*
* echo "This is a sample plaintext for testing the decryption filter" > plaintext.txt
* nrfkms wrap -k TEST_AES_KEY -c test -f plaintext.txt --format native -t aes --aad "sample aad"
*
* Wrapped CEK stored in the resulting wrapped_aek-aes-... file
*
* Ciphertext and NONCE (IV) taken from the encrypted_asset-... file, which is in format
* |nonce (12 bytes)|ciphertext|tag (16 bytes)|
*
*/
const uint8_t decrypt_test_wrapped_cek[] = {
0x7d, 0xd6, 0xf4, 0xd3, 0x52, 0x44, 0x5a, 0x3a, 0x67, 0xb8, 0xcc,
0x74, 0x5b, 0x4b, 0x6f, 0x70, 0x62, 0xc3, 0xf2, 0x7b, 0x6b, 0x14,
0xf1, 0x06, 0x57, 0xa3, 0x68, 0x32, 0x44, 0xc3, 0x85, 0x77, 0x86,
0xe7, 0xda, 0x15, 0xbf, 0xf8, 0x9e, 0x63,
};

const uint8_t decrypt_test_ciphertext_aes_kw[] = {
/* tag (16 bytes) */
0xdc, 0xe6, 0x95, 0xac, 0x0f, 0x61, 0x87, 0x17, 0x51, 0x48, 0xb4, 0xa1,
0x8e, 0x09, 0x89, 0xb4,
/* ciphertext */
0x8b, 0xfb, 0xd9, 0xe4, 0xcf, 0xde, 0xf8, 0xcf, 0xe5, 0x69, 0x9d, 0x6d,
0x92, 0x8a, 0x04, 0xf8, 0x26, 0x22, 0xd5, 0xd8, 0xe8, 0x77, 0x18, 0x5a,
0x01, 0x13, 0xba, 0xd5, 0x23, 0x72, 0xae, 0x80, 0x44, 0xed, 0xea, 0xdf,
0x74, 0x79, 0x8a, 0x83, 0x52, 0x72, 0x2f, 0x43, 0x06, 0xe9, 0xd4, 0xbb,
0x54, 0x8a, 0x0d, 0xea, 0x7f, 0xe6, 0x48, 0xf0, 0xfd, 0x0e, 0xbb, 0xaa,
0xa3,
};

const uint8_t decrypt_test_iv_aes_kw[] = {
0x61, 0xb4, 0x70, 0x53, 0xa5, 0xe2, 0x05, 0x68, 0xfe, 0x77, 0x12, 0x89,
};

/**
* Encryption without wrapping CEK achieved by running:
*
* echo "This is a sample plaintext for testing the decryption filter" > plaintext.txt
* nrfkms encrypt -k TEST_AES_KEY -c test -f plaintext.txt --aad "sample aad" --format native
*
* Ciphertext and NONCE (IV) taken from the encrypted_data_using_TEST_AES_KEY-test.bin file,
* which is in format |nonce (12 bytes)|tag (16 bytes)|ciphertext|
*/

const uint8_t decrypt_test_ciphertext_direct[] = {
/* tag (16 bytes) */
0x4d, 0x21, 0x30, 0xb7, 0xce, 0x8a, 0xd6, 0x00, 0xe4, 0x04, 0xbb, 0x32,
0x72, 0x7a, 0xbb, 0x7c,
/* ciphertext */
0xf0, 0x72, 0xdb, 0x63, 0x03, 0xdd, 0x24, 0x69,
0xd4, 0xbf, 0xd7, 0xa0, 0xec, 0xfa, 0x66, 0x58, 0x95, 0x2b, 0xc1, 0xc2,
0x9d, 0x82, 0x02, 0x1a, 0xd7, 0x5b, 0xc0, 0x01, 0xce, 0x0b, 0x79, 0x53,
0xe7, 0xdb, 0x0d, 0x35, 0xab, 0xef, 0x81, 0xc8, 0x68, 0xc5, 0xa7, 0x22,
0x90, 0xea, 0xd0, 0x7f, 0x36, 0xed, 0x14, 0xbe, 0x30, 0xf2, 0x81, 0x56,
0x7e, 0x2e, 0x5f, 0xd8, 0x7c,
};

const uint8_t decrypt_test_iv_direct[] = {
0x60, 0x90, 0x6d, 0xb2, 0xfe, 0xc3, 0xc8, 0x5a, 0xf0, 0x28, 0xb1, 0xb6,
};

const suit_manifest_class_id_t decrypt_test_sample_class_id = {
{0x5b, 0x46, 0x9f, 0xd1, 0x90, 0xee, 0x53, 0x9c, 0xa3, 0x18, 0x68, 0x1b, 0x03, 0x69, 0x5e,
0x36}};

psa_status_t decrypt_test_init_encryption_key(const uint8_t *data, size_t size,
psa_key_id_t *key_id, psa_key_id_t alg, uint8_t *cbor_key_id)
{
psa_status_t status;

/* Configure the key attributes */
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;

psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_VOLATILE);
psa_set_key_algorithm(&key_attributes, alg);
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_AES);
psa_set_key_bits(&key_attributes, 256);

status = psa_import_key(&key_attributes, data, size, key_id);

/* Encode KEK key ID as CBOR unsigned int */
cbor_key_id[0] = 0x1A;
cbor_key_id[1] = ((*key_id >> 24) & 0xFF);
cbor_key_id[2] = ((*key_id >> 16) & 0xFF);
cbor_key_id[3] = ((*key_id >> 8) & 0xFF);
cbor_key_id[4] = ((*key_id >> 0) & 0xFF);

return status;
}
90 changes: 90 additions & 0 deletions tests/subsys/suit/common/decrypt_utils/decrypt_test_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/

#ifndef DECRYPT_TEST_UTILS_H_
#define DECRYPT_TEST_UTILS_H_

#include <psa/crypto.h>
#include <suit_metadata.h>

/* This module holds common utilities for SUIT decrypt filter tests.
* It defines default encryption key data, plaintext and ciphertext along with other
* suit_encryption_info struct members that can be used while testing.
*
* WARNING: All of the const values defined in this module CAN be changed freely.
* This also means that any test depending on them should expect it.
*/

#ifdef __cplusplus
extern "C" {
#endif

/* Default initalization for suit_encryption_info struct.
* 'cek_key_id_cbor' - should be taken from init_encryption_key() call.
*/
#define DECRYPT_TEST_ENC_INFO_DEFAULT_INIT(cek_key_id_cbor) \
{ \
.enc_alg_id = suit_cose_aes256_gcm, \
.IV = { \
.value = decrypt_test_iv_direct, \
.len = sizeof(decrypt_test_iv_direct), \
}, \
.aad = { \
.value = decrypt_test_aad, \
.len = strlen(decrypt_test_aad), \
}, \
.kw_alg_id = suit_cose_direct, \
.kw_key.direct = {.key_id = {.value = (cek_key_id_cbor), \
.len = sizeof((cek_key_id_cbor))},} \
}

#define DECRYPT_TEST_KEY_LENGTH 32
extern const uint8_t decrypt_test_key_data[DECRYPT_TEST_KEY_LENGTH];

#define DECRYPT_TEST_PLAINTEXT_LENGTH 61
extern const uint8_t decrypt_test_plaintext[DECRYPT_TEST_PLAINTEXT_LENGTH];

#define DECRYPT_TEST_AAD_LENGTH 11
extern const uint8_t decrypt_test_aad[DECRYPT_TEST_AAD_LENGTH];

#define DECRYPT_TEST_WRAPPED_CEK_LENGTH 40
extern const uint8_t decrypt_test_wrapped_cek[DECRYPT_TEST_WRAPPED_CEK_LENGTH];

#define DECRYPT_TEST_CIPHERTEXT_AES_KW_LENGTH (DECRYPT_TEST_PLAINTEXT_LENGTH + 16)
extern const uint8_t decrypt_test_ciphertext_aes_kw[DECRYPT_TEST_CIPHERTEXT_AES_KW_LENGTH];

#define DECRYPT_TEST_IV_AES_KW_LENGTH 12
extern const uint8_t decrypt_test_iv_aes_kw[DECRYPT_TEST_IV_AES_KW_LENGTH];

#define DECRYPT_TEST_CIPHERTEXT_DIRECT_LENGTH (DECRYPT_TEST_PLAINTEXT_LENGTH + 16)
extern const uint8_t decrypt_test_ciphertext_direct[DECRYPT_TEST_CIPHERTEXT_DIRECT_LENGTH];

#define DECRYPT_TEST_IV_DIRECT_LENGTH 12
extern const uint8_t decrypt_test_iv_direct[DECRYPT_TEST_IV_DIRECT_LENGTH];

extern const suit_manifest_class_id_t decrypt_test_sample_class_id;

/**
* @brief Initialize encryption key with given binary data.
* The key is initialized as PSA_KEY_TYPE_AES.

Check warning on line 72 in tests/subsys/suit/common/decrypt_utils/decrypt_test_utils.h

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

SPACE_BEFORE_TAB

tests/subsys/suit/common/decrypt_utils/decrypt_test_utils.h:72 please, no space before tabs
*
* @param[in] data binary key data.
* @param[in] size size of @a data.
* @param[out] key_id key ID of imported key.
* @param[in] alg encryption algorithm.
* @param[out] cbor_key_id key ID of imported key in cbor format.
*
* @retval PSA_SUCCESS successfuly imported given key.

Check warning on line 80 in tests/subsys/suit/common/decrypt_utils/decrypt_test_utils.h

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

TYPO_SPELLING

tests/subsys/suit/common/decrypt_utils/decrypt_test_utils.h:80 'successfuly' may be misspelled - perhaps 'successfully'?
* @retval PSA_ERROR_* returned error from dependent PSA calls.
*/
psa_status_t decrypt_test_init_encryption_key(const uint8_t *data, size_t size,
psa_key_id_t *key_id, psa_key_id_t alg, uint8_t *cbor_key_id);

#ifdef __cplusplus
}
#endif

#endif /* DECRYPT_TEST_UTILS_H_ */
1 change: 1 addition & 0 deletions tests/subsys/suit/copy/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ zephyr_library_link_libraries(suit_sink_selector_interface)
zephyr_library_link_libraries(suit_storage_interface)
zephyr_library_link_libraries(suit_manifest_variables)
zephyr_library_link_libraries(suit_ipuc)
zephyr_library_link_libraries(suit_decrypt_test_utils)
2 changes: 2 additions & 0 deletions tests/subsys/suit/copy/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ CONFIG_FLASH_MAP=y

CONFIG_SUIT_IPUC=y
CONFIG_MOCK_SDFW_ARBITER=y

CONFIG_SUIT_STREAM_FILTER_DECRYPT=y
99 changes: 99 additions & 0 deletions tests/subsys/suit/copy/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <mocks_sdfw.h>
#include <suit_manifest_variables.h>
#include <suit_storage.h>
#include <decrypt_test_utils.h>

#define WRITE_ADDR 0x1A00080000
#define TEST_MFST_VAR_NVM_ID 1
Expand Down Expand Up @@ -654,3 +655,101 @@ ZTEST(copy_tests, test_mfst_nvm_var_to_mram_NOK)
zassert_equal(ret, SUIT_SUCCESS,
"Failed to release destination component handle after the test: %d", ret);
}

ZTEST(copy_tests, test_integrated_fetch_and_copy_to_msink_encrypted_OK)
{
memptr_storage_handle_t handle;
struct zcbor_string source = {
.value = decrypt_test_ciphertext_direct,
.len = sizeof(decrypt_test_ciphertext_direct)
};

suit_component_t src_handle;
/* [h'CAND_IMG', h'02'] */
uint8_t valid_src_value[] = {0x82, 0x49, 0x68, 'C', 'A', 'N', 'D',
'_', 'I', 'M', 'G', 0x41, 0x02};

struct zcbor_string valid_src_component_id = {
.value = valid_src_value,
.len = sizeof(valid_src_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_src_component_id, false, &src_handle);

zassert_equal(ret, SUIT_SUCCESS, "create_component_handle failed - error %i", ret);

ret = suit_plat_fetch_integrated(src_handle, &source, &valid_manifest_component_id, NULL);
zassert_equal(ret, SUIT_SUCCESS, "suit_plat_fetch failed - error %i", ret);

ret = suit_plat_component_impl_data_get(src_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_ptr(decrypt_test_ciphertext_direct, payload,
"Retrieved payload doesn't mach ciphertext_direct");
zassert_equal(sizeof(decrypt_test_ciphertext_direct), payload_size,
"Retrieved payload_size doesn't mach size of ciphertext_direct");
zassert_not_null(payload, "Retrieved payload is NULL");

/* 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),
};

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_copy(dst_handle, src_handle, &valid_manifest_component_id, &enc_info);
zassert_equal(ret, SUIT_SUCCESS, "suit_plat_copy failed - error %i", ret);

ret = suit_plat_component_impl_data_get(dst_handle, &handle);
zassert_equal(ret, SUIT_SUCCESS, "suit_plat_component_impl_data_get failed - error %i",
ret);

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(dst_handle);
zassert_equal(ret, SUIT_SUCCESS, "dst_handle release failed - error %i", ret);

ret = suit_plat_release_component_handle(src_handle);
zassert_equal(ret, SUIT_SUCCESS, "src_handle release failed - error %i", ret);

ret = suit_memptr_storage_release(handle);
zassert_equal(ret, SUIT_PLAT_SUCCESS, "memptr_storage handle release failed - error %i",
ret);

psa_destroy_key(cek_key_id);
}
1 change: 1 addition & 0 deletions tests/subsys/suit/decrypt_filter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ target_include_directories(suit_mci INTERFACE ${ZEPHYR_NRF_MODULE_DIR}/subsys/su
zephyr_library_link_libraries(suit_stream_filters_interface)
zephyr_library_link_libraries(suit_stream_sources_interface)
zephyr_library_link_libraries(suit_mci)
zephyr_library_link_libraries(suit_decrypt_test_utils)
Loading

0 comments on commit 83fecc4

Please sign in to comment.