#!/bin/bash

#PS4='$LINENO:'
[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".."
CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup

if [ -n "$CRYPTSETUP_TESTS_RUN_IN_MESON" ]; then
	CRYPTSETUP_VALGRIND=$CRYPTSETUP
else
	CRYPTSETUP_VALGRIND=../.libs/cryptsetup
	CRYPTSETUP_LIB_VALGRIND=../.libs
fi

FAST_PBKDF2="--pbkdf pbkdf2 --pbkdf-force-iterations 1000"
FAST_PBKDF_ARGON="--pbkdf-force-iterations 4 --pbkdf-memory 32 --pbkdf-parallel 1"
DEFAULT_ARGON="argon2i"

DEV=""
OVRDEV="123reenc321"
DEVBIG="reenc2134"
DEV_NAME=reenc9768
DEV_NAME2=reenc97682
IMG=reenc-data
IMG_HDR=$IMG.hdr
HEADER_LUKS2_PV=blkid-luks2-pv.img
IMG_FS=xfs_512_block_size.img
KEY1=key1
VKEY1=vkey1
VKEY2=vkey2
BACKUP_FILE=reenc_header_baseline.img
PWD1="93R4P4pIqAH8"
PWD2="1cND4319812f"
PWD3="1-9Qu5Ejfnqv"
PWD4="d54uVMKW-'7M"
PWD5="oDe{yCiZ_';_"
PWD6="T'E'Y_}<'9=q"
PWD7="UR%]T;x*w{3V"

DEV_LINK="reenc-test-link"
KEYRING="luks2_reencryption_test_kr"
KEY_TYPE="user"
KEY_NAME1="luks2-reencryption-test1"
KEY_NAME2="luks2-reencryption-test2"
KEY_NAME3="luks2-reencryption-test3"
KEY_NAME4="luks2-reencryption-test4"
KEY_NAME5="luks2-reencryption-test5"
KEY_NAME6="luks2-reencryption-test6"
KEY_NAME7="luks2-reencryption-key-desc1"
KEY_NAME_VK1="luks2-reencryption-test-vk1"
KEY_NAME_VK2="luks2-reencryption-test-vk2"
KEY_SPEC1="${KEYRING}::%${KEY_TYPE}:${KEY_NAME1}"
KEY_SPEC2="${KEYRING}::%${KEY_TYPE}:${KEY_NAME2}"
HAVE_KEYRING=0
JSON_MSIZE=16384
IMG_JSON=luks2-digest-1.json

FIPS_MODE=$(cat /proc/sys/crypto/fips_enabled 2>/dev/null)

dm_crypt_features()
{
	VER_STR=$(dmsetup targets | grep crypt | cut -f2 -dv)
	[ -z "$VER_STR" ] && fail "Failed to parse dm-crypt version."

	VER_MAJ=$(echo $VER_STR | cut -f 1 -d.)
	VER_MIN=$(echo $VER_STR | cut -f 2 -d.)
	VER_PTC=$(echo $VER_STR | cut -f 3 -d.)

	[ $VER_MAJ -lt 1 ] && return
	[ $VER_MAJ -gt 1 ] && {
		DM_PERF_CPU=1
		DM_SECTOR_SIZE=1
		return
	}

	[ $VER_MIN -lt 14 ] && return
	DM_PERF_CPU=1
	if [ $VER_MIN -ge 17 ]; then
		DM_SECTOR_SIZE=1
	fi
}

dm_delay_features()
{
	local _ver_str=$(dmsetup targets | grep delay | cut -f2 -dv)
	[ -z "$_ver_str" ] && return 1
	return 0
}

# $1 path to scsi debug bdev
scsi_debug_teardown() {
	local _tries=15;

	while [ -b "$1" -a $_tries -gt 0 ]; do
		rmmod scsi_debug >/dev/null 2>&1
		if [ -b "$1" ]; then
			sleep .1
			_tries=$((_tries-1))
		fi
	done

	test ! -b "$1" || rmmod scsi_debug >/dev/null 2>&1
}

remove_mapping()
{
	[ -b /dev/mapper/$DEV_NAME ] && {
		dmsetup resume $DEV_NAME
		dmsetup remove --retry $DEV_NAME
	}
	[ -b /dev/mapper/$DEV_NAME2 ] && {
		dmsetup resume $DEV_NAME2
		dmsetup remove --retry $DEV_NAME2
	}
	[ -b /dev/mapper/$DEV_NAME-overlay ] && {
		dmsetup resume $DEV_NAME-overlay
		dmsetup remove --retry $DEV_NAME-overlay
	}
	[ -b /dev/mapper/$DEV_NAME-hotzone-forward ] && {
		dmsetup resume $DEV_NAME-hotzone-forward
		dmsetup remove --retry $DEV_NAME-hotzone-forward
	}
	[ -b /dev/mapper/$DEV_NAME-hotzone-backward ] && {
		dmsetup resume $DEV_NAME-hotzone-backward
		dmsetup remove --retry $DEV_NAME-hotzone-backward
	}
	[ -b /dev/mapper/$OVRDEV ] && dmsetup remove --retry $OVRDEV 2>/dev/null
	[ -b /dev/mapper/$OVRDEV-err ] && dmsetup remove --retry $OVRDEV-err 2>/dev/null
	[ -n "$LOOPDEV" ] && losetup -d $LOOPDEV
	unset LOOPDEV
	rm -f $IMG $IMG_JSON $IMG_HDR $KEY1 $VKEY1 $VKEY2 $BACKUP_FILE $DEVBIG $DEV_LINK $HEADER_LUKS2_PV $IMG_FS >/dev/null 2>&1
	rmmod scsi_debug >/dev/null 2>&1
	scsi_debug_teardown $DEV
}

cleanup_keyring()
{
	if [ $HAVE_KEYRING -eq 1 ]; then
		keyctl unlink %:$KEYRING "@s" >/dev/null 2>&1 || echo "Failed to unlink test keyring."
	fi
}

fail()
{
	local frame=0
	[ -n "$1" ] && echo "$1"
	echo "FAILED backtrace:"
	while caller $frame; do	((frame++)); done
	remove_mapping
	cleanup_keyring
	exit 2
}

_sigchld() { local c=$?; [ $c -eq 139 ] && fail "Segfault"; [ $c -eq 134 ] && fail "Aborted"; }
trap _sigchld CHLD

skip()
{
	[ -n "$1" ] && echo "$1"
	remove_mapping
	cleanup_keyring
	exit 77
}

fips_mode()
{
	[ -n "$FIPS_MODE" ] && [ "$FIPS_MODE" -gt 0 ]
}

add_scsi_device() {
	scsi_debug_teardown $DEV
	if [ -d /sys/module/scsi_debug ] ; then
		echo "Cannot use scsi_debug module (in use or compiled-in), test skipped."
		exit 77
	fi
	modprobe scsi_debug $@ delay=0 >/dev/null 2>&1
	if [ $? -ne 0 ] ; then
		echo "This kernel seems to not support proper scsi_debug module, test skipped."
		exit 77
	fi

	sleep 1
	DEV="/dev/"$(grep -l -e scsi_debug /sys/block/*/device/model | cut -f4 -d /)
	[ -b $DEV ] || fail "Cannot find $DEV."
}

open_crypt() # $1 pwd, $2 hdr
{
	if [ -n "$2" ] ; then
		echo "$1" | $CRYPTSETUP luksOpen $DEV $DEV_NAME --header $2 || fail
	elif [ -n "$1" ] ; then
		echo "$1" | $CRYPTSETUP luksOpen $DEV $DEV_NAME || fail
	else
		$CRYPTSETUP luksOpen -d $KEY1 $DEV $DEV_NAME || fail
	fi
}

wipe_dev_head() # $1 dev, $2 length (in MiBs)
{
	dd if=/dev/zero of=$1 bs=1M count=$2 conv=notrunc >/dev/null 2>&1
}

wipe_dev() # $1 dev
{
	if [ -b $1 ] ; then
		blkdiscard --zeroout $1 2>/dev/null || dd if=/dev/zero of=$1 bs=1M conv=notrunc >/dev/null 2>&1
		if [ $# -gt 2 ]; then
			dd if=/dev/urandom of=$1 bs=1M seek=$2 conv=notrunc >/dev/null 2>&1
		fi
	else
		local size=$(stat --printf="%s" $1)
		truncate -s 0 $1 || fail
		if [ $# -gt 2 ]; then
			local diff=$((size-$2*1024*1024))
			echo "size: $size, diff: $diff"
			truncate -s $diff $1 || fail
			# wipe_dev_head $1 $((diff/(1024*1024)))
			dd if=/dev/urandom of=$1 bs=1M seek=$2 size=$((diff/(1024*1024))) conv=notrunc >/dev/null 2>&1
		else
			truncate -s $size $1 || fail
		fi
	fi
}

wipe() # $1 pass, $2 hdr
{
	open_crypt $1 $2
	wipe_dev /dev/mapper/$DEV_NAME
	udevadm settle >/dev/null 2>&1
	$CRYPTSETUP luksClose $DEV_NAME || fail
}

prepare() # $1 dev1_siz
{
	remove_mapping

	if [ ! -e $KEY1 ]; then
		dd if=/dev/urandom of=$KEY1 count=1 bs=32 >/dev/null 2>&1
	fi

	if [ ! -e $VKEY1 ]; then
		echo -n $'\x44\xc6\x74\x4f\x41\x4e\x50\xc0\x79\xc2\x2d\x5b\x5f\x68\x84\x17' >$VKEY1
		echo -n $'\x9c\x03\xba\xbe\x4d\x0f\x9a\x75\xb3\x90\x70\x32\x0a\xf8\xae\xc4'>>$VKEY1
	fi

#	if [ ! -e $VKEY2 ]; then
#		echo -n $'\x41\xc6\x74\x4f\x41\x4e\x50\xc0\x79\xc2\x2d\x5b\x5f\x68\x84\x17' >$VKEY2
#		echo -n $'\x9c\x03\xba\xbe\x4d\x0f\x9a\x75\xb3\x90\x70\x32\x0a\xf8\xae\xc5'>>$VKEY2
#	fi

	add_scsi_device $@
}

preparebig() # $1 dev1_siz
{
	remove_mapping

	if [ ! -e $KEY1 ]; then
		dd if=/dev/urandom of=$KEY1 count=1 bs=32 >/dev/null 2>&1
	fi

	truncate -s "$1"M $DEVBIG || fail
	LOOPDEV=$(losetup -f)
	losetup -f $DEVBIG || fail
	DEV=$LOOPDEV
}

check_hash_dev() # $1 dev, $2 hash
{
	HASH=$(sha1sum $1 | cut -d' ' -f 1)
	[ $HASH != "$2" ] && fail "HASH differs (expected: $2) (result $HASH)"
}

check_hash() # $1 pwd, $2 hash, $3 hdr
{
	open_crypt $1 $3
	check_hash_dev /dev/mapper/$DEV_NAME $2
	$CRYPTSETUP remove $DEV_NAME || fail
}

check_hash_dev_head() # $1 dev, $2 len, $3 hash
{
	local hash=$(dd if=$1 bs=512 count=$2 2>/dev/null | sha1sum | cut -d' ' -f1)
	[ $hash != "$3" ] && fail "HASH differs (expected: $3) (result $hash)"
}

check_hash_head() # $1 pwd, $2 len, $3 hash, $4 hdr
{
	open_crypt $1 $4
	check_hash_dev_head /dev/mapper/$DEV_NAME $2 $3
	$CRYPTSETUP remove $DEV_NAME || fail
}

resize_file() # $1 dev, $2 shrink bytes
{
	local size=$(stat --printf="%s" $1)
	truncate -s $(($size + $2)) $1 || fail
	losetup -c $LOOPDEV
}

error_io() { # $1 dmdev, $2 data dev, $3 offset, $4 size
	local _dev_size=$(blockdev --getsz /dev/mapper/$1)
	local _offset=$(($3+$4))
	local _size=$((_dev_size-_offset))
	local _table=
	dmsetup create $1-err --table "0 $_dev_size error" || fail

	if [ $3 -ne 0 ]; then
		_table="0 $3 linear $2 0\n"
	fi

	_table=$_table"$3 $4 error"

	if [ $_size -ne 0 ]; then
		_table="$_table\n$_offset $_size linear $2 $_offset"
	fi

	echo -e "$_table" | dmsetup load $1 || fail
	dmsetup resume $1 || fail
	blockdev --setra 0 /dev/mapper/$1
}

error_writes() { # $1 dmdev, $2 data dev, $3 offset, $4 size
	local _dev_size=$(blockdev --getsz /dev/mapper/$1)
	local _offset=$(($3+$4))
	local _size=$((_dev_size-_offset))
	local _err=$1-err
	local _table=
	dmsetup create $_err --table "0 $_dev_size error" || fail

	if [ $3 -ne 0 ]; then
		_table="0 $3 linear $2 0\n"
	fi

	_table=$_table"$3 $4 delay $2 $3 0 /dev/mapper/$_err $3 0"

	if [ $_size -ne 0 ]; then
		_table="$_table\n$_offset $_size linear $2 $_offset"
	fi

	echo -e "$_table" | dmsetup load $1 || fail
	dmsetup resume $1 || fail
	blockdev --setra 0 /dev/mapper/$1
	blockdev --setra 0 /dev/mapper/$_err
}

fix_writes() { # $1 dmdev, $2 data dev
	local _dev_size=$(blockdev --getsz /dev/mapper/$1)
	dmsetup load $1 --table "0 $_dev_size linear $2 0" || fail
	dmsetup resume $1 || fail
	dmsetup remove --retry $1-err 2>/dev/null || fail
}

prepare_linear_dev() {
	local _sizemb=$1
	shift

	if [ "$_sizemb" -gt 32 ]; then
		preparebig $_sizemb
	else
		prepare dev_size_mb=$_sizemb $@
	fi

	dmsetup create $OVRDEV --table "0 $((_sizemb*1024*2)) linear $DEV 0" || fail

	OLD_DEV=$DEV
	DEV=/dev/mapper/$OVRDEV
}

get_error_offsets() # $1 devsize, $2 minimal offset, $3 sector_size [512 if omitted], $4 max offset
{
	local _devsize=$(($1*1024*2))
	local _sector_size=${3:-512}
	local _max_offset=${4:-$_devsize}
	_sector_size=$((_sector_size/512))

	# 8 sectors minimal size (4096)
	ERRLENGTH=$((($RANDOM%56)+8))
	ERRLENGTH=$(($ERRLENGTH-($ERRLENGTH%$_sector_size)))

	ERROFFSET=$(($2+((2*$RANDOM)%($_max_offset-$2-$ERRLENGTH))))
	ERROFFSET=$(($ERROFFSET-($ERROFFSET%$_sector_size)))
}

reencrypt_recover() { # $1 sector size, $2 resilience, $3 digest, [$4 header]
	echo -n "resilience mode: $2 ..."
	local _hdr=""
	test -z "$4" || _hdr="--header $4"

	error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV $_hdr --hotzone-size 1M --resilience $2 --sector-size $1 --force-offline-reencrypt -q $FAST_PBKDF_ARGON >/dev/null 2>&1 && fail
	fix_writes $OVRDEV $OLD_DEV

	echo $PWD1 | $CRYPTSETUP -q repair $DEV $_hdr || fail

	check_hash $PWD1 $3 $4

	echo $PWD1 | $CRYPTSETUP reencrypt $DEV $_hdr --resilience $2 --sector-size $1 -q $FAST_PBKDF_ARGON || fail
	check_hash $PWD1 $3 $4

	echo "[OK]"
}

reencrypt_recover_online() { # $1 sector size, $2 resilience, $3 digest, [$4 header]
	echo -n "resilience mode: $2 ..."
	local _hdr=""
	test -z "$4" || _hdr="--header $4"

	echo $PWD1 | $CRYPTSETUP open $DEV $_hdr $DEV_NAME || fail

	error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
	echo $PWD1 | $CRYPTSETUP reencrypt --active-name $DEV_NAME $_hdr --hotzone-size 1M --resilience $2 --sector-size $1 -q $FAST_PBKDF_ARGON >/dev/null 2>&1 && fail
	$CRYPTSETUP status $DEV_NAME $_hdr | grep -q "reencryption:  in-progress" || fail
	$CRYPTSETUP close $DEV_NAME || fail
	fix_writes $OVRDEV $OLD_DEV

	# recovery during activation
	echo $PWD1 | $CRYPTSETUP open $DEV $_hdr $DEV_NAME || fail
	check_hash_dev /dev/mapper/$DEV_NAME $3

	$CRYPTSETUP luksDump ${4:-$DEV} | grep -q "online-reencrypt"
	if [ $? -eq 0 ]; then
		$CRYPTSETUP status $DEV_NAME $_hdr | grep -q "reencryption:  in-progress" || fail
		echo $PWD1 | $CRYPTSETUP reencrypt --active-name $DEV_NAME $_hdr --resilience $2 --resume-only -q || fail
		check_hash_dev /dev/mapper/$DEV_NAME $3
	fi

	$CRYPTSETUP close $DEV_NAME || fail
	echo "[OK]"
}

reencrypt_recover_online_vk() { # $1 sector size, $2 resilience, $3 digest, [$4 header]
	echo -n "resilience mode: $2 ..."
	local _hdr=""
	test -z "$4" || _hdr="--header $4"

	echo $PWD1 | $CRYPTSETUP open $DEV $_hdr $DEV_NAME || fail
	echo $PWD1 | $CRYPTSETUP reencrypt --active-name $DEV_NAME $_hdr --hotzone-size 1M --resilience $2 --sector-size $1 -q $FAST_PBKDF_ARGON --init-only >/dev/null 2>&1 || fail
	$CRYPTSETUP close $DEV_NAME || fail

	echo $PWD1 | $CRYPTSETUP open --link-vk-to-keyring $KEY_SPEC1 --link-vk-to-keyring $KEY_SPEC2 $DEV $_hdr $DEV_NAME || fail

	error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
	echo $PWD1 | $CRYPTSETUP reencrypt --active-name $DEV_NAME $_hdr --hotzone-size 1M --resilience $2 --sector-size $1 -q $FAST_PBKDF_ARGON >/dev/null 2>&1 && fail
	$CRYPTSETUP status $DEV_NAME $_hdr | grep -q "reencryption:  in-progress" || fail
	$CRYPTSETUP close $DEV_NAME || fail
	fix_writes $OVRDEV $OLD_DEV

	# recovery during activation
	$CRYPTSETUP open --volume-key-keyring $KEY_NAME1 --volume-key-keyring $KEY_NAME2 $DEV $_hdr $DEV_NAME || fail
	check_hash_dev /dev/mapper/$DEV_NAME $3

	$CRYPTSETUP luksDump ${4:-$DEV} | grep -q "online-reencrypt"
	if [ $? -eq 0 ]; then
		$CRYPTSETUP status $DEV_NAME $_hdr | grep -q "reencryption:  in-progress" || fail
		echo $PWD1 | $CRYPTSETUP reencrypt --active-name $DEV_NAME $_hdr --resilience $2 --resume-only -q || fail
		check_hash_dev /dev/mapper/$DEV_NAME $3
	fi

	$CRYPTSETUP close $DEV_NAME || fail
	echo "[OK]"
}

encrypt_recover() { # $1 sector size, $2 reduce size, $3 digest, $4 device size in sectors, $5 origin digest
	wipe_dev $DEV
	check_hash_dev $DEV $5

	echo -n "resilience mode: datashift ..."

	echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --reduce-device-size $2 --sector-size $1 -q $FAST_PBKDF_ARGON --init-only >/dev/null 2>&1 || fail

	error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q >/dev/null 2>&1 && fail
	fix_writes $OVRDEV $OLD_DEV

	echo $PWD1 | $CRYPTSETUP -q repair $DEV || fail

	$CRYPTSETUP luksDump $DEV | grep -q "online-reencrypt"
	if [ $? -eq 0 ]; then
		check_hash $PWD1 $3
		echo $PWD1 | $CRYPTSETUP reencrypt $DEV --sector-size $1 -q $FAST_PBKDF_ARGON || fail
	fi

	check_hash_head $PWD1 $4 $3

	echo "[OK]"
}

encrypt_recover_online() { # $1 sector size, $2 reduce size, $3 digest, $4 device size in sectors, $5 origin digest
	wipe_dev $DEV
	check_hash_dev $DEV $5

	echo -n "resilience mode: datashift ..."

	echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --reduce-device-size $2 --sector-size $1 -q $FAST_PBKDF_ARGON --init-only > /dev/null || fail
	echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail

	error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q >/dev/null 2>&1 && fail
	$CRYPTSETUP status $DEV_NAME | grep -q "reencryption:  in-progress" || fail
	$CRYPTSETUP close $DEV_NAME || fail
	fix_writes $OVRDEV $OLD_DEV

	# recovery in activation
	echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail

	$CRYPTSETUP luksDump $DEV | grep -q "online-reencrypt"
	if [ $? -eq 0 ]; then
		$CRYPTSETUP status $DEV_NAME | grep -q "reencryption:  in-progress" || fail
		check_hash_dev /dev/mapper/$DEV_NAME $3
		echo $PWD1 | $CRYPTSETUP reencrypt --resume-only --active-name $DEV_NAME -q || fail
	fi

	$CRYPTSETUP close $DEV_NAME || fail
	check_hash_head $PWD1 $4 $3

	echo "[OK]"
}

encrypt_recover_detached() { # $1 sector size, $2 resilience, $3 digest, $4 hdr
	wipe_dev $DEV
	check_hash_dev $DEV $3

	echo -n "resilience mode: $2 ..."

	error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --hotzone-size 1M --header $4 --resilience $2 --sector-size $1 -q $FAST_PBKDF_ARGON 2>/dev/null && fail
	fix_writes $OVRDEV $OLD_DEV

	echo $PWD1 | $CRYPTSETUP repair $DEV --header $4 || fail

	check_hash $PWD1 $3 $4

	echo $PWD1 | $CRYPTSETUP reencrypt $DEV --header $4 --resilience $2 --sector-size $1 -q $FAST_PBKDF_ARGON || fail
	check_hash $PWD1 $3 $4

	[ -f $4 ] && rm -f $4

	echo "[OK]"
}

encrypt_recover_detached_online() { # $1 sector size, $2 resilience, $3 digest, $4 hdr
	wipe_dev $DEV
	check_hash_dev $DEV $3

	echo -n "resilience mode: $2 ..."

	echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --hotzone-size 1M --header $4 --resilience $2 --sector-size $1 -q $FAST_PBKDF_ARGON --init-only || fail
	echo $PWD1 | $CRYPTSETUP open $DEV --header $4 $DEV_NAME || fail

	error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
	echo $PWD1 | $CRYPTSETUP reencrypt -q $DEV --header $4 --hotzone-size 1M  2>/dev/null && fail
	$CRYPTSETUP status $DEV_NAME --header $4 | grep -q "reencryption:  in-progress" || fail
	$CRYPTSETUP close $DEV_NAME || fail
	fix_writes $OVRDEV $OLD_DEV

	echo $PWD1 | $CRYPTSETUP open $DEV --header $4 $DEV_NAME || fail
	check_hash_dev /dev/mapper/$DEV_NAME $3

	$CRYPTSETUP luksDump $4 | grep -q "online-reencrypt"
	if [ $? -eq 0 ]; then
		$CRYPTSETUP status $DEV_NAME --header $4 | grep -q "reencryption:  in-progress" || fail
		echo $PWD1 | $CRYPTSETUP reencrypt --active-name $DEV_NAME --resume-only --header $4 --resilience $2 -q || fail
		check_hash_dev /dev/mapper/$DEV_NAME $3
	fi

	$CRYPTSETUP close $DEV_NAME || fail

	[ -f $4 ] && rm -f $4

	echo "[OK]"
}

decrypt_recover_detached() { # $1 sector size, $2 resilience, $3 digest, $4 hdr
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size $1 --header $4 $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1 $4
	check_hash $PWD1 $3 $4

	echo -n "resilience mode: $2 ..."

	error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV --decrypt --hotzone-size 1M --header $4 --resilience $2 -q 2>/dev/null && fail
	fix_writes $OVRDEV $OLD_DEV

	echo $PWD1 | $CRYPTSETUP repair $DEV --header $4 || fail

	$CRYPTSETUP luksDump $4 | grep -q "online-reencrypt"
	if [ $? -eq 0 ]; then
		check_hash $PWD1 $3 $4
		echo $PWD1 | $CRYPTSETUP reencrypt $DEV --resume-only --header $4 --resilience $2 -q || fail
	fi

	check_hash_dev $DEV $3

	[ -f $4 ] && rm -f $4

	echo "[OK]"
}

decrypt_recover_detached_online() { # $1 sector size, $2 resilience, $3 digest, $4 hdr
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size $1 --header $4 $FAST_PBKDF_ARGON $DEV || fail
	echo $PWD1 | $CRYPTSETUP open $DEV --header $4 $DEV_NAME || fail
	wipe_dev /dev/mapper/$DEV_NAME
	check_hash_dev /dev/mapper/$DEV_NAME $3

	echo -n "resilience mode: $2 ..."

	error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV --decrypt --hotzone-size 1M --header $4 --resilience $2 -q 2>/dev/null && fail
	$CRYPTSETUP status $DEV_NAME --header $4 | grep -q "reencryption:  in-progress" || fail
	$CRYPTSETUP close $DEV_NAME || fail
	fix_writes $OVRDEV $OLD_DEV

	# recovery during activation
	echo $PWD1 | $CRYPTSETUP open $DEV --header $4 $DEV_NAME || fail

	$CRYPTSETUP luksDump $4 | grep -q "online-reencrypt"
	if [ $? -eq 0 ]; then
		$CRYPTSETUP status $DEV_NAME --header $4 | grep -q "reencryption:  in-progress" || fail
		check_hash_dev /dev/mapper/$DEV_NAME $3
		echo $PWD1 | $CRYPTSETUP reencrypt $DEV --header $4 --resilience $2 -q || fail
	fi

	$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 && fail
	check_hash_dev $DEV $3

	[ -f $4 ] && rm -f $4

	echo "[OK]"
}

decrypt_recover() { # $1 hash, $2 hdr, $3 dev size, $4 resilience, $5 hotzone size
	local _res=""
	local _maxhz=""
	test -z "$4" || _res="--resilience $4"
	test -z "$5" || _maxhz="--hotzone-size $5"
	echo -n "[${4:-default}]"

	echo $PWD1 | $CRYPTSETUP reencrypt $DEV --decrypt --header $2 --init-only $_maxhz >/dev/null || fail

	error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV --header $2 -q $_res >/dev/null 2>&1 && fail
	fix_writes $OVRDEV $OLD_DEV

	echo $PWD1 | $CRYPTSETUP -q repair $DEV --header $2 || fail

	$CRYPTSETUP luksDump $2 | grep -q "online-reencrypt"
	if [ $? -eq 0 ]; then
		check_hash $PWD1 $1 $2
		echo $PWD1 | $CRYPTSETUP reencrypt $DEV --header $2 $_res -q $FAST_PBKDF_ARGON || fail
	fi

	check_hash_dev_head $DEV $3 $1

	[ -f $2 ] && rm -f $2

	echo -n "[OK]"
}

decrypt_recover_online() { # $1 hash, $2 hdr, $3 dev size
	local _res=""
	local _maxhz=""
	test -z "$4" || _res="--resilience $4"
	test -z "$5" || _maxhz="--hotzone-size $5"
	echo -n "[${4:-default}]"

	echo $PWD1 | $CRYPTSETUP reencrypt $DEV --decrypt --header $2 $_maxhz --init-only >/dev/null 2>&1 || fail

	error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV --header $2 -q $_res >/dev/null 2>&1 && fail
	$CRYPTSETUP status $DEV_NAME --header $2 | grep -q "reencryption:  in-progress" || fail
	$CRYPTSETUP close $DEV_NAME || fail
	fix_writes $OVRDEV $OLD_DEV

	# recovery during activation
	echo $PWD1 | $CRYPTSETUP open $DEV --header $2 $DEV_NAME || fail

	check_hash_dev /dev/mapper/$DEV_NAME $1
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV --header $2 -q || fail

	$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 && fail
	check_hash_dev_head $DEV $3 $1

	[ -f $2 ] && rm -f $2

	echo -n "[OK]"
}

decrypt_recover_online_moved() { # $1 hash, $2 hdr, $3 dev size
	local _res=""
	local _maxhz=""
	test -z "$4" || _res="--resilience $4"
	test -z "$5" || _maxhz="--hotzone-size $5"
	echo -n "[${4:-default}]"

	echo $PWD1 | $CRYPTSETUP reencrypt $DEV --decrypt --header $2 $_maxhz $_res --init-only >/dev/null 2>&1 || fail

	error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV --header $2 -q $_res >/dev/null 2>&1 && fail
	$CRYPTSETUP status $DEV_NAME --header $2 | grep -q "reencryption:  in-progress" || fail
	$CRYPTSETUP close $DEV_NAME || fail
	fix_writes $OVRDEV $OLD_DEV

	# recovery but activation fails due to last segment recovery makes it plaintext device
	echo $PWD1 | $CRYPTSETUP open $DEV --header $2 $DEV_NAME 2>/dev/null && fail

	$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 && fail
	check_hash_dev_head $DEV $3 $1

	[ -f $2 ] && rm -f $2

	echo -n "[OK]"
}

# sector size (bytes)
# reenc dev size (sectors)
# reenc dev digest
# resilience
# orig size
# orig size digest
# hdr (optional)
reencrypt_offline_fixed_size() {
	local _esz=$(($1>>9))
	local _hdr=""
	# round-up fixed size to megabytes
	local _mbs=$((($2>>11)+1))
	test -z "$7" || _hdr="--header $7"

	if [ -z "$7" ]; then
		echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --offset 16384 $FAST_PBKDF_ARGON $DEV || fail
	else
		echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --header $7 $FAST_PBKDF_ARGON $DEV || fail
	fi
	echo $PWD1 | $CRYPTSETUP open $_hdr $DEV $DEV_NAME || fail
	wipe_dev_head /dev/mapper/$DEV_NAME $_mbs
	$CRYPTSETUP close $DEV_NAME || fail

	# reencrypt with fixed device size
	echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV  $_hdr --sector-size $1 --device-size $2s --resilience $4 --force-offline-reencrypt || fail

	check_hash_head $PWD1 $2 $3 $7
	wipe $PWD1 $7

	# try to reencrypt device size + 1 encryption sector size
	echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --sector-size $1 --init-only --force-offline-reencrypt || fail
	echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --device-size $(($5+_esz))s --resilience $4 2>/dev/null && fail
	check_hash $PWD1 $6 $7

	# misaligned reencryption size
	if [ $_esz -gt 1 ]; then
		echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --device-size $(($2+_esz-1))s --resilience $4 2>/dev/null && fail
		$CRYPTSETUP luksDump ${7:-$DEV} | grep -q "2: crypt" || fail
		$CRYPTSETUP luksDump ${7:-$DEV} | grep -q "3: crypt" && fail
		check_hash $PWD1 $6 $7
	fi
}

# sector size (bytes)
# reenc dev size (sectors)
# reenc dev digest
# resilience
# orig size
# orig size digest
# hdr
encrypt_offline_fixed_size() {
	local _esz=$(($1>>9))

	# reencrypt with fixed device size
	wipe_dev $DEV
	echo $PWD1 | $CRYPTSETUP reencrypt --encrypt -q $FAST_PBKDF_ARGON $DEV --header $7 --sector-size $1 --device-size $2s --resilience $4 || fail
	check_hash_head $PWD1 $2 $3 $7
	[ -f $7 ] && rm -f $7

	# try to reencrypt device size + 1 encryption sector size
	wipe_dev $DEV
	echo $PWD1 | $CRYPTSETUP reencrypt --encrypt -q $FAST_PBKDF_ARGON $DEV --header $7 --sector-size $1 --init-only || fail
	echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV  --header $7 --device-size $(($5+_esz))s --resilience $4 2>/dev/null && fail
	check_hash $PWD1 $6 $7

	# misaligned reencryption size
	if [ $_esz -gt 1 ]; then
		[ -f $7 ] && rm -f $7
		echo $PWD1 | $CRYPTSETUP reencrypt --encrypt -q $FAST_PBKDF_ARGON $DEV --header $7 --sector-size $1 --init-only || fail
		echo $PWD1 | $CRYPTSETUP reencrypt -q $DEV --header $7 --device-size $(($2+_esz-1))s --resilience $4 2>/dev/null && fail
		$CRYPTSETUP luksDump $7 | grep -q "2: crypt" || fail
		$CRYPTSETUP luksDump $7 | grep -q "3: crypt" && fail
		check_hash $PWD1 $6 $7
	fi

	[ -f $7 ] && rm -f $7
}

# sector size (bytes)
# reenc dev size (sectors)
# reenc dev digest
# resilience
# orig size
# orig size digest
# hdr
decrypt_offline_fixed_size() {
	local _esz=$(($1>>9))

	# decrypt with fixed device size
	echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -q $FAST_PBKDF_ARGON $DEV --header $7 --sector-size $1 || fail
	wipe $PWD1 $7
	echo $PWD1 | $CRYPTSETUP reencrypt --decrypt -q $DEV --header $7 --device-size $2s --resilience $4 || fail

	dmsetup load $OVRDEV --table "0 $2 linear $OLD_DEV 0" || fail
	dmsetup resume $OVRDEV || fail
	check_hash_dev $DEV $3
	dmsetup load $OVRDEV --table "0 $5 linear $OLD_DEV 0" || fail
	dmsetup resume $OVRDEV || fail

	# try to decrypt device size + 1 encryption sector size
	echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -q $FAST_PBKDF_ARGON $DEV --header $7 --sector-size $1 || fail
	wipe $PWD1 $7
	echo $PWD1 | $CRYPTSETUP reencrypt --decrypt -q $FAST_PBKDF_ARGON $DEV --header $7 --init-only || fail
	echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV --header $7 --device-size $(($5+_esz))s --resilience $4 2>/dev/null && fail
	check_hash $PWD1 $6 $7

	# misaligned reencryption size
	if [ $_esz -gt 1 ]; then
		echo $PWD1 | $CRYPTSETUP reencrypt -q $DEV --header $7 --device-size $(($2+_esz-1))s --resilience $4 2>/dev/null && fail
		$CRYPTSETUP luksDump $7 | grep -q "2: linear\|2: crypt" || fail
		$CRYPTSETUP luksDump $7 | grep -q "3: crypt\|3: linear" && fail
		check_hash $PWD1 $6 $7
	fi
}

# sector size (bytes)
# reenc dev size (sectors)
# reenc dev digest
# resilience
# orig size
# orig size digest
# hdr (optional)
reencrypt_online_fixed_size() {
	local _esz=$(($1>>9))
	local _hdr=""
	test -z "$7" || _hdr="--header $7"

	if [ -z "$_hdr" ]; then
		echo $PWD1 | $CRYPTSETUP -q luksFormat --sector-size 512 --type luks2 --offset 16384 $FAST_PBKDF_ARGON $DEV || fail
	else
		echo $PWD1 | $CRYPTSETUP -q luksFormat --sector-size 512 --type luks2 $_hdr $FAST_PBKDF_ARGON $DEV || fail
	fi
	wipe $PWD1 $7

	# reencrypt with fixed device size
	echo $PWD1 | $CRYPTSETUP open $DEV $_hdr $DEV_NAME || fail
	echo $PWD1 | $CRYPTSETUP resize $DEV_NAME $_hdr --size $2 || fail
	echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --sector-size $1 --resilience $4 || fail
	$CRYPTSETUP -q status  $DEV_NAME | grep "size:" | grep -q "$2 \[512-byte units\]" || fail
	$CRYPTSETUP close $DEV_NAME || fail
	check_hash_head $PWD1 $2 $3 $7
	wipe $PWD1 $7

	# active device != requested reencryption size
	echo $PWD1 | $CRYPTSETUP open $DEV $_hdr $DEV_NAME || fail
	echo $PWD1 | $CRYPTSETUP resize $DEV_NAME $_hdr --size $2 || fail
	echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --sector-size $1 --init-only || fail
	echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --device-size $(($2-_esz))s --resilience $4 2>/dev/null && fail
	echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --device-size $2s --resilience $4 || fail
	$CRYPTSETUP -q status  $DEV_NAME | grep "size:" | grep -q "$2 \[512-byte units\]" || fail
	$CRYPTSETUP close $DEV_NAME || fail
	check_hash_head $PWD1 $2 $3 $7

	# misaligned reencryption size
	if [ $_esz -gt 1 ]; then
		if [ -z "$_hdr" ]; then
			echo $PWD1 | $CRYPTSETUP -q luksFormat --sector-size 512 --type luks2 --offset 16384 $FAST_PBKDF_ARGON $DEV || fail
		else
			echo $PWD1 | $CRYPTSETUP -q luksFormat --sector-size 512 --type luks2 $_hdr $FAST_PBKDF_ARGON $DEV || fail
		fi
		wipe $PWD1 $7
		check_hash $PWD1 $6 $7

		echo $PWD1 | $CRYPTSETUP open $DEV $_hdr $DEV_NAME || fail
		echo $PWD1 | $CRYPTSETUP resize $DEV_NAME $_hdr --size $(($2+_esz-1)) || fail
		echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --sector-size $1 --init-only || fail
		echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --resilience $4 2>/dev/null && fail
		$CRYPTSETUP close $DEV_NAME || fail
		check_hash $PWD1 $6 $7
	fi

	[ -n "$7" -a -f "$7" ] && rm -f $7
}

reformat_reencrypt_device() {
	if [ ! -e $BACKUP_FILE ]; then
		echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -S0 $FAST_PBKDF_ARGON $DEV || fail
		echo -e "$PWD1\n$PWD2" | $CRYPTSETUP -q luksAddKey -S2 $FAST_PBKDF_ARGON $DEV || fail
		echo -e "$PWD1\n$PWD3" | $CRYPTSETUP -q luksAddKey -S4 $FAST_PBKDF_ARGON $DEV || fail
		echo -e "$PWD1\n$PWD4" | $CRYPTSETUP -q luksAddKey -S6 $FAST_PBKDF_ARGON $DEV || fail
		echo -e "$PWD1\n$PWD5" | $CRYPTSETUP -q luksAddKey -S8 $FAST_PBKDF_ARGON $DEV || fail
		echo -e "$PWD1\n$PWD6" | $CRYPTSETUP -q luksAddKey -S10 $FAST_PBKDF_ARGON $DEV || fail
		echo -e "$PWD1\n$PWD7" | $CRYPTSETUP -q luksAddKey -S12 $FAST_PBKDF_ARGON $DEV || fail

		$CRYPTSETUP token add $DEV --token-id 1 --key-description $KEY_NAME3 -S6
		$CRYPTSETUP token add $DEV --token-id 2 --key-description $KEY_NAME4 -S8
		$CRYPTSETUP token add $DEV --token-id 3 --key-description $KEY_NAME5 -S10
		echo -e "{\"type\":\"luks2-keyring\",\"keyslots\":[\"4\", \"12\"],\"key_description\":\"$KEY_NAME6\"}" | $CRYPTSETUP token import $DEV --token-id 4 --json-file -

		$CRYPTSETUP -q luksHeaderBackup --header-backup-file $BACKUP_FILE $DEV || fail
	else
		$CRYPTSETUP -q luksHeaderRestore --header-backup-file $BACKUP_FILE $DEV || fail
	fi
}

prepare_vk_keyring()
{
	local s_desc=$(keyctl rdescribe @s | cut -d';' -f5)
	local us_desc=$(keyctl rdescribe @us | cut -d';' -f5)

	if [ "$s_desc" = "$us_desc" -a -n "$s_desc" ]; then
		echo "Session keyring is missing. Giving new one to parent process..."
		keyctl new_session > /dev/null || fail
	fi

	keyctl newring $KEYRING "@s" >/dev/null || fail "Failed to setup test keyring environment"
	keyctl search "@s" keyring $KEYRING >/dev/null 2>&1 || fail "Could not find test keyring in a session keyring."

	echo -n $PWD4 | keyctl padd user $KEY_NAME3 %:$KEYRING > /dev/null || fail
	echo -n "wRonG_passFrejz" | keyctl padd user $KEY_NAME5 %:$KEYRING > /dev/null || fail
	echo -n $PWD7 | keyctl padd user $KEY_NAME6 %:$KEYRING > /dev/null || fail
	echo -n $PWD2 | keyctl padd user $KEY_NAME7 %:$KEYRING > /dev/null || fail
}

setup_luks2_env() {
	echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c aes-xts-plain64 $FAST_PBKDF_ARGON $DEV || fail
	echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
	local check_keyring=$($CRYPTSETUP status $DEV_NAME | grep "key location: keyring")
	if [ -n "$check_keyring" ]; then
		bin_check keyctl
		HAVE_KEYRING=1
		prepare_vk_keyring
	fi
	DEF_XTS_KEY=$($CRYPTSETUP status $DEV_NAME | grep "keysize:" | sed 's/\(  keysize: \)\([0-9]\+\)\(.*\)/\2/')
	[ -n "$DEF_XTS_KEY" ] || fail "Failed to parse xts mode key size."
	$CRYPTSETUP close $DEV_NAME || fail
}

check_blkid() {
	bin_check blkid
	xz -dkf $HEADER_LUKS2_PV.xz
	if ! $($CRYPTSETUP --version | grep -q "BLKID"); then
		HAVE_BLKID=0
	elif $(blkid -p -n crypto_LUKS $HEADER_LUKS2_PV >/dev/null 2>&1); then
		HAVE_BLKID=1
		xz -dkf $IMG_FS.xz
		blkid $IMG_FS | grep -q BLOCK_SIZE && BLKID_BLOCK_SIZE_SUPPORT=1
	else
		HAVE_BLKID=0
	fi
}

valgrind_setup()
{
	command -v valgrind >/dev/null || fail "Cannot find valgrind."
	[ ! -f $CRYPTSETUP_VALGRIND ] && fail "Unable to get location of cryptsetup executable."
	[ ! -f valg.sh ] && fail "Unable to get location of valg runner script."
	if [ -z "$CRYPTSETUP_TESTS_RUN_IN_MESON" ]; then
		export LD_LIBRARY_PATH="$CRYPTSETUP_LIB_VALGRIND:$LD_LIBRARY_PATH"
	fi
}

valgrind_run()
{
	INFOSTRING="$(basename ${BASH_SOURCE[1]})-line-${BASH_LINENO[0]}" ./valg.sh ${CRYPTSETUP_VALGRIND} "$@"
}

bin_check()
{
	command -v $1 >/dev/null || skip "WARNING: test require $1 binary, test skipped."
}

_dd()
{
	dd $@ status=none conv=notrunc bs=1
}

img_json_save()
{
	local _hdr=$IMG
	[ -z "$1" ] || _hdr="$1"
	# FIXME: why --json-file cannot be used?
	$CRYPTSETUP luksDump --dump-json-metadata $_hdr | jq -c -M . | tr -d '\n' >$IMG_JSON
}

# header mangle functions
img_update_json()
{
	local _hdr="$IMG"
	local LUKS2_BIN1_OFFSET=448
	local LUKS2_BIN2_OFFSET=$((LUKS2_BIN1_OFFSET + $JSON_MSIZE))
	local LUKS2_JSON_SIZE=$(($JSON_MSIZE - 4096))

	# if present jq script, mangle JSON
	if [ -n "$1" ]; then
		local JSON=$(cat $IMG_JSON)
		echo $JSON | jq -M -c "$1" >$IMG_JSON || fail
		local JSON=$(cat $IMG_JSON)
		echo $JSON | tr -d '\n' >$IMG_JSON || fail
	fi

	[ -z "$2" ] || _hdr="$2"

	# wipe JSON areas
	_dd if=/dev/zero of=$_hdr count=$LUKS2_JSON_SIZE seek=4096
	_dd if=/dev/zero of=$_hdr count=$LUKS2_JSON_SIZE seek=$(($JSON_MSIZE + 4096))

	# write JSON data
	_dd if=$IMG_JSON of=$_hdr count=$LUKS2_JSON_SIZE seek=4096
	_dd if=$IMG_JSON of=$_hdr count=$LUKS2_JSON_SIZE seek=$(($JSON_MSIZE + 4096))

	# erase sha256 checksums
	_dd if=/dev/zero of=$_hdr count=64 seek=$LUKS2_BIN1_OFFSET
	_dd if=/dev/zero of=$_hdr count=64 seek=$LUKS2_BIN2_OFFSET

	# calculate sha256 and write chexksums
	local SUM1_HEX=$(_dd if=$_hdr count=$JSON_MSIZE | sha256sum | cut -d ' ' -f 1)
	echo $SUM1_HEX | xxd -r -p | _dd of=$_hdr seek=$LUKS2_BIN1_OFFSET count=64 || fail

	local SUM2_HEX=$(_dd if=$_hdr skip=$JSON_MSIZE count=$JSON_MSIZE | sha256sum | cut -d ' ' -f 1)
	echo $SUM2_HEX | xxd -r -p | _dd of=$_hdr seek=$LUKS2_BIN2_OFFSET count=64 || fail
}

[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
fips_mode && skip "This test cannot be run in FIPS mode."
modprobe --dry-run scsi_debug >/dev/null 2>&1 || skip "This kernel seems to not support proper scsi_debug module, test skipped."
modprobe dm-crypt >/dev/null 2>&1 || fail "dm-crypt failed to load"
modprobe dm-delay > /dev/null 2>&1
dm_crypt_features
bin_check jq
bin_check sha256sum
bin_check xxd

if [ -n "$DM_SECTOR_SIZE" ]; then
	TEST_SECTORS="512 4096"
else
	TEST_SECTORS="512"
fi

modinfo scsi_debug -p | grep -q opt_xferlen_exp && OPT_XFERLEN_EXP="opt_xferlen_exp=6"

export LANG=C

[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run

# REENCRYPTION tests

# 28 MiBs of zeros (32MiBs - 4MiB LUKS2 header)
HASH1=4da90c0638bd7d29ce3d0ace3df5ee99706c23da
# 1 MiB of zeros
HASH2=3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3
# 256 MiBs of zeros
HASH3=7b91dbdc56c5781edf6c8847b4aa6965566c5c75
# 64 MiBs of zeroes
HASH4=44fac4bedde4df04b9572ac665d3ac2c5cd00c7d
# 56 MiBs of zeroes
HASH5=bcd8ce9b30a43b2dacdf479493c93e167ef60946
# 43 MiBs of zeroes
HASH6=2cf8a5f40a2ab5373c5425d6071da480f1ce08e8
# 31 MiBs of zeroes
HASH7=7ed56dd14d2841cf169fe503d097be04192666bd
# 60 MiBs of zeroes
HASH8=233ba936226a3ac499e67babaebd0d4aafb9761a
# 240 MiBs of zeroes (256MiBs - 16MiBs default LUKS2 header size)
HASH9=045eebed703cce308e049deb019b877f0445862f
# 16 MiBs of zeroes
HASH10=3b4417fc421cee30a9ad0fd9319220a8dae32da2

prepare dev_size_mb=32
setup_luks2_env

# Check that we can use other ciphers than AES in userspace backend.
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c twofish-xts-plain64 $FAST_PBKDF_ARGON $DEV || skip "Cannot use Twofish cipher, test skipped"
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON 2>/dev/null || skip "Cannot use Twofish cipher, test skipped"
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c serpent-xts-plain64 $FAST_PBKDF_ARGON $DEV || skip "Cannot use Serpent cipher, test skipped"
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON 2>/dev/null || skip "Cannot use Serpent cipher, test skipped."
wipe_dev $DEV

echo "[1] Reencryption"
echo -n "[512 sector]"
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c aes-cbc-essiv:sha256 --offset 8192 $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1
check_hash $PWD1 $HASH1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON 2>&1 | tail -1 | grep -q "not supported" && skip " No reenryption support, test skipped."
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH1
# simple test --active-name can consume absolute path to mapping
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -c aes-xts-plain64 --init-only $FAST_PBKDF_ARGON || fail
echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
echo $PWD1 | $CRYPTSETUP reencrypt --active-name /dev/mapper/$DEV_NAME --resilience none -q || fail
XTS_KEY=$($CRYPTSETUP status $DEV_NAME | grep "keysize:" | sed 's/\(  keysize: \)\([0-9]\+\)\(.*\)/\2/')
[ "$XTS_KEY" -eq "$DEF_XTS_KEY" ] || fail "xts mode has wrong key size after reencryption ($XTS_KEY != expected $DEF_XTS_KEY)"
echo $PWD1 | $CRYPTSETUP close $DEV_NAME || fail
echo -n "[OK][4096 sector]"
prepare sector_size=4096 dev_size_mb=32
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c aes-cbc-essiv:sha256 --offset 8192 $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH1
if [ -n "$DM_SECTOR_SIZE" ]; then
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON --sector-size 4096 || fail
	check_hash $PWD1 $HASH1
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal --sector-size 2048 $FAST_PBKDF_ARGON || fail
	check_hash $PWD1 $HASH1
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON --sector-size 1024 || fail
	check_hash $PWD1 $HASH1
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum --sector-size 512 $FAST_PBKDF_ARGON || fail
	check_hash $PWD1 $HASH1
fi
echo -n "[OK][4096/512 sector]"
prepare sector_size=512 physblk_exp=3 dev_size_mb=32
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c aes-cbc-essiv:sha256 --offset 8192 $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH1
echo "[OK]"

# reencrypt minimal device size (FIXME: change data device size to single encryption sector size)
# temporary small device size is default luks2 hdr size + 1MiB
echo -n "[small device reencryption]"
prepare dev_size_mb=5
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c aes-cbc-essiv:sha256 --offset 8192 $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1
check_hash $PWD1 $HASH2
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH2
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH2
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH2
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH2
if [ -n "$DM_SECTOR_SIZE" ]; then
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON --sector-size 4096 --force-offline-reencrypt || fail
	check_hash $PWD1 $HASH2
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal --sector-size 2048 $FAST_PBKDF_ARGON || fail
	check_hash $PWD1 $HASH2
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON --sector-size 1024 || fail
	check_hash $PWD1 $HASH2
	echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum --sector-size 512 $FAST_PBKDF_ARGON || fail
	check_hash $PWD1 $HASH2
fi
echo "[OK]"

echo "[2] Encryption with data shift"
# well, movin' zeroes :-)
preparebig 64
wipe_dev $DEV
check_hash_dev $DEV $HASH4
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c aes-cbc-essiv:sha256 -s 128 --reduce-device-size 8M -q $FAST_PBKDF_ARGON || fail
check_hash_head $PWD1 $((56*1024*2)) $HASH5
wipe_dev $DEV
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c twofish-cbc-essiv:sha256 -s 128 --reduce-device-size 21M -q $FAST_PBKDF_ARGON || fail
check_hash_head $PWD1 $((43*1024*2)) $HASH6
wipe_dev $DEV
# offset 21504 equals 10,5MiBs, equals --reduce-device-size 21M from test above (30M is ignored here, we'll reduce it to 21M in cryptsetup anyway)
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c twofish-cbc-essiv:sha256 -s 128 --offset 21504 --reduce-device-size 30M -q $FAST_PBKDF_ARGON > /dev/null || fail
check_hash_head $PWD1 $((43*1024*2)) $HASH6
wipe_dev $DEV
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --reduce-device-size 33M -q $FAST_PBKDF_ARGON || fail
check_hash_head $PWD1 $((31*1024*2)) $HASH7
wipe_dev $DEV
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --reduce-device-size 64M -q $FAST_PBKDF_ARGON > /dev/null 2>&1 && fail
echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --reduce-device-size 8M --init-only -q $FAST_PBKDF_ARGON $DEV || fail
resize_file $DEVBIG -512
echo $PWD1 | $CRYPTSETUP reencrypt -q $DEV 2> /dev/null && fail
resize_file $DEVBIG 512
wipe_dev $DEV
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c aes-cbc-essiv:sha256 -s 128 --offset 32760 --reduce-device-size 8M -q $FAST_PBKDF_ARGON --init-only >/dev/null 2>&1 && fail
# data offset at 21MiB
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --header $IMG_HDR --offset 43008 --reduce-device-size 21M -q $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH6 $IMG_HDR
$CRYPTSETUP luksHeaderRestore --header-backup-file $IMG_HDR $DEV -q || fail
check_hash $PWD1 $HASH6

# Device activation after encryption initialization
wipe_dev $DEV
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --init-only -c aes-cbc-essiv:sha256 -s 128 -S11 --reduce-device-size 8M -q $FAST_PBKDF_ARGON $DEV_NAME >/dev/null || fail
$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 || fail
check_hash_dev /dev/mapper/$DEV_NAME $HASH5
echo $PWD1 | $CRYPTSETUP reencrypt --resume-only $DEV -q || fail
check_hash_dev /dev/mapper/$DEV_NAME $HASH5

echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c aes-cbc-essiv:sha256 -s 128 --reduce-device-size 8M -q $FAST_PBKDF_ARGON $DEV_NAME 2>/dev/null && fail
$CRYPTSETUP close $DEV_NAME
check_hash_head $PWD1 $((56*1024*2)) $HASH5

# Device activation using key file
wipe_dev $DEV
echo -n $PWD1 > $KEY1
$CRYPTSETUP reencrypt $DEV --encrypt --init-only -c aes-cbc-essiv:sha256 -s 128 --reduce-device-size 8M --key-file $KEY1 -q $FAST_PBKDF_ARGON $DEV_NAME >/dev/null || fail
$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 || fail
$CRYPTSETUP close $DEV_NAME
echo $PWD1 | $CRYPTSETUP open $DEV --test-passphrase || fail

# Small device encryption test
preparebig 65
# wipe only 1st MiB (final data size after encryption)
wipe_dev $DEV 1
check_hash_dev_head $DEV 2048 $HASH2
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --reduce-device-size 64M -q $FAST_PBKDF_ARGON || fail
check_hash_head $PWD1 2048 $HASH2

wipe_dev_head $DEV 1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --reduce-device-size 64M --init-only -q $FAST_PBKDF_ARGON $DEV_NAME >/dev/null || fail
check_hash_dev_head /dev/mapper/$DEV_NAME 2048 $HASH2
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q || fail
check_hash_dev_head /dev/mapper/$DEV_NAME 2048 $HASH2
$CRYPTSETUP close $DEV_NAME || fail

# encryption with both data shift and reduced data size
prepare_linear_dev 65

# --reduce-device-size + --device-size (1MiB+512B) is larger than real device size
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --init-only --device-size 2049s --reduce-device-size 64M -q $FAST_PBKDF_ARGON >/dev/null 2>&1 && fail
$CRYPTSETUP isLuks --type luks2 $DEV && fail
# no changes in data device
check_hash_dev_head $DEV 2048 $HASH2

# --reduce-device-size (1MiB+8KiB) + --device-size (64MiB - (8KiB-512B)) is larger than real device size
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --init-only --device-size 131057s --reduce-device-size 2064s -q $FAST_PBKDF_ARGON >/dev/null 2>&1 && fail
$CRYPTSETUP isLuks --type luks2 $DEV && fail
# no changes in data device
check_hash_dev_head $DEV 2048 $HASH2

echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --device-size 1M --reduce-device-size 32M -q $FAST_PBKDF_ARGON || fail
check_hash_head $PWD1 2048 $HASH2

# test limit values (--device-size + --reduce-device-size = real device size)
wipe_dev_head $DEV 43
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --device-size 43M --reduce-device-size 22M -q $FAST_PBKDF_ARGON || fail
check_hash_head $PWD1 88064 $HASH6

wipe_dev_head $DEV 1
# check reencryption code does not touch data in-between --device-size and --reduce-device-size
# data device: [ reduce-device-size / 2 ] [ device-size ] [ error minefield ] [ reduce-device-size / 2]
ERROFFSET=34816
ERRLENGTH=65536
error_io $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --device-size 1M --reduce-device-size 32M -q $FAST_PBKDF_ARGON  || fail
fix_writes $OVRDEV $OLD_DEV
check_hash_head $PWD1 2048 $HASH2

wipe_dev_head $DEV 43

ERROFFSET=104448
ERRLENGTH=12288
# data device: [ reduce-device-size / 2 ] [ device-size ] [ error minefield ] [ reduce-device-size / 2]
error_io $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --device-size 43M --reduce-device-size 16M -q $FAST_PBKDF_ARGON || fail
fix_writes $OVRDEV $OLD_DEV
check_hash_head $PWD1 88064 $HASH6

echo "[3] Encryption with detached header"
preparebig 256
wipe_dev $DEV
echo $PWD1 | $CRYPTSETUP reencrypt --encrypt -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
check_hash $PWD1 $HASH3 $IMG_HDR
wipe_dev $DEV
rm -f $IMG_HDR
echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --resilience journal --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
check_hash $PWD1 $HASH3 $IMG_HDR
wipe_dev $DEV
rm -f $IMG_HDR
echo $PWD1 | $CRYPTSETUP reencrypt --encrypt -c twofish-cbc-essiv:sha256 -s 128 --resilience none --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
check_hash $PWD1 $HASH3 $IMG_HDR
wipe_dev $DEV
rm -f $IMG_HDR
echo $PWD1 | $CRYPTSETUP reencrypt --encrypt -c serpent-xts-plain --resilience checksum --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
check_hash $PWD1 $HASH3 $IMG_HDR
rm -f $IMG_HDR

# Device activation after encryption initialization
wipe_dev $DEV
echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --init-only -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV $DEV_NAME >/dev/null || fail
$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 || fail
check_hash_dev /dev/mapper/$DEV_NAME $HASH3
echo $PWD1 | $CRYPTSETUP reencrypt --resume-only --header $IMG_HDR --active-name $DEV_NAME -q || fail
check_hash_dev /dev/mapper/$DEV_NAME $HASH3
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c aes-cbc-essiv:sha256 -s 128 --reduce-device-size 8M -q $FAST_PBKDF_ARGON $DEV_NAME 2>/dev/null && fail
$CRYPTSETUP close $DEV_NAME
check_hash $PWD1 $HASH3 $IMG_HDR
rm -f $IMG_HDR

# Device encryption with data offset set in detached header
wipe_dev $DEV
dd if=/dev/urandom of=$DEV bs=512 count=32768 >/dev/null 2>&1
echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --header $IMG_HDR --offset 32768 -q $FAST_PBKDF_ARGON $DEV || fail
check_hash $PWD1 $HASH9 $IMG_HDR
rm -f $IMG_HDR

# Device activation using key file
wipe_dev $DEV
echo -n $PWD1 > $KEY1
$CRYPTSETUP reencrypt $DEV --encrypt --init-only -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR --key-file $KEY1 -q $FAST_PBKDF_ARGON $DEV_NAME >/dev/null || fail
$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 || fail
$CRYPTSETUP close $DEV_NAME
echo $PWD1 | $CRYPTSETUP open --header $IMG_HDR $DEV --test-passphrase || fail

# Encrypt without size reduction must not allow header device same as data device
wipe_dev_head $DEV 1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --type luks2 --encrypt --header $DEV -q $FAST_PBKDF_ARGON 2>/dev/null && fail
$CRYPTSETUP isLUKS $DEV 2>/dev/null && fail
ln -s $DEV $DEV_LINK || fail
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --type luks2 --encrypt --header $DEV_LINK -q $FAST_PBKDF_ARGON 2>/dev/null && fail
$CRYPTSETUP isLUKS $DEV 2>/dev/null && fail
rm -f $DEV_LINK || fail

dd if=/dev/zero of=$IMG bs=4k count=1 >/dev/null 2>&1
echo $PWD1 | $CRYPTSETUP reencrypt $IMG --type luks2 --encrypt --header $IMG -q $FAST_PBKDF_ARGON 2>/dev/null && fail
$CRYPTSETUP isLUKS $IMG 2>/dev/null && fail
ln -s $IMG $DEV_LINK || fail
echo $PWD1 | $CRYPTSETUP reencrypt $IMG --type luks2 --encrypt --header $DEV_LINK -q $FAST_PBKDF_ARGON 2>/dev/null && fail
$CRYPTSETUP isLUKS $IMG 2>/dev/null && fail

echo "[4] Reencryption with detached header"
wipe $PWD1 $IMG_HDR
echo $PWD1 | $CRYPTSETUP reencrypt -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
check_hash $PWD1 $HASH3 $IMG_HDR
echo $PWD1 | $CRYPTSETUP reencrypt --resilience journal --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
check_hash $PWD1 $HASH3 $IMG_HDR
echo $PWD1 | $CRYPTSETUP reencrypt -c twofish-cbc-essiv:sha256 -s 128 --resilience none --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
check_hash $PWD1 $HASH3 $IMG_HDR
echo $PWD1 | $CRYPTSETUP reencrypt -c serpent-xts-plain --resilience checksum --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
check_hash $PWD1 $HASH3 $IMG_HDR
# trivial check for detached header misuse
dd if=/dev/zero of=$IMG bs=4k count=1 >/dev/null 2>&1
echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
echo $PWD1 | $CRYPTSETUP open $IMG $DEV_NAME --header $IMG_HDR || fail
echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME2 --header $IMG_HDR || fail
echo $PWD1 | $CRYPTSETUP reencrypt --active-name $DEV_NAME --header $IMG_HDR -q || fail
# key description mismatch in active device
echo $PWD1 | $CRYPTSETUP reencrypt --active-name $DEV_NAME2 --header $IMG_HDR >/dev/null 2>&1 && fail
# also check it can abort initialization in this case
$CRYPTSETUP luksDump $IMG_HDR | grep -q "online-reencrypt" && fail
$CRYPTSETUP close $DEV_NAME || fail
$CRYPTSETUP close $DEV_NAME2 || fail

echo "[5] Decryption with detached header"
echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 --sector-size 512 -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1 $IMG_HDR
echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --header $IMG_HDR $DEV || fail
check_hash_dev $DEV $HASH3
echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1 $IMG_HDR
echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --resilience journal --header $IMG_HDR $DEV || fail
check_hash_dev $DEV $HASH3
echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c twofish-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1 $IMG_HDR
echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --resilience none --header $IMG_HDR $DEV || fail
check_hash_dev $DEV $HASH3
echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c serpent-xts-plain --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1 $IMG_HDR
echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --resilience checksum --header $IMG_HDR $DEV || fail
check_hash_dev $DEV $HASH3

# check deferred remove works as expected after decryption
echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 --sector-size 512 -c serpent-xts-plain --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
open_crypt $PWD1 $IMG_HDR
dmsetup create $DEV_NAME2 --table "0 1 linear /dev/mapper/$DEV_NAME 0" || fail
echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --resilience checksum --header $IMG_HDR --active-name $DEV_NAME || fail
$CRYPTSETUP status $DEV_NAME >/dev/null || fail
dmsetup remove --retry $DEV_NAME2
$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 && fail

# check tool can block some funny user ideas
preparebig 64
ln -s $DEV $DEV_LINK || fail
echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c serpent-xts-plain -q $FAST_PBKDF_ARGON $DEV || fail
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV -q 2>/dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV --header $DEV -q 2>/dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV --header $DEV_LINK -q 2>/dev/null && fail
open_crypt $PWD1
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --active-name $DEV_NAME -q 2>/dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --active-name $DEV_NAME --header $DEV -q 2>/dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --active-name $DEV_NAME --header $DEV_LINK -q 2>/dev/null && fail
$CRYPTSETUP status $DEV_NAME | grep -q "reencryption:  in-progress" && fail
$CRYPTSETUP close $DEV_NAME

# yet another funny idea
rm -f $IMG_HDR
$CRYPTSETUP luksHeaderBackup --header-backup-file $IMG_HDR $DEV || fail
chmod +w $IMG_HDR || fail
command -v wipefs >/dev/null && {
	wipefs -a $DEV >/dev/null 2>&1  || fail
}
open_crypt $PWD1 $IMG_HDR
echo $PWD1 | $CRYPTSETUP reencrypt --active-name $DEV_NAME --decrypt --header $IMG_HDR -q 2>/dev/null && fail
$CRYPTSETUP status $DEV_NAME | grep -q "reencryption:  in-progress" && fail
$CRYPTSETUP close $DEV_NAME || fail

if ! dm_delay_features; then
	echo "dm-delay target is missing, skipping recovery tests."
	remove_mapping
	exit 0
fi

echo "[6] Reencryption recovery"
# (check opt-io size optimization in reencryption code does not affect recovery)
# device with opt-io size 32k
prepare_linear_dev 32 opt_blks=64 $OPT_XFERLEN_EXP
OFFSET=8192

echo "sector size 512->512"

get_error_offsets 32 $OFFSET
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1

echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
reencrypt_recover 512 checksum $HASH1
reencrypt_recover 512 journal $HASH1

if [ -n "$DM_SECTOR_SIZE" ]; then
	echo "sector size 512->4096"

	get_error_offsets 32 $OFFSET 4096
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	reencrypt_recover 4096 checksum $HASH1
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1
	reencrypt_recover 4096 journal $HASH1

	echo "sector size 4096->4096"

	get_error_offsets 32 $OFFSET 4096
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 --sector-size 4096 -c aes-cbc-essiv:sha256 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	reencrypt_recover 4096 checksum $HASH1
	reencrypt_recover 4096 journal $HASH1
fi

echo "[7] Reencryption recovery (online i/o error)"

echo "sector size 512->512"

get_error_offsets 32 $OFFSET
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1

echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
reencrypt_recover_online 512 checksum $HASH1
reencrypt_recover_online 512 journal $HASH1

if [ -n "$DM_SECTOR_SIZE" ]; then
	echo "sector size 512->4096"

	get_error_offsets 32 $OFFSET 4096
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	reencrypt_recover_online 4096 checksum $HASH1
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1
	reencrypt_recover_online 4096 journal $HASH1

	echo "sector size 4096->4096"

	get_error_offsets 32 $OFFSET 4096
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 --sector-size 4096 -c aes-cbc-essiv:sha256 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	reencrypt_recover_online 4096 checksum $HASH1
	reencrypt_recover_online 4096 journal $HASH1
fi

if [ $HAVE_KEYRING -eq 1 ]; then
	echo "sector size 512->512 (recovery by VK)"

	get_error_offsets 32 $OFFSET
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1

	echo "ERR writes to sectors (recovery by VK) [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	reencrypt_recover_online_vk 512 checksum $HASH1
	reencrypt_recover_online_vk 512 journal $HASH1

	if [ -n "$DM_SECTOR_SIZE" ]; then
		echo "sector size 512->4096"

		get_error_offsets 32 $OFFSET 4096
		echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
		wipe $PWD1

		echo "ERR writes to sectors (recovery by VK) [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
		reencrypt_recover_online_vk 4096 checksum $HASH1
		echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
		wipe $PWD1
		reencrypt_recover_online_vk 4096 journal $HASH1

		echo "sector size 4096->4096"

		get_error_offsets 32 $OFFSET 4096
		echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 --sector-size 4096 -c aes-cbc-essiv:sha256 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
		wipe $PWD1

		echo "ERR writes to sectors (recovery by VK) [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
		reencrypt_recover_online_vk 4096 checksum $HASH1
		reencrypt_recover_online_vk 4096 journal $HASH1
	fi
fi

echo "[8] Reencryption with detached header recovery"
prepare_linear_dev 31 opt_blks=64 $OPT_XFERLEN_EXP

echo "sector size 512->512"

get_error_offsets 31 0
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1 $IMG_HDR
check_hash $PWD1 $HASH7 $IMG_HDR

echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
reencrypt_recover 512 checksum $HASH7 $IMG_HDR
reencrypt_recover 512 journal $HASH7 $IMG_HDR

if [ -n "$DM_SECTOR_SIZE" ]; then
	echo "sector size 512->4096"

	get_error_offsets 31 0 4096
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1 $IMG_HDR

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	reencrypt_recover 4096 checksum $HASH7 $IMG_HDR
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1 $IMG_HDR
	reencrypt_recover 4096 journal $HASH7 $IMG_HDR

	echo "sector size 4096->4096"

	get_error_offsets 31 0 4096
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 4096 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1 $IMG_HDR

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	reencrypt_recover 4096 checksum $HASH7 $IMG_HDR
	reencrypt_recover 4096 journal $HASH7 $IMG_HDR
fi

echo "[9] Reencryption with detached header recovery (online i/o error)"

echo "sector size 512->512"

get_error_offsets 31 0
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1 $IMG_HDR

echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
reencrypt_recover_online 512 checksum $HASH7 $IMG_HDR
reencrypt_recover_online 512 journal $HASH7 $IMG_HDR

if [ -n "$DM_SECTOR_SIZE" ]; then
	echo "sector size 512->4096"

	get_error_offsets 31 0 4096
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1 $IMG_HDR

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	reencrypt_recover_online 4096 checksum $HASH7 $IMG_HDR
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1 $IMG_HDR
	reencrypt_recover_online 4096 journal $HASH7 $IMG_HDR

	echo "sector size 4096->4096"

	get_error_offsets 31 0 4096
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 4096 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1 $IMG_HDR

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	reencrypt_recover_online 4096 checksum $HASH7 $IMG_HDR
	reencrypt_recover_online 4096 journal $HASH7 $IMG_HDR
fi

echo "[10] Encryption recovery"
prepare_linear_dev 64
OFFSET=$((2*1024*2))

echo "sector size 512"

get_error_offsets 64 $OFFSET 512 $((62*1024*2))

echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
encrypt_recover 512 4M $HASH8 $((60*1024*2)) $HASH4

if [ -n "$DM_SECTOR_SIZE" ]; then
	echo "sector size 4096"

	get_error_offsets 64 $OFFSET 4096 $((62*1024*2))

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	encrypt_recover 4096 4M $HASH8 $((60*1024*2)) $HASH4
fi

echo "[11] Encryption recovery (online i/o error)"

echo "sector size 512"

get_error_offsets 64 $OFFSET 512 $((62*1024*2))

echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
encrypt_recover_online 512 4M $HASH8 $((60*1024*2)) $HASH4

if [ -n "$DM_SECTOR_SIZE" ]; then
	echo "sector size 4096"

	get_error_offsets 64 $OFFSET 4096 $((62*1024*2))

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	encrypt_recover_online 4096 4M $HASH8 $((60*1024*2)) $HASH4
fi

echo "[12] Encryption with detached header recovery"
prepare_linear_dev 31 opt_blks=64 $OPT_XFERLEN_EXP

get_error_offsets 31 0

echo "sector size 512"

echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
encrypt_recover_detached 512 checksum $HASH7 $IMG_HDR
encrypt_recover_detached 512 journal $HASH7 $IMG_HDR

if [ -n "$DM_SECTOR_SIZE" ]; then
	get_error_offsets 31 0 4096

	echo "sector size 4096"

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	encrypt_recover_detached 4096 checksum $HASH7 $IMG_HDR
	encrypt_recover_detached 4096 journal $HASH7 $IMG_HDR
fi

echo "[13] Encryption with detached header recovery (online i/o error)"

get_error_offsets 31 0

echo "sector size 512"

echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
encrypt_recover_detached_online 512 checksum $HASH7 $IMG_HDR
encrypt_recover_detached_online 512 journal $HASH7 $IMG_HDR

if [ -n "$DM_SECTOR_SIZE" ]; then
	get_error_offsets 31 0 4096

	echo "sector size 4096"

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	encrypt_recover_detached_online 4096 checksum $HASH7 $IMG_HDR
	encrypt_recover_detached_online 4096 journal $HASH7 $IMG_HDR
fi

echo "[14] Decryption with detached header recovery"

echo "sector size 512"

# TODO: What should decryption do when it finishes decryption during recovery (with open)
get_error_offsets 31 2049

echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
decrypt_recover_detached 512 journal $HASH7 $IMG_HDR
decrypt_recover_detached 512 checksum $HASH7 $IMG_HDR

if [ -n "$DM_SECTOR_SIZE" ]; then
	echo "sector size 4096"

	# TODO: What should decryption do when it finishes decryption during recovery (with open)
	get_error_offsets 31 2048 4096

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	decrypt_recover_detached 4096 checksum $HASH7 $IMG_HDR
	decrypt_recover_detached 4096 journal $HASH7 $IMG_HDR
fi

echo "[15] Decryption with detached header recovery (online i/o error)"

echo "sector size 512"

# TODO: What should decryption do when it finishes decryption during recovery (with open)
get_error_offsets 31 2049

echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
decrypt_recover_detached_online 512 journal $HASH7 $IMG_HDR
decrypt_recover_detached_online 512 checksum $HASH7 $IMG_HDR

if [ -n "$DM_SECTOR_SIZE" ]; then
	echo "sector size 4096"

	# TODO: What should decryption do when it finishes decryption during recovery (with open)
	get_error_offsets 31 2048 4096

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	decrypt_recover_detached_online 4096 checksum $HASH7 $IMG_HDR
	decrypt_recover_detached_online 4096 journal $HASH7 $IMG_HDR
fi

echo "[16] Offline reencryption with fixed device size."
preparebig 68

for test_sector_size in $TEST_SECTORS; do
printf "sector size %4s: " $test_sector_size
for test_res in checksum journal none; do
	echo -n "[$test_res]"
	reencrypt_offline_fixed_size $test_sector_size 2048		$HASH2 $test_res $((60*1024*2)) $HASH8
	reencrypt_offline_fixed_size $test_sector_size $((28*1024*2))	$HASH1 $test_res $((60*1024*2)) $HASH8
	reencrypt_offline_fixed_size $test_sector_size $((31*1024*2))	$HASH7 $test_res $((60*1024*2)) $HASH8
	echo -n "[OK]"
done
echo ""
done

echo "[17] Online reencryption with fixed device size."
for test_sector_size in $TEST_SECTORS; do
printf "sector size %4s: " $test_sector_size
for test_res in checksum journal none; do
	echo -n "[$test_res]"
	reencrypt_online_fixed_size $test_sector_size 2048		$HASH2 $test_res $((60*1024*2)) $HASH8
	reencrypt_online_fixed_size $test_sector_size $((28*1024*2))	$HASH1 $test_res $((60*1024*2)) $HASH8
	reencrypt_online_fixed_size $test_sector_size $((31*1024*2))	$HASH7 $test_res $((60*1024*2)) $HASH8
	echo -n "[OK]"
done
echo ""
done

echo "[18] Offline reencryption with fixed device size (detached header)."
preparebig 60

for test_sector_size in $TEST_SECTORS; do
printf "sector size %4s: " $test_sector_size
for test_res in checksum journal none; do
	echo -n "[$test_res]"
	reencrypt_offline_fixed_size $test_sector_size 2048		$HASH2 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
	reencrypt_offline_fixed_size $test_sector_size $((28*1024*2))	$HASH1 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
	reencrypt_offline_fixed_size $test_sector_size $((31*1024*2))	$HASH7 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
	echo -n "[OK]"
done
echo ""
done

echo "[19] Online reencryption with fixed device size (detached header)."
for test_sector_size in $TEST_SECTORS; do
printf "sector size %4s: " $test_sector_size
for test_res in checksum journal none; do
	echo -n "[$test_res]"
	reencrypt_online_fixed_size $test_sector_size 2048		$HASH2 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
	reencrypt_online_fixed_size $test_sector_size $((28*1024*2))	$HASH1 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
	reencrypt_online_fixed_size $test_sector_size $((31*1024*2))	$HASH7 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
	echo -n "[OK]"
done
echo ""
done

echo "[20] Offline encryption with fixed device size (detached header)."
for test_sector_size in $TEST_SECTORS; do
printf "sector size %4s: " $test_sector_size
for test_res in checksum journal none; do
	echo -n "[$test_res]"
	encrypt_offline_fixed_size $test_sector_size 2048		$HASH2 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
	encrypt_offline_fixed_size $test_sector_size $((28*1024*2))	$HASH1 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
	encrypt_offline_fixed_size $test_sector_size $((31*1024*2))	$HASH7 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
	echo -n "[OK]"
done
echo ""
done

echo "[21] Offline decryption with fixed device size (detached header)."
prepare_linear_dev 60
for test_sector_size in $TEST_SECTORS; do
printf "sector size %4s: " $test_sector_size
for test_res in checksum journal none; do
	echo -n "[$test_res]"
	decrypt_offline_fixed_size $test_sector_size 2048		$HASH2 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
	decrypt_offline_fixed_size $test_sector_size $((28*1024*2))	$HASH1 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
	decrypt_offline_fixed_size $test_sector_size $((31*1024*2))	$HASH7 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
	echo -n "[OK]"
done
echo ""
done

echo "[22] Multi-keyslot device reencryption"
prepare dev_size_mb=17
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --offset 32768 $FAST_PBKDF_ARGON $DEV || fail
echo -e "$PWD1\n$PWD2" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV || fail
echo -e "$PWD1\n$PWD3" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1

echo -e "$PWD1\n$PWD2\n$PWD3" | $CRYPTSETUP reencrypt $DEV -q || fail
check_hash $PWD1 $HASH2
check_hash $PWD2 $HASH2
check_hash $PWD3 $HASH2

# check at least pbkdf type is preserved
$CRYPTSETUP luksDump $DEV | grep -e "3: luks2" -A5 | grep -q "argon2" || fail
$CRYPTSETUP luksDump $DEV | grep -e "4: luks2" -A5 | grep -q "pbkdf2" || fail
$CRYPTSETUP luksDump $DEV | grep -e "5: luks2" -A5 | grep -q "argon2" || fail

echo $PWD1 | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV $KEY1 || fail

# with more keyslots, specific has to be selected
$CRYPTSETUP reencrypt $DEV -d $KEY1 -q 2>/dev/null && fail
$CRYPTSETUP reencrypt $DEV -d $KEY1 -q -S0 || fail
open_crypt
check_hash_dev /dev/mapper/$DEV_NAME $HASH2
$CRYPTSETUP close $DEV_NAME

# there should be single keyslot now
$CRYPTSETUP reencrypt $DEV -d $KEY1 -q || fail
echo $PWD1 | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV -S1 -d $KEY1 || fail

echo $PWD3 | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV -S2 --unbound --key-size 32 || fail
echo $PWD3 | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV -S22 --unbound --key-size 32 || fail
echo $PWD3 | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV -S23 --unbound --key-size 32 || fail

echo $PWD1 | $CRYPTSETUP reencrypt $DEV -S1 -q || fail
$CRYPTSETUP open --test-passphrase -d $KEY1 $DEV 2>/dev/null && fail
echo $PWD3 | $CRYPTSETUP open --test-passphrase -S2 $DEV || fail
echo $PWD3 | $CRYPTSETUP open --test-passphrase -S22 $DEV || fail
check_hash $PWD1 $HASH2

# fill 31 keyslots
COUNT=27
while [ $COUNT -gt 0 ]; do
	echo -e "$PWD1\n$PWD1" | $CRYPTSETUP luksAddKey $DEV -q $FAST_PBKDF_ARGON || fail
	COUNT=$((COUNT-1))
done

echo $PWD1 | $CRYPTSETUP reencrypt $DEV -S0 -q 2>/dev/null && fail
echo $PWD1 | $CRYPTSETUP luksKillSlot $DEV 30 || fail
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -S0 || fail

COUNT=14
while [ $COUNT -gt 0 ]; do
	echo -e "$PWD1\n$PWD1" | $CRYPTSETUP luksAddKey $DEV -q $FAST_PBKDF_ARGON || fail
	COUNT=$((COUNT-1))
done

echo -e "$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1" | $CRYPTSETUP reencrypt $DEV -q 2>/dev/null && fail
echo $PWD1 | $CRYPTSETUP luksKillSlot $DEV 1 || fail
# one wrong passphrase
echo -e "$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD2" | $CRYPTSETUP reencrypt $DEV -q 2>/dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --resume-only -q 2>/dev/null && fail
echo -e "$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1" | $CRYPTSETUP reencrypt $DEV -q  || fail

#test error path behaves as expected for initialization with not enough space in binary area
# create LUKS2 header with keyslots binary space for exactly 4 keyslots
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --luks2-keyslots-size $((4*258048)) -S0 -s512 --cipher aes-xts-plain64 $FAST_PBKDF_ARGON $DEV >/dev/null || fail
echo -e "$PWD1\n$PWD2" | $CRYPTSETUP luksAddKey -S1 $DEV -q $FAST_PBKDF_ARGON || fail
echo -e "$PWD1\n$PWD2" | $CRYPTSETUP luksAddKey -S2 $DEV -q $FAST_PBKDF_ARGON || fail
# there is not enough space in binary area for keyslot id 4 (replacement for id 2)
echo -e "$PWD1\n$PWD2\n$PWD2" | $CRYPTSETUP reencrypt $DEV --init-only -q 2>/dev/null && fail
$CRYPTSETUP luksDump $DEV | grep -q "online-reencrypt" && fail
# check cli removed all unbound keyslots created in-before reencryption initialization
$CRYPTSETUP luksDump $DEV | grep -q "unbound" && fail

echo $PWD1 | $CRYPTSETUP luksKillSlot $DEV 2 || fail
# there is not enough space in binary area for reencryption keyslot
echo -e "$PWD1\n$PWD2" | $CRYPTSETUP reencrypt $DEV --init-only -q 2>/dev/null && fail
$CRYPTSETUP luksDump $DEV | grep -q "online-reencrypt" && fail
# check cli removed all unbound keyslots created in-before reencryption initialization
$CRYPTSETUP luksDump $DEV | grep -q "unbound" && fail

echo "[23] Reencryption with specified new volume key"
prepare dev_size_mb=32
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 256 -c aes-cbc-essiv:sha256 --offset 8192 $FAST_PBKDF_ARGON $DEV || fail
echo -e "$PWD1\n$PWD3" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -S0 $FAST_PBKDF_ARGON --new-volume-key-file $VKEY1 --new-key-size 128 || fail
check_hash $PWD1 $HASH1
$CRYPTSETUP luksErase -q $DEV || fail
echo $PWD1 | $CRYPTSETUP luksAddKey -q $FAST_PBKDF_ARGON --volume-key-file $VKEY1 -s 128 $DEV || fail
check_hash $PWD1 $HASH1

echo "[24] Reencryption with initial cipher_null"
# aka custom encryption
prepare dev_size_mb=32
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c cipher_null-ecb --offset 8192 $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -c aes-xts-plain64 -q $FAST_PBKDF_ARGON || fail
check_hash $PWD1 $HASH1

# online
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c cipher_null-ecb --offset 8192 $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1
echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -c aes-xts-plain64 -q $FAST_PBKDF_ARGON || fail
check_hash_dev /dev/mapper/$DEV_NAME $HASH1
if [ $HAVE_KEYRING -gt 0 ]; then
	$CRYPTSETUP status $DEV_NAME | grep -q "key location: keyring" || fail
fi
$CRYPTSETUP close $DEV_NAME

# FIXME: Add test luks2 image with both keyslot and data using cipher_null and verify LUKS2 reencryption fixes this.

## simulate LUKS2 device with cipher_null in both keyslot and segment (it can be created only by up conversion from LUKS1)
#echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 -s 128 -c cipher_null-ecb --offset 8192 $FAST_PBKDF2 $DEV || fail
#$CRYPTSETUP convert -q --type luks2 $DEV || fail
#wipe $PWD1
#echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON >/dev/null || fail
#check_hash $PWD1 $HASH1
## both keyslot and segment cipher must not be null after reencryption with default params
#$CRYPTSETUP luksDump $DEV | grep -q "cipher_null" && fail

# multistep reencryption with initial cipher_null
preparebig 64
echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 $DEV -c null --offset 16384 -q $FAST_PBKDF_ARGON || fail
echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME
wipe_dev /dev/mapper/$DEV_NAME
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --hotzone-size 1M --resilience none -q $FAST_PBKDF_ARGON >/dev/null || fail
$CRYPTSETUP close $DEV_NAME
check_hash $PWD1 $HASH5

echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 $DEV -c null --offset 16384 -q $FAST_PBKDF_ARGON || fail
wipe $PWD1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV --hotzone-size 1M --resilience none -q $FAST_PBKDF_ARGON >/dev/null || fail
check_hash $PWD1 $HASH5

echo "[25] Reencryption recovery with cipher_null"
# (check opt-io size optimization in reencryption code does not affect recovery)
# device with opt-io size 32k
prepare_linear_dev 32 opt_blks=64 $OPT_XFERLEN_EXP
OFFSET=8192

echo "sector size 512->512"

get_error_offsets 32 $OFFSET
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1

echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
reencrypt_recover 512 checksum $HASH1
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1
reencrypt_recover 512 journal $HASH1

if [ -n "$DM_SECTOR_SIZE" ]; then
	echo "sector size 512->4096"

	get_error_offsets 32 $OFFSET 4096
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	reencrypt_recover 4096 checksum $HASH1
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1
	reencrypt_recover 4096 journal $HASH1

	echo "sector size 4096->4096"

	get_error_offsets 32 $OFFSET 4096
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 4096 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	reencrypt_recover 4096 checksum $HASH1
	reencrypt_recover 4096 journal $HASH1
fi

echo "[26] Reencryption recovery with cipher_null (online i/o error)"

echo "sector size 512->512"

get_error_offsets 32 $OFFSET
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 -c null --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1

echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
reencrypt_recover_online 512 checksum $HASH1
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 -c null --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1
reencrypt_recover_online 512 journal $HASH1

if [ -n "$DM_SECTOR_SIZE" ]; then
	echo "sector size 512->4096"

	get_error_offsets 32 $OFFSET 4096
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	reencrypt_recover_online 4096 checksum $HASH1
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1
	reencrypt_recover_online 4096 journal $HASH1

	echo "sector size 4096->4096"

	get_error_offsets 32 $OFFSET 4096
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 4096 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	reencrypt_recover_online 4096 checksum $HASH1
	reencrypt_recover_online 4096 journal $HASH1
fi

echo "[27] Verify test passphrase mode works with reencryption metadata"
echo $PWD1 | $CRYPTSETUP -S5 -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV || fail
echo -e "$PWD1\n$PWD1" | $CRYPTSETUP luksAddKey --unbound -s80 -S0 $FAST_PBKDF_ARGON $DEV || fail
echo $PWD1 | $CRYPTSETUP reencrypt --init-only $DEV || fail
echo $PWD1 | $CRYPTSETUP open --test-passphrase $DEV || fail

echo $PWD1 | $CRYPTSETUP -q luksFormat -S5 --header $IMG_HDR --type luks2 $FAST_PBKDF_ARGON $DEV || fail
echo -e "$PWD1\n$PWD1" | $CRYPTSETUP luksAddKey --unbound -s80 -S0 $FAST_PBKDF_ARGON $IMG_HDR || fail
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --init-only --header $IMG_HDR $DEV || fail
echo $PWD1 | $CRYPTSETUP open --test-passphrase $IMG_HDR || fail
rm -f $IMG_HDR
wipe_dev_head $DEV 1

echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --init-only --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
echo $PWD1 | $CRYPTSETUP open --test-passphrase $IMG_HDR || fail

echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --init-only --reduce-device-size 8M $FAST_PBKDF_ARGON $DEV || fail
echo $PWD1 | $CRYPTSETUP open --test-passphrase $DEV || fail

echo "[28] Prevent nested encryption"
prepare_linear_dev 32 opt_blks=64 $OPT_XFERLEN_EXP

#device already LUKS2
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF2 $DEV || fail

echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks1 --reduce-device-size 2m $FAST_PBKDF2 $DEV 2>/dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks1 --header $IMG_HDR $FAST_PBKDF2 $DEV 2>/dev/null && fail
test -f $IMG_HDR && fail
echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks2 --reduce-device-size 2m $FAST_PBKDF2 $DEV 2>/dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks2 --header $IMG_HDR $FAST_PBKDF2 $DEV 2>/dev/null && fail
test -f $IMG_HDR && fail
#type mismatch
echo $PWD1 | $CRYPTSETUP reencrypt -q --type luks1 $DEV 2>/dev/null && fail
wipe_dev $DEV

#detached header already LUKS2
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --header $IMG_HDR $FAST_PBKDF2 $DEV || fail

echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks1 --header $IMG_HDR $FAST_PBKDF2 $DEV 2>/dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks2 --header $IMG_HDR $FAST_PBKDF2 $DEV 2>/dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt -q --type luks1 --header $IMG_HDR $DEV 2>/dev/null && fail
rm -f $IMG_HDR

#data device already in reencryption
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF2 $DEV || fail
echo $PWD1 | $CRYPTSETUP reencrypt --init-only $FAST_PBKDF_ARGON $DEV || fail

echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks1 --header $IMG_HDR $FAST_PBKDF2 $DEV 2>/dev/null && fail
test -f $IMG_HDR && fail
echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks2 --header $IMG_HDR $FAST_PBKDF2 $DEV 2>/dev/null && fail
test -f $IMG_HDR && fail
#type mismatch
echo $PWD1 | $CRYPTSETUP reencrypt -q --type luks1 $DEV 2>/dev/null && fail
wipe_dev $DEV
rm -f $IMG_HDR

#header in reencryption (type mismatch)
echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --init-only --type luks2 --header $IMG_HDR $FAST_PBKDF2 $DEV || fail
echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks1 --header $IMG_HDR $FAST_PBKDF2 $DEV 2>/dev/null && fail

echo "[29] Conflicting reencryption parameters"
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF2 $DEV || fail
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --init-only $FAST_PBKDF_ARGON || fail
echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --reduce-device-size 4M $DEV -q $FAST_PBKDF_ARGON 2> /dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV -q $FAST_PBKDF_ARGON 2> /dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience datashift 2> /dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience datashift-checksum 2> /dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience datashift-journal 2> /dev/null && fail
wipe_dev_head $DEV 1
echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --init-only --reduce-device-size 16M $DEV -q $FAST_PBKDF_ARGON 2> /dev/null || fail
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV -q $FAST_PBKDF_ARGON 2> /dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience journal 2> /dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience datashift-checksum 2> /dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience datashift-journal 2> /dev/null && fail
wipe_dev_head $DEV 1
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --header $IMG_HDR $FAST_PBKDF2 $DEV || fail
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --header $IMG_HDR --init-only $FAST_PBKDF_ARGON || fail
echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --header $IMG_HDR $DEV -q $FAST_PBKDF_ARGON 2> /dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --header $IMG_HDR $DEV -q $FAST_PBKDF_ARGON 2> /dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --header $IMG_HDR --resilience datashift-checksum 2>/dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --header $IMG_HDR --resilience datashift-journal 2>/dev/null && fail
rm -f $IMG_HDR
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --encrypt --header $IMG_HDR --init-only $FAST_PBKDF_ARGON || fail
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --header $IMG_HDR $DEV -q $FAST_PBKDF_ARGON 2> /dev/null && fail
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --header $IMG_HDR $FAST_PBKDF2 $DEV || fail
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR --init-only $FAST_PBKDF_ARGON || fail
echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --header $IMG_HDR $DEV -q $FAST_PBKDF_ARGON 2> /dev/null && fail
rm -f $IMG_HDR
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF2 $DEV || fail
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV --header $IMG_HDR -q --init-only $FAST_PBKDF_ARGON --resilience datashift 2> /dev/null && fail
test -f $IMG_HDR && fail
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV --header $IMG_HDR -q --init-only $FAST_PBKDF_ARGON --resilience none 2> /dev/null && fail
test -f $IMG_HDR && fail
$CRYPTSETUP luksDump $DEV | grep -q "online-reencrypt" && fail
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV --header $IMG_HDR -q --init-only $FAST_PBKDF_ARGON --resilience checksum --hotzone-size 4m || fail
$CRYPTSETUP isLuks $DEV -q && fail
# $CRYPTSETUP luksDump $IMG_HDR
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV --header $IMG_HDR -q --resilience datashift 2> /dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV --header $IMG_HDR -q --resilience none 2> /dev/null && fail
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV --header $IMG_HDR -q --resilience journal || fail
rm -f $IMG_HDR

check_blkid
if [ "$HAVE_BLKID" -gt 0 ]; then
	echo "[30] Prevent nested encryption of broken LUKS device"
	rm -f $IMG_HDR
	xz -dkf $HEADER_LUKS2_PV.xz
	wipe_dev $DEV

	# broken header
	echo $PWD1 | $CRYPTSETUP reencrypt -q --header $HEADER_LUKS2_PV $DEV $FAST_PBKDF_ARGON --encrypt --type luks2 2>/dev/null && fail
	$CRYPTSETUP isLuks $HEADER_LUKS2_PV && fail
	# broken device
	echo $PWD1 | $CRYPTSETUP reencrypt -q $HEADER_LUKS2_PV $FAST_PBKDF_ARGON --encrypt --force-offline-reencrypt --type luks2 --reduce-device-size 8m 2>/dev/null && fail
	$CRYPTSETUP isLuks $HEADER_LUKS2_PV && fail
	# broken data device only
	echo $PWD1 | $CRYPTSETUP reencrypt -q --header $IMG_HDR $HEADER_LUKS2_PV $FAST_PBKDF_ARGON --encrypt --force-offline-reencrypt --type luks2 2>/dev/null && fail
	test -f $IMG_HDR && fail
fi

if [ -n "$DM_SECTOR_SIZE" -a $HAVE_BLKID -gt 0 ]; then
	echo "[31] Prevent dangerous sector size increase"
	preparebig 64
	echo $PWD1 | $CRYPTSETUP luksFormat -q --sector-size 512 --type luks2 $FAST_PBKDF_ARGON $DEV || fail

	# block encryption sector size increase on offline device
	echo $PWD1 | $CRYPTSETUP reencrypt --init-only -q --sector-size 1024 $FAST_PBKDF_ARGON $DEV 2>/dev/null && fail
	$CRYPTSETUP luksDump $DEV | grep -q "online-reencrypt" && fail
	echo $PWD1 | $CRYPTSETUP reencrypt -q --sector-size 1024 $FAST_PBKDF_ARGON $DEV 2>/dev/null && fail
	$CRYPTSETUP luksDump $DEV | grep -q "online-reencrypt" && fail
	$CRYPTSETUP luksDump $DEV | grep -q "sector: 1024" && fail

	# --force-offline-reencrypt can bypass the constraint
	echo $PWD1 | $CRYPTSETUP reencrypt --force-offline-reencrypt --init-only -q --sector-size 1024 $FAST_PBKDF_ARGON $DEV || fail
	# resume must work
	echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV || fail

	# online with no superblock is fine
	echo $PWD1 | $CRYPTSETUP open -q $DEV $DEV_NAME || fail
	echo $PWD1 | $CRYPTSETUP reencrypt --init-only -q --sector-size 4096 $FAST_PBKDF_ARGON $DEV || fail
	$CRYPTSETUP close $DEV_NAME || fail

	# sector size decrease is ok
	echo $PWD1 | $CRYPTSETUP luksFormat -q --sector-size 4096 --type luks2 $FAST_PBKDF_ARGON $DEV || fail
	echo $PWD1 | $CRYPTSETUP reencrypt --init-only -q --sector-size 1024 $FAST_PBKDF_ARGON $DEV || fail

	if [ -n "$BLKID_BLOCK_SIZE_SUPPORT" ]; then
		xz -dkf $IMG_FS.xz
		# encryption checks must work in offline mode
		echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --force-offline-reencrypt --sector-size 1024 -q --header $IMG_HDR $IMG_FS $FAST_PBKDF_ARGON --init-only --type luks2 2>/dev/null && fail
		test -f $IMG_HDR && fail

		echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --force-offline-reencrypt --sector-size 1024 -q --header $IMG_HDR $IMG_FS $FAST_PBKDF_ARGON --type luks2 2>/dev/null && fail
		test -f $IMG_HDR && fail

		echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --force-offline-reencrypt --sector-size 1024 -q --reduce-device-size 8m $IMG_FS $FAST_PBKDF_ARGON --init-only --type luks2 2>/dev/null && fail
		$CRYPTSETUP isLuks $IMG_FS && fail
		echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --force-offline-reencrypt --sector-size 1024 -q --reduce-device-size 8m $IMG_FS $FAST_PBKDF_ARGON --type luks2 2>/dev/null && fail
		$CRYPTSETUP isLuks $IMG_FS && fail

		echo $PWD1 | $CRYPTSETUP luksFormat -q --sector-size 512 --type luks2 $FAST_PBKDF_ARGON $DEV || fail
		echo $PWD1 | $CRYPTSETUP open -q $DEV $DEV_NAME || fail
		dd if=$IMG_FS of=/dev/mapper/$DEV_NAME bs=1M >/dev/null 2>&1

		echo $PWD1 | $CRYPTSETUP reencrypt --init-only -q --sector-size 1024 $FAST_PBKDF_ARGON $DEV 2>/dev/null && fail
		$CRYPTSETUP status $DEV_NAME | grep -q "reencryption:  in-progress" && fail
		echo $PWD1 | $CRYPTSETUP reencrypt --init-only -q --sector-size 1024 --active-name $DEV_NAME $FAST_PBKDF_ARGON 2>/dev/null && fail
		$CRYPTSETUP status $DEV_NAME | grep -q "reencryption:  in-progress" && fail
		echo $PWD1 | $CRYPTSETUP reencrypt -q --sector-size 1024 $FAST_PBKDF_ARGON $DEV 2>/dev/null && fail
		$CRYPTSETUP luksDump $DEV | grep -q "sector: 512" || fail
		echo $PWD1 | $CRYPTSETUP reencrypt -q --sector-size 1024 --active-name $DEV_NAME $FAST_PBKDF_ARGON 2>/dev/null && fail
		$CRYPTSETUP luksDump $DEV | grep -q "sector: 512" || fail
	fi
fi

echo "[32] Removal of encryption (LUKS2 legacy cryptsetup-reencrypt test)."
prepare dev_size_mb=32
OFFSET=8192

# offline decryption with shift
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
wipe $PWD1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
check_hash_dev_head $DEV 57344 $HASH1
# FIXME: Should not reencryption remove it automatically?
rm -f $IMG_HDR

# online decryption with shift
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
wipe_dev /dev/mapper/$DEV_NAME
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
check_hash_dev_head $DEV 57344 $HASH1
# FIXME: Should not reencryption remove it automatically?
rm -f $IMG_HDR

# offline decryption (separate initialization and decryption steps)
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
wipe $PWD1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR --init-only || fail
check_hash $PWD1 $HASH1 $IMG_HDR
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
check_hash_dev_head $DEV 57344 $HASH1
# FIXME: Should not reencryption remove it automatically?
rm -f $IMG_HDR

# online decryption with shift
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
wipe_dev /dev/mapper/$DEV_NAME
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR --init-only || fail
check_hash_dev /dev/mapper/$DEV_NAME $HASH1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
check_hash_dev_head $DEV 57344 $HASH1
# FIXME: Should not reencryption remove it automatically?
rm -f $IMG_HDR

# same tests just with date size == LUKS2 header size
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 32768 || fail
wipe $PWD1
check_hash $PWD1 $HASH10
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
check_hash_dev_head $DEV 32768 $HASH10
# FIXME: Should not reencryption remove it automatically?
rm -f $IMG_HDR

echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 32768 || fail
echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
wipe_dev /dev/mapper/$DEV_NAME
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
check_hash_dev_head $DEV 32768 $HASH10
# FIXME: Should not reencryption remove it automatically?
rm -f $IMG_HDR

echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 32768 || fail
wipe $PWD1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR --init-only || fail
check_hash $PWD1 $HASH10 $IMG_HDR
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
check_hash_dev_head $DEV 32768 $HASH10
# FIXME: Should not reencryption remove it automatically?
rm -f $IMG_HDR

echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 32768 || fail
echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
wipe_dev /dev/mapper/$DEV_NAME
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR --init-only || fail
check_hash_dev /dev/mapper/$DEV_NAME $HASH10
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
check_hash_dev_head $DEV 32768 $HASH10
# FIXME: Should not reencryption remove it automatically?
rm -f $IMG_HDR

# 1MiB data size
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 63488 || fail
echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
wipe_dev /dev/mapper/$DEV_NAME
# --hotzone-size larger than data expected to get auto corrected by library
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR --init-only --hotzone-size 4M || fail
check_hash_dev /dev/mapper/$DEV_NAME $HASH2
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
check_hash_dev_head $DEV 2048 $HASH2
rm -f $IMG_HDR

# small device (less than header size)
prepare dev_size_mb=5
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -S5 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
wipe $PWD1
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
check_hash_dev_head $DEV 2048 $HASH2
# FIXME: Should not reencryption remove it automatically?
rm -f $IMG_HDR

echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -S5 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
wipe_dev /dev/mapper/$DEV_NAME
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
check_hash_dev_head $DEV 2048 $HASH2
rm -f $IMG_HDR

# initialization by --active-name parameter
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
wipe_dev /dev/mapper/$DEV_NAME
echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --header $IMG_HDR --active-name $DEV_NAME || fail
check_hash_dev_head $DEV 2048 $HASH2
rm -f $IMG_HDR

# initialization and resume by --active-name parameter
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
wipe_dev /dev/mapper/$DEV_NAME
echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --header $IMG_HDR --active-name $DEV_NAME --init-only || fail
check_hash_dev /dev/mapper/$DEV_NAME $HASH2
echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --header $IMG_HDR --active-name $DEV_NAME || fail
check_hash_dev_head $DEV 2048 $HASH2
rm -f $IMG_HDR

# run in single command by --active-name parameter
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
wipe_dev /dev/mapper/$DEV_NAME
echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --header $IMG_HDR --active-name $DEV_NAME || fail
check_hash_dev_head $DEV 2048 $HASH2
rm -f $IMG_HDR

# Regression test for decryption with detached header and digest id != 0
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
img_json_save $DEV
# replace digest id 0 with 1
img_update_json '.digests."1" = .digests."0" | del(.digests."0")' $DEV
wipe $PWD1
check_hash $PWD1 $HASH2
echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --header $IMG_HDR $DEV || fail
check_hash_dev_head $DEV 2048 $HASH2
rm -f $IMG_HDR

echo "[33] Decryption with datashift recovery (error in shift area)."
prepare_linear_dev 32
echo "sector size 512"

# avoid error in moved segment area on purpose
# Also do not create write error in last segment because
# that would not trigger reencryption crash (read would pass)
get_error_offsets 32 $OFFSET 512 $((32-1024*2-$OFFSET))
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1

echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
echo -n "resilience:"
decrypt_recover $HASH1 $IMG_HDR $((32*1024*2-$OFFSET))

if [ -n "$DM_SECTOR_SIZE" ]; then
	echo -e "\nsector size 4096"

	get_error_offsets 32 $OFFSET 4096 $((32-1024*2-$OFFSET))
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 4096 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	echo -n "resilience:"
	decrypt_recover $HASH1 $IMG_HDR $((32*1024*2-$OFFSET))
fi
echo ""

echo "[34] Decryption with datashift recovery (error in moved segment)."
echo "sector size 512"

HZ_SIZE=$((3*1024*2))

# move injected error in moved segment area
get_error_offsets 32 0 512 $HZ_SIZE
echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"

echo -n "resilience:"
for res in datashift-journal datashift-checksum; do

	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
	wipe $PWD1

	decrypt_recover $HASH1 $IMG_HDR $((32*1024*2-$OFFSET)) $res $(($HZ_SIZE*512))
done

if [ -n "$DM_SECTOR_SIZE" ]; then
	echo -e "\nsector size 4096"

	# move injected error in moved segment area
	get_error_offsets 32 0 4096 $HZ_SIZE
	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"

	echo -n "resilience:"
	for res in datashift-journal datashift-checksum; do
		echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 4096 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
		wipe $PWD1

		decrypt_recover $HASH1 $IMG_HDR $((32*1024*2-$OFFSET)) $res $(($HZ_SIZE*512))
	done
fi
echo ""

echo "[35] Decryption with datashift recovery (online i/o error in shift area)."
echo "sector size 512"

echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
wipe_dev /dev/mapper/$DEV_NAME

# avoid error in moved segment area on purpose
# Also do not create write error in last segment because
# that would not trigger reencryption crash (read would pass)
get_error_offsets 32 $OFFSET 512 $((32-1024*2-$OFFSET))
echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"

echo -n "resilience:"
decrypt_recover_online $HASH1 $IMG_HDR $((32*1024*2-$OFFSET))

if [ -n "$DM_SECTOR_SIZE" ]; then
	echo -e "\nsector size 4096"

	get_error_offsets 32 $OFFSET 4096 $((32-1024*2-$OFFSET))
	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 4096 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
	echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
	wipe_dev /dev/mapper/$DEV_NAME

	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
	echo -n "resilience:"
	decrypt_recover_online $HASH1 $IMG_HDR $((32*1024*2-$OFFSET))
fi
echo ""

echo "[36] Decryption with datashift recovery (online i/o error in moved segment)."
echo "sector size 512"

# move injected error in moved segment area
get_error_offsets 32 0 512 $HZ_SIZE
echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"

echo -n "resilience:"
for res in datashift-journal datashift-checksum; do

	echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
	echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
	wipe_dev /dev/mapper/$DEV_NAME

	decrypt_recover_online_moved $HASH1 $IMG_HDR $((32*1024*2-$OFFSET)) $res $(($HZ_SIZE*512))
done

if [ -n "$DM_SECTOR_SIZE" ]; then
	echo -e "\nsector size 4096"

	get_error_offsets 32 0 4096 $HZ_SIZE
	echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"

	echo -n "resilience:"
	for res in datashift-journal datashift-checksum; do

		echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 4096 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
		echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
		wipe_dev /dev/mapper/$DEV_NAME

		decrypt_recover_online_moved $HASH1 $IMG_HDR $((32*1024*2-$OFFSET)) $res $(($HZ_SIZE*512))
	done
fi
echo ""

echo "[37] Decryption with datashift (large data offsets)"
prepare_linear_dev 512

echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset 1015808 --luks2-keyslots-size 16M $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --header $IMG_HDR $DEV -q || fail
check_hash_dev_head $DEV $((16*1024*2)) $HASH10
rm -f $IMG_HDR

echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset 1015808 --luks2-keyslots-size 16M $FAST_PBKDF_ARGON $DEV || fail
echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME
wipe_dev /dev/mapper/$DEV_NAME
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --header $IMG_HDR $DEV -q || fail
check_hash_dev_head $DEV $((16*1024*2)) $HASH10
rm -f $IMG_HDR

echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset 1046528 --luks2-keyslots-size 16M $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --header $IMG_HDR $DEV -q || fail
check_hash_dev_head $DEV 2048 $HASH2
rm -f $IMG_HDR

echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset 1046528 --luks2-keyslots-size 16M $FAST_PBKDF_ARGON $DEV || fail
echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME
wipe_dev /dev/mapper/$DEV_NAME
echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --header $IMG_HDR $DEV -q || fail
check_hash_dev_head $DEV 2048 $HASH2

echo "[38] Reencrypt by keyslot context (new initialization and resume methods)"
prepare dev_size_mb=32

# keyslot0  - pass1
# keyslot2  - pass2
# keyslot4  - pass3, token4 (wrong pass)
# keyslot6  - pass4, token1
# keyslot8  - pass5, token2 (missing)
# keyslot10 - pass6, token3 (wrong pass)
# keyslot12 - pass7, token4
reformat_reencrypt_device

# tokens
$CRYPTSETUP reencrypt -q --token-id 1 $DEV <&- || fail
reformat_reencrypt_device
$CRYPTSETUP reencrypt -q --token-only $DEV <&- || fail
reformat_reencrypt_device
$CRYPTSETUP reencrypt -q --token-type luks2-keyring $DEV <&- || fail

reformat_reencrypt_device
# keyslots 6 and 12 are unlocked by tokens 1 and 4
echo -e "$PWD1\n$PWD2\n$PWD3\n$PWD5\n$PWD6" | $CRYPTSETUP reencrypt -q $DEV || fail

# keyfile
echo -n $PWD2 > $KEY1
reformat_reencrypt_device
$CRYPTSETUP reencrypt -q --key-file $KEY1 $DEV 2>/dev/null <&- && fail
$CRYPTSETUP reencrypt -q --key-file $KEY1 -S4 $DEV 2>/dev/null <&- && fail
$CRYPTSETUP reencrypt -q --key-file $KEY1 -S2 $DEV <&- || fail

# passphrase in keyring
reformat_reencrypt_device
$CRYPTSETUP reencrypt -q --key-description $KEY_NAME7 $DEV 2>/dev/null <&- && fail
$CRYPTSETUP reencrypt -q --key-description $KEY_NAME7 -S4 $DEV 2>/dev/null <&- && fail
$CRYPTSETUP reencrypt -q --key-description $KEY_NAME7 -S2 $DEV <&- || fail

# tokens
reformat_reencrypt_device
$CRYPTSETUP reencrypt -q --token-id 1 $DEV --init-only <&- || fail
$CRYPTSETUP open --token-id 1 $DEV $DEV_NAME <&- || fail
$CRYPTSETUP reencrypt -q --token-id 1 $DEV --resume-only <&- || fail
$CRYPTSETUP close $DEV_NAME || fail

reformat_reencrypt_device
$CRYPTSETUP reencrypt -q --token-only --init-only $DEV <&- || fail
$CRYPTSETUP open --token-only $DEV $DEV_NAME <&- || fail
$CRYPTSETUP reencrypt -q --token-only --resume-only $DEV <&- || fail
$CRYPTSETUP close $DEV_NAME || fail

reformat_reencrypt_device
$CRYPTSETUP reencrypt -q --token-type luks2-keyring --init-only $DEV <&- || fail
$CRYPTSETUP open --token-type luks2-keyring $DEV $DEV_NAME <&- || fail
$CRYPTSETUP reencrypt -q --token-type luks2-keyring --resume-only $DEV <&- || fail
$CRYPTSETUP close $DEV_NAME || fail

reformat_reencrypt_device
echo -e "$PWD1\n$PWD2\n$PWD3\n$PWD5\n$PWD6" | $CRYPTSETUP reencrypt --init-only -q $DEV || fail
echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
echo -e $PWD1 | $CRYPTSETUP reencrypt --resume-only -q $DEV || fail
$CRYPTSETUP close $DEV_NAME || fail

# keyfile
echo -n $PWD2 > $KEY1
reformat_reencrypt_device
$CRYPTSETUP reencrypt -q --init-only --key-file $KEY1 $DEV 2>/dev/null <&- && fail
$CRYPTSETUP reencrypt -q --init-only --key-file $KEY1 -S4 $DEV 2>/dev/null <&- && fail

$CRYPTSETUP reencrypt -q --init-only --key-file $KEY1 -S2 $DEV <&- || fail
$CRYPTSETUP open --key-file $KEY1 $DEV $DEV_NAME <&- || fail
$CRYPTSETUP reencrypt -q --resume-only --key-file $KEY1 $DEV <&- || fail
$CRYPTSETUP close $DEV_NAME || fail

# passphrase in keyring
reformat_reencrypt_device
$CRYPTSETUP reencrypt -q --init-only --key-description $KEY_NAME7 $DEV 2>/dev/null <&- && fail
$CRYPTSETUP reencrypt -q --init-only --key-description $KEY_NAME7 -S4 $DEV 2>/dev/null <&- && fail
$CRYPTSETUP reencrypt -q --init-only --key-description $KEY_NAME7 -S2 $DEV <&- || fail
$CRYPTSETUP open -q --key-description $KEY_NAME7 $DEV $DEV_NAME <&- || fail
$CRYPTSETUP reencrypt -q --resume-only --key-description $KEY_NAME7 $DEV <&- || fail
$CRYPTSETUP close $DEV_NAME || fail

# volume key
reformat_reencrypt_device
echo $PWD1 | $CRYPTSETUP luksDump --dump-volume-key --volume-key-file $VKEY2 $DEV >/dev/null || fail

$CRYPTSETUP -q reencrypt --force-no-keyslots --volume-key-file $VKEY2 --new-volume-key-file $VKEY1 --new-key-size 256 $DEV <&- || fail

# missing key size information (no active keyslot)
$CRYPTSETUP open --test-passphrase --volume-key-file $VKEY1 $DEV <&- 2>/dev/null && fail
$CRYPTSETUP open --test-passphrase --volume-key-file $VKEY1 --key-size 256 $DEV <&- || fail
$CRYPTSETUP open --test-passphrase --volume-key-file $VKEY2 --key-size 512 $DEV 2>/dev/null <&- && fail

echo $PWD1 | $CRYPTSETUP luksAddKey --volume-key-file $VKEY1 --key-size 256 $FAST_PBKDF_ARGON $DEV || fail
echo $PWD1 | $CRYPTSETUP open --test-passphrase $DEV || fail

reformat_reencrypt_device
$CRYPTSETUP -q reencrypt --init-only --force-no-keyslots --volume-key-file $VKEY2 --new-volume-key-file $VKEY1 --new-key-size 256 $DEV <&- || fail

# For unlock operation we do not require specific --volume-key-file ordering (unlike in reencryption initialization)
$CRYPTSETUP -q open --test-passphrase --volume-key-file $VKEY1 --key-size 256 --volume-key-file $VKEY2 --key-size 512 $DEV <&- || fail

# with 2 --volume-key-files options --key-size must be also supplied twice
$CRYPTSETUP -q open --test-passphrase --volume-key-file $VKEY1 --volume-key-file $VKEY2 --key-size 512 $DEV 2>/dev/null <&- && fail
# Missing old volume key
$CRYPTSETUP -q open --test-passphrase --volume-key-file $VKEY2 --key-size 512 $DEV <&- 2>/dev/null && fail

$CRYPTSETUP -q open --volume-key-file $VKEY2 --key-size 512 --volume-key-file $VKEY1 --key-size 256 $DEV $DEV_NAME <&- || fail

$CRYPTSETUP -q reencrypt --resume-only --volume-key-file $VKEY2 --new-volume-key-file $VKEY1 --key-size 512 --new-key-size 256 $DEV <&- || fail
$CRYPTSETUP close $DEV_NAME || fail

# wrong key size
$CRYPTSETUP -q open --test-passphrase --volume-key-file $VKEY1 --key-size 512 $DEV <&- 2>/dev/null && fail
# wrong key
$CRYPTSETUP -q open --test-passphrase --volume-key-file $VKEY2 --key-size 256 $DEV <&- 2>/dev/null && fail

# missing key size
$CRYPTSETUP -q open --test-passphrase --volume-key-file $VKEY1 $DEV <&- 2>/dev/null && fail
$CRYPTSETUP -q open --test-passphrase --volume-key-file $VKEY1 --key-size 256 $DEV <&- || fail

# no remaining keyslots test (--force-no-keyslots is not needed)
reformat_reencrypt_device
$CRYPTSETUP -q luksErase $DEV || fail

$CRYPTSETUP -q reencrypt --volume-key-file $VKEY2 -s 512 --new-volume-key-file $VKEY1 --new-key-size 256 $DEV <&- || fail
$CRYPTSETUP open --test-passphrase --volume-key-file $VKEY1 --key-size 256 $DEV <&- || fail

reformat_reencrypt_device
$CRYPTSETUP -q luksErase $DEV || fail
$CRYPTSETUP -q reencrypt --init-only --volume-key-file $VKEY2 -s 512 --new-volume-key-file $VKEY1 --new-key-size 256 $DEV <&- || fail

# For unlock operation we do not require specific --volume-key-file ordering (unlike in reencryption initialization)
$CRYPTSETUP -q open --test-passphrase --volume-key-file $VKEY1 --key-size 256 --volume-key-file $VKEY2 --key-size 512 $DEV <&- || fail

# with 2 --volume-key-files options --key-size must be also supplied twice
$CRYPTSETUP -q open --test-passphrase --volume-key-file $VKEY1 --volume-key-file $VKEY2 --key-size 512 $DEV 2>/dev/null <&- && fail
# Missing old volume key
$CRYPTSETUP -q open --test-passphrase --volume-key-file $VKEY2 --key-size 512 $DEV <&- 2>/dev/null && fail

$CRYPTSETUP -q open --volume-key-file $VKEY2 --key-size 512 --volume-key-file $VKEY1 --key-size 256 $DEV $DEV_NAME <&- || fail

$CRYPTSETUP -q reencrypt --resume-only --volume-key-file $VKEY2 --new-volume-key-file $VKEY1 --key-size 512 --new-key-size 256 $DEV <&- || fail
$CRYPTSETUP close $DEV_NAME || fail

# reencryption by volume key in kernel keyring
reformat_reencrypt_device
head -c 64 $VKEY2 | keyctl padd user $KEY_NAME_VK2 %:$KEYRING > /dev/null || fail
head -c 32 $VKEY1 | keyctl padd user $KEY_NAME_VK1 %:$KEYRING > /dev/null || fail

$CRYPTSETUP -q reencrypt --force-no-keyslots --volume-key-keyring $KEY_NAME_VK2 --new-volume-key-keyring $KEY_NAME_VK1 $DEV <&- || fail

# test unlock with key after reencryption
$CRYPTSETUP open --test-passphrase --volume-key-keyring $KEY_NAME_VK1 $DEV <&- || fail
$CRYPTSETUP open --test-passphrase --volume-key-keyring $KEY_NAME_VK2 $DEV <&- 2>/dev/null && fail

reformat_reencrypt_device
$CRYPTSETUP -q reencrypt --init-only --force-no-keyslots --volume-key-keyring $KEY_NAME_VK2 --new-volume-key-keyring $KEY_NAME_VK1 $DEV <&- || fail

# For unlock operation we do not require specific --volume-key-file ordering (unlike in reencryption initialization)
$CRYPTSETUP -q open --test-passphrase --volume-key-keyring $KEY_NAME_VK1 --volume-key-keyring $KEY_NAME_VK2 $DEV <&- || fail

# Missing old volume key
$CRYPTSETUP -q open --test-passphrase --volume-key-keyring $KEY_NAME_VK1 $DEV <&- 2>/dev/null && fail

$CRYPTSETUP -q open --volume-key-keyring $KEY_NAME_VK1 --volume-key-keyring $KEY_NAME_VK2 $DEV $DEV_NAME <&- || fail

$CRYPTSETUP -q reencrypt --resume-only --volume-key-keyring $KEY_NAME_VK2 --new-volume-key-keyring $KEY_NAME_VK1 $DEV <&- || fail
$CRYPTSETUP close $DEV_NAME || fail

# no remaining keyslots test (--force-no-keyslots is not needed)
reformat_reencrypt_device
$CRYPTSETUP -q luksErase $DEV || fail

$CRYPTSETUP -q reencrypt --volume-key-keyring $KEY_NAME_VK2 --new-volume-key-keyring $KEY_NAME_VK1 $DEV <&- || fail

reformat_reencrypt_device
$CRYPTSETUP -q luksErase $DEV || fail
$CRYPTSETUP -q reencrypt --init-only --volume-key-keyring $KEY_NAME_VK2 --new-volume-key-keyring $KEY_NAME_VK1 $DEV <&- || fail

# For unlock operation we do not require specific --volume-key-file ordering (unlike in reencryption initialization)
$CRYPTSETUP -q open --test-passphrase --volume-key-keyring $KEY_NAME_VK1 --volume-key-keyring $KEY_NAME_VK2 $DEV <&- || fail

# Missing old volume key
$CRYPTSETUP -q open --test-passphrase --volume-key-keyring $KEY_NAME_VK2 $DEV <&- 2>/dev/null && fail

$CRYPTSETUP -q open --volume-key-keyring $KEY_NAME_VK1 --volume-key-keyring $KEY_NAME_VK2 $DEV $DEV_NAME <&- || fail

$CRYPTSETUP -q reencrypt --resume-only --volume-key-keyring $KEY_NAME_VK2 --new-volume-key-keyring $KEY_NAME_VK1 $DEV <&- || fail
$CRYPTSETUP close $DEV_NAME || fail

# test b0rked kernel keys
reformat_reencrypt_device
$CRYPTSETUP -q luksErase $DEV || fail

head -c 63 $VKEY2 | keyctl padd user $KEY_NAME_VK2 %:$KEYRING > /dev/null || fail
$CRYPTSETUP -q reencrypt --init-only --volume-key-keyring $KEY_NAME_VK2 --new-volume-key-keyring $KEY_NAME_VK1 $DEV <&- 2>/dev/null && fail
$CRYPTSETUP luksDump $DEV | grep -q "online-reencrypt" && fail "Failed initialization with reencryption flag"

head -c 64 $VKEY2 | keyctl padd user $KEY_NAME_VK2 %:$KEYRING > /dev/null || fail
$CRYPTSETUP open --test-passphrase --volume-key-keyring $KEY_NAME_VK2 $DEV || fail

# new key size incompatible with cipher
head -c 31 $VKEY1 | keyctl padd user $KEY_NAME_VK1 %:$KEYRING > /dev/null || fail
$CRYPTSETUP -q reencrypt --init-only --volume-key-keyring $KEY_NAME_VK2 --new-volume-key-keyring $KEY_NAME_VK1 $DEV <&- 2>/dev/null && fail
$CRYPTSETUP luksDump $DEV | grep -q "online-reencrypt" && fail "Failed initialization with reencryption flag"

remove_mapping
cleanup_keyring
exit 0
