| #! /bin/bash |
| # SPDX-License-Identifier: GPL-2.0-only |
| # Copyright 2021 Google LLC |
| # |
| # FS QA Test No. 624 |
| # |
| # Test retrieving the Merkle tree and fs-verity descriptor of a verity file |
| # using FS_IOC_READ_VERITY_METADATA. |
| # |
| . ./common/preamble |
| _begin_fstest auto quick verity |
| |
| # Override the default cleanup function. |
| _cleanup() |
| { |
| cd / |
| _restore_fsverity_signatures |
| rm -f $tmp.* |
| } |
| |
| . ./common/filter |
| . ./common/verity |
| |
| _require_scratch_verity |
| _disable_fsverity_signatures |
| fsv_orig_file=$SCRATCH_MNT/file |
| fsv_file=$SCRATCH_MNT/file.fsv |
| |
| _scratch_mkfs_verity &>> $seqres.full |
| _scratch_mount |
| _fsv_create_enable_file $fsv_file |
| _require_fsverity_dump_metadata $fsv_file |
| |
| # Test FS_IOC_READ_VERITY_METADATA on a file that uses the given Merkle tree |
| # block size. |
| test_block_size() |
| { |
| local block_size=$1 |
| local digest_size=32 # assuming SHA-256 |
| local i |
| |
| # Create the file. Make the file size big enough to result in multiple |
| # Merkle tree levels being needed. The following expression computes a |
| # file size that needs 2 blocks at level 0, and thus 1 block at level 1. |
| local file_size=$((block_size * (2 * (block_size / digest_size)))) |
| head -c $file_size /dev/zero > $fsv_orig_file |
| local tree_params=("--salt=abcd" "--block-size=$block_size") |
| cp $fsv_orig_file $fsv_file |
| _fsv_enable $fsv_file "${tree_params[@]}" |
| |
| # Use the 'fsverity digest' command to compute the expected Merkle tree, |
| # descriptor, and file digest. |
| # |
| # Ideally we'd just hard-code expected values into the .out file and |
| # echo the actual values. That doesn't quite work here, since all these |
| # values depend on the Merkle tree block size, and the Merkle tree block |
| # sizes that are supported (and thus get tested here) vary. Therefore, |
| # we calculate the expected values in userspace with the help of |
| # 'fsverity digest', then do explicit comparisons with them. This works |
| # fine as long as fsverity-utils and the kernel don't get broken in the |
| # same way, in which case generic/575 should detect the problem anyway. |
| local expected_file_digest=$(_fsv_digest $fsv_orig_file \ |
| "${tree_params[@]}" \ |
| --out-merkle-tree=$tmp.merkle_tree.expected \ |
| --out-descriptor=$tmp.descriptor.expected) |
| local merkle_tree_size=$(_get_filesize $tmp.merkle_tree.expected) |
| local descriptor_size=$(_get_filesize $tmp.descriptor.expected) |
| |
| # 'fsverity measure' should return the expected file digest. |
| local actual_file_digest=$(_fsv_measure $fsv_file) |
| if [ "$actual_file_digest" != "$expected_file_digest" ]; then |
| echo "Measure returned $actual_file_digest but expected $expected_file_digest" |
| fi |
| |
| # Test dumping the Merkle tree. |
| _fsv_dump_merkle_tree $fsv_file > $tmp.merkle_tree.actual |
| if ! cmp $tmp.merkle_tree.expected $tmp.merkle_tree.actual; then |
| echo "Dumped Merkle tree didn't match" |
| fi |
| |
| # Test dumping the Merkle tree in chunks. |
| for (( i = 0; i < merkle_tree_size; i += 997 )); do |
| _fsv_dump_merkle_tree $fsv_file --offset=$i --length=997 |
| done > $tmp.merkle_tree.actual |
| if ! cmp $tmp.merkle_tree.expected $tmp.merkle_tree.actual; then |
| echo "Dumped Merkle tree (in chunks) didn't match" |
| fi |
| |
| # Test dumping the descriptor. |
| _fsv_dump_descriptor $fsv_file > $tmp.descriptor.actual |
| if ! cmp $tmp.descriptor.expected $tmp.descriptor.actual; then |
| echo "Dumped descriptor didn't match" |
| fi |
| |
| # Test dumping the descriptor in chunks. |
| for (( i = 0; i < descriptor_size; i += 13 )); do |
| _fsv_dump_descriptor $fsv_file --offset=$i --length=13 |
| done > $tmp.descriptor.actual |
| if ! cmp $tmp.descriptor.expected $tmp.descriptor.actual; then |
| echo "Dumped descriptor (in chunks) didn't match" |
| fi |
| } |
| |
| # Always test FSV_BLOCK_SIZE. Also test some other block sizes if they happen |
| # to be supported. |
| _fsv_scratch_begin_subtest "Testing block_size=FSV_BLOCK_SIZE" |
| test_block_size $FSV_BLOCK_SIZE |
| for block_size in 1024 4096 16384 65536; do |
| _fsv_scratch_begin_subtest "Testing block_size=$block_size if supported" |
| if (( block_size == FSV_BLOCK_SIZE )); then |
| continue # Skip redundant test case. |
| fi |
| if ! _fsv_can_enable $fsv_file --block-size=$block_size; then |
| echo "block_size=$block_size is unsupported" >> $seqres.full |
| continue |
| fi |
| test_block_size $block_size |
| done |
| |
| # success, all done |
| status=0 |
| exit |