mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-06-13 15:03:58 +03:00
Add test/py/tests/test_fit_verity.py covering: - mkimage writes correct dm-verity properties for matched and mismatched block sizes (4096/4096 and 4096/1024); - veritysetup verify re-checks the digest against the .itb's external data section; - mkimage rejects dm-verity images built without -E. All tests are skipped if veritysetup is not installed on the host. Signed-off-by: Daniel Golle <daniel@makrotopia.org> Reviewed-by: Simon Glass <sjg@chromium.org>
176 lines
5.9 KiB
Python
176 lines
5.9 KiB
Python
# SPDX-License-Identifier: GPL-2.0+
|
|
#
|
|
# Copyright 2026 Daniel Golle <daniel@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.
|
|
The computed digest is then re-verified by running ``veritysetup verify``
|
|
against the external data section of the .itb.
|
|
|
|
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 struct
|
|
import pytest
|
|
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 = <%d>;
|
|
hash-block-size = <%d>;
|
|
};
|
|
};
|
|
};
|
|
|
|
configurations {
|
|
default = "conf-1";
|
|
conf-1 {
|
|
description = "test config";
|
|
loadables = "rootfs";
|
|
};
|
|
};
|
|
};
|
|
"""
|
|
|
|
def _fdt_totalsize(path):
|
|
"""Read the totalsize field from an FDT header (offset 4, big-endian u32)."""
|
|
with open(path, 'rb') as f:
|
|
magic, totalsize = struct.unpack('>II', f.read(8))
|
|
assert magic == 0xd00dfeed, f'not an FDT: magic={magic:#x}'
|
|
return totalsize
|
|
|
|
|
|
def _run_round_trip(ubman, tempdir, data_block_size, hash_block_size):
|
|
"""Build a FIT with dm-verity, verify written properties, re-verify with veritysetup."""
|
|
mkimage = ubman.config.build_dir + '/tools/mkimage'
|
|
|
|
rootfs_file = os.path.join(tempdir, 'rootfs.bin')
|
|
its_file = os.path.join(tempdir, 'image.its')
|
|
fit_file = os.path.join(tempdir, 'image.itb')
|
|
|
|
# 64 data blocks of 0xa5
|
|
num_blocks = 64
|
|
data_size = data_block_size * num_blocks
|
|
with open(rootfs_file, 'wb') as f:
|
|
f.write(bytes([0xa5]) * data_size)
|
|
|
|
with open(its_file, 'w') as f:
|
|
f.write(ITS_TEMPLATE % (data_block_size, hash_block_size))
|
|
|
|
dtc_args = f'-I dts -O dtb -i {tempdir}'
|
|
utils.run_and_log(ubman,
|
|
[mkimage, '-E', '-D', dtc_args, '-f', its_file, fit_file])
|
|
|
|
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):
|
|
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'
|
|
|
|
assert fdt_get(verity_path, 'algo') == 'sha256'
|
|
assert int(fdt_get(verity_path, 'data-block-size')) == data_block_size
|
|
assert int(fdt_get(verity_path, 'hash-block-size')) == hash_block_size
|
|
|
|
nblk = int(fdt_get(verity_path, 'num-data-blocks'))
|
|
assert nblk == num_blocks, f'num-data-blocks {nblk} != {num_blocks}'
|
|
|
|
hblk = int(fdt_get(verity_path, 'hash-start-block'))
|
|
# With --no-superblock, hash-start-block = data_size / hash-block-size
|
|
assert hblk == data_size // hash_block_size, \
|
|
f'hash-start-block {hblk} != {data_size // hash_block_size}'
|
|
|
|
digest = fdt_get_hex(verity_path, 'digest')
|
|
assert len(digest) == 64 and digest != '0' * 64
|
|
salt = fdt_get_hex(verity_path, 'salt')
|
|
assert len(salt) == 64
|
|
|
|
# Re-verify the digest with veritysetup against the .itb's external data.
|
|
# With -E, image data sits after the FIT FDT at (fdt_totalsize + data-offset).
|
|
data_offset = int(fdt_get('/images/rootfs', 'data-offset'))
|
|
data_size_full = int(fdt_get('/images/rootfs', 'data-size'))
|
|
ext_pos = _fdt_totalsize(fit_file) + data_offset
|
|
expanded = os.path.join(tempdir, 'expanded.bin')
|
|
with open(fit_file, 'rb') as src, open(expanded, 'wb') as dst:
|
|
src.seek(ext_pos)
|
|
dst.write(src.read(data_size_full))
|
|
|
|
utils.run_and_log(ubman, [
|
|
'veritysetup', 'verify', expanded, expanded, digest,
|
|
'--no-superblock',
|
|
f'--data-block-size={data_block_size}',
|
|
f'--hash-block-size={hash_block_size}',
|
|
f'--data-blocks={nblk}',
|
|
'--hash=sha256',
|
|
f'--salt={salt}',
|
|
f'--hash-offset={data_size}',
|
|
])
|
|
|
|
|
|
@pytest.mark.requiredtool('dtc')
|
|
@pytest.mark.requiredtool('fdtget')
|
|
@pytest.mark.requiredtool('veritysetup')
|
|
@pytest.mark.parametrize('data_block_size,hash_block_size,subdir', [
|
|
(4096, 4096, 'verity-equal'),
|
|
(4096, 1024, 'verity-unequal'),
|
|
])
|
|
def test_mkimage_verity(ubman, data_block_size, hash_block_size, subdir):
|
|
"""mkimage writes correct dm-verity properties and the digest verifies.
|
|
|
|
Run with matching and mismatched block sizes so the
|
|
``hash-start-block != num-data-blocks`` path is exercised.
|
|
"""
|
|
tempdir = os.path.join(ubman.config.result_dir, subdir)
|
|
os.makedirs(tempdir, exist_ok=True)
|
|
_run_round_trip(ubman, tempdir, data_block_size, hash_block_size)
|
|
|
|
|
|
@pytest.mark.requiredtool('dtc')
|
|
@pytest.mark.requiredtool('veritysetup')
|
|
def test_mkimage_verity_requires_external(ubman):
|
|
"""mkimage rejects dm-verity without -E with the expected diagnostic."""
|
|
|
|
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 % (4096, 4096))
|
|
|
|
dtc_args = f'-I dts -O dtb -i {tempdir}'
|
|
utils.run_and_log_expect_exception(
|
|
ubman,
|
|
[mkimage, '-D', dtc_args, '-f', its_file, fit_file],
|
|
1, 'dm-verity requires external data')
|