@@ -27,9 +27,19 @@ type ImageSummary struct {
2727 SHA256 string `json:"sha256,omitempty"`
2828 SizeBytes int64 `json:"sizeBytes,omitempty"`
2929 PartitionTable PartitionTableSummary `json:"partitionTable,omitempty"`
30+ Verity * VerityInfo `json:"verity,omitempty" yaml:"verity,omitempty"`
3031 // SBOM SBOMSummary `json:"sbom,omitempty"`
3132}
3233
34+ // VerityInfo holds dm-verity detection information.
35+ type VerityInfo struct {
36+ Enabled bool `json:"enabled" yaml:"enabled"`
37+ Method string `json:"method,omitempty" yaml:"method,omitempty"` // "systemd-verity", "custom-initramfs", "unknown"
38+ RootDevice string `json:"rootDevice,omitempty" yaml:"rootDevice,omitempty"`
39+ HashPartition int `json:"hashPartition,omitempty" yaml:"hashPartition,omitempty"` // partition index, 0 if none
40+ Notes []string `json:"notes,omitempty" yaml:"notes,omitempty"`
41+ }
42+
3343// PartitionTableSummary holds information about the partition table of the disk image.
3444type PartitionTableSummary struct {
3545 Type string
@@ -299,11 +309,15 @@ func (d *DiskfsInspector) inspectCore(
299309 }
300310 ptSummary .Partitions = partitionsWithFS
301311
312+ // Detect dm-verity configuration
313+ verityInfo := detectVerity (ptSummary )
314+
302315 return & ImageSummary {
303316 File : imagePath ,
304317 SizeBytes : sizeBytes ,
305318 PartitionTable : ptSummary ,
306319 SHA256 : sha256sum ,
320+ Verity : verityInfo ,
307321 }, nil
308322}
309323
@@ -554,3 +568,119 @@ func computeFileSHA256(f *os.File) (string, error) {
554568
555569 return hex .EncodeToString (h .Sum (nil )), nil
556570}
571+
572+ // detectVerity inspects the partition table and UKI cmdline to detect dm-verity configuration.
573+ func detectVerity (pt PartitionTableSummary ) * VerityInfo {
574+ info := & VerityInfo {}
575+
576+ // Look for hash partition (common names/types)
577+ hashPartIdx := - 1
578+ for i , p := range pt .Partitions {
579+ name := strings .ToLower (p .Name )
580+ // Check for common hash partition names
581+ if strings .Contains (name , "hash" ) || name == "roothashmap" {
582+ hashPartIdx = i
583+ info .HashPartition = p .Index
584+ info .Notes = append (info .Notes , fmt .Sprintf ("Hash partition found: %s (partition %d)" , p .Name , p .Index ))
585+ break
586+ }
587+ }
588+
589+ // Extract cmdline from UKI if present
590+ var cmdline string
591+ for _ , p := range pt .Partitions {
592+ if p .Filesystem != nil && p .Filesystem .HasUKI {
593+ for _ , efi := range p .Filesystem .EFIBinaries {
594+ if efi .IsUKI && efi .Cmdline != "" {
595+ cmdline = efi .Cmdline
596+ break
597+ }
598+ }
599+ }
600+ if cmdline != "" {
601+ break
602+ }
603+ }
604+
605+ if cmdline == "" {
606+ // No cmdline found, dm-verity not detected
607+ if hashPartIdx >= 0 {
608+ info .Notes = append (info .Notes , "Hash partition exists but no UKI cmdline found" )
609+ }
610+ return nil
611+ }
612+
613+ // Check for dm-verity indicators in cmdline
614+ // 1. systemd.verity_* parameters (standard systemd-verity)
615+ if strings .Contains (cmdline , "systemd.verity_name=" ) ||
616+ strings .Contains (cmdline , "systemd.verity_root_data=" ) ||
617+ strings .Contains (cmdline , "systemd.verity_root_hash=" ) {
618+ info .Enabled = true
619+ info .Method = "systemd-verity"
620+ info .Notes = append (info .Notes , "systemd.verity_* parameters found in cmdline" )
621+
622+ // Extract root device from cmdline
623+ if strings .Contains (cmdline , "root=" ) {
624+ for _ , part := range strings .Fields (cmdline ) {
625+ if strings .HasPrefix (part , "root=" ) {
626+ info .RootDevice = strings .TrimPrefix (part , "root=" )
627+ break
628+ }
629+ }
630+ }
631+
632+ if hashPartIdx >= 0 {
633+ info .Notes = append (info .Notes , fmt .Sprintf ("Hash partition present at index %d" , hashPartIdx ))
634+ } else {
635+ info .Notes = append (info .Notes , "WARNING: systemd.verity_* found but no hash partition detected" )
636+ }
637+ return info
638+ }
639+
640+ // 2. root=/dev/mapper/*verity* pattern (custom initramfs, e.g., EMT/EMF tpm-cryptsetup)
641+ if strings .Contains (cmdline , "root=/dev/mapper/" ) && strings .Contains (cmdline , "verity" ) {
642+ info .Enabled = true
643+ info .Method = "custom-initramfs"
644+ info .Notes = append (info .Notes , "root=/dev/mapper/*verity* pattern found in cmdline" )
645+
646+ // Extract the exact root device
647+ for _ , part := range strings .Fields (cmdline ) {
648+ if strings .HasPrefix (part , "root=" ) {
649+ info .RootDevice = strings .TrimPrefix (part , "root=" )
650+ break
651+ }
652+ }
653+
654+ if hashPartIdx >= 0 {
655+ info .Notes = append (info .Notes , fmt .Sprintf ("Hash partition present at index %d" , hashPartIdx ))
656+ info .Notes = append (info .Notes , "Likely using separate hash partition for dm-verity" )
657+ } else {
658+ info .Notes = append (info .Notes , "No separate hash partition detected" )
659+ info .Notes = append (info .Notes , "Likely using custom initramfs (e.g., dracut tpm-cryptsetup module)" )
660+ info .Notes = append (info .Notes , "Hash data may be: appended to rootfs, embedded in FDE, or managed by initramfs" )
661+ }
662+ return info
663+ }
664+
665+ // 3. Check for roothash= parameter (direct hash specification)
666+ if strings .Contains (cmdline , "roothash=" ) {
667+ info .Enabled = true
668+ info .Method = "roothash-parameter"
669+ info .Notes = append (info .Notes , "roothash= parameter found in cmdline" )
670+
671+ for _ , part := range strings .Fields (cmdline ) {
672+ if strings .HasPrefix (part , "root=" ) {
673+ info .RootDevice = strings .TrimPrefix (part , "root=" )
674+ break
675+ }
676+ }
677+
678+ if hashPartIdx >= 0 {
679+ info .Notes = append (info .Notes , fmt .Sprintf ("Hash partition present at index %d" , hashPartIdx ))
680+ }
681+ return info
682+ }
683+
684+ // No dm-verity detected
685+ return nil
686+ }
0 commit comments