[PATCH v2 7/7] test: py: add mkimage dm-verity round-trip test
Daniel Golle
daniel at makrotopia.org
Thu Apr 16 03:47:11 CEST 2026
Add test/py/tests/test_fit_verity.py with two tests.
Both tests are skipped if veritysetup is not installed on the host.
Signed-off-by: Daniel Golle <daniel at makrotopia.org>
---
v2: new patch
test/py/tests/test_fit_verity.py | 153 +++++++++++++++++++++++++++++++
1 file changed, 153 insertions(+)
create mode 100644 test/py/tests/test_fit_verity.py
diff --git a/test/py/tests/test_fit_verity.py b/test/py/tests/test_fit_verity.py
new file mode 100644
index 00000000000..670232995cc
--- /dev/null
+++ b/test/py/tests/test_fit_verity.py
@@ -0,0 +1,153 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright 2026 Daniel Golle <daniel at makrotopia.org>
+
+"""
+Test mkimage dm-verity Merkle-tree generation
+
+Build a minimal .its with a dm-verity subnode (user-provided properties only),
+run mkimage -E, and verify that the computed properties (digest, salt,
+num-data-blocks, hash-start-block) are written into the resulting FIT.
+
+This test does not run the sandbox. It only exercises the host tool 'mkimage'.
+Requires 'veritysetup' from the cryptsetup package on the build host.
+"""
+
+import os
+import shutil
+import pytest
+import struct
+import utils
+
+ITS_TEMPLATE = """\
+/dts-v1/;
+
+/ {
+ description = "dm-verity test";
+ #address-cells = <1>;
+
+ images {
+ rootfs {
+ description = "test filesystem";
+ data = /incbin/("./rootfs.bin");
+ type = "filesystem";
+ arch = "sandbox";
+ compression = "none";
+
+ dm-verity {
+ algo = "sha256";
+ data-block-size = <4096>;
+ hash-block-size = <4096>;
+ };
+ };
+ };
+
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ description = "test config";
+ loadables = "rootfs";
+ };
+ };
+};
+"""
+
+def have_veritysetup():
+ return shutil.which('veritysetup') is not None
+
+
+ at pytest.mark.requiredtool('dtc')
+ at pytest.mark.requiredtool('fdtget')
+ at pytest.mark.skipif(not have_veritysetup(),
+ reason='veritysetup not installed')
+def test_mkimage_verity(ubman):
+ """Test that mkimage computes dm-verity properties correctly."""
+
+ mkimage = ubman.config.build_dir + '/tools/mkimage'
+ tempdir = os.path.join(ubman.config.result_dir, 'verity')
+ os.makedirs(tempdir, exist_ok=True)
+
+ rootfs_file = os.path.join(tempdir, 'rootfs.bin')
+ its_file = os.path.join(tempdir, 'image.its')
+ fit_file = os.path.join(tempdir, 'image.itb')
+
+ # Create a dummy filesystem image: 64 x 4096-byte blocks of 0xa5
+ block_size = 4096
+ num_blocks = 64
+ with open(rootfs_file, 'wb') as f:
+ f.write(bytes([0xa5]) * block_size * num_blocks)
+
+ with open(its_file, 'w') as f:
+ f.write(ITS_TEMPLATE)
+
+ # Build the FIT with external data (required for dm-verity)
+ dtc_args = f'-I dts -O dtb -i {tempdir}'
+ utils.run_and_log(ubman,
+ [mkimage, '-E', '-D', dtc_args, '-f', its_file, fit_file])
+
+ # Helper to read FIT properties via fdtget
+ def fdt_get(node, prop):
+ val = utils.run_and_log(
+ ubman, f'fdtget {fit_file} {node} {prop}')
+ return val.strip()
+
+ def fdt_get_hex(node, prop):
+ """Read a byte-array property as hex string."""
+ val = utils.run_and_log(
+ ubman, f'fdtget -tbx {fit_file} {node} {prop}')
+ return ''.join(b.zfill(2) for b in val.strip().split())
+
+ verity_path = '/images/rootfs/dm-verity'
+
+ # Verify mkimage filled in the computed properties
+ algo = fdt_get(verity_path, 'algo')
+ assert algo == 'sha256', f'algo mismatch: {algo}'
+
+ dbs = int(fdt_get(verity_path, 'data-block-size'))
+ assert dbs == block_size, f'data-block-size mismatch: {dbs}'
+
+ hbs = int(fdt_get(verity_path, 'hash-block-size'))
+ assert hbs == block_size, f'hash-block-size mismatch: {hbs}'
+
+ nblk = int(fdt_get(verity_path, 'num-data-blocks'))
+ assert nblk == num_blocks, f'num-data-blocks mismatch: {nblk} != {num_blocks}'
+
+ hblk = int(fdt_get(verity_path, 'hash-start-block'))
+ # With --no-superblock, hash-start-block == num-data-blocks
+ assert hblk == num_blocks, f'hash-start-block mismatch: {hblk} != {num_blocks}'
+
+ # digest and salt should be non-empty hex strings (sha256 = 32 bytes = 64 hex chars)
+ digest = fdt_get_hex(verity_path, 'digest')
+ assert len(digest) == 64, f'digest length mismatch: {len(digest)} (expected 64)'
+ # Should not be all zeros
+ assert digest != '0' * 64, 'digest is all zeros'
+
+ salt = fdt_get_hex(verity_path, 'salt')
+ assert len(salt) == 64, f'salt length mismatch: {len(salt)} (expected 64)'
+
+
+ at pytest.mark.requiredtool('dtc')
+ at pytest.mark.skipif(not have_veritysetup(),
+ reason='veritysetup not installed')
+def test_mkimage_verity_requires_external(ubman):
+ """Test that mkimage rejects dm-verity without -E flag."""
+
+ mkimage = ubman.config.build_dir + '/tools/mkimage'
+ tempdir = os.path.join(ubman.config.result_dir, 'verity_no_ext')
+ os.makedirs(tempdir, exist_ok=True)
+
+ rootfs_file = os.path.join(tempdir, 'rootfs.bin')
+ its_file = os.path.join(tempdir, 'image.its')
+ fit_file = os.path.join(tempdir, 'image.itb')
+
+ with open(rootfs_file, 'wb') as f:
+ f.write(bytes([0xa5]) * 4096 * 8)
+
+ with open(its_file, 'w') as f:
+ f.write(ITS_TEMPLATE)
+
+ # Without -E, mkimage should fail for dm-verity images
+ dtc_args = f'-I dts -O dtb -i {tempdir}'
+ with pytest.raises(Exception):
+ utils.run_and_log(ubman,
+ [mkimage, '-D', dtc_args, '-f', its_file, fit_file])
--
2.53.0
More information about the U-Boot
mailing list