#!/usr/bin/perl
# Generate dm-verity hashes and sign the root hash
#
# Output looks like
#
# VERITY header information for hdd.img
# UUID:               73532888-a3e9-4f16-a50a-1d03a265b94f
# Hash type:          1
# Data blocks:        7680
# Data block size:    4096
# Hash block size:    4096
# Hash algorithm:     sha256
# Salt:               3d0cd593d29715005794c4e1cd5164c14ba6456c3dbd2c6d8a26007c01ca9937
# Root hash:          91beda90d7fa1ab92463344966eb56ec9706f4f26063933a86d701a02a961a10
#
my $usage = <<"";
Usage:
size=32
dd if=/dev/zero of=hdd.img bs=1M count=$size
mkfs.ext4 -b 4096 ./hdd.img
mkdir /tmp/loop
sudo mount -o loop hdd.img /tmp/loop
(populate /tmp/loop)
sudo umount /tmp/loop
veritysetup \
	--hash-offset $[$size*1024*1024] \
	--data-blocks $[$size*1024*1024/4096] \
	format hdd.img hdd.img \
| ./verity-sign /dev/sda1 \
| gpg --clearsign \
| tee hdd.table


use warnings;
use strict;

my $dev = shift;

local $_ = <STDIN>;
chomp;

my ($orig_device) = /^VERITY header information for (.*)/
	or die "Missing VERITY header\n";

my %params;

$dev ||= $orig_device;

while(<>)
{
	chomp;
	my ($key,$value) = split /:\s+/;
	$params{$key} = $value;
}

my @missing;


# All the necessary parameters were in the header, generate
# the command to mount the filesystem
my $data_blocks = $params{'Data blocks'}
	or push @missing, 'Data blocks';
my $data_block_size = $params{'Data block size'}
	or push @missing, 'Data block size';
my $hash_block_size = $params{'Hash block size'}
	or push @missing, 'Hash block size';
my $salt = $params{'Salt'}
	or push @missing, 'Salt';
my $root_hash = $params{'Root hash'}
	or push @missing, 'Root hash';
my $hash_type = $params{'Hash type'}
	or push @missing, 'Hash type';
my $hash_algorithm = $params{'Hash algorithm'}
	or push @missing, 'Hash algorithm';

# Check for any missing parameters
die "Missing parameter: ", join(', ', @missing), "\n"
	if @missing;

# Compute the derived parameters
my $data_size = $data_blocks * $data_block_size;
my $data_size_512b = $data_size / 512;
my $first_hash_block = $data_blocks + 1;

# The table must be on a single line
my $table = sprintf "0 %d verity %d %s %s %d %d %d %d %s %s %s",
	$data_size_512b,
	$hash_type,
	$dev,
	$dev,
	$data_block_size,
	$hash_block_size,
	$data_blocks,
	$first_hash_block,
	$hash_algorithm,
	$root_hash,
	$salt,
	;

print "dmsetup create --readonly boot --table '$table'\n";
print "dmsetup mknodes boot\n";
print "mount -o ro /dev/mapper/boot /boot\n";

__END__