• Tutorials
  • dm-verity: Step-by-Step Guide for Embedded Linux

    Ansichten140

    #What is dm-verity?

    dm-verity is a Linux device-mapper target that provides transparent, read-only integrity verification for block devices.

    It uses a Merkle hash tree to verify each block whenever it is read. The final value at the top of the tree—the root hash—acts as the trust anchor. If a data block or hash-tree block is modified, verification fails.

    dm-verity detects unauthorized or accidental changes, but it does not encrypt data.

    #Prerequisites

    Enable the required kernel options:

     1CONFIG_BLK_DEV_DM=y
     2CONFIG_DM_VERITY=y
     3
     4# Optional: create the mapping through dm-mod.create=
     5CONFIG_DM_INIT=y
     6
     7# Optional: verify a signed root hash
     8CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y
    

    Userspace requires veritysetup, provided by Cryptsetup 2.x or newer.

    The protected filesystem must be used read-only. Common choices include:

    • SquashFS
    • EROFS
    • ext4 mounted read-only

    #Step 1: Create a read-only root filesystem

    Create a SquashFS image:

     1mksquashfs rootfs/ rootfs.squashfs -noappend
    

    Alternatively, create an EROFS image:

     1mkfs.erofs rootfs.erofs rootfs/
    

    Do not modify the image after generating its hash tree. Even a one-byte change invalidates the root hash.

    #Step 2: Generate the hash tree

    Create a separate hash-tree image:

     1veritysetup format rootfs.squashfs rootfs.hashtree
    

    The output includes information similar to:

     1UUID:               ...
     2Data blocks:        ...
     3Hash blocks:        ...
     4Data block size:    4096
     5Hash block size:    4096
     6Hash algorithm:     sha256
     7Salt:               f00d...
     8Root hash:          a1b2c3...
    

    Save the root hash:

     1veritysetup format rootfs.squashfs rootfs.hashtree | \
     2    awk '/Root hash/ {print $3}' > roothash.txt
    

    Keep the complete output or at least these parameters:

    • Root hash
    • Salt
    • Hash algorithm
    • Data-block size
    • Hash-block size
    • Number of data blocks

    The root hash must be stored securely because it is the trust anchor.

    #Optional single-partition layout

    The hash tree can be appended to the filesystem image instead of using a separate partition. Calculate a block-aligned offset first:

     1DATA_SIZE=$(stat -c %s rootfs.squashfs)
     2HASH_OFFSET=$(((DATA_SIZE + 4095) / 4096 * 4096))
     3DATA_BLOCKS=$((HASH_OFFSET / 4096))
     4
     5truncate -s "$HASH_OFFSET" rootfs.squashfs
     6
     7veritysetup format rootfs.squashfs rootfs.squashfs \
     8    --hash-offset="$HASH_OFFSET" \
     9    --data-blocks="$DATA_BLOCKS"
    

    The partition must be large enough for both the filesystem and the generated hash tree.

    #Step 3: Flash the images

    One possible eMMC layout is:

    | Partition | Content | | ---------------- | ------------------------- | | /dev/mmcblk0p1 | Boot files | | /dev/mmcblk0p2 | Read-only root filesystem | | /dev/mmcblk0p3 | dm-verity hash tree |

    Write the images:

     1dd if=rootfs.squashfs of=/dev/mmcblk0p2 bs=4M conv=fsync
     2dd if=rootfs.hashtree of=/dev/mmcblk0p3 bs=4M conv=fsync
    

    Warning: Verify the device names carefully. Writing to the wrong block device can destroy data.

    #Step 4: Open the verified device manually

    Create a verified mapping named vroot:

     1veritysetup open \
     2    /dev/mmcblk0p2 \
     3    vroot \
     4    /dev/mmcblk0p3 \
     5    "$(cat roothash.txt)"
    

    Mount the verified filesystem:

     1mkdir -p /mnt/vroot
     2mount -o ro /dev/mapper/vroot /mnt/vroot
    

    Check its status:

     1veritysetup status vroot
    

    Close it after testing:

     1umount /mnt/vroot
     2veritysetup close vroot
    

    #Step 5: Test corruption detection

    Perform this test only on a disposable test image or test device.

    With the verity mapping closed, modify one byte in the data partition:

     1dd if=/dev/urandom \
     2    of=/dev/mmcblk0p2 \
     3    bs=1 count=1 seek=100000 \
     4    conv=notrunc
    

    Open the mapping again and read the complete device:

     1veritysetup open \
     2    /dev/mmcblk0p2 \
     3    vroot \
     4    /dev/mmcblk0p3 \
     5    "$(cat roothash.txt)"
     6
     7dd if=/dev/mapper/vroot of=/dev/null bs=1M
    

    When the corrupted block is read, the operation should fail. The kernel log should contain a message similar to:

     1device-mapper: verity: data block N is corrupted
    

    Check the log with:

     1dmesg | grep -i verity
    

    Redeploy the original filesystem and hash tree after this destructive test.

    #Step 6: Configure dm-verity during boot

    The verified device must be created before Linux mounts the real root filesystem.

    #Option A: Initramfs

    An initramfs can run veritysetup open before switch_root.

    Example kernel command line:

     1roothash=a1b2c3... verityroot=/dev/mmcblk0p2 verityhash=/dev/mmcblk0p3 root=/dev/mapper/vroot ro
    

    The initramfs can create the mapping with:

     1veritysetup open \
     2    "$verityroot" \
     3    vroot \
     4    "$verityhash" \
     5    "$roothash"
    

    The kernel command line must be protected by the verified-boot chain. Otherwise, an attacker could replace both the filesystem and its root hash.

    #Option B: dm-mod.create=

    With CONFIG_DM_INIT=y, the kernel can create the mapping without an initramfs:

     1dm-mod.create="vroot,,,ro,0 <data_sectors> verity 1 /dev/mmcblk0p2 /dev/mmcblk0p3 4096 4096 <data_blocks> 0 sha256 <roothash> <salt>" root=/dev/dm-0 ro
    

    The parameters include:

    • Device size in 512-byte sectors
    • Data device
    • Hash-tree device
    • Data- and hash-block sizes
    • Number of data blocks
    • Hash-tree start block
    • Hash algorithm
    • Root hash
    • Salt

    #Option C: systemd

    Systems using systemd can use systemd-veritysetup. Configuration can come from:

    • roothash= or usrhash= kernel parameters
    • /etc/veritytab
    • Discoverable GPT partitions
    • Embedded verity metadata

    #Step 7: Establish the chain of trust

    The root hash must itself be authenticated. Otherwise, an attacker can create a new hash tree and supply a new root hash for a modified filesystem.

    A complete chain of trust can be:

    1. The immutable Boot ROM verifies the first-stage bootloader.
    2. The first-stage bootloader verifies the next boot stage.
    3. U-Boot verifies a signed FIT configuration.
    4. The FIT image contains or authenticates the kernel, device tree, initramfs, command line, and dm-verity root hash.
    5. Linux uses the trusted root hash to verify the root filesystem.

    Suitable mechanisms include:

    • Signed FIT images
    • UEFI Secure Boot
    • Signed kernel and initramfs images
    • Xilinx authenticated or encrypted boot images

    #Signed root hash

    With the following kernel option enabled:

     1CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y
    

    veritysetup can supply a PKCS#7 root-hash signature:

     1veritysetup open \
     2    /dev/mmcblk0p2 \
     3    vroot \
     4    /dev/mmcblk0p3 \
     5    "$(cat roothash.txt)" \
     6    --root-hash-signature=sig.p7s
    

    The signing certificate must be trusted by the kernel keyring.

    #Updates and A/B partitioning

    Because dm-verity protects an immutable image, the filesystem cannot be updated in place. Every update requires:

    1. Building a new filesystem image.
    2. Generating a new hash tree.
    3. Obtaining a new root hash.
    4. Signing or authenticating that root hash.
    5. Deploying the complete image and tree.

    An A/B layout works particularly well:

    | Slot | Root filesystem | Hash tree | | ---- | --------------- | ---------- | | A | rootfs_a | verity_a | | B | rootfs_b | verity_b |

    The inactive slot can be updated while the active slot remains bootable. If the new system fails, the bootloader can roll back to the previous slot.

    #Corruption policies

    dm-verity can respond to corruption in different ways:

    • Return an I/O error
    • Ignore corrupted blocks
    • Restart the system
    • Panic the kernel

    Available options include:

     1ignore_corruption
     2restart_on_corruption
     3panic_on_corruption
    

    For security-sensitive systems, silently ignoring corruption is normally inappropriate. Restarting or panicking can be combined with a bootloader rollback mechanism.

    #Forward error correction

    dm-verity optionally supports forward error correction:

     1veritysetup format \
     2    rootfs.squashfs \
     3    rootfs.hashtree \
     4    --fec-device=rootfs.fec
    

    FEC can recover some corrupted data instead of only detecting it. It requires additional storage and does not replace protection of the root hash.

    #dm-verity compared with related technologies

    | Technology | Protection scope | Writable? | Main use | | ------------ | --------------------: | -----------------: | --------------------------------------- | | dm-verity | Complete block device | No | Immutable root filesystem | | fs-verity | Individual files | Not after enabling | Authenticating selected files | | dm-integrity | Block device | Yes | Integrity metadata for writable storage | | dm-crypt | Block device | Yes | Data confidentiality through encryption |

    #Conclusion

    dm-verity provides strong runtime integrity protection for read-only Linux filesystems. Every block is checked against a Merkle hash tree, allowing the kernel to detect malicious modification and accidental corruption.

    However, dm-verity alone does not establish secure boot. Its root hash must be authenticated by a higher layer, such as a signed FIT image, UEFI Secure Boot, or a Xilinx secure-boot chain.

    A robust embedded design combines:

    • Secure or verified boot
    • A trusted dm-verity root hash
    • A read-only root filesystem
    • An A/B update mechanism
    • A defined corruption and rollback policy

    Together, these components establish a continuous chain of trust from the first boot instruction to every filesystem block read by Linux.

    profile image of Martin Mitkov

    Martin Mitkov

    Martin is a founder and CEO of Mitkov Systems GmbH.

    More posts from Martin Mitkov