[PATCH v2 5/5] test: boot: add regression tests for the FIT cipher bounds checks
Pranav Rajendran <[email protected]>
| Newsgroups | org.u-boot-project.lists.u-boot |
|---|---|
| Message-ID | <[email protected]> |
Add a fit_cipher unit test suite, gated on CONFIG_FIT_CIPHER like the code it exercises, covering the three defects fixed in this series: - image_aes_decrypt() rejects a cipher_len that is not a whole number of AES blocks - image_aes_decrypt() rejects an unciphered size larger than the ciphertext, and accepts the boundary case where they are equal - fit_image_get_data_size_unciphered() rejects a 'data-size-unciphered' property that is not exactly one fdt32_t long, built via a minimal FIT constructed with libfdt, and accepts a correctly sized one Signed-off-by: Pranav Rajendran <[email protected]> --- v2: - New in v2, per Simon's request for regression tests on this series. test/boot/Makefile | 1 + test/boot/fit_cipher.c | 113 +++++++++++++++++++++++++++++++++++++++++ test/cmd_ut.c | 2 + 3 files changed, 116 insertions(+) create mode 100644 test/boot/fit_cipher.c diff --git a/test/boot/Makefile b/test/boot/Makefile index 59a87028704..cef2a236d9b 100644 --- a/test/boot/Makefile +++ b/test/boot/Makefile @@ -18,6 +18,7 @@ ifdef CONFIG_UT_DM obj-$(CONFIG_$(PHASE_)OF_LIBFDT) += image_fdt.o endif endif +obj-$(CONFIG_$(PHASE_)FIT_CIPHER) += fit_cipher.o obj-$(CONFIG_$(PHASE_)FIT_VERITY) += fit_verity.o obj-$(CONFIG_MEASURED_BOOT) += measurement.o diff --git a/test/boot/fit_cipher.c b/test/boot/fit_cipher.c new file mode 100644 index 00000000000..b98c69fcb0b --- /dev/null +++ b/test/boot/fit_cipher.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Regression tests for the FIT cipher bounds checks in + * image_aes_decrypt() and fit_image_get_data_size_unciphered(). + * + * Copyright 2026 Pranav Rajendran <[email protected]> + */ + +#include <errno.h> +#include <image.h> +#include <uboot_aes.h> +#include <u-boot/aes.h> +#include <linux/libfdt.h> +#include <test/test.h> +#include <test/ut.h> + +#define FIT_CIPHER_TEST(_name, _flags) UNIT_TEST(_name, _flags, fit_cipher) + +/* A cipher_len that is not a whole number of AES blocks must be rejected */ +static int fit_cipher_test_unaligned_len(struct unit_test_state *uts) +{ + struct image_cipher_info info; + unsigned char key[AES128_KEY_LENGTH] = { 0 }; + unsigned char iv[AES_BLOCK_LENGTH] = { 0 }; + unsigned char cipher[AES_BLOCK_LENGTH + 1] = { 0 }; + void *data = NULL; + size_t size = 0; + int ret; + + memset(&info, 0, sizeof(info)); + info.cipher = image_get_cipher_algo("aes128"); + ut_assertnonnull(info.cipher); + info.key = key; + info.iv = iv; + + ret = image_aes_decrypt(&info, cipher, sizeof(cipher), &data, &size); + ut_asserteq(-EINVAL, ret); + ut_assertnull(data); + + return 0; +} +FIT_CIPHER_TEST(fit_cipher_test_unaligned_len, 0); + +/* + * An unciphered size larger than the ciphertext must be rejected; a size + * exactly equal to the ciphertext length is the valid boundary case. + */ +static int fit_cipher_test_oversized_unciphered_size(struct unit_test_state *uts) +{ + struct image_cipher_info info; + unsigned char key[AES128_KEY_LENGTH] = { 0 }; + unsigned char iv[AES_BLOCK_LENGTH] = { 0 }; + unsigned char cipher[AES_BLOCK_LENGTH * 2] = { 0 }; + void *data = NULL; + size_t size = 0; + int ret; + + memset(&info, 0, sizeof(info)); + info.cipher = image_get_cipher_algo("aes128"); + ut_assertnonnull(info.cipher); + info.key = key; + info.iv = iv; + + info.size_unciphered = sizeof(cipher) + 1; + ret = image_aes_decrypt(&info, cipher, sizeof(cipher), &data, &size); + ut_asserteq(-EINVAL, ret); + ut_assertnull(data); + + /* the boundary itself, size_unciphered == cipher_len, is valid */ + info.size_unciphered = sizeof(cipher); + ut_assertok(image_aes_decrypt(&info, cipher, sizeof(cipher), &data, + &size)); + ut_assertnonnull(data); + ut_asserteq(sizeof(cipher), size); + free(data); + + return 0; +} +FIT_CIPHER_TEST(fit_cipher_test_oversized_unciphered_size, 0); + +/* + * A 'data-size-unciphered' property that is not exactly one fdt32_t long + * must be rejected rather than read out of bounds. + */ +static int fit_cipher_test_data_size_unciphered_len(struct unit_test_state *uts) +{ + char fit[512]; + int images, img, ret; + u16 truncated = 0x1234; + fdt32_t valid = cpu_to_fdt32(0x100); + size_t data_size = 0; + + ut_assertok(fdt_create_empty_tree(fit, sizeof(fit))); + images = fdt_add_subnode(fit, 0, "images"); + ut_assert(images >= 0); + + img = fdt_add_subnode(fit, images, "kernel"); + ut_assert(img >= 0); + ut_assertok(fdt_setprop(fit, img, "data-size-unciphered", + &truncated, sizeof(truncated))); + + ret = fit_image_get_data_size_unciphered(fit, img, &data_size); + ut_asserteq(-EINVAL, ret); + + /* a correctly sized property still works */ + ut_assertok(fdt_setprop(fit, img, "data-size-unciphered", + &valid, sizeof(valid))); + ut_assertok(fit_image_get_data_size_unciphered(fit, img, &data_size)); + ut_asserteq(0x100, data_size); + + return 0; +} +FIT_CIPHER_TEST(fit_cipher_test_data_size_unciphered_len, 0); diff --git a/test/cmd_ut.c b/test/cmd_ut.c index 4328670d0d6..95e86f7dcf6 100644 --- a/test/cmd_ut.c +++ b/test/cmd_ut.c @@ -59,6 +59,7 @@ SUITE_DECL(env); SUITE_DECL(exit); SUITE_DECL(fdt); SUITE_DECL(fdt_overlay); +SUITE_DECL(fit_cipher); SUITE_DECL(fit_verity); SUITE_DECL(font); SUITE_DECL(hush); @@ -88,6 +89,7 @@ static struct suite suites[] = { SUITE(exit, "shell exit and variables"), SUITE(fdt, "fdt command"), SUITE(fdt_overlay, "device tree overlays"), + SUITE(fit_cipher, "FIT cipher bounds checks"), SUITE(fit_verity, "FIT dm-verity cmdline generation"), SUITE(font, "font command"), SUITE(hush, "hush behaviour"), -- 2.50.1 (Apple Git-155)