| #!/bin/bash | |
| VERITYSETUP=../src/veritysetup | |
| DEV_NAME=verity3273 | |
| DEV_OUT="$DEV_NAME.out" | |
| IMG=verity-data | |
| IMG_HASH=verity-hash | |
| IMG_TMP=tst-dev | |
| FEC_DEV=tst_fec123 | |
| function remove_mapping() | |
| { | |
| [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove $DEV_NAME >/dev/null 2>&1 | |
| [ ! -z "$LOOPDEV1" ] && losetup -d $LOOPDEV1 >/dev/null 2>&1 | |
| rm -f $IMG $IMG_HASH $DEV_OUT $FEC_DEV $IMG_TMP >/dev/null 2>&1 | |
| LOOPDEV1="" | |
| LOOPDEV2="" | |
| } | |
| function fail() | |
| { | |
| [ -n "$1" ] && echo "$1" | |
| echo "FAILED" | |
| [ -f $DEV_OUT ] && cat $DEV_OUT | |
| remove_mapping | |
| exit 2 | |
| } | |
| function skip() | |
| { | |
| [ -n "$1" ] && echo "$1" | |
| exit 0 | |
| } | |
| function prepare() # $1 dev1_siz [$2 dev2_size] | |
| { | |
| remove_mapping | |
| dd if=/dev/zero of=$IMG bs=1k count=$1 >/dev/null 2>&1 | |
| LOOPDEV1=$(losetup -f 2>/dev/null) | |
| [ -z "$LOOPDEV1" ] && fail "No free loop device" | |
| losetup $LOOPDEV1 $IMG | |
| [ -z "$2" ] && return | |
| LOOPDEV2=$IMG_HASH | |
| } | |
| function wipe() | |
| { | |
| dd if=/dev/zero of=$LOOPDEV1 bs=256k >/dev/null 2>&1 | |
| rm -f $IMG_HASH $DEV_OUT >/dev/null 2>&1 | |
| } | |
| function check_exists() | |
| { | |
| [ -b /dev/mapper/$DEV_NAME ] || fail | |
| } | |
| function check_version() | |
| { | |
| VER_STR=$(dmsetup targets | grep verity | cut -f 3 -dv) | |
| VER_MAJ=$(echo $VER_STR | cut -f 1 -d.) | |
| VER_MIN=$(echo $VER_STR | cut -f 2 -d.) | |
| # option supported in 1.3 | |
| test $VER_MAJ -gt 1 && return 0 | |
| test $VER_MIN -ge 3 && return 0 | |
| return 1 | |
| } | |
| function compare_out() # $1 what, $2 expected | |
| { | |
| OPT=$(grep -v "^#" $DEV_OUT | grep -i "$1" | sed -e s/.*\:\ // ) | |
| [ -z "$OPT" ] && fail | |
| [ $OPT != $2 ] && fail "$1 differs ($2)" | |
| } | |
| function check_root_hash() # $1 size, $2 hash, $3 salt, $4 version, $5 hash, [$6 offset] | |
| { | |
| if [ -z "$LOOPDEV2" ] ; then | |
| BLOCKS=$(($6 / $1)) | |
| DEV_PARAMS="$LOOPDEV1 $LOOPDEV1 \ | |
| --hash-offset $6 \ | |
| --data-blocks=$BLOCKS --debug" | |
| else | |
| DEV_PARAMS="$LOOPDEV1 $LOOPDEV2" | |
| fi | |
| for sb in yes no; do | |
| FORMAT_PARAMS="--format=$4 --data-block-size=$1 --hash-block-size=$1 --hash=$5 --salt=$3" | |
| if [ $sb == yes ] ; then | |
| VERIFY_PARAMS="" | |
| else | |
| FORMAT_PARAMS="$FORMAT_PARAMS --no-superblock" | |
| VERIFY_PARAMS=$FORMAT_PARAMS | |
| fi | |
| for fail in data hash; do | |
| wipe | |
| echo -n "V$4(sb=$sb) $5 block size $1: " | |
| $VERITYSETUP format $DEV_PARAMS $FORMAT_PARAMS >$DEV_OUT || fail | |
| echo -n "[root hash]" | |
| compare_out "root hash" $2 | |
| compare_out "salt" "$3" | |
| $VERITYSETUP verify $DEV_PARAMS $VERIFY_PARAMS $2 >>$DEV_OUT 2>&1 || fail | |
| echo -n "[verify]" | |
| $VERITYSETUP create $DEV_NAME $DEV_PARAMS $VERIFY_PARAMS $2 >>$DEV_OUT 2>&1 || fail | |
| check_exists | |
| echo -n "[activate]" | |
| dd if=/dev/mapper/$DEV_NAME of=/dev/null bs=$1 2>/dev/null | |
| dmsetup status $DEV_NAME | grep "verity V" >/dev/null || fail | |
| echo -n "[in-kernel verify]" | |
| $VERITYSETUP remove $DEV_NAME >/dev/null 2>&1 || fail | |
| case $fail in | |
| data) | |
| dd if=/dev/urandom of=$LOOPDEV1 bs=1 seek=3456 count=8 conv=notrunc 2>/dev/null | |
| TXT="data_dev" | |
| ;; | |
| hash) | |
| if [ -z "$LOOPDEV2" ] ; then | |
| dd if=/dev/urandom of=$LOOPDEV1 bs=1 seek=$((8193 + $4)) count=8 conv=notrunc 2>/dev/null | |
| else | |
| dd if=/dev/urandom of=$LOOPDEV2 bs=1 seek=8193 count=8 conv=notrunc 2>/dev/null | |
| fi | |
| TXT="hash_dev" | |
| ;; | |
| esac | |
| $VERITYSETUP verify $DEV_PARAMS $VERIFY_PARAMS $2 >>$DEV_OUT 2>&1 && \ | |
| fail "userspace check for $TXT corruption" | |
| $VERITYSETUP create $DEV_NAME $DEV_PARAMS $VERIFY_PARAMS $2 >>$DEV_OUT 2>&1 || \ | |
| fail "activation" | |
| dd if=/dev/mapper/$DEV_NAME of=/dev/null bs=$1 2>/dev/null | |
| dmsetup status $DEV_NAME | grep "verity V" >/dev/null && \ | |
| fail "in-kernel check for $TXT corruption" | |
| $VERITYSETUP remove $DEV_NAME >/dev/null 2>&1 || fail "deactivation" | |
| echo "[$TXT corruption]" | |
| done | |
| done | |
| } | |
| function corrupt_device() # $1 device, $2 device_size(in bytes), $3 #{corrupted_bytes} | |
| { | |
| ARR=(`seq 0 $(($2 - 1)) | shuf`) | |
| CORRUPT=$3 | |
| while [ "$CORRUPT" -gt 0 ]; do | |
| echo "1" | dd of=$1 bs=1 count=1 seek=${ARR[$CORRUPT]} conv=notrunc > /dev/null 2>&1 | |
| CORRUPT=$(($CORRUPT - 1)) | |
| done | |
| } | |
| # $1 data_device, $2 hash_device, $3 fec_device, $4 data/hash_block_size(in bytes), | |
| # $5 data_size(in blocks), $6 device_size(in blocks), $7 hash_offset(in bytes), | |
| # $8 fec_offset(in bytes), $9 fec_roots, ${10} corrupted_bytes, [${11} superblock(y/n), ${12} salt] | |
| function check_fec() | |
| { | |
| INDEX=25 | |
| dd if=/dev/zero of=$1 bs=$4 count=$6 > /dev/null 2>&1 | |
| echo -n "Block_size: $4, Data_size: $(($4 * $5))B, FEC_roots: $9, Corrupted_bytes: ${10} " | |
| PARAMS=" --data-block-size=$4 --hash-block-size=$4 " | |
| if [ "$5" -ne "$6" ]; then | |
| PARAMS="$PARAMS --data-blocks=$5" | |
| fi | |
| if [ "$7" -ne 0 ]; then | |
| PARAMS="$PARAMS --hash-offset=$7" | |
| fi | |
| if [ "$8" -ne 0 ]; then | |
| PARAMS="$PARAMS --fec-offset=$8" | |
| fi | |
| if [ "${11}" == "n" ]; then | |
| INDEX=24 | |
| echo -n "[no-superblock]" | |
| PARAMS="$PARAMS --no-superblock -s=${12}" | |
| elif [ -n "${12}" ]; then | |
| PARAMS="$PARAMS -s=${12}" | |
| fi | |
| if [[ "$1" == "$2" && "$1" == "$3" ]]; then | |
| echo -n "[one_device_test]" | |
| dd if=/dev/zero of=$IMG_TMP bs=$4 count=$5 > /dev/null 2>&1 | |
| ARR=(`sha256sum $IMG_TMP`) | |
| HASH_ORIG=${ARR[0]} | |
| else | |
| ARR=(`sha256sum $1`) | |
| HASH_ORIG=${ARR[0]} | |
| fi | |
| ARR=(`$VERITYSETUP format $1 $2 --fec-device=$3 $PARAMS`) | |
| SALT=${ARR[$INDEX]} | |
| ROOT_HASH=${ARR[$(($INDEX+3))]} | |
| corrupt_device $1 $(($5 * $4)) ${10} | |
| $VERITYSETUP create $DEV_NAME $1 $2 $ROOT_HASH --fec-device=$3 $PARAMS > /dev/null 2>&1 | |
| if [ "$?" -ne "0" ] ; then | |
| echo "[N/A, test skipped]" | |
| return 3 | |
| fi | |
| udevadm settle | |
| dd if=/dev/mapper/$DEV_NAME of=$IMG_TMP > /dev/null 2>&1 | |
| ARR=(`sha256sum $IMG_TMP`) | |
| HASH_REPAIRED=${ARR[0]} | |
| $VERITYSETUP remove $DEV_NAME | |
| rm $1 $2 $3 $IMG_TMP > /dev/null 2>&1 | |
| if [ "$HASH_ORIG" != "$HASH_REPAIRED" ]; then | |
| echo -n "[correction failed]" | |
| return 1 | |
| fi | |
| echo "[file was repaired][OK]" | |
| } | |
| function check_option() # $1 size, $2 hash, $3 salt, $4 version, $5 hash, $6 CLI option, $7 status option | |
| { | |
| DEV_PARAMS="$LOOPDEV1 $LOOPDEV2" | |
| FORMAT_PARAMS="--format=$4 --data-block-size=$1 --hash-block-size=$1 --hash=$5 --salt=$3" | |
| echo -n "Option $6 " | |
| $VERITYSETUP format $DEV_PARAMS $FORMAT_PARAMS >/dev/null 2>&1 || fail | |
| $VERITYSETUP create $DEV_NAME $DEV_PARAMS $2 $6 >/dev/null 2>&1 || fail | |
| check_exists | |
| $VERITYSETUP status $DEV_NAME 2>/dev/null | grep flags | grep -q $7 || fail | |
| dmsetup table $DEV_NAME 2>/dev/null | grep -q $7 || fail | |
| $VERITYSETUP remove $DEV_NAME >/dev/null 2>&1 || fail | |
| echo "[OK]" | |
| } | |
| function valgrind_setup() | |
| { | |
| which valgrind >/dev/null 2>&1 || fail "Cannot find valgrind." | |
| [ ! -f $CRYPTSETUP_VALGRIND ] && fail "Unable to get location of cryptsetup executable." | |
| #export LD_LIBRARY_PATH="$CRYPTSETUP_LIB_VALGRIND:$LD_LIBRARY_PATH" | |
| } | |
| function valgrind_run() | |
| { | |
| INFOSTRING="$(basename ${BASH_SOURCE[1]})-line-${BASH_LINENO[0]}" ./valg.sh ${VERITYSETUP} "$@" | |
| } | |
| function checkOffsetBug() # $1 size, $2 hash-offset, $3 data-blocks | |
| { | |
| echo -n "Size :: $1 B | Hash-offset :: $2 blocks | Data-blocks :: $3 " | |
| dd if=/dev/zero of=$IMG bs=1 count=0 seek=$1 >/dev/null 2>&1 | |
| $VERITYSETUP --data-blocks=$3 --hash-offset=$2 format $IMG $IMG >/dev/null 2>&1 || fail "Test [hash-offset greater than 2G] failed" | |
| echo "[OK]" | |
| remove_mapping | |
| } | |
| function checkOverlapBug() # $1 size, $2 hash-offset, $3 data-blocks | |
| { | |
| echo -n "Size :: $1 B | Hash-offset :: $2 blocks | " | |
| dd if=/dev/zero of=$IMG bs=1 count=0 seek=$1 >/dev/null 2>&1 | |
| if [ -z $3 ] ; then | |
| # veritysetup must fail | |
| $VERITYSETUP --hash-offset=$2 format $IMG $IMG >/dev/null 2>&1 && fail "Test [overlap with option \"--data-blocks\" not entered] failed" | |
| else | |
| $VERITYSETUP --data-blocks=$3 --hash-offset=$2 format $IMG $IMG >/dev/null 2>&1 || fail "Test [overlap with option \"--data-blocks\" entered] failed" | |
| RET=$? | |
| [ "$3" -gt "$(($2 / 4096))" ] && [ "$RET" -eq "0" ] && fail "Test [overlap - hash-offset in data area] failed" | |
| fi | |
| echo "[OK]" | |
| remove_mapping | |
| } | |
| [ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped." | |
| [ ! -x "$VERITYSETUP" ] && skip "Cannot find $VERITYSETUP, test skipped." | |
| [ -n "$VALG" ] && valgrind_setup && VERITYSETUP=valgrind_run | |
| modprobe dm-verity >/dev/null 2>&1 | |
| dmsetup targets | grep verity >/dev/null 2>&1 || skip "Cannot find dm-verity target, test skipped." | |
| # VERITYSETUP tests | |
| SALT=e48da609055204e89ae53b655ca2216dd983cf3cb829f34f63a297d106d53e2d | |
| echo "Verity tests [separate devices]" | |
| prepare 8192 1024 | |
| check_root_hash 512 9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174 $SALT 1 sha256 | |
| check_root_hash 1024 54d92778750495d1f80832b486ebd007617d746271511bbf0e295e143da2b3df $SALT 1 sha256 | |
| check_root_hash 4096 e522df0f97da4febb882ac40f30b37dc0b444bf6df418929463fa25280f09d5c $SALT 1 sha256 | |
| # version 0 | |
| check_root_hash 4096 cbbf4ebd004ef65e29b935bb635a39cf754d677f3fa10b0126da725bbdf10f7d $SALT 0 sha256 | |
| # no salt | |
| check_root_hash 4096 ef29c902d87350f1da4bfa536e16cebc162a909bf89abe448b81ec500d4fb9bf - 1 sha256 | |
| # sha1 | |
| check_root_hash 1024 d0e9163ca8844aaa2e88fe5265a8c5d9ee494a99 $SALT 1 sha1 | |
| check_root_hash 1024 73509e8e868be6b8ac939817a98a3d35121413b2 dadada 1 sha1 | |
| echo "Verity tests [one device offset]" | |
| prepare $((8192 + 1024)) | |
| check_root_hash 512 9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174 $SALT 1 sha256 8388608 | |
| check_root_hash 1024 54d92778750495d1f80832b486ebd007617d746271511bbf0e295e143da2b3df $SALT 1 sha256 8388608 | |
| check_root_hash 4096 e522df0f97da4febb882ac40f30b37dc0b444bf6df418929463fa25280f09d5c $SALT 1 sha256 8388608 | |
| # version 0 | |
| check_root_hash 4096 cbbf4ebd004ef65e29b935bb635a39cf754d677f3fa10b0126da725bbdf10f7d $SALT 0 sha256 8388608 | |
| # no salt | |
| check_root_hash 4096 ef29c902d87350f1da4bfa536e16cebc162a909bf89abe448b81ec500d4fb9bf - 1 sha256 8388608 | |
| # sha1 | |
| check_root_hash 1024 d0e9163ca8844aaa2e88fe5265a8c5d9ee494a99 $SALT 1 sha1 8388608 | |
| check_root_hash 1024 73509e8e868be6b8ac939817a98a3d35121413b2 dadada 1 sha1 8388608 | |
| if check_version ; then | |
| echo "Verity data corruption options test." | |
| SALT=e48da609055204e89ae53b655ca2216dd983cf3cb829f34f63a297d106d53e2d | |
| HASH=9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174 | |
| prepare 8192 1024 | |
| check_option 512 $HASH $SALT 1 sha256 "--ignore-corruption" "ignore_corruption" | |
| check_option 512 $HASH $SALT 1 sha256 "--restart-on-corruption" "restart_on_corruption" | |
| check_option 512 $HASH $SALT 1 sha256 "--ignore-zero-blocks" "ignore_zero_blocks" | |
| check_option 512 $HASH $SALT 1 sha256 "--ignore-corruption --ignore-zero-blocks" "ignore_corruption" | |
| fi | |
| echo "Veritysetup [hash-offset bigger than 2G works] " | |
| checkOffsetBug 3000000000 2499997696 256 | |
| checkOffsetBug 10000000000 8000000000 128 | |
| echo "Veritysetup [overlap-detection] " | |
| checkOverlapBug 2097152 1433600 | |
| checkOverlapBug 2097152 1433600 350 | |
| if check_version ; then | |
| echo "Veritysetup [FEC tests]" | |
| command -v shuf >/dev/null 2>&1 || skip "WARNING: shuf tool required." | |
| for INDEX in {1..4}; do | |
| # in the first iteration check if we can usae FEC (it can be compiled-out) | |
| (check_fec $IMG $IMG $IMG 4096 30 150 163840 409600 $(($RANDOM % 23 + 2)) $(($INDEX * 4)) ) | |
| RET=$? | |
| [ "$RET" -eq "3" ] && break | |
| [ "$RET" -eq "0" ] || fail "FEC repair failed" | |
| (check_fec $IMG $IMG_HASH $FEC_DEV 4096 30 30 0 0 $(($RANDOM % 23 + 2)) $(($INDEX * 4)) 'n' $SALT) || fail "FEC repair failed" | |
| (check_fec $IMG $IMG_HASH $FEC_DEV 4096 35 35 0 0 $(($RANDOM % 23 + 2)) $(($INDEX * 4))) || fail "FEC repair failed" | |
| (check_fec $IMG $IMG_HASH $FEC_DEV 512 2000 2000 0 0 $(($RANDOM % 23 + 2)) $(($INDEX * 4))) || fail "FEC repair failed" | |
| (check_fec $IMG $IMG_HASH $FEC_DEV 1024 2000 2000 0 0 $(($RANDOM % 23 + 2)) $(($INDEX * 4))) || fail "FEC repair failed" | |
| # this test should fail | |
| (check_fec $IMG $IMG_HASH $FEC_DEV 4096 30 30 0 0 $(($RANDOM % 23 + 2)) $(($RANDOM % 200 + 200))) && fail "FEC repair must fail" | |
| echo "[OK]" | |
| done | |
| fi | |
| remove_mapping | |
| exit 0 |