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..45ff155327cf 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,17 @@ 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 +240,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 +265,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 +348,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 +360,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 +407,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..d74ca5595768 --- /dev/null +++ b/tests/subsys/suit/unit/suit_decrypt_filter/CMakeLists.txt @@ -0,0 +1,20 @@ +# +# Copyright (c) 2025 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..e8b98bb73845 --- /dev/null +++ b/tests/subsys/suit/unit/suit_decrypt_filter/Kconfig @@ -0,0 +1,10 @@ +# +# Copyright (c) 2025 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..c57e7e84377a --- /dev/null +++ b/tests/subsys/suit/unit/suit_decrypt_filter/prj.conf @@ -0,0 +1,16 @@ +# +# Copyright (c) 2025 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..411e87ab3968 --- /dev/null +++ b/tests/subsys/suit/unit/suit_decrypt_filter/src/main.c @@ -0,0 +1,508 @@ +/* + * Copyright (c) 2025 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 in_sink; + struct stream_sink out_sink; + size_t decrypted_output_length; + psa_status_t aead_update_status; + psa_status_t aead_verify_status; +}; + +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; +} + +/* Fake interfaces definitions for the decrypted data output sink (fixture->out_sink). */ +FAKE_VALUE_FUNC(suit_plat_err_t, out_sink_release, void *); +FAKE_VALUE_FUNC(suit_plat_err_t, out_sink_erase, void *); +FAKE_VALUE_FUNC(suit_plat_err_t, out_sink_write, void *, const uint8_t *, size_t); +FAKE_VALUE_FUNC(suit_plat_err_t, out_sink_used_storage, void *, size_t *); + +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_FAKE(out_sink_release); + RESET_FAKE(out_sink_erase); + RESET_FAKE(out_sink_write); + RESET_FAKE(out_sink_used_storage); + + /* Reset common FFF internal structures */ + FFF_RESET_HISTORY(); + + memset(fixture, 0, sizeof(struct suit_decrypt_filter_tests_fixture)); + fixture->out_sink.write = out_sink_write; + fixture->out_sink.used_storage = out_sink_used_storage; + fixture->out_sink.erase = out_sink_erase; + fixture->out_sink.release = out_sink_release; +} + +static void test_after(void *f) +{ + struct suit_decrypt_filter_tests_fixture *fixture = + (struct suit_decrypt_filter_tests_fixture *)f; + + if (fixture->in_sink.release && fixture->in_sink.ctx) { + fixture->in_sink.release(fixture->in_sink.ctx); + } +} + +ZTEST_SUITE(suit_decrypt_filter_tests, NULL, test_suite_setup, test_before, + test_after, 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->in_sink, &enc_info, + &sample_class_id, &fixture->out_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->in_sink.ctx, NULL, + "Invalid fixture->in_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->in_sink, &enc_info, + &sample_class_id, &fixture->out_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->in_sink.ctx, NULL, + "Invalid fixture->in_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->in_sink, &enc_info, + &sample_class_id, &fixture->out_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->in_sink.ctx, NULL, + "Invalid fixture->in_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->in_sink, &enc_info, + &sample_class_id, &fixture->out_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->in_sink.ctx, NULL, + "Invalid fixture->in_sink.ctx value"); +} + +ZTEST_F(suit_decrypt_filter_tests, test_filter_get_success_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->in_sink, &enc_info, + &sample_class_id, &fixture->out_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->in_sink.ctx && fixture->in_sink.write + && fixture->in_sink.erase && fixture->in_sink.release + && fixture->in_sink.flush && fixture->in_sink.used_storage, 0, + "Invalid fixture->in_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->in_sink, &enc_info, + &sample_class_id, &fixture->out_sink); + + zassert_equal(err, SUIT_PLAT_SUCCESS, + "Incorrect error code when getting decrypt filter"); + + err = fixture->in_sink.release(fixture->in_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(out_sink_release_fake.call_count, 1, + "Incorrect number of out_sink release function calls"); + + err = fixture->in_sink.write(fixture->in_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_success_path) +{ + /* Array of random values big enoguh to exceed SINGLE_CHUNK_SIZE in decrypt filter. */ + const uint8_t dummy_encrypted_array[256]; + 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->in_sink, &enc_info, + &sample_class_id, &fixture->out_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->in_sink.write(fixture->in_sink.ctx, dummy_encrypted_array, + sizeof(dummy_encrypted_array)); + + zassert_equal(err, SUIT_PLAT_SUCCESS, + "Incorrect error code when calling filter write interface"); + zassert_equal(out_sink_write_fake.call_count, 2, + "Incorrect number of out_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->in_sink, &enc_info, + &sample_class_id, &fixture->out_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->in_sink.write(fixture->in_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->in_sink.flush(fixture->in_sink.ctx); + + zassert_equal(err, SUIT_PLAT_ERR_AUTHENTICATION, + "Incorrect error code when calling filter flush interface"); + zassert_equal(out_sink_erase_fake.call_count, 1, + "Incorrect number of out_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_success_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->in_sink, &enc_info, + &sample_class_id, &fixture->out_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->in_sink.write(fixture->in_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->in_sink.flush(fixture->in_sink.ctx); + + zassert_equal(err, SUIT_PLAT_SUCCESS, + "Incorrect error code when calling filter flush interface"); + zassert_equal(out_sink_write_fake.call_count, 2, + "Incorrect number of out_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_out_sink_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->in_sink, &enc_info, + &sample_class_id, &fixture->out_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->in_sink.write(fixture->in_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. */ + out_sink_write_fake.return_val = SUIT_PLAT_ERR_NOMEM; + + err = fixture->in_sink.flush(fixture->in_sink.ctx); + + zassert_equal(err, SUIT_PLAT_ERR_NOMEM, + "Incorrect error code when calling filter flush interface"); + zassert_equal(out_sink_write_fake.call_count, 2, + "Incorrect number of out_sink write function calls"); + zassert_equal(psa_aead_abort_fake.call_count, 0, + "Invalid number of calls to psa_aead_abort"); + zassert_equal(out_sink_erase_fake.call_count, 1, + "Incorrect number of out_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..b47fe61f3ccb --- /dev/null +++ b/tests/subsys/suit/unit/suit_decrypt_filter/testcase.yaml @@ -0,0 +1,7 @@ +tests: + suit.unit.suit_decrypt_filter: + type: unit + tags: >- + suit + suit_decrypt_filter + ci_tests_subsys_suit