[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)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.