Skip to content

Commit

Permalink
This commit extends integration tests for fetch, copy and write
Browse files Browse the repository at this point in the history
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 10, 2025
1 parent 509fa66 commit de59529
Show file tree
Hide file tree
Showing 11 changed files with 597 additions and 171 deletions.
4 changes: 4 additions & 0 deletions tests/subsys/suit/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@ endif()

zephyr_interface_library_named(suit_update_magic_values)
target_include_directories(suit_update_magic_values INTERFACE .)

zephyr_interface_library_named(suit_decrypt_test_utils)
zephyr_include_directories(${CMAKE_CURRENT_LIST_DIR}/decrypt_utils)
zephyr_library_sources(${CMAKE_CURRENT_LIST_DIR}/decrypt_utils/decrypt_test_utils.c)
134 changes: 134 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,134 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/


#include "decrypt_test_utils.h"
#include <zephyr/ztest.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}};

void 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);

zassert_equal(status, PSA_SUCCESS, "Failed to import key");

/* Encode KEK key ID as CBOR unsigned int */
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);
}
65 changes: 65 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,65 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/

#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.
*/


/* 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;

void 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);
2 changes: 2 additions & 0 deletions tests/subsys/suit/copy/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ CONFIG_FLASH_MAP=y

CONFIG_SUIT_IPUC=y
CONFIG_MOCK_SDFW_ARBITER=y

CONFIG_SUIT_STREAM_FILTER_DECRYPT=y
94 changes: 94 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 @@ -653,3 +654,96 @@ 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)};

Check warning on line 662 in tests/subsys/suit/copy/src/main.c

View workflow job for this annotation

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

LONG_LINE

tests/subsys/suit/copy/src/main.c:662 line length of 113 exceeds 100 columns

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,
};

const psa_status_t status = psa_crypto_init();
zassert_equal(status, PSA_SUCCESS, "Failed to init psa crypto");

Check warning on line 680 in tests/subsys/suit/copy/src/main.c

View workflow job for this annotation

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

LINE_SPACING

tests/subsys/suit/copy/src/main.c:680 Missing a blank line after declarations

decrypt_test_init_encryption_key(decrypt_test_key_data, sizeof(decrypt_test_key_data),
&cek_key_id, PSA_ALG_GCM, cek_key_id_cbor);

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);

Check warning on line 732 in tests/subsys/suit/copy/src/main.c

View workflow job for this annotation

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

LEADING_SPACE

tests/subsys/suit/copy/src/main.c:732 please, no spaces at the start of a line
zassert_equal(memcmp(decrypt_test_plaintext, payload, strlen(decrypt_test_plaintext)), 0 ,

Check failure on line 733 in tests/subsys/suit/copy/src/main.c

View workflow job for this annotation

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

SPACING

tests/subsys/suit/copy/src/main.c:733 space prohibited before that ',' (ctx:WxE)
"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);
}
Loading

0 comments on commit de59529

Please sign in to comment.