From 0c2651ac265744ae6c65234383389796f9d3c30f Mon Sep 17 00:00:00 2001 From: Michal Kozikowski Date: Thu, 2 Jan 2025 15:51:54 +0100 Subject: [PATCH] tests: Add decrypt filter unit tests. This commit implements unit tests for decryption stream filter in SUIT. As a result of tests implementation and run, minor changes to error handling were added to decryption filter implementation. Also, some parameter renamig was applied. Ref: NCSDK-30925 Signed-off-by: Michal Kozikowski --- .../include/suit_decrypt_filter.h | 8 +- .../stream_filters/src/suit_decrypt_filter.c | 59 +- .../unit/mocks/include/mock_suit_crypto.h | 15 + .../suit/unit/mocks/include/mock_suit_mci.h | 3 + .../unit/suit_decrypt_filter/CMakeLists.txt | 20 + .../suit/unit/suit_decrypt_filter/Kconfig | 10 + .../suit/unit/suit_decrypt_filter/prj.conf | 16 + .../suit/unit/suit_decrypt_filter/src/main.c | 542 ++++++++++++++++++ .../unit/suit_decrypt_filter/testcase.yaml | 8 + 9 files changed, 652 insertions(+), 29 deletions(-) create mode 100644 tests/subsys/suit/unit/suit_decrypt_filter/CMakeLists.txt create mode 100644 tests/subsys/suit/unit/suit_decrypt_filter/Kconfig create mode 100644 tests/subsys/suit/unit/suit_decrypt_filter/prj.conf create mode 100644 tests/subsys/suit/unit/suit_decrypt_filter/src/main.c create mode 100644 tests/subsys/suit/unit/suit_decrypt_filter/testcase.yaml diff --git a/subsys/suit/stream/stream_filters/include/suit_decrypt_filter.h b/subsys/suit/stream/stream_filters/include/suit_decrypt_filter.h index cbcad30913ce..0d3faaa3af96 100644 --- a/subsys/suit/stream/stream_filters/include/suit_decrypt_filter.h +++ b/subsys/suit/stream/stream_filters/include/suit_decrypt_filter.h @@ -18,17 +18,17 @@ extern "C" { /** * @brief Get decrypt filter object * - * @param[out] dec_sink Pointer to destination sink_stream to pass decrypted data + * @param[out] in_sink Pointer to input sink_stream to pass encrypted data * @param[in] enc_info Pointer to the structure with encryption info. * @param[in] class_id Pointer to the manifest class ID of the destination component - * @param[in] enc_sink Pointer to source sink_stream to be filled with encrypted data + * @param[in] out_sink Pointer to output sink_stream to be filled with decrypted data * * @return SUIT_PLAT_SUCCESS if success otherwise error code */ -suit_plat_err_t suit_decrypt_filter_get(struct stream_sink *dec_sink, +suit_plat_err_t suit_decrypt_filter_get(struct stream_sink *in_sink, struct suit_encryption_info *enc_info, const suit_manifest_class_id_t *class_id, - struct stream_sink *enc_sink); + struct stream_sink *out_sink); #ifdef __cplusplus } diff --git a/subsys/suit/stream/stream_filters/src/suit_decrypt_filter.c b/subsys/suit/stream/stream_filters/src/suit_decrypt_filter.c index 3e65cb0e4cef..892cad3919ef 100644 --- a/subsys/suit/stream/stream_filters/src/suit_decrypt_filter.c +++ b/subsys/suit/stream/stream_filters/src/suit_decrypt_filter.c @@ -24,7 +24,7 @@ LOG_MODULE_REGISTER(suit_decrypt_filter, CONFIG_SUIT_LOG_LEVEL); struct decrypt_ctx { mbedtls_svc_key_id_t cek_key_id; psa_aead_operation_t operation; - struct stream_sink enc_sink; + struct stream_sink out_sink; size_t tag_size; size_t stored_tag_bytes; uint8_t tag[PSA_AEAD_TAG_MAX_SIZE]; @@ -67,8 +67,8 @@ static suit_plat_err_t erase(void *ctx) decrypt_ctx->stored_tag_bytes = 0; memset(decrypt_ctx->tag, 0, sizeof(decrypt_ctx->tag)); - if (decrypt_ctx->enc_sink.erase != NULL) { - res = decrypt_ctx->enc_sink.erase(decrypt_ctx->enc_sink.ctx); + if (decrypt_ctx->out_sink.erase != NULL) { + res = decrypt_ctx->out_sink.erase(decrypt_ctx->out_sink.ctx); } } else { res = SUIT_PLAT_ERR_INVAL; @@ -131,7 +131,7 @@ static suit_plat_err_t write(void *ctx, const uint8_t *buf, size_t size) goto cleanup; } - err = decrypt_ctx->enc_sink.write(decrypt_ctx->enc_sink.ctx, decrypted_buf, + err = decrypt_ctx->out_sink.write(decrypt_ctx->out_sink.ctx, decrypted_buf, decrypted_len); if (err != SUIT_PLAT_SUCCESS) { @@ -195,13 +195,15 @@ static suit_plat_err_t flush(void *ctx) } else { LOG_INF("Firmware decryption successful"); - /* Using enc_sink without a write API is blocked by the filter constructor. + /* Using out_sink without a write API is blocked by the filter constructor. */ if (decrypted_len > 0) { - res = decrypt_ctx->enc_sink.write(decrypt_ctx->enc_sink.ctx, + res = decrypt_ctx->out_sink.write(decrypt_ctx->out_sink.ctx, decrypted_buf, decrypted_len); if (res != SUIT_PLAT_SUCCESS) { LOG_ERR("Failed to write decrypted data: %d", res); + /* Revert all the changes so that no decrypted data remains */ + erase(decrypt_ctx); } } } @@ -236,16 +238,16 @@ static suit_plat_err_t release(void *ctx) suit_plat_err_t res = flush(ctx); - if (decrypt_ctx->enc_sink.release != NULL) { + if (decrypt_ctx->out_sink.release != NULL) { suit_plat_err_t release_ret = - decrypt_ctx->enc_sink.release(decrypt_ctx->enc_sink.ctx); + decrypt_ctx->out_sink.release(decrypt_ctx->out_sink.ctx); if (res == SUIT_SUCCESS) { res = release_ret; } } - zeroize(&decrypt_ctx->enc_sink, sizeof(struct stream_sink)); + zeroize(&decrypt_ctx->out_sink, sizeof(struct stream_sink)); decrypt_ctx->in_use = false; @@ -261,8 +263,8 @@ static suit_plat_err_t used_storage(void *ctx, size_t *size) return SUIT_PLAT_ERR_INVAL; } - if (decrypt_ctx->enc_sink.used_storage != NULL) { - return decrypt_ctx->enc_sink.used_storage(decrypt_ctx->enc_sink.ctx, size); + if (decrypt_ctx->out_sink.used_storage != NULL) { + return decrypt_ctx->out_sink.used_storage(decrypt_ctx->out_sink.ctx, size); } return SUIT_PLAT_ERR_UNSUPPORTED; @@ -344,10 +346,10 @@ static suit_plat_err_t get_psa_alg_info(enum suit_cose_alg cose_alg_id, psa_algo return SUIT_PLAT_SUCCESS; } -suit_plat_err_t suit_decrypt_filter_get(struct stream_sink *dec_sink, +suit_plat_err_t suit_decrypt_filter_get(struct stream_sink *in_sink, struct suit_encryption_info *enc_info, const suit_manifest_class_id_t *class_id, - struct stream_sink *enc_sink) + struct stream_sink *out_sink) { suit_plat_err_t ret = SUIT_PLAT_SUCCESS; @@ -356,8 +358,8 @@ suit_plat_err_t suit_decrypt_filter_get(struct stream_sink *dec_sink, return SUIT_PLAT_ERR_BUSY; } - if ((enc_info == NULL) || (enc_sink == NULL) || (dec_sink == NULL) || - (enc_sink->write == NULL) || class_id == NULL) { + if ((enc_info == NULL) || (out_sink == NULL) || (in_sink == NULL) || + (out_sink->write == NULL) || class_id == NULL) { return SUIT_PLAT_ERR_INVAL; } @@ -403,23 +405,30 @@ suit_plat_err_t suit_decrypt_filter_get(struct stream_sink *dec_sink, status = psa_aead_update_ad(&ctx.operation, enc_info->aad.value, enc_info->aad.len); + if (status != PSA_SUCCESS) { + LOG_ERR("Failed to pass additional data for authentication operation: %d", status); + psa_aead_abort(&ctx.operation); + ctx.in_use = false; + return SUIT_PLAT_ERR_CRASH; + } + ctx.stored_tag_bytes = 0; - memcpy(&ctx.enc_sink, enc_sink, sizeof(struct stream_sink)); + memcpy(&ctx.out_sink, out_sink, sizeof(struct stream_sink)); - dec_sink->ctx = &ctx; + in_sink->ctx = &ctx; - dec_sink->write = write; - dec_sink->erase = erase; - dec_sink->release = release; - dec_sink->flush = flush; - if (enc_sink->used_storage != NULL) { - dec_sink->used_storage = used_storage; + in_sink->write = write; + in_sink->erase = erase; + in_sink->release = release; + in_sink->flush = flush; + if (out_sink->used_storage != NULL) { + in_sink->used_storage = used_storage; } else { - dec_sink->used_storage = NULL; + in_sink->used_storage = NULL; } /* Seeking is not possible on encrypted payload. */ - dec_sink->seek = NULL; + in_sink->seek = NULL; return SUIT_PLAT_SUCCESS; } diff --git a/tests/subsys/suit/unit/mocks/include/mock_suit_crypto.h b/tests/subsys/suit/unit/mocks/include/mock_suit_crypto.h index cbefde63f794..ba8b5743135f 100644 --- a/tests/subsys/suit/unit/mocks/include/mock_suit_crypto.h +++ b/tests/subsys/suit/unit/mocks/include/mock_suit_crypto.h @@ -18,6 +18,15 @@ FAKE_VALUE_FUNC(psa_status_t, psa_hash_abort, psa_hash_operation_t *); FAKE_VALUE_FUNC(psa_status_t, psa_hash_verify, psa_hash_operation_t *, const uint8_t *, size_t); FAKE_VALUE_FUNC(psa_status_t, psa_verify_message, mbedtls_svc_key_id_t, psa_algorithm_t, const uint8_t *, size_t, const uint8_t *, size_t); +FAKE_VALUE_FUNC(psa_status_t, psa_aead_update, psa_aead_operation_t *, const uint8_t *, size_t, + uint8_t *, size_t, size_t *); +FAKE_VALUE_FUNC(psa_status_t, psa_aead_abort, psa_aead_operation_t *); +FAKE_VALUE_FUNC(psa_status_t, psa_aead_verify, psa_aead_operation_t *, uint8_t *, size_t, size_t *, + const uint8_t *, size_t); +FAKE_VALUE_FUNC(psa_status_t, psa_aead_set_nonce, psa_aead_operation_t *, const uint8_t *, size_t); +FAKE_VALUE_FUNC(psa_status_t, psa_aead_update_ad, psa_aead_operation_t *, const uint8_t *, size_t); +FAKE_VALUE_FUNC(psa_status_t, psa_aead_decrypt_setup, psa_aead_operation_t *, + mbedtls_svc_key_id_t, psa_algorithm_t); static inline void mock_suit_crypto_reset(void) { @@ -26,6 +35,12 @@ static inline void mock_suit_crypto_reset(void) RESET_FAKE(psa_hash_abort); RESET_FAKE(psa_hash_verify); RESET_FAKE(psa_verify_message); + RESET_FAKE(psa_aead_update); + RESET_FAKE(psa_aead_abort); + RESET_FAKE(psa_aead_verify); + RESET_FAKE(psa_aead_set_nonce); + RESET_FAKE(psa_aead_update_ad); + RESET_FAKE(psa_aead_decrypt_setup); } #endif /* MOCK_SUIT_CRYPTO_H__ */ diff --git a/tests/subsys/suit/unit/mocks/include/mock_suit_mci.h b/tests/subsys/suit/unit/mocks/include/mock_suit_mci.h index fb8143012220..c5700c875483 100644 --- a/tests/subsys/suit/unit/mocks/include/mock_suit_mci.h +++ b/tests/subsys/suit/unit/mocks/include/mock_suit_mci.h @@ -48,6 +48,8 @@ FAKE_VALUE_FUNC(int, suit_mci_manifest_parent_child_declaration_validate, FAKE_VALUE_FUNC(int, suit_mci_manifest_process_dependency_validate, const suit_manifest_class_id_t *, const suit_manifest_class_id_t *); FAKE_VALUE_FUNC(int, suit_mci_init); +FAKE_VALUE_FUNC(int, suit_mci_fw_encryption_key_id_validate, const suit_manifest_class_id_t *, + uint32_t); static inline void mock_suit_mci_reset(void) { @@ -74,6 +76,7 @@ static inline void mock_suit_mci_reset(void) RESET_FAKE(suit_mci_manifest_process_dependency_validate); RESET_FAKE(suit_mci_manifest_parent_child_declaration_validate); RESET_FAKE(suit_mci_init); + RESET_FAKE(suit_mci_fw_encryption_key_id_validate); } #endif /* MOCK_SUIT_MCI_H__ */ diff --git a/tests/subsys/suit/unit/suit_decrypt_filter/CMakeLists.txt b/tests/subsys/suit/unit/suit_decrypt_filter/CMakeLists.txt new file mode 100644 index 000000000000..1dba7a92d06d --- /dev/null +++ b/tests/subsys/suit/unit/suit_decrypt_filter/CMakeLists.txt @@ -0,0 +1,20 @@ +# +# Copyright (c) 2023 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +cmake_minimum_required(VERSION 3.20.0) + +include(../cmake/test_template.cmake) + +project(suit_decrypt_filter) +target_include_directories(testbinary PRIVATE + ${SUIT_SUBSYS_DIR}/stream/stream_filters/include + ${SUIT_SUBSYS_DIR}/utils/include/ +) + +target_sources(testbinary PRIVATE + src/main.c + ${SUIT_SUBSYS_DIR}/stream/stream_filters/src/suit_decrypt_filter.c +) diff --git a/tests/subsys/suit/unit/suit_decrypt_filter/Kconfig b/tests/subsys/suit/unit/suit_decrypt_filter/Kconfig new file mode 100644 index 000000000000..3f9efe4e184e --- /dev/null +++ b/tests/subsys/suit/unit/suit_decrypt_filter/Kconfig @@ -0,0 +1,10 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Include and define MOCK_* Kconfigs +rsource "../mocks/Kconfig" + +source "Kconfig.zephyr" diff --git a/tests/subsys/suit/unit/suit_decrypt_filter/prj.conf b/tests/subsys/suit/unit/suit_decrypt_filter/prj.conf new file mode 100644 index 000000000000..26ef63e53fcc --- /dev/null +++ b/tests/subsys/suit/unit/suit_decrypt_filter/prj.conf @@ -0,0 +1,16 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_MOCK_SUIT_PROCESSOR=y +CONFIG_MOCK_SUIT_PLATFORM=y +CONFIG_MOCK_DIGEST_SINK=y +CONFIG_MOCK_GENERIC_ADDRESS_STREAMER=y +CONFIG_MOCK_SUIT_UTILS=y +CONFIG_MOCK_SUIT_MEMPTR_STORAGE=y +CONFIG_MOCK_SUIT_CRYPTO=y +CONFIG_MOCK_SUIT_MCI=y +CONFIG_MOCK_SUIT_METADATA=y +CONFIG_MOCK_SUIT_PLATFORM_INTERNAL=y diff --git a/tests/subsys/suit/unit/suit_decrypt_filter/src/main.c b/tests/subsys/suit/unit/suit_decrypt_filter/src/main.c new file mode 100644 index 000000000000..3e5cf7a46035 --- /dev/null +++ b/tests/subsys/suit/unit/suit_decrypt_filter/src/main.c @@ -0,0 +1,542 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include +#include +#include + +#define KEY_ID_FWENC_APPLICATION_GEN1 0x40022000 + +#define ENC_INFO_DEFAULT_INIT { \ + .enc_alg_id = suit_cose_aes256_gcm, \ + .IV = { \ + .value = iv_direct, \ + .len = sizeof(iv_direct), \ + }, \ + .aad = { \ + .value = aad, \ + .len = sizeof(aad), \ + }, \ + .kw_alg_id = suit_cose_direct, \ + .kw_key.direct = {.key_id = {.value = cek_key_id_cbor, \ + .len = sizeof(cek_key_id_cbor)},} \ + } + +/** + * 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| + */ + +static const uint8_t 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, +}; + + +static const uint8_t iv_direct[] = { + 0x60, 0x90, 0x6d, 0xb2, 0xfe, 0xc3, 0xc8, 0x5a, 0xf0, 0x28, 0xb1, 0xb6, +}; + +static const suit_manifest_class_id_t sample_class_id = { + {0x5b, 0x46, 0x9f, 0xd1, 0x90, 0xee, 0x53, 0x9c, 0xa3, 0x18, 0x68, 0x1b, 0x03, 0x69, 0x5e, + 0x36} +}; + +static const uint8_t cek_key_id_cbor[] = { + 0x1A, 0x40, 0x02, 0x20, 0x00, + }; + +struct suit_decrypt_filter_tests_fixture { + struct stream_sink dec_sink; + struct stream_sink ram_sink; + size_t decrypted_output_length; + psa_status_t aead_update_status; + psa_status_t aead_verify_status; + suit_plat_err_t ram_sink_write_status; + + /* ram_sink interfaces call counters. Not really 'fixtures', but something + we verify as a side effect of test run. */ + int ram_sink_write_call_cnt; + int ram_sink_erase_call_cnt; + int ram_sink_release_call_cnt; +}; + +static struct suit_decrypt_filter_tests_fixture tests_fixture; + +static const uint8_t aad[] = { + "sample aad" +}; + +/* Custom fake functions implementation - for returning values by reference. */ +static suit_plat_err_t custom_suit_plat_decode_key_id(struct zcbor_string *key_id, + uint32_t *integer_key_id) +{ + (void)key_id; + + *integer_key_id = KEY_ID_FWENC_APPLICATION_GEN1; + return SUIT_PLAT_SUCCESS; +} + +static psa_status_t custom_psa_aead_update(psa_aead_operation_t *operation, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + (void)operation; + (void)input; + (void)input_length; + (void)output; + (void)output_size; + + *output_length = tests_fixture.decrypted_output_length; + + return tests_fixture.aead_update_status; +} + +static psa_status_t custom_psa_aead_verify(psa_aead_operation_t *operation, + uint8_t *plaintext, + size_t plaintext_size, + size_t *plaintext_length, + const uint8_t *tag, + size_t tag_length) +{ + (void)operation; + (void)plaintext; + (void)plaintext_size; + (void)tag; + (void)tag_length; + + *plaintext_length = tests_fixture.decrypted_output_length; + + return tests_fixture.aead_verify_status; +} + +/* dummy interface functions for the decrypted data output sink (fixture->ram_sink). */ +static suit_plat_err_t release(void *ctx) +{ + (void)ctx; + + tests_fixture.ram_sink_release_call_cnt++; + + return SUIT_PLAT_SUCCESS; +} + +static suit_plat_err_t erase(void *ctx) +{ + (void)ctx; + + tests_fixture.ram_sink_erase_call_cnt++; + + return SUIT_PLAT_SUCCESS; +} + +static suit_plat_err_t write_ram(void *ctx, const uint8_t *buf, size_t size) +{ + + (void)ctx; + (void)buf; + (void)size; + + tests_fixture.ram_sink_write_call_cnt++; + + return tests_fixture.ram_sink_write_status; +} + +static suit_plat_err_t used_storage(void *ctx, size_t *size) +{ + (void)ctx; + (void)size; + + return SUIT_PLAT_SUCCESS; +} + +static void *test_suite_setup(void) +{ + return &tests_fixture; +} + +static void test_suite_teardown(void *fixture) +{ + (void)fixture; +} + +static void test_before(void *f) +{ + struct suit_decrypt_filter_tests_fixture *fixture = + (struct suit_decrypt_filter_tests_fixture* )f; + + /* Reset mocks */ + mocks_reset(); + + /* Reset common FFF internal structures */ + FFF_RESET_HISTORY(); + + if (fixture->dec_sink.release && fixture->dec_sink.ctx) + { + fixture->dec_sink.release(fixture->dec_sink.ctx); + } + + memset(fixture, 0, sizeof(struct suit_decrypt_filter_tests_fixture)); + fixture->ram_sink.write = write_ram; + fixture->ram_sink.used_storage = used_storage; + fixture->ram_sink.erase = erase; + fixture->ram_sink.release = release; +} + +ZTEST_SUITE(suit_decrypt_filter_tests, NULL, test_suite_setup, test_before, + NULL, test_suite_teardown); + +ZTEST_F(suit_decrypt_filter_tests, test_key_id_validation_fail) +{ + struct suit_encryption_info enc_info = ENC_INFO_DEFAULT_INIT; + + suit_mci_fw_encryption_key_id_validate_fake.return_val = MCI_ERR_WRONGKEYID; + suit_plat_decode_key_id_fake.return_val = SUIT_PLAT_SUCCESS; + + suit_plat_err_t err = suit_decrypt_filter_get(&fixture->dec_sink, &enc_info, + &sample_class_id, &fixture->ram_sink); + + zassert_equal(err, SUIT_PLAT_ERR_AUTHENTICATION, + "Incorrect error code when getting decrypt filter"); + zassert_equal(suit_mci_fw_encryption_key_id_validate_fake.call_count, 1, + "Invalid number of calls to suit_mci_fw_encryption_key_id_validate"); + zassert_equal_ptr(suit_mci_fw_encryption_key_id_validate_fake.arg0_val, &sample_class_id, + "Invalid class ID passed to suit_mci_fw_encryption_key_id_validate"); + zassert_equal(psa_aead_decrypt_setup_fake.call_count, 0, + "Invalid number of calls to psa_aead_decrypt_setup"); + zassert_equal(psa_aead_set_nonce_fake.call_count, 0, + "Invalid number of calls to psa_aead_set_nonce"); + zassert_equal(psa_aead_update_ad_fake.call_count, 0, + "Invalid number of calls to psa_aead_update_ad"); + zassert_equal(psa_aead_abort_fake.call_count, 0, + "Invalid number of calls to psa_aead_abort"); + zassert_equal_ptr(fixture->dec_sink.ctx, NULL, + "Invalid fixture->dec_sink.ctx value"); +} + +ZTEST_F(suit_decrypt_filter_tests, test_decryption_setup_fail) +{ + struct suit_encryption_info enc_info = ENC_INFO_DEFAULT_INIT; + + suit_mci_fw_encryption_key_id_validate_fake.return_val = SUIT_PLAT_SUCCESS; + suit_plat_decode_key_id_fake.return_val = SUIT_PLAT_SUCCESS; + psa_aead_decrypt_setup_fake.return_val = PSA_ERROR_GENERIC_ERROR; + + suit_plat_err_t err = suit_decrypt_filter_get(&fixture->dec_sink, &enc_info, + &sample_class_id, &fixture->ram_sink); + + zassert_equal(err, SUIT_PLAT_ERR_CRASH, + "Incorrect error code when getting decrypt filter"); + zassert_equal(suit_mci_fw_encryption_key_id_validate_fake.call_count, 1, + "Invalid number of calls to suit_mci_fw_encryption_key_id_validate"); + zassert_equal_ptr(suit_mci_fw_encryption_key_id_validate_fake.arg0_val, &sample_class_id, + "Invalid class ID passed to suit_mci_fw_encryption_key_id_validate"); + zassert_equal(psa_aead_decrypt_setup_fake.call_count, 1, + "Invalid number of calls to psa_aead_decrypt_setup"); + zassert_equal(psa_aead_set_nonce_fake.call_count, 0, + "Invalid number of calls to psa_aead_set_nonce"); + zassert_equal(psa_aead_update_ad_fake.call_count, 0, + "Invalid number of calls to psa_aead_update_ad"); + zassert_equal(psa_aead_abort_fake.call_count, 1, + "Invalid number of calls to psa_aead_abort"); + zassert_equal_ptr(fixture->dec_sink.ctx, NULL, + "Invalid fixture->dec_sink.ctx value"); +} + +ZTEST_F(suit_decrypt_filter_tests, test_decryption_set_nonce_fail) +{ + struct suit_encryption_info enc_info = ENC_INFO_DEFAULT_INIT; + + suit_mci_fw_encryption_key_id_validate_fake.return_val = SUIT_PLAT_SUCCESS; + suit_plat_decode_key_id_fake.return_val = SUIT_PLAT_SUCCESS; + psa_aead_decrypt_setup_fake.return_val = PSA_SUCCESS; + psa_aead_set_nonce_fake.return_val = PSA_ERROR_GENERIC_ERROR; + + suit_plat_err_t err = suit_decrypt_filter_get(&fixture->dec_sink, &enc_info, + &sample_class_id, &fixture->ram_sink); + + zassert_equal(err, SUIT_PLAT_ERR_CRASH, + "Incorrect error code when getting decrypt filter"); + zassert_equal(suit_mci_fw_encryption_key_id_validate_fake.call_count, 1, + "Invalid number of calls to suit_mci_fw_encryption_key_id_validate"); + zassert_equal_ptr(suit_mci_fw_encryption_key_id_validate_fake.arg0_val, &sample_class_id, + "Invalid class ID passed to suit_mci_fw_encryption_key_id_validate"); + zassert_equal(psa_aead_decrypt_setup_fake.call_count, 1, + "Invalid number of calls to psa_aead_decrypt_setup"); + zassert_equal_ptr(psa_aead_set_nonce_fake.arg1_val, iv_direct, + "Invalid IV passed to psa_aead_set_nonce"); + zassert_equal(psa_aead_set_nonce_fake.arg2_val, sizeof(iv_direct), + "Invalid IV length passed to psa_aead_set_nonce"); + zassert_equal(psa_aead_set_nonce_fake.call_count, 1, + "Invalid number of calls to psa_aead_set_nonce"); + zassert_equal(psa_aead_update_ad_fake.call_count, 0, + "Invalid number of calls to psa_aead_update_ad"); + zassert_equal(psa_aead_abort_fake.call_count, 1, + "Invalid number of calls to psa_aead_abort"); + zassert_equal_ptr(fixture->dec_sink.ctx, NULL, + "Invalid fixture->dec_sink.ctx value"); +} + +ZTEST_F(suit_decrypt_filter_tests, test_decryption_update_ad_fail) +{ + struct suit_encryption_info enc_info = ENC_INFO_DEFAULT_INIT; + + suit_mci_fw_encryption_key_id_validate_fake.return_val = SUIT_PLAT_SUCCESS; + suit_plat_decode_key_id_fake.return_val = SUIT_PLAT_SUCCESS; + psa_aead_decrypt_setup_fake.return_val = PSA_SUCCESS; + psa_aead_set_nonce_fake.return_val = PSA_SUCCESS; + psa_aead_update_ad_fake.return_val = PSA_ERROR_GENERIC_ERROR; + + suit_plat_err_t err = suit_decrypt_filter_get(&fixture->dec_sink, &enc_info, + &sample_class_id, &fixture->ram_sink); + + zassert_equal(err, SUIT_PLAT_ERR_CRASH, + "Incorrect error code when getting decrypt filter"); + zassert_equal(suit_mci_fw_encryption_key_id_validate_fake.call_count, 1, + "Invalid number of calls to suit_mci_fw_encryption_key_id_validate"); + zassert_equal_ptr(suit_mci_fw_encryption_key_id_validate_fake.arg0_val, &sample_class_id, + "Invalid class ID passed to suit_mci_fw_encryption_key_id_validate"); + zassert_equal(psa_aead_decrypt_setup_fake.call_count, 1, + "Invalid number of calls to psa_aead_decrypt_setup"); + zassert_equal_ptr(psa_aead_set_nonce_fake.arg1_val, iv_direct, + "Invalid IV passed to psa_aead_set_nonce"); + zassert_equal(psa_aead_set_nonce_fake.arg2_val, sizeof(iv_direct), + "Invalid IV length passed to psa_aead_set_nonce"); + zassert_equal(psa_aead_set_nonce_fake.call_count, 1, + "Invalid number of calls to psa_aead_set_nonce"); + zassert_equal(psa_aead_update_ad_fake.call_count, 1, + "Invalid number of calls to psa_aead_update_ad"); + zassert_equal_ptr(psa_aead_update_ad_fake.arg1_val, aad, + "Invalid ad passed to psa_aead_update_ad"); + zassert_equal(psa_aead_update_ad_fake.arg2_val, sizeof(aad), + "Invalid ad length passed to psa_aead_update_ad"); + zassert_equal(psa_aead_abort_fake.call_count, 1, + "Invalid number of calls to psa_aead_abort"); + zassert_equal_ptr(fixture->dec_sink.ctx, NULL, + "Invalid fixture->dec_sink.ctx value"); +} + +ZTEST_F(suit_decrypt_filter_tests, test_filter_get_happy_path) +{ + struct suit_encryption_info enc_info = ENC_INFO_DEFAULT_INIT; + + suit_mci_fw_encryption_key_id_validate_fake.return_val = SUIT_PLAT_SUCCESS; + suit_plat_decode_key_id_fake.return_val = SUIT_PLAT_SUCCESS; + psa_aead_decrypt_setup_fake.return_val = PSA_SUCCESS; + psa_aead_set_nonce_fake.return_val = PSA_SUCCESS; + psa_aead_update_ad_fake.return_val = PSA_SUCCESS; + + suit_plat_err_t err = suit_decrypt_filter_get(&fixture->dec_sink, &enc_info, + &sample_class_id, &fixture->ram_sink); + + zassert_equal(err, SUIT_PLAT_SUCCESS, + "Incorrect error code when getting decrypt filter"); + zassert_equal(suit_mci_fw_encryption_key_id_validate_fake.call_count, 1, + "Invalid number of calls to suit_mci_fw_encryption_key_id_validate"); + zassert_equal_ptr(suit_mci_fw_encryption_key_id_validate_fake.arg0_val, &sample_class_id, + "Invalid class ID passed to suit_mci_fw_encryption_key_id_validate"); + zassert_equal(psa_aead_decrypt_setup_fake.call_count, 1, + "Invalid number of calls to psa_aead_decrypt_setup"); + zassert_equal_ptr(psa_aead_set_nonce_fake.arg1_val, iv_direct, + "Invalid IV passed to psa_aead_set_nonce"); + zassert_equal(psa_aead_set_nonce_fake.arg2_val, sizeof(iv_direct), + "Invalid IV length passed to psa_aead_set_nonce"); + zassert_equal(psa_aead_set_nonce_fake.call_count, 1, + "Invalid number of calls to psa_aead_set_nonce"); + zassert_equal(psa_aead_update_ad_fake.call_count, 1, + "Invalid number of calls to psa_aead_update_ad"); + zassert_equal_ptr(psa_aead_update_ad_fake.arg1_val, aad, + "Invalid ad passed to psa_aead_update_ad"); + zassert_equal(psa_aead_update_ad_fake.arg2_val, sizeof(aad), + "Invalid ad length passed to psa_aead_update_ad"); + zassert_equal(psa_aead_abort_fake.call_count, 0, + "Invalid number of calls to psa_aead_abort"); + zassert_not_equal( fixture->dec_sink.ctx && fixture->dec_sink.write + && fixture->dec_sink.erase && fixture->dec_sink.release + && fixture->dec_sink.flush && fixture->dec_sink.used_storage, 0, + "Invalid fixture->dec_sink.ctx value"); +} + +ZTEST_F(suit_decrypt_filter_tests, test_write_filter_not_initialized) +{ + struct suit_encryption_info enc_info = ENC_INFO_DEFAULT_INIT; + + suit_plat_decode_key_id_fake.custom_fake = custom_suit_plat_decode_key_id; + + suit_plat_err_t err = suit_decrypt_filter_get(&fixture->dec_sink, &enc_info, + &sample_class_id, &fixture->ram_sink); + + zassert_equal(err, SUIT_PLAT_SUCCESS, + "Incorrect error code when getting decrypt filter"); + + err = fixture->dec_sink.release(fixture->dec_sink.ctx); + + // we expect SUIT_PLAT_ERR_INCORRECT_STATE because we didn't store any tag data. + zassert_equal(err, SUIT_PLAT_ERR_INCORRECT_STATE, + "Incorrect error code when releasing filter"); + zassert_equal(fixture->ram_sink_release_call_cnt, 1, + "Incorrect number of ram_sink release function calls"); + + err = fixture->dec_sink.write(fixture->dec_sink.ctx, ciphertext_direct, + sizeof(ciphertext_direct)); + + zassert_equal(err, SUIT_PLAT_ERR_INVAL, + "Incorrect error code when calling filter write interface"); +} + +ZTEST_F(suit_decrypt_filter_tests, test_write_happy_path) +{ + const uint8_t dummy_decrypted_array[256]; // Array of random values big enoguh to + // exceed SINGLE_CHUNK_SIZE in decrypt filter. + struct suit_encryption_info enc_info = ENC_INFO_DEFAULT_INIT; + + suit_plat_decode_key_id_fake.custom_fake = custom_suit_plat_decode_key_id; + + suit_plat_err_t err = suit_decrypt_filter_get(&fixture->dec_sink, &enc_info, + &sample_class_id, &fixture->ram_sink); + + zassert_equal(err, SUIT_PLAT_SUCCESS, + "Incorrect error code when getting decrypt filter"); + + psa_aead_update_fake.custom_fake = custom_psa_aead_update; + fixture->aead_update_status = PSA_SUCCESS; + fixture->decrypted_output_length = 100; // Anything greater than 0. + + err = fixture->dec_sink.write(fixture->dec_sink.ctx, dummy_decrypted_array, + sizeof(dummy_decrypted_array)); + + zassert_equal(err, SUIT_PLAT_SUCCESS, + "Incorrect error code when calling filter write interface"); + zassert_equal(fixture->ram_sink_write_call_cnt, 2, + "Incorrect number of ram_sink write function calls"); +} + +ZTEST_F(suit_decrypt_filter_tests, test_flush_verify_fail) +{ + struct suit_encryption_info enc_info = ENC_INFO_DEFAULT_INIT; + + suit_plat_decode_key_id_fake.custom_fake = custom_suit_plat_decode_key_id; + suit_plat_err_t err = suit_decrypt_filter_get(&fixture->dec_sink, &enc_info, + &sample_class_id, &fixture->ram_sink); + + zassert_equal(err, SUIT_PLAT_SUCCESS, + "Incorrect error code when getting decrypt filter"); + + psa_aead_update_fake.custom_fake = custom_psa_aead_update; + fixture->aead_update_status = PSA_SUCCESS; + fixture->decrypted_output_length = 100; // Anything greater than 0. + + err = fixture->dec_sink.write(fixture->dec_sink.ctx, ciphertext_direct, + sizeof(ciphertext_direct)); + + zassert_equal(err, SUIT_PLAT_SUCCESS, + "Incorrect error code when calling filter write interface"); + + psa_aead_verify_fake.custom_fake = custom_psa_aead_verify; + fixture->aead_verify_status = PSA_ERROR_GENERIC_ERROR; + + err = fixture->dec_sink.flush(fixture->dec_sink.ctx); + + zassert_equal(err, SUIT_PLAT_ERR_AUTHENTICATION, + "Incorrect error code when calling filter flush interface"); + zassert_equal(fixture->ram_sink_erase_call_cnt, 1, + "Incorrect number of ram_sink write function calls"); + zassert_equal(psa_aead_abort_fake.call_count, 1, + "Invalid number of calls to psa_aead_abort"); +} + +ZTEST_F(suit_decrypt_filter_tests, test_flush_happy_path) +{ + struct suit_encryption_info enc_info = ENC_INFO_DEFAULT_INIT; + + suit_plat_decode_key_id_fake.custom_fake = custom_suit_plat_decode_key_id; + suit_plat_err_t err = suit_decrypt_filter_get(&fixture->dec_sink, &enc_info, + &sample_class_id, &fixture->ram_sink); + + zassert_equal(err, SUIT_PLAT_SUCCESS, + "Incorrect error code when getting decrypt filter"); + + psa_aead_update_fake.custom_fake = custom_psa_aead_update; + fixture->aead_update_status = PSA_SUCCESS; + fixture->decrypted_output_length = 100; // Anything greater than 0. + + err = fixture->dec_sink.write(fixture->dec_sink.ctx, ciphertext_direct, + sizeof(ciphertext_direct)); + + zassert_equal(err, SUIT_PLAT_SUCCESS, + "Incorrect error code when calling filter write interface"); + + psa_aead_verify_fake.custom_fake = custom_psa_aead_verify; + fixture->aead_verify_status = PSA_SUCCESS; + fixture->decrypted_output_length = 100; // Anything greater than 0. + + err = fixture->dec_sink.flush(fixture->dec_sink.ctx); + + zassert_equal(err, SUIT_PLAT_SUCCESS, + "Incorrect error code when calling filter flush interface"); + zassert_equal(fixture->ram_sink_write_call_cnt, 2, // Called 2 times because we + // also call write (< SINGLE_CHUNK_SIZE). + "Incorrect number of ram_sink write function calls"); + zassert_equal(psa_aead_abort_fake.call_count, 0, + "Invalid number of calls to psa_aead_abort"); +} + +ZTEST_F(suit_decrypt_filter_tests, test_flush_ram_write_fail) +{ + struct suit_encryption_info enc_info = ENC_INFO_DEFAULT_INIT; + + suit_plat_decode_key_id_fake.custom_fake = custom_suit_plat_decode_key_id; + suit_plat_err_t err = suit_decrypt_filter_get(&fixture->dec_sink, &enc_info, + &sample_class_id, &fixture->ram_sink); + + zassert_equal(err, SUIT_PLAT_SUCCESS, + "Incorrect error code when getting decrypt filter"); + + psa_aead_update_fake.custom_fake = custom_psa_aead_update; + fixture->aead_update_status = PSA_SUCCESS; + fixture->decrypted_output_length = 100; // Anything greater than 0. + + err = fixture->dec_sink.write(fixture->dec_sink.ctx, ciphertext_direct, + sizeof(ciphertext_direct)); + + zassert_equal(err, SUIT_PLAT_SUCCESS, + "Incorrect error code when calling filter write interface"); + + psa_aead_verify_fake.custom_fake = custom_psa_aead_verify; + fixture->aead_verify_status = PSA_SUCCESS; + fixture->decrypted_output_length = 100; // Anything greater than 0. + tests_fixture.ram_sink_write_status = SUIT_PLAT_ERR_NOMEM; + + err = fixture->dec_sink.flush(fixture->dec_sink.ctx); + + zassert_equal(err, SUIT_PLAT_ERR_NOMEM, + "Incorrect error code when calling filter flush interface"); + zassert_equal(fixture->ram_sink_write_call_cnt, 2, // Called 2 times because we + // also call write (< SINGLE_CHUNK_SIZE). + "Incorrect number of ram_sink write function calls"); + zassert_equal(psa_aead_abort_fake.call_count, 0, + "Invalid number of calls to psa_aead_abort"); + zassert_equal(fixture->ram_sink_erase_call_cnt, 1, + "Incorrect number of ram_sink erase function calls"); +} diff --git a/tests/subsys/suit/unit/suit_decrypt_filter/testcase.yaml b/tests/subsys/suit/unit/suit_decrypt_filter/testcase.yaml new file mode 100644 index 000000000000..6cf62d760b0e --- /dev/null +++ b/tests/subsys/suit/unit/suit_decrypt_filter/testcase.yaml @@ -0,0 +1,8 @@ +tests: + suit.unit.suit_decrypt_filter: + type: unit + tags: >- + suit + suit_decrypt_filter + ci_tests_subsys_suit + \ No newline at end of file