[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