summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 00:31:19 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 00:31:19 +0000
commit6e33fee6f4a7e2041dd276995b402ca036fcab14 (patch)
tree85be5c41f2715d7d4d24cfa220197f1e2c778259 /tests
parentInitial commit. (diff)
downloadcryptsetup-upstream.tar.xz
cryptsetup-upstream.zip
Adding upstream version 2:2.1.0.upstream/2%2.1.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rwxr-xr-xtests/00modules-test28
-rw-r--r--tests/Makefile.am119
-rwxr-xr-xtests/align-test318
-rwxr-xr-xtests/align-test2340
-rw-r--r--tests/api-test-2.c3552
-rw-r--r--tests/api-test.c1917
-rw-r--r--tests/api_test.h116
-rw-r--r--tests/blkid-luks2-pv.img.xzbin0 -> 5236 bytes
-rwxr-xr-xtests/blockwise-compat375
-rwxr-xr-xtests/compat-test952
-rwxr-xr-xtests/compat-test2954
-rw-r--r--tests/compatimage.img.xzbin0 -> 66732 bytes
-rw-r--r--tests/compatimage2.img.xzbin0 -> 66356 bytes
-rw-r--r--tests/compatv10image.img.xzbin0 -> 66688 bytes
-rw-r--r--tests/conversion_imgs.tar.xzbin0 -> 8149472 bytes
-rw-r--r--tests/crypto-vectors.c632
-rw-r--r--tests/cryptsetup-valg-supps36
-rwxr-xr-xtests/device-test257
-rw-r--r--tests/differ.c166
-rwxr-xr-xtests/discards-test84
-rw-r--r--tests/evil_hdr-keyslot_overlap.xzbin0 -> 676 bytes
-rw-r--r--tests/evil_hdr-luks_hdr_damage.xzbin0 -> 612 bytes
-rw-r--r--tests/evil_hdr-payload_overwrite.xzbin0 -> 612 bytes
-rw-r--r--tests/evil_hdr-small_luks_device.xzbin0 -> 452 bytes
-rw-r--r--tests/evil_hdr-stripes_payload_dmg.xzbin0 -> 616 bytes
-rwxr-xr-xtests/generators/generate-luks2-area-in-json-hdr-space-json0.img.sh72
-rwxr-xr-xtests/generators/generate-luks2-argon2-leftover-params.img.sh71
-rwxr-xr-xtests/generators/generate-luks2-correct-full-json0.img.sh87
-rwxr-xr-xtests/generators/generate-luks2-corrupted-hdr0-with-correct-chks.img.sh65
-rwxr-xr-xtests/generators/generate-luks2-corrupted-hdr1-with-correct-chks.img.sh66
-rwxr-xr-xtests/generators/generate-luks2-invalid-checksum-both-hdrs.img.sh52
-rwxr-xr-xtests/generators/generate-luks2-invalid-checksum-hdr0.img.sh43
-rwxr-xr-xtests/generators/generate-luks2-invalid-checksum-hdr1.img.sh48
-rwxr-xr-xtests/generators/generate-luks2-invalid-json-size-c0.img.sh68
-rwxr-xr-xtests/generators/generate-luks2-invalid-json-size-c1.img.sh68
-rwxr-xr-xtests/generators/generate-luks2-invalid-json-size-c2.img.sh85
-rwxr-xr-xtests/generators/generate-luks2-invalid-keyslots-size-c0.img.sh71
-rwxr-xr-xtests/generators/generate-luks2-invalid-keyslots-size-c1.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-invalid-keyslots-size-c2.img.sh68
-rwxr-xr-xtests/generators/generate-luks2-invalid-object-type-json0.img.sh70
-rwxr-xr-xtests/generators/generate-luks2-invalid-opening-char-json0.img.sh70
-rwxr-xr-xtests/generators/generate-luks2-keyslot-missing-digest.img.sh72
-rwxr-xr-xtests/generators/generate-luks2-keyslot-too-many-digests.img.sh70
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-128k-secondary.img.sh97
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-128k.img.sh94
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-16k-secondary.img.sh97
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-1m-secondary.img.sh97
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-1m.img.sh94
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-256k-secondary.img.sh97
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-256k.img.sh94
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-2m-secondary.img.sh96
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-2m.img.sh94
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-32k-secondary.img.sh97
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-32k.img.sh94
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-4m-secondary.img.sh96
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-4m.img.sh94
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-512k-secondary.img.sh97
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-512k.img.sh94
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-64k-inv-area-c0.img.sh94
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-64k-inv-area-c1.img.sh96
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-64k-inv-keyslots-size-c0.img.sh96
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-64k-secondary.img.sh97
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-64k.img.sh94
-rwxr-xr-xtests/generators/generate-luks2-missing-keyslot-referenced-in-digest.img.sh74
-rwxr-xr-xtests/generators/generate-luks2-missing-keyslot-referenced-in-token.img.sh72
-rwxr-xr-xtests/generators/generate-luks2-missing-segment-referenced-in-digest.img.sh74
-rwxr-xr-xtests/generators/generate-luks2-missing-trailing-null-byte-json0.img.sh89
-rwxr-xr-xtests/generators/generate-luks2-non-null-byte-beyond-json0.img.sh72
-rwxr-xr-xtests/generators/generate-luks2-non-null-bytes-beyond-json0.img.sh76
-rwxr-xr-xtests/generators/generate-luks2-overlapping-areas-c0-json0.img.sh68
-rwxr-xr-xtests/generators/generate-luks2-overlapping-areas-c1-json0.img.sh70
-rwxr-xr-xtests/generators/generate-luks2-overlapping-areas-c2-json0.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-pbkdf2-leftover-params-0.img.sh71
-rwxr-xr-xtests/generators/generate-luks2-pbkdf2-leftover-params-1.img.sh71
-rwxr-xr-xtests/generators/generate-luks2-segment-crypt-missing-encryption.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-crypt-missing-ivoffset.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-crypt-missing-sectorsize.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-crypt-wrong-encryption.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-crypt-wrong-ivoffset.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-crypt-wrong-sectorsize-0.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-crypt-wrong-sectorsize-1.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-crypt-wrong-sectorsize-2.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-missing-offset.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-missing-size.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-missing-type.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-two.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-unknown-type.img.sh68
-rwxr-xr-xtests/generators/generate-luks2-segment-wrong-flags-element.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-wrong-flags.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-wrong-offset.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-wrong-size-0.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-wrong-size-1.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-wrong-size-2.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-segment-wrong-type.img.sh67
-rwxr-xr-xtests/generators/generate-luks2-uint64-max-segment-size.img.sh68
-rwxr-xr-xtests/generators/generate-luks2-uint64-overflow-segment-size.img.sh66
-rwxr-xr-xtests/generators/generate-luks2-uint64-signed-segment-size.img.sh67
-rw-r--r--tests/generators/lib.sh180
-rw-r--r--tests/img_fs_ext4.img.xzbin0 -> 5680 bytes
-rw-r--r--tests/img_fs_vfat.img.xzbin0 -> 6124 bytes
-rw-r--r--tests/img_fs_xfs.img.xzbin0 -> 7676 bytes
-rwxr-xr-xtests/integrity-compat-test363
-rwxr-xr-xtests/keyring-compat-test199
-rwxr-xr-xtests/keyring-test237
-rwxr-xr-xtests/loopaes-test174
-rwxr-xr-xtests/luks1-compat-test102
-rw-r--r--tests/luks1-images.tar.xzbin0 -> 67192 bytes
-rwxr-xr-xtests/luks2-integrity-test168
-rwxr-xr-xtests/luks2-validation-test231
-rw-r--r--tests/luks2_header_requirements.xzbin0 -> 130568 bytes
-rw-r--r--tests/luks2_header_requirements_free.xzbin0 -> 130492 bytes
-rw-r--r--tests/luks2_keyslot_unassigned.img.xzbin0 -> 270548 bytes
-rw-r--r--tests/luks2_mda_images.tar.xzbin0 -> 156028 bytes
-rw-r--r--tests/luks2_valid_hdr.img.xzbin0 -> 3684 bytes
-rwxr-xr-xtests/mode-test169
-rwxr-xr-xtests/password-hash-test188
-rwxr-xr-xtests/reencryption-compat-test385
-rwxr-xr-xtests/reencryption-compat-test2459
-rwxr-xr-xtests/tcrypt-compat-test162
-rw-r--r--tests/tcrypt-images.tar.xzbin0 -> 281088 bytes
-rw-r--r--tests/test_utils.c600
-rw-r--r--tests/unit-utils-io.c346
-rwxr-xr-xtests/valg-api.sh11
-rwxr-xr-xtests/valg.sh11
-rw-r--r--tests/valid_header_file.xzbin0 -> 608 bytes
-rwxr-xr-xtests/verity-compat-test439
126 files changed, 19738 insertions, 0 deletions
diff --git a/tests/00modules-test b/tests/00modules-test
new file mode 100755
index 0000000..ddf08b8
--- /dev/null
+++ b/tests/00modules-test
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+echo "Cryptsetup test environment ($(date))"
+uname -a
+
+if [ -f /etc/os-release ] ; then
+ source /etc/os-release
+ echo "$PRETTY_NAME ($NAME) $VERSION"
+fi
+
+[ -x ../cryptsetup ] && ../cryptsetup --version
+[ -x ../veritysetup ] && ../veritysetup --version
+[ -x ../integritysetup ] && ../integritysetup --version
+[ -x ../cryptsetup-reencrypt ] && ../cryptsetup-reencrypt --version
+
+[ $(id -u) != 0 ] && exit 77
+
+modprobe dm-crypt >/dev/null 2>&1
+modprobe dm-verity >/dev/null 2>&1
+modprobe dm-integrity >/dev/null 2>&1
+modprobe dm-zero >/dev/null 2>&1
+
+dmsetup version
+
+echo "Device mapper targets:"
+dmsetup targets
+
+exit 0
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..c74ee2b
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,119 @@
+TESTS = 00modules-test \
+ api-test \
+ api-test-2 \
+ compat-test \
+ compat-test2 \
+ loopaes-test \
+ align-test \
+ align-test2 \
+ discards-test \
+ mode-test \
+ password-hash-test \
+ tcrypt-compat-test \
+ luks1-compat-test \
+ device-test \
+ keyring-test \
+ keyring-compat-test \
+ luks2-validation-test \
+ luks2-integrity-test \
+ vectors-test \
+ blockwise-compat
+
+if VERITYSETUP
+TESTS += verity-compat-test
+endif
+
+if REENCRYPT
+TESTS += reencryption-compat-test reencryption-compat-test2
+
+endif
+
+if INTEGRITYSETUP
+TESTS += integrity-compat-test
+endif
+
+EXTRA_DIST = compatimage.img.xz compatv10image.img.xz \
+ compatimage2.img.xz \
+ conversion_imgs.tar.xz \
+ luks2_keyslot_unassigned.img.xz \
+ img_fs_ext4.img.xz img_fs_vfat.img.xz img_fs_xfs.img.xz \
+ valid_header_file.xz \
+ luks2_valid_hdr.img.xz \
+ luks2_header_requirements.xz \
+ luks2_header_requirements_free.xz \
+ luks2_mda_images.tar.xz \
+ evil_hdr-payload_overwrite.xz \
+ evil_hdr-stripes_payload_dmg.xz \
+ evil_hdr-luks_hdr_damage.xz \
+ evil_hdr-small_luks_device.xz \
+ evil_hdr-keyslot_overlap.xz \
+ tcrypt-images.tar.xz \
+ luks1-images.tar.xz \
+ 00modules-test \
+ compat-test \
+ compat-test2 \
+ loopaes-test align-test discards-test mode-test password-hash-test \
+ align-test2 verity-compat-test \
+ reencryption-compat-test \
+ reencryption-compat-test2 \
+ tcrypt-compat-test \
+ luks1-compat-test \
+ luks2-validation-test generators \
+ luks2-integrity-test \
+ device-test \
+ keyring-test \
+ keyring-compat-test \
+ integrity-compat-test \
+ cryptsetup-valg-supps valg.sh valg-api.sh \
+ blockwise-compat \
+ blkid-luks2-pv.img.xz
+
+CLEANFILES = cryptsetup-tst* valglog* *-fail-*.log
+clean-local:
+ -rm -rf tcrypt-images luks1-images luks2-images conversion_imgs luks2_valid_hdr.img blkid-luks2-pv-img blkid-luks2-pv-img.bcp
+
+differ_SOURCES = differ.c
+differ_CFLAGS = $(AM_CFLAGS) -Wall -O2
+
+api_test_SOURCES = api-test.c api_test.h test_utils.c
+api_test_LDADD = ../libcryptsetup.la
+api_test_LDFLAGS = $(AM_LDFLAGS) -static
+api_test_CFLAGS = -g -Wall -O0 $(AM_CFLAGS) -I$(top_srcdir)/lib/ -I$(top_srcdir)/lib/luks1
+api_test_CPPFLAGS = $(AM_CPPFLAGS) -include config.h
+
+api_test_2_SOURCES = api-test-2.c api_test.h test_utils.c
+api_test_2_LDADD = ../libcryptsetup.la
+api_test_2_LDFLAGS = $(AM_LDFLAGS) -static
+api_test_2_CFLAGS = -g -Wall -O0 $(AM_CFLAGS) -I$(top_srcdir)/lib/ -I$(top_srcdir)/lib/luks1
+api_test_2_CPPFLAGS = $(AM_CPPFLAGS) -include config.h
+
+vectors_test_SOURCES = crypto-vectors.c
+vectors_test_LDADD = ../libcrypto_backend.la @CRYPTO_LIBS@ @LIBARGON2_LIBS@
+vectors_test_LDFLAGS = $(AM_LDFLAGS) -static
+vectors_test_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/lib/crypto_backend/ @CRYPTO_CFLAGS@
+vectors_test_CPPFLAGS = $(AM_CPPFLAGS) -include config.h
+
+unit_utils_io_SOURCES = unit-utils-io.c
+unit_utils_io_LDADD = ../libutils_io.la
+unit_utils_io_LDFLAGS = $(AM_LDFLAGS) -static
+unit_utils_io_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/lib
+unit_utils_io_CPPFLAGS = $(AM_CPPFLAGS) -include config.h
+
+check_PROGRAMS = api-test api-test-2 differ vectors-test unit-utils-io
+
+conversion_imgs:
+ @tar xJf conversion_imgs.tar.xz
+
+compatimage.img:
+ @xz -k -d compatimage.img.xz
+
+valgrind-check: api-test api-test-2 differ
+ @VALG=1 ./compat-test
+ @VALG=1 ./compat-test2
+ @VALG=1 ./luks2-validation-test
+ @VALG=1 ./verity-compat-test
+ @VALG=1 ./integrity-compat-test
+ @INFOSTRING="api-test-000" ./valg-api.sh ./api-test
+ @INFOSTRING="api-test-002" ./valg-api.sh ./api-test-2
+
+.PHONY: valgrind-check
diff --git a/tests/align-test b/tests/align-test
new file mode 100755
index 0000000..55437ec
--- /dev/null
+++ b/tests/align-test
@@ -0,0 +1,318 @@
+#!/bin/bash
+
+CRYPTSETUP="../cryptsetup"
+DEV=""
+DEV_STACKED="luks0xbabe"
+DEV_NAME="dummyalign"
+MNT_DIR="./mnt_luks"
+PWD1="93R4P4pIqAH8"
+PWD2="mymJeD8ivEhE"
+FAST_PBKDF="--pbkdf-force-iterations 1000"
+
+cleanup() {
+ udevadm settle >/dev/null 2>&1
+ if [ -d "$MNT_DIR" ] ; then
+ umount -f $MNT_DIR 2>/dev/null
+ rmdir $MNT_DIR 2>/dev/null
+ fi
+ [ -b /dev/mapper/$DEV_STACKED ] && dmsetup remove --retry $DEV_STACKED >/dev/null 2>&1
+ [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove --retry $DEV_NAME >/dev/null 2>&1
+ # FIXME scsi_debug sometimes in-use here
+ sleep 1
+ rmmod scsi_debug 2>/dev/null
+ sleep 1
+}
+
+fail()
+{
+ if [ -n "$1" ] ; then echo "FAIL $1" ; fi
+ echo "FAILED at line $(caller)"
+ cleanup
+ exit 100
+}
+
+skip()
+{
+ echo "TEST SKIPPED: $1"
+ cleanup
+ exit 0
+}
+
+function 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 -o \( $VER_MIN -eq 14 -a $VER_PTC -ge 5 \) ]; then
+ DM_SECTOR_SIZE=1
+ fi
+}
+
+add_device() {
+ modprobe scsi_debug $@ delay=0
+ if [ $? -ne 0 ] ; then
+ echo "This kernel seems to not support proper scsi_debug module, test skipped."
+ exit 77
+ fi
+
+ sleep 2
+ DEV=$(grep -l -e scsi_debug /sys/block/*/device/model | cut -f4 -d /)
+
+ if [ ! -e /sys/block/$DEV/alignment_offset ] ; then
+ echo "This kernel seems to not support topology info, test skipped."
+ cleanup
+ exit 77
+ fi
+
+ DEV="/dev/$DEV"
+ [ -b $DEV ] || fail "Cannot find $DEV."
+}
+
+format() # key_bits expected [forced]
+{
+ if [ -z "$3" ] ; then
+ echo -n "Formatting using topology info ($1 bits key)..."
+ echo $PWD1 | $CRYPTSETUP luksFormat --type luks1 $DEV -q $FAST_PBKDF -c aes-cbc-essiv:sha256 -s $1 || fail
+ else
+ echo -n "Formatting using forced sector alignment $3 ($1 bits key)..."
+ echo $PWD1 | $CRYPTSETUP luksFormat --type luks1 $DEV -q $FAST_PBKDF -s $1 -c aes-cbc-essiv:sha256 --align-payload=$3 ||fail
+ fi
+
+ # check the device can be activated
+ echo $PWD1 | $CRYPTSETUP luksOpen $DEV $DEV_NAME || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+
+ ALIGN=$($CRYPTSETUP luksDump $DEV |grep "Payload offset" | sed -e s/.*\\t//)
+ #echo "ALIGN = $ALIGN"
+
+ [ -z "$ALIGN" ] && fail
+ [ $ALIGN -ne $2 ] && fail "Expected alignment differs: expected $2 != detected $ALIGN"
+
+ # test some operation, just in case
+ echo -e "$PWD1\n$PWD2" | $CRYPTSETUP luksAddKey $DEV $FAST_PBKDF --key-slot 1
+ [ $? -ne 0 ] && fail "Keyslot add failed."
+
+ $CRYPTSETUP -q luksKillSlot $DEV 1
+ [ $? -ne 0 ] && fail "Keyslot removal failed."
+
+ echo "PASSED"
+}
+
+get_offsets()
+{
+ $CRYPTSETUP luksDump $DEV | grep "$1" | cut -s -d ':' -f 2 | sed -e 's/\s//g' -e :a -e N -e 's/\n/:/g' -e 's/\s//g' -e ta
+}
+
+format_null()
+{
+ if [ $3 -eq 0 ] ; then
+ echo -n "Formatting using topology info ($1 bits key) [slot 0"
+ echo | $CRYPTSETUP luksFormat --type luks1 $DEV -q $FAST_PBKDF -c null -s $1 || fail
+ else
+ echo -n "Formatting using forced sector alignment $3 ($1 bits key) [slot 0"
+ echo | $CRYPTSETUP luksFormat --type luks1 $DEV -q $FAST_PBKDF -c null -s $1 --align-payload=$3 || fail
+ fi
+
+ # check the device can be activated
+ echo | $CRYPTSETUP luksOpen $DEV $DEV_NAME || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+
+ POFF=$(get_offsets "Payload offset")
+ [ -z "$POFF" ] && fail
+ [ $POFF != $2 ] && fail "Expected data offset differs: expected $2 != detected $POFF"
+ if [ -n "$4" ] ; then
+ for j in 1 2 3 4 5 6 7 ; do
+ echo -e "\n" | $CRYPTSETUP luksAddKey $DEV -q $FAST_PBKDF --key-slot $j -c null $PARAMS
+ echo -n $j
+ [ $? -ne 0 ] && fail
+ done
+
+ KOFF=$(get_offsets "Key material offset")
+ [ -z "$KOFF" ] && fail
+ [ $KOFF != $4 ] && fail "Expected keyslots offsets differ: expected $4 != detected $KOFF"
+ fi
+
+ echo "]...PASSED"
+}
+
+format_plain() # sector size
+{
+ echo -n "Formatting plain device (sector size $1)..."
+ if [ -n "$DM_SECTOR_SIZE" ] ; then
+ echo $PWD1 | $CRYPTSETUP open --type plain --hash sha256 --sector-size $1 $DEV $DEV_NAME || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+ echo "PASSED"
+ else
+ echo "N/A"
+ fi
+}
+
+format_plain_fail() # sector size
+{
+ echo -n "Formatting plain device (sector size $1, must fail)..."
+ if [ -n "$DM_SECTOR_SIZE" ] ; then
+ echo $PWD1 | $CRYPTSETUP open --type plain --hash sha256 --sector-size $1 $DEV $DEV_NAME >/dev/null 2>&1 && fail
+ echo "PASSED"
+ else
+ echo "N/A"
+ fi
+}
+
+if [ $(id -u) != 0 ]; then
+ echo "WARNING: You must be root to run this test, test skipped."
+ exit 77
+fi
+
+dm_crypt_features
+modprobe --dry-run scsi_debug || exit 77
+cleanup
+
+echo "# Create desktop-class 4K drive"
+echo "# (logical_block_size=512, physical_block_size=4096, alignment_offset=0)"
+add_device dev_size_mb=16 sector_size=512 physblk_exp=3 num_tgts=1
+format 256 4096
+format 256 2056 8
+format 128 2048
+format 128 1032 8
+format 256 8192 8192
+format 128 8192 8192
+cleanup
+
+echo "# Create desktop-class 4K drive with misaligned opt-io (some bad USB enclosures)"
+echo "# (logical_block_size=512, physical_block_size=4096, alignment_offset=0, opt-io=1025)"
+add_device dev_size_mb=16 sector_size=512 physblk_exp=3 num_tgts=1 opt_blks=1025
+format 256 4096
+format 256 2056 8
+format 128 2048
+format 128 1032 8
+format 256 8192 8192
+format 128 8192 8192
+cleanup
+
+echo "# Create desktop-class 4K drive w/ 63-sector DOS partition compensation"
+echo "# (logical_block_size=512, physical_block_size=4096, alignment_offset=3584)"
+add_device dev_size_mb=16 sector_size=512 physblk_exp=3 lowest_aligned=7 num_tgts=1
+format 256 4103
+format 256 2056 8
+format 128 2055
+format 128 1032 8
+cleanup
+
+echo "# Create enterprise-class 4K drive"
+echo "# (logical_block_size=4096, physical_block_size=4096, alignment_offset=0)"
+add_device dev_size_mb=16 sector_size=4096 num_tgts=1 opt_blks=64
+format 256 4096
+format 256 2056 8
+format 128 2048
+format 128 1032 8
+cleanup
+
+echo "# Create classic 512B drive and stack dm-linear"
+echo "# (logical_block_size=512, physical_block_size=512, alignment_offset=0)"
+add_device dev_size_mb=16 sector_size=512 num_tgts=1
+DEV2=$DEV
+DEV=/dev/mapper/$DEV_STACKED
+dmsetup create $DEV_STACKED --table "0 32768 linear $DEV2 0"
+format 256 4096
+format 256 2056 8
+format 128 2048
+format 128 1032 8
+format 128 8192 8192
+cleanup
+
+echo "# Create classic 512B drive and stack dm-linear (plain mode)"
+add_device dev_size_mb=16 sector_size=512 num_tgts=1
+DEV2=$DEV
+DEV=/dev/mapper/$DEV_STACKED
+dmsetup create $DEV_STACKED --table "0 32768 linear $DEV2 0"
+format_plain 512
+format_plain 1024
+format_plain 2048
+format_plain 4096
+format_plain_fail 1111
+format_plain_fail 8192
+echo "# Create classic 512B drive, unaligned to 4096 and stack dm-linear (plain mode)"
+dmsetup remove --retry $DEV_STACKED >/dev/null 2>&1
+dmsetup create $DEV_STACKED --table "0 32762 linear $DEV2 0"
+format_plain 512
+format_plain 1024
+format_plain_fail 2048
+format_plain_fail 4096
+cleanup
+
+echo "# Offset check: 512B sector drive"
+add_device dev_size_mb=16 sector_size=512 num_tgts=1
+# |k| expO reqO expected slot offsets
+format_null 64 2048 0 8:72:136:200:264:328:392:456
+format_null 64 520 1
+format_null 64 520 8
+format_null 64 640 128
+format_null 64 2048 2048
+format_null 128 2048 0 8:136:264:392:520:648:776:904
+format_null 128 1032 1
+format_null 128 1032 8
+format_null 128 1152 128
+format_null 128 2048 2048
+format_null 256 4096 0 8:264:520:776:1032:1288:1544:1800
+format_null 256 2056 1
+format_null 256 2056 8
+format_null 256 2176 128
+format_null 256 4096 2048
+format_null 512 4096 0 8:512:1016:1520:2024:2528:3032:3536
+format_null 512 4040 1
+format_null 512 4040 8
+format_null 512 4096 128
+format_null 512 4096 2048
+cleanup
+
+echo "# Offset check: 4096B sector drive"
+add_device dev_size_mb=16 sector_size=4096 num_tgts=1 opt_blks=64
+format_null 64 2048 0 8:72:136:200:264:328:392:456
+format_null 64 520 1
+format_null 64 520 8
+format_null 64 640 128
+format_null 64 2048 2048
+format_null 128 2048 0 8:136:264:392:520:648:776:904
+format_null 128 1032 1
+format_null 128 1032 8
+format_null 128 1152 128
+format_null 128 2048 2048
+format_null 256 4096 0 8:264:520:776:1032:1288:1544:1800
+format_null 256 2056 1
+format_null 256 2056 8
+format_null 256 2176 128
+format_null 256 4096 2048
+format_null 512 4096 0 8:512:1016:1520:2024:2528:3032:3536
+format_null 512 4040 1
+format_null 512 4040 8
+format_null 512 4096 128
+format_null 512 4096 2048
+cleanup
+
+echo "# Create enterprise-class 4K drive with fs and LUKS images."
+# loop device here presents 512 block but images have 4k block
+# cryptsetup should properly use 4k block on direct-io
+add_device dev_size_mb=32 sector_size=4096 physblk_exp=0 num_tgts=1 opt_blks=64
+for file in $(ls img_fs_*.img.xz) ; do
+ echo "Format using fs image $file."
+ xz -d -c $file | dd of=$DEV bs=1M 2>/dev/null || fail "bad image"
+ [ ! -d $MNT_DIR ] && mkdir $MNT_DIR
+ mount $DEV $MNT_DIR || skip "Mounting image is not available."
+ echo $PWD1 | $CRYPTSETUP luksFormat --type luks1 --key-size 256 $FAST_PBKDF $MNT_DIR/luks.img || fail
+ echo $PWD2 | $CRYPTSETUP luksFormat --type luks1 --key-size 256 $FAST_PBKDF $MNT_DIR/luks.img --header $MNT_DIR/luks_header.img || fail
+ umount $MNT_DIR
+done
+cleanup
diff --git a/tests/align-test2 b/tests/align-test2
new file mode 100755
index 0000000..bedc3dd
--- /dev/null
+++ b/tests/align-test2
@@ -0,0 +1,340 @@
+#!/bin/bash
+
+CRYPTSETUP="../cryptsetup"
+DEV=""
+DEV_STACKED="luks0xbabe"
+DEV_NAME="dummyalign"
+MNT_DIR="./mnt_luks"
+PWD1="93R4P4pIqAH8"
+PWD2="mymJeD8ivEhE"
+FAST_PBKDF="--pbkdf pbkdf2 --pbkdf-force-iterations 1000"
+
+cleanup() {
+ udevadm settle >/dev/null 2>&1
+ if [ -d "$MNT_DIR" ] ; then
+ umount -f $MNT_DIR 2>/dev/null
+ rmdir $MNT_DIR 2>/dev/null
+ fi
+ [ -b /dev/mapper/$DEV_STACKED ] && dmsetup remove --retry $DEV_STACKED >/dev/null 2>&1
+ [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove --retry $DEV_NAME >/dev/null 2>&1
+ # FIXME scsi_debug sometimes in-use here
+ sleep 1
+ rmmod scsi_debug 2>/dev/null
+ sleep 1
+}
+
+fail()
+{
+ if [ -n "$1" ] ; then echo "FAIL $1" ; fi
+ echo "FAILED at line $(caller)"
+ cleanup
+ exit 100
+}
+
+skip()
+{
+ echo "TEST SKIPPED: $1"
+ cleanup
+ exit 0
+}
+
+function 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 -o \( $VER_MIN -eq 14 -a $VER_PTC -ge 5 \) ]; then
+ DM_SECTOR_SIZE=1
+ fi
+}
+
+add_device() {
+ modprobe scsi_debug $@ delay=0
+ if [ $? -ne 0 ] ; then
+ echo "This kernel seems to not support proper scsi_debug module, test skipped."
+ exit 77
+ fi
+
+ sleep 2
+ DEV=$(grep -l -e scsi_debug /sys/block/*/device/model | cut -f4 -d /)
+
+ if [ ! -e /sys/block/$DEV/alignment_offset ] ; then
+ echo "This kernel seems to not support topology info, test skipped."
+ cleanup
+ exit 77
+ fi
+
+ DEV="/dev/$DEV"
+ [ -b $DEV ] || fail "Cannot find $DEV."
+}
+
+format() # expected [forced] [encryption_sector_size]
+{
+ local _sec_size=512
+
+ local _exp=$1
+
+ if [ "${2:0:1}" = "s" ]; then
+ _sec_size=${2:1}
+ shift
+ fi
+
+ test "${3:0:1}" = "s" && _sec_size=${3:1}
+
+ test $_sec_size -eq 512 || local _smsg=" (encryption sector size $_sec_size)"
+
+ if [ -z "$2" ] ; then
+ echo -n "Formatting using topology info$_smsg..."
+ echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF --type luks2 $DEV -q -c aes-cbc-essiv:sha256 --sector-size $_sec_size >/dev/null || fail
+ else
+ echo -n "Formatting using forced sector alignment $2$_smsg..."
+ echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF --type luks2 $DEV -q -c aes-cbc-essiv:sha256 --align-payload=$2 --sector-size $_sec_size >/dev/null || fail
+ fi
+
+ # check the device can be activated
+ if [ -n "$DM_SECTOR_SIZE" ] ; then
+ echo $PWD1 | $CRYPTSETUP luksOpen $DEV $DEV_NAME || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+ fi
+
+ ALIGN=$($CRYPTSETUP luksDump $DEV | tee /tmp/last_dump | grep -A1 "0: crypt" | grep "offset:" | cut -d ' ' -f2)
+ # echo "ALIGN = $ALIGN"
+
+ [ -z "$ALIGN" ] && fail
+ ALIGN=$((ALIGN/512))
+ [ $ALIGN -ne $_exp ] && fail "Expected alignment differs: expected $_exp != detected $ALIGN"
+
+ # test some operation, just in case
+ echo -e "$PWD1\n$PWD2" | $CRYPTSETUP luksAddKey $DEV $FAST_PBKDF --key-slot 1
+ [ $? -ne 0 ] && fail "Keyslot add failed."
+
+ $CRYPTSETUP -q luksKillSlot $DEV 1
+ [ $? -ne 0 ] && fail "Keyslot removal failed."
+
+ echo "PASSED"
+}
+
+format_fail() # expected [forced] [encryption_sector_size]
+{
+ local _sec_size=512
+
+ local _exp=$1
+
+ if [ "${2:0:1}" = "s" ]; then
+ _sec_size=${2:1}
+ shift
+ fi
+
+ test "${3:0:1}" = "s" && _sec_size=${3:1}
+
+ test $_sec_size -eq 512 || local _smsg=" (encryption sector size $_sec_size)"
+
+ if [ -z "$2" ] ; then
+ echo -n "Formatting using topology info$_smsg (must fail)..."
+ echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF --type luks2 $DEV -q -c aes-cbc-essiv:sha256 --sector-size $_sec_size >/dev/null 2>&1 && fail
+ else
+ echo -n "Formatting using forced sector alignment $2$_smsg (must fail)..."
+ echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF --type luks2 $DEV -q -c aes-cbc-essiv:sha256 --align-payload=$2 --sector-size $_sec_size >/dev/null 2>&1 && fail
+ fi
+
+ echo "PASSED"
+}
+
+if [ $(id -u) != 0 ]; then
+ echo "WARNING: You must be root to run this test, test skipped."
+ exit 77
+fi
+
+dm_crypt_features
+modprobe --dry-run scsi_debug || exit 77
+cleanup
+
+add_device dev_size_mb=32
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF --type luks2 $DEV -q >/dev/null || fail
+EXPCT=$($CRYPTSETUP luksDump $DEV | grep "offset: " | cut -f 2 -d ' ')
+test "$EXPCT" -gt 512 || fail
+EXPCT=$((EXPCT/512))
+echo "Default alignment detected: $EXPCT sectors"
+cleanup
+
+echo "# Create desktop-class 4K drive"
+echo "# (logical_block_size=512, physical_block_size=4096, alignment_offset=0)"
+add_device dev_size_mb=32 sector_size=512 physblk_exp=3 num_tgts=1
+format $EXPCT
+format $EXPCT s1024
+format $EXPCT s2048
+format $EXPCT s4096
+format $EXPCT 1
+format $EXPCT 1 s1024
+format $EXPCT 1 s2048
+format $EXPCT 1 s4096
+format $EXPCT 8
+format $EXPCT 8 s1024
+format $EXPCT 8 s2048
+format $EXPCT 8 s4096
+format $((EXPCT+1)) $((EXPCT+1))
+format_fail $((EXPCT+1)) $((EXPCT+1)) s1024
+format_fail $((EXPCT+1)) $((EXPCT+1)) s2048
+format_fail $((EXPCT+1)) $((EXPCT+1)) s4096
+format $EXPCT $EXPCT
+format $EXPCT $EXPCT s1024
+format $EXPCT $EXPCT s2048
+format $EXPCT $EXPCT s4096
+cleanup
+
+echo "# Create desktop-class 4K drive with misaligned opt-io (some bad USB enclosures)"
+echo "# (logical_block_size=512, physical_block_size=4096, alignment_offset=0, opt-io=1025)"
+add_device dev_size_mb=32 sector_size=512 physblk_exp=3 num_tgts=1 opt_blks=1025
+format $EXPCT
+format $EXPCT s1024
+format $EXPCT s2048
+format $EXPCT s4096
+format $EXPCT 1
+format $EXPCT 1 s1024
+format $EXPCT 1 s2048
+format $EXPCT 1 s4096
+format $EXPCT 8
+format $EXPCT 8 s1024
+format $EXPCT 8 s2048
+format $EXPCT 8 s4096
+format $((EXPCT+1)) $((EXPCT+1))
+format_fail $((EXPCT+1)) $((EXPCT+1)) s1024
+format_fail $((EXPCT+1)) $((EXPCT+1)) s2048
+format_fail $((EXPCT+1)) $((EXPCT+1)) s4096
+format $EXPCT $EXPCT
+format $EXPCT $EXPCT s1024
+format $EXPCT $EXPCT s2048
+format $EXPCT $EXPCT s4096
+cleanup
+
+echo "# Create desktop-class 4K drive w/ 1-sector shift (original bug report)"
+echo "# (logical_block_size=512, physical_block_size=4096, alignment_offset=512)"
+add_device dev_size_mb=32 sector_size=512 physblk_exp=3 lowest_aligned=1 num_tgts=1
+format $((EXPCT+1))
+format_fail $((EXPCT+1)) s1024
+format_fail $((EXPCT+1)) s2048
+format_fail $((EXPCT+1)) s4096
+format $EXPCT 1
+format $EXPCT 1 s1024
+format $EXPCT 1 s2048
+format $EXPCT 1 s4096
+format $EXPCT 8
+format $EXPCT 8 s1024
+format $EXPCT 8 s2048
+format $EXPCT 8 s4096
+format $((EXPCT+1)) $((EXPCT+1))
+format_fail $((EXPCT+1)) $((EXPCT+1)) s1024
+format_fail $((EXPCT+1)) $((EXPCT+1)) s2048
+format_fail $((EXPCT+1)) $((EXPCT+1)) s4096
+format $EXPCT $EXPCT
+format $EXPCT $EXPCT s1024
+format $EXPCT $EXPCT s2048
+format $EXPCT $EXPCT s4096
+cleanup
+
+echo "# Create desktop-class 4K drive w/ 63-sector DOS partition compensation"
+echo "# (logical_block_size=512, physical_block_size=4096, alignment_offset=3584)"
+add_device dev_size_mb=32 sector_size=512 physblk_exp=3 lowest_aligned=7 num_tgts=1
+format $((EXPCT+7))
+format_fail $((EXPCT+7)) s1024
+format_fail $((EXPCT+7)) s2048
+format_fail $((EXPCT+7)) s4096
+format $EXPCT 1
+format $EXPCT 1 s1024
+format $EXPCT 1 s2048
+format $EXPCT 1 s4096
+format $EXPCT 8
+format $EXPCT 8 s1024
+format $EXPCT 8 s2048
+format $EXPCT 8 s4096
+format $((EXPCT+1)) $((EXPCT+1))
+format_fail $((EXPCT+1)) $((EXPCT+1)) s1024
+format_fail $((EXPCT+1)) $((EXPCT+1)) s2048
+format_fail $((EXPCT+1)) $((EXPCT+1)) s4096
+format $EXPCT $EXPCT
+format $EXPCT $EXPCT s1024
+format $EXPCT $EXPCT s2048
+format $EXPCT $EXPCT s4096
+cleanup
+
+echo "# Create enterprise-class 4K drive"
+echo "# (logical_block_size=4096, physical_block_size=4096, alignment_offset=0)"
+add_device dev_size_mb=32 sector_size=4096 num_tgts=1 opt_blks=64
+format $EXPCT
+format $EXPCT s1024
+format $EXPCT s2048
+format $EXPCT s4096
+format $EXPCT 1
+format $EXPCT 1 s1024
+format $EXPCT 1 s2048
+format $EXPCT 1 s4096
+format $EXPCT 8
+format $EXPCT 8 s1024
+format $EXPCT 8 s2048
+format $EXPCT 8 s4096
+#FIXME: kernel limits issue?
+##format $((EXPCT+1)) $((EXPCT+1))
+format_fail $((EXPCT+1)) $((EXPCT+1)) s1024
+format_fail $((EXPCT+1)) $((EXPCT+1)) s2048
+format_fail $((EXPCT+1)) $((EXPCT+1)) s4096
+format $EXPCT $EXPCT
+format $EXPCT $EXPCT s1024
+format $EXPCT $EXPCT s2048
+format $EXPCT $EXPCT s4096
+cleanup
+
+echo "# Create classic 512B drive and stack dm-linear"
+echo "# (logical_block_size=512, physical_block_size=512, alignment_offset=0)"
+add_device dev_size_mb=32 sector_size=512 num_tgts=1
+DEV2=$DEV
+DEV=/dev/mapper/$DEV_STACKED
+dmsetup create $DEV_STACKED --table "0 65536 linear $DEV2 0"
+format $EXPCT
+format $EXPCT s1024
+format $EXPCT s2048
+format $EXPCT s4096
+format $EXPCT 1
+format $EXPCT 1 s1024
+format $EXPCT 1 s2048
+format $EXPCT 1 s4096
+format $EXPCT 8
+format $EXPCT 8 s1024
+format $EXPCT 8 s2048
+format $EXPCT 8 s4096
+format $((EXPCT+1)) $((EXPCT+1))
+format_fail $((EXPCT+1)) $((EXPCT+1)) s1024
+format_fail $((EXPCT+1)) $((EXPCT+1)) s2048
+format_fail $((EXPCT+1)) $((EXPCT+1)) s4096
+format $EXPCT $EXPCT
+format $EXPCT $EXPCT s1024
+format $EXPCT $EXPCT s2048
+format $EXPCT $EXPCT s4096
+cleanup
+
+echo "# Create enterprise-class 4K drive with fs and LUKS images."
+# loop device here presents 512 block but images have 4k block
+# cryptsetup should properly use 4k block on direct-io
+add_device dev_size_mb=32 sector_size=4096 physblk_exp=0 num_tgts=1 opt_blks=64
+for file in $(ls img_fs_*.img.xz) ; do
+ echo "Format using fs image $file."
+ xz -d -c $file | dd of=$DEV bs=1M 2>/dev/null || fail "bad image"
+ [ ! -d $MNT_DIR ] && mkdir $MNT_DIR
+ mount $DEV $MNT_DIR || skip "Mounting image is not available."
+ echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF --type luks2 $MNT_DIR/luks.img --offset 8192 || fail
+ echo $PWD2 | $CRYPTSETUP luksFormat $FAST_PBKDF --type luks2 $MNT_DIR/luks.img --header $MNT_DIR/luks_header.img || fail
+ umount $MNT_DIR
+done
+cleanup
diff --git a/tests/api-test-2.c b/tests/api-test-2.c
new file mode 100644
index 0000000..36d8016
--- /dev/null
+++ b/tests/api-test-2.c
@@ -0,0 +1,3552 @@
+/*
+ * cryptsetup library LUKS2 API check functions
+ *
+ * Copyright (C) 2009-2019 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2019 Milan Broz
+ * Copyright (C) 2016-2019 Ondrej Kozina
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#ifdef KERNEL_KEYRING
+#include <linux/keyctl.h>
+#include <sys/syscall.h>
+#ifndef HAVE_KEY_SERIAL_T
+#define HAVE_KEY_SERIAL_T
+#include <stdint.h>
+typedef int32_t key_serial_t;
+#endif
+#endif
+
+#include "api_test.h"
+#include "luks.h"
+#include "libcryptsetup.h"
+
+#define DMDIR "/dev/mapper/"
+
+#define DEVICE_1_UUID "28632274-8c8a-493f-835b-da802e1c576b"
+#define DEVICE_EMPTY_name "crypt_zero"
+#define DEVICE_EMPTY DMDIR DEVICE_EMPTY_name
+#define DEVICE_ERROR_name "crypt_error"
+#define DEVICE_ERROR DMDIR DEVICE_ERROR_name
+
+#define CDEVICE_1 "ctest1"
+#define CDEVICE_2 "ctest2"
+#define CDEVICE_WRONG "O_o"
+#define H_DEVICE "head_ok"
+#define H_DEVICE_WRONG "head_wr"
+#define L_DEVICE_1S "luks_onesec"
+#define L_DEVICE_0S "luks_zerosec"
+#define L_DEVICE_WRONG "luks_wr"
+#define L_DEVICE_OK "luks_ok"
+#define REQS_LUKS2_HEADER "luks2_header_requirements"
+#define NO_REQS_LUKS2_HEADER "luks2_header_requirements_free"
+#define BACKUP_FILE "csetup_backup_file"
+#define IMAGE1 "compatimage2.img"
+#define IMAGE_EMPTY "empty.img"
+#define IMAGE_PV_LUKS2_SEC "blkid-luks2-pv.img"
+
+#define KEYFILE1 "key1.file"
+#define KEY1 "compatkey"
+
+#define KEYFILE2 "key2.file"
+#define KEY2 "0123456789abcdef"
+
+#define PASSPHRASE "blabla"
+#define PASSPHRASE1 "albalb"
+
+#define DEVICE_TEST_UUID "12345678-1234-1234-1234-123456789abc"
+
+#define DEVICE_WRONG "/dev/Ooo_"
+#define DEVICE_CHAR "/dev/zero"
+#define THE_LFILE_TEMPLATE "cryptsetup-tstlp.XXXXXX"
+
+#define KEY_DESC_TEST0 "cs_token_test:test_key0"
+#define KEY_DESC_TEST1 "cs_token_test:test_key1"
+
+#define CONV_DIR "conversion_imgs"
+#define CONV_L1_128 "l1_128b"
+#define CONV_L1_256 "l1_256b"
+#define CONV_L1_512 "l1_512b"
+#define CONV_L2_128 "l2_128b"
+#define CONV_L2_128_FULL "l2_128b_full"
+#define CONV_L2_256 "l2_256b"
+#define CONV_L2_256_FULL "l2_256b_full"
+#define CONV_L2_512 "l2_512b"
+#define CONV_L2_512_FULL "l2_512b_full"
+#define CONV_L1_128_DET "l1_128b_det"
+#define CONV_L1_256_DET "l1_256b_det"
+#define CONV_L1_512_DET "l1_512b_det"
+#define CONV_L2_128_DET "l2_128b_det"
+#define CONV_L2_128_DET_FULL "l2_128b_det_full"
+#define CONV_L2_256_DET "l2_256b_det"
+#define CONV_L2_256_DET_FULL "l2_256b_det_full"
+#define CONV_L2_512_DET "l2_512b_det"
+#define CONV_L2_512_DET_FULL "l2_512b_det_full"
+#define CONV_L1_256_LEGACY "l1_256b_legacy_offset"
+#define CONV_L1_256_UNMOVABLE "l1_256b_unmovable"
+#define PASS0 "aaa"
+#define PASS1 "hhh"
+#define PASS2 "ccc"
+#define PASS3 "ddd"
+#define PASS4 "eee"
+#define PASS5 "fff"
+#define PASS6 "ggg"
+#define PASS7 "bbb"
+#define PASS8 "iii"
+
+static int _fips_mode = 0;
+
+static char *DEVICE_1 = NULL;
+static char *DEVICE_2 = NULL;
+static char *DEVICE_3 = NULL;
+static char *DEVICE_4 = NULL;
+static char *DEVICE_5 = NULL;
+static char *DEVICE_6 = NULL;
+
+static char *tmp_file_1 = NULL;
+static char *test_loop_file = NULL;
+
+// Helpers
+
+static unsigned cpus_online(void)
+{
+ static long r = -1;
+
+ if (r < 0) {
+ r = sysconf(_SC_NPROCESSORS_ONLN);
+ if (r < 0)
+ r = 1;
+ }
+
+ return r;
+}
+
+static uint32_t adjusted_pbkdf_memory(void)
+{
+ long pagesize = sysconf(_SC_PAGESIZE);
+ long pages = sysconf(_SC_PHYS_PAGES);
+ uint64_t memory_kb;
+
+ if (pagesize <= 0 || pages <= 0)
+ return DEFAULT_LUKS2_MEMORY_KB;
+
+ memory_kb = pagesize / 1024 * pages / 2;
+
+ if (memory_kb < DEFAULT_LUKS2_MEMORY_KB)
+ return (uint32_t)memory_kb;
+
+ return DEFAULT_LUKS2_MEMORY_KB;
+}
+
+static unsigned _min(unsigned a, unsigned b)
+{
+ return a < b ? a : b;
+}
+
+static int get_luks2_offsets(int metadata_device,
+ unsigned int alignpayload_sec,
+ unsigned int sector_size,
+ uint64_t *r_header_size,
+ uint64_t *r_payload_offset)
+{
+ struct crypt_device *cd = NULL;
+ static uint64_t default_header_size = 0;
+
+
+ if (!default_header_size) {
+ if (crypt_init(&cd, THE_LOOP_DEV))
+ return -EINVAL;
+ if (crypt_format(cd, CRYPT_LUKS2, "aes", "xts-plain64", NULL, NULL, 64, NULL)) {
+ crypt_free(cd);
+ return -EINVAL;
+ }
+
+ default_header_size = crypt_get_data_offset(cd);
+
+ crypt_free(cd);
+ }
+
+ if (!sector_size)
+ sector_size = 512; /* default? */
+
+ if ((sector_size % 512) && (sector_size % 4096))
+ return -1;
+
+ if (r_payload_offset) {
+ if (metadata_device)
+ *r_payload_offset = DIV_ROUND_UP_MODULO(default_header_size * 512, (alignpayload_sec ?: 1) * sector_size);
+ else
+ *r_payload_offset = alignpayload_sec * sector_size;
+
+ *r_payload_offset /= sector_size;
+ }
+
+ if (r_header_size)
+ *r_header_size = default_header_size;
+
+ return 0;
+}
+
+static void _remove_keyfiles(void)
+{
+ remove(KEYFILE1);
+ remove(KEYFILE2);
+}
+
+#if HAVE_DECL_DM_TASK_RETRY_REMOVE
+#define DM_RETRY "--retry "
+#else
+#define DM_RETRY ""
+#endif
+
+static void _cleanup_dmdevices(void)
+{
+ struct stat st;
+
+ if (!stat(DMDIR H_DEVICE, &st))
+ _system("dmsetup remove " DM_RETRY H_DEVICE, 0);
+
+ if (!stat(DMDIR H_DEVICE_WRONG, &st))
+ _system("dmsetup remove " DM_RETRY H_DEVICE_WRONG, 0);
+
+ if (!stat(DMDIR L_DEVICE_0S, &st))
+ _system("dmsetup remove " DM_RETRY L_DEVICE_0S, 0);
+
+ if (!stat(DMDIR L_DEVICE_1S, &st))
+ _system("dmsetup remove " DM_RETRY L_DEVICE_1S, 0);
+
+ if (!stat(DMDIR L_DEVICE_WRONG, &st))
+ _system("dmsetup remove " DM_RETRY L_DEVICE_WRONG, 0);
+
+ if (!stat(DMDIR L_DEVICE_OK, &st))
+ _system("dmsetup remove " DM_RETRY L_DEVICE_OK, 0);
+
+ t_dev_offset = 0;
+}
+
+static void _cleanup(void)
+{
+ struct stat st;
+
+ //_system("udevadm settle", 0);
+
+ if (!stat(DMDIR CDEVICE_1, &st))
+ _system("dmsetup remove " CDEVICE_1, 0);
+
+ if (!stat(DMDIR CDEVICE_2, &st))
+ _system("dmsetup remove " CDEVICE_2, 0);
+
+ if (!stat(DEVICE_EMPTY, &st))
+ _system("dmsetup remove " DEVICE_EMPTY_name, 0);
+
+ if (!stat(DEVICE_ERROR, &st))
+ _system("dmsetup remove " DEVICE_ERROR_name, 0);
+
+ _cleanup_dmdevices();
+
+ if (loop_device(THE_LOOP_DEV))
+ loop_detach(THE_LOOP_DEV);
+
+ if (loop_device(DEVICE_1))
+ loop_detach(DEVICE_1);
+
+ if (loop_device(DEVICE_2))
+ loop_detach(DEVICE_2);
+
+ if (loop_device(DEVICE_3))
+ loop_detach(DEVICE_3);
+
+ if (loop_device(DEVICE_4))
+ loop_detach(DEVICE_4);
+
+ if (loop_device(DEVICE_5))
+ loop_detach(DEVICE_5);
+
+ if (loop_device(DEVICE_6))
+ loop_detach(DEVICE_6);
+
+ _system("rm -f " IMAGE_EMPTY, 0);
+ _system("rm -f " IMAGE1, 0);
+ _system("rm -rf " CONV_DIR, 0);
+
+ if (test_loop_file)
+ remove(test_loop_file);
+ if (tmp_file_1)
+ remove(tmp_file_1);
+
+ remove(REQS_LUKS2_HEADER);
+ remove(NO_REQS_LUKS2_HEADER);
+ remove(BACKUP_FILE);
+ remove(IMAGE_PV_LUKS2_SEC);
+ remove(IMAGE_PV_LUKS2_SEC ".bcp");
+
+ _remove_keyfiles();
+
+ free(tmp_file_1);
+ free(test_loop_file);
+ free(THE_LOOP_DEV);
+ free(DEVICE_1);
+ free(DEVICE_2);
+ free(DEVICE_3);
+ free(DEVICE_4);
+ free(DEVICE_5);
+ free(DEVICE_6);
+}
+
+static int _setup(void)
+{
+ int fd, ro = 0;
+ char cmd[128];
+
+ test_loop_file = strdup(THE_LFILE_TEMPLATE);
+ if ((fd=mkstemp(test_loop_file)) == -1) {
+ printf("cannot create temporary file with template %s\n", test_loop_file);
+ return 1;
+ }
+ close(fd);
+ snprintf(cmd, sizeof(cmd), "dd if=/dev/zero of=%s bs=%d count=%d 2>/dev/null",
+ test_loop_file, SECTOR_SIZE, TST_LOOP_FILE_SIZE);
+ if (_system(cmd, 1))
+ return 1;
+
+ fd = loop_attach(&THE_LOOP_DEV, test_loop_file, 0, 0, &ro);
+ close(fd);
+
+ tmp_file_1 = strdup(THE_LFILE_TEMPLATE);
+ if ((fd=mkstemp(tmp_file_1)) == -1) {
+ printf("cannot create temporary file with template %s\n", tmp_file_1);
+ return 1;
+ }
+ close(fd);
+ snprintf(cmd, sizeof(cmd), "dd if=/dev/zero of=%s bs=%d count=%d 2>/dev/null",
+ tmp_file_1, SECTOR_SIZE, 10);
+ if (_system(cmd, 1))
+ return 1;
+
+ _system("dmsetup create " DEVICE_EMPTY_name " --table \"0 10000 zero\"", 1);
+ _system("dmsetup create " DEVICE_ERROR_name " --table \"0 10000 error\"", 1);
+
+ _system(" [ ! -e " IMAGE1 " ] && xz -dk " IMAGE1 ".xz", 1);
+ fd = loop_attach(&DEVICE_1, IMAGE1, 0, 0, &ro);
+ close(fd);
+
+ _system("dd if=/dev/zero of=" IMAGE_EMPTY " bs=1M count=32 2>/dev/null", 1);
+ fd = loop_attach(&DEVICE_2, IMAGE_EMPTY, 0, 0, &ro);
+ close(fd);
+
+ _system(" [ ! -e " NO_REQS_LUKS2_HEADER " ] && xz -dk " NO_REQS_LUKS2_HEADER ".xz", 1);
+ fd = loop_attach(&DEVICE_4, NO_REQS_LUKS2_HEADER, 0, 0, &ro);
+ close(fd);
+
+ _system(" [ ! -e " REQS_LUKS2_HEADER " ] && xz -dk " REQS_LUKS2_HEADER ".xz", 1);
+ fd = loop_attach(&DEVICE_5, REQS_LUKS2_HEADER, 0, 0, &ro);
+ close(fd);
+
+ _system(" [ ! -e " IMAGE_PV_LUKS2_SEC " ] && xz -dk " IMAGE_PV_LUKS2_SEC ".xz", 1);
+ _system(" [ ! -e " IMAGE_PV_LUKS2_SEC ".bcp ] && cp " IMAGE_PV_LUKS2_SEC " " IMAGE_PV_LUKS2_SEC ".bcp", 1);
+ fd = loop_attach(&DEVICE_6, IMAGE_PV_LUKS2_SEC, 0, 0, &ro);
+ close(fd);
+
+ _system(" [ ! -d " CONV_DIR " ] && tar xJf " CONV_DIR ".tar.xz 2>/dev/null", 1);
+
+ if (_system("modprobe dm-crypt", 1))
+ return 1;
+
+ if (t_dm_check_versions())
+ return 1;
+
+ _system("rmmod dm-crypt", 0);
+
+ _fips_mode = fips_mode();
+ if (_debug)
+ printf("FIPS MODE: %d\n", _fips_mode);
+
+ /* Use default log callback */
+ crypt_set_log_callback(NULL, &global_log_callback, NULL);
+
+ return 0;
+}
+
+#ifdef KERNEL_KEYRING
+static key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t keyring)
+{
+ return syscall(__NR_add_key, type, description, payload, plen, keyring);
+}
+
+static key_serial_t keyctl_unlink(key_serial_t key, key_serial_t keyring)
+{
+ return syscall(__NR_keyctl, KEYCTL_UNLINK, key, keyring);
+}
+
+static key_serial_t request_key(const char *type,
+ const char *description,
+ const char *callout_info,
+ key_serial_t keyring)
+{
+ return syscall(__NR_request_key, type, description, callout_info, keyring);
+}
+
+static key_serial_t _kernel_key_by_segment(struct crypt_device *cd, int segment)
+{
+ char key_description[1024];
+
+ if (snprintf(key_description, sizeof(key_description), "cryptsetup:%s-d%u", crypt_get_uuid(cd), segment) < 1)
+ return -1;
+
+ return request_key("logon", key_description, NULL, 0);
+}
+
+static int _volume_key_in_keyring(struct crypt_device *cd, int segment)
+{
+ return _kernel_key_by_segment(cd, segment) >= 0 ? 0 : -1;
+}
+
+static int _drop_keyring_key(struct crypt_device *cd, int segment)
+{
+ key_serial_t kid = _kernel_key_by_segment(cd, segment);
+
+ if (kid < 0)
+ return -1;
+
+ return keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING);
+}
+#endif
+
+static int test_open(struct crypt_device *cd,
+ int token,
+ char **buffer,
+ size_t *buffer_len,
+ void *usrptr)
+{
+ const char *str = (const char *)usrptr;
+
+ *buffer = strdup(str);
+ if (!*buffer)
+ return -ENOMEM;
+ *buffer_len = strlen(*buffer);
+
+ return 0;
+}
+
+static int test_validate(struct crypt_device *cd, const char *json)
+{
+ return (strstr(json, "magic_string") == NULL);
+}
+
+static void UseLuks2Device(void)
+{
+ struct crypt_device *cd;
+ char key[128];
+ size_t key_size;
+
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ OK_(crypt_activate_by_passphrase(cd, NULL, CRYPT_ANY_SLOT, KEY1, strlen(KEY1), 0));
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1), 0));
+ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1), 0), "already open");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ FAIL_(crypt_deactivate(cd, CDEVICE_1), "no such device");
+
+#if KERNEL_KEYRING
+ // repeat previous tests and check kernel keyring is released when not needed
+ if (t_dm_crypt_keyring_support()) {
+ OK_(crypt_activate_by_passphrase(cd, NULL, CRYPT_ANY_SLOT, KEY1, strlen(KEY1), 0));
+ FAIL_(_drop_keyring_key(cd, 0), "");
+ OK_(crypt_activate_by_passphrase(cd, NULL, CRYPT_ANY_SLOT, KEY1, strlen(KEY1), CRYPT_ACTIVATE_KEYRING_KEY));
+ OK_(_drop_keyring_key(cd, 0));
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1), 0));
+ OK_(_drop_keyring_key(cd, 0));
+ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1), 0), "already open");
+ FAIL_(_volume_key_in_keyring(cd, 0), "");
+ OK_(crypt_activate_by_passphrase(cd, NULL, CRYPT_ANY_SLOT, KEY1, strlen(KEY1), 0));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ FAIL_(_volume_key_in_keyring(cd, 0), "");
+ }
+#endif
+
+ key_size = 16;
+ OK_(strcmp("aes", crypt_get_cipher(cd)));
+ OK_(strcmp("cbc-essiv:sha256", crypt_get_cipher_mode(cd)));
+ OK_(strcmp(DEVICE_1_UUID, crypt_get_uuid(cd)));
+ EQ_((int)key_size, crypt_get_volume_key_size(cd));
+ EQ_(8192, crypt_get_data_offset(cd));
+
+ if (!_fips_mode) {
+ EQ_(0, crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key, &key_size, KEY1, strlen(KEY1)));
+ OK_(crypt_volume_key_verify(cd, key, key_size));
+ OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ key[1] = ~key[1];
+ FAIL_(crypt_volume_key_verify(cd, key, key_size), "key mismatch");
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "key mismatch");
+ }
+ crypt_free(cd);
+}
+
+static void SuspendDevice(void)
+{
+ int suspend_status;
+ struct crypt_device *cd;
+
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1), 0));
+
+ suspend_status = crypt_suspend(cd, CDEVICE_1);
+ if (suspend_status == -ENOTSUP) {
+ printf("WARNING: Suspend/Resume not supported, skipping test.\n");
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+ return;
+ }
+
+ OK_(suspend_status);
+#ifdef KERNEL_KEYRING
+ FAIL_(_volume_key_in_keyring(cd, 0), "");
+#endif
+ FAIL_(crypt_suspend(cd, CDEVICE_1), "already suspended");
+
+ FAIL_(crypt_resume_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1)-1), "wrong key");
+ OK_(crypt_resume_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1)));
+ FAIL_(crypt_resume_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1)), "not suspended");
+
+ OK_(prepare_keyfile(KEYFILE1, KEY1, strlen(KEY1)));
+ OK_(crypt_suspend(cd, CDEVICE_1));
+ FAIL_(crypt_resume_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1 "blah", 0), "wrong keyfile");
+ FAIL_(crypt_resume_by_keyfile_offset(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 1, 0), "wrong key");
+ OK_(crypt_resume_by_keyfile_offset(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 0, 0));
+ FAIL_(crypt_resume_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 0), "not suspended");
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ /* create LUKS device with detached header */
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_set_data_device(cd, DEVICE_2));
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1), 0));
+ crypt_free(cd);
+
+ /* Should be able to suspend but not resume if not header specified */
+ OK_(crypt_init_by_name(&cd, CDEVICE_1));
+ OK_(crypt_suspend(cd, CDEVICE_1));
+ FAIL_(crypt_suspend(cd, CDEVICE_1), "already suspended");
+ FAIL_(crypt_resume_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1)-1), "no header");
+ crypt_free(cd);
+
+ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, DEVICE_1));
+ OK_(crypt_resume_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1)));
+
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ _remove_keyfiles();
+}
+
+static void AddDeviceLuks2(void)
+{
+ enum { OFFSET_1M = 2048 , OFFSET_2M = 4096, OFFSET_4M = 8192, OFFSET_8M = 16384 };
+ struct crypt_device *cd;
+ struct crypt_pbkdf_type pbkdf = {
+ .type = CRYPT_KDF_ARGON2I,
+ .hash = "sha256",
+ .parallel_threads = 4,
+ .max_memory_kb = 1024,
+ .time_ms = 1
+ }, pbkdf_tmp;
+ struct crypt_params_luks2 params = {
+ .pbkdf = &pbkdf,
+ .data_device = DEVICE_2,
+ .sector_size = 512
+ };
+ char key[128], key2[128], key3[128];
+
+ const char *passphrase = "blabla", *passphrase2 = "nsdkFI&Y#.sd";
+ const char *mk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ const char *mk_hex2 = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1e";
+ size_t key_size = strlen(mk_hex) / 2;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+ uint64_t r_payload_offset, r_header_size, r_size_1;
+ uint64_t mdata_size, keyslots_size;
+
+ /* Cannot use Argon2 in FIPS */
+ if (_fips_mode) {
+ pbkdf.type = CRYPT_KDF_PBKDF2;
+ pbkdf.parallel_threads = 0;
+ pbkdf.max_memory_kb = 0;
+ }
+
+ crypt_decode_key(key, mk_hex, key_size);
+ crypt_decode_key(key3, mk_hex2, key_size);
+
+ // init test devices
+ OK_(get_luks2_offsets(1, 0, 0, &r_header_size, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size));
+ OK_(create_dmdevice_over_loop(H_DEVICE_WRONG, r_header_size - 1));
+
+ //default metadata sizes
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size));
+ EQ_(mdata_size, 0);
+ EQ_(keyslots_size, 0);
+ OK_(crypt_set_metadata_size(cd, 0, 0));
+ OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size));
+ EQ_(mdata_size, 0);
+ EQ_(keyslots_size, 0);
+ OK_(crypt_set_metadata_size(cd, 0x004000, 0x004000));
+ OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size));
+ EQ_(mdata_size, 0x004000);
+ EQ_(keyslots_size, 0x004000);
+ OK_(crypt_set_metadata_size(cd, 0x008000, 0x008000));
+ OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size));
+ EQ_(mdata_size, 0x008000);
+ EQ_(keyslots_size, 0x008000);
+ FAIL_(crypt_set_metadata_size(cd, 0x008001, 0x008000), "Wrong size");
+ FAIL_(crypt_set_metadata_size(cd, 0x008000, 0x008001), "Wrong size");
+ crypt_free(cd);
+
+ // metadata settings
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_set_metadata_size(cd, 0x080000, 0x080000));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase)), 7);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size));
+ EQ_(mdata_size, 0x080000);
+ EQ_(keyslots_size, 0x080000);
+ crypt_free(cd);
+ // default
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size));
+ EQ_(mdata_size, 0x04000);
+ EQ_(keyslots_size, (r_header_size * 512) - 2 * 0x04000);
+ crypt_free(cd);
+
+ // format
+ OK_(crypt_init(&cd, DMDIR H_DEVICE_WRONG));
+ params.data_alignment = 0;
+ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params), "Not enough space for keyslots material");
+ crypt_free(cd);
+
+ // test payload_offset = 0 for encrypted device with external header device
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_get_data_offset(cd), 0);
+ crypt_free(cd);
+
+ params.data_alignment = 0;
+ params.data_device = NULL;
+
+ // test payload_offset = 0. format() should look up alignment offset from device topology
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(!(crypt_get_data_offset(cd) > 0));
+ crypt_free(cd);
+
+ // set_data_offset has priority, alignment must be 0 or must be compatible
+ params.data_alignment = 0;
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(crypt_set_data_offset(cd, OFFSET_8M));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_get_data_offset(cd), OFFSET_8M);
+ crypt_free(cd);
+
+ // Load gets the value from metadata
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(crypt_set_data_offset(cd, OFFSET_2M));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_get_data_offset(cd), OFFSET_8M);
+ crypt_free(cd);
+
+ params.data_alignment = OFFSET_4M;
+ OK_(crypt_init(&cd, DEVICE_2));
+ FAIL_(crypt_set_data_offset(cd, OFFSET_2M + 1), "Not aligned to 4096"); // must be aligned to 4k
+ OK_(crypt_set_data_offset(cd, OFFSET_2M));
+ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params), "Alignment not compatible");
+ OK_(crypt_set_data_offset(cd, OFFSET_4M));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_get_data_offset(cd), OFFSET_4M);
+ crypt_free(cd);
+
+ /*
+ * test limit values for backing device size
+ */
+ params.data_alignment = OFFSET_4M;
+ OK_(get_luks2_offsets(1, params.data_alignment, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_0S, r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_1S, r_payload_offset + 1));
+ OK_(create_dmdevice_over_loop(L_DEVICE_WRONG, r_payload_offset - 1));
+
+ // 1 sector less than required
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_WRONG));
+ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params), "Device too small");
+ crypt_free(cd);
+
+ // 0 sectors for encrypted area
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_0S));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Encrypted area too small");
+ crypt_free(cd);
+
+ // 1 sector for encrypted area
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_get_data_offset(cd), r_payload_offset);
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(t_device_size(DMDIR CDEVICE_1, &r_size_1));
+ EQ_(r_size_1, SECTOR_SIZE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ // restrict format only to empty context
+ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params), "Context is already formatted");
+ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, NULL), "Context is already formatted");
+ // change data device to wrong one
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_0S));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Device too small");
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ params.data_alignment = 0;
+ params.data_device = DEVICE_2;
+
+ // generate keyslot material at the end of luks header
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_((int)key_size, crypt_get_volume_key_size(cd));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase)), 7);
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 7, passphrase, strlen(passphrase) ,0), 7);
+
+ OK_(crypt_keyslot_get_pbkdf(cd, 7, &pbkdf_tmp));
+ OK_(strcmp(pbkdf_tmp.type, pbkdf.type));
+ NULL_(pbkdf_tmp.hash);
+ EQ_(0, pbkdf_tmp.time_ms); /* not usable in per-keyslot call */
+ OK_(!(pbkdf_tmp.iterations >= 4));
+ OK_(!(pbkdf_tmp.max_memory_kb >= 32));
+ OK_(!(pbkdf_tmp.parallel_threads >= 1));
+
+ crypt_free(cd);
+ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, DMDIR H_DEVICE));
+ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params), "Context is already formatted");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ crypt_free(cd);
+ // check active status without header
+ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, NULL));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ NULL_(crypt_get_type(cd));
+ OK_(strcmp(cipher, crypt_get_cipher(cd)));
+ OK_(strcmp(cipher_mode, crypt_get_cipher_mode(cd)));
+ EQ_((int)key_size, crypt_get_volume_key_size(cd));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ params.data_alignment = OFFSET_1M;
+ params.data_device = NULL;
+
+ // test uuid mismatch and _init_by_name_and_header
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ crypt_free(cd);
+ params.data_alignment = 0;
+ params.data_device = DEVICE_2;
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ crypt_free(cd);
+ // there we've got uuid mismatch
+ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, DMDIR H_DEVICE));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ NULL_(crypt_get_type(cd));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Device is active");
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_2, key, key_size, 0), "Device is active");
+ EQ_(crypt_status(cd, CDEVICE_2), CRYPT_INACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ params.data_device = NULL;
+
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+
+ // even with no keyslots defined it can be activated by volume key
+ OK_(crypt_volume_key_verify(cd, key, key_size));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_2, key, key_size, 0));
+ EQ_(crypt_status(cd, CDEVICE_2), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_2));
+
+ // now with keyslot
+ EQ_(7, crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase)));
+ EQ_(CRYPT_SLOT_ACTIVE_LAST, crypt_keyslot_status(cd, 7));
+ EQ_(7, crypt_activate_by_passphrase(cd, CDEVICE_2, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0));
+ EQ_(crypt_status(cd, CDEVICE_2), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_2));
+
+ crypt_set_iteration_time(cd, 1);
+ EQ_(1, crypt_keyslot_add_by_volume_key(cd, 1, key, key_size, KEY1, strlen(KEY1)));
+ OK_(prepare_keyfile(KEYFILE1, KEY1, strlen(KEY1)));
+ OK_(prepare_keyfile(KEYFILE2, KEY2, strlen(KEY2)));
+ EQ_(2, crypt_keyslot_add_by_keyfile(cd, 2, KEYFILE1, 0, KEYFILE2, 0));
+ FAIL_(crypt_keyslot_add_by_keyfile_offset(cd, 3, KEYFILE1, 0, 1, KEYFILE2, 0, 1), "wrong key");
+ EQ_(3, crypt_keyslot_add_by_keyfile_offset(cd, 3, KEYFILE1, 0, 0, KEYFILE2, 0, 1));
+ EQ_(4, crypt_keyslot_add_by_keyfile_offset(cd, 4, KEYFILE2, 0, 1, KEYFILE1, 0, 1));
+ FAIL_(crypt_activate_by_keyfile(cd, CDEVICE_2, CRYPT_ANY_SLOT, KEYFILE2, strlen(KEY2)-1, 0), "key mismatch");
+ EQ_(2, crypt_activate_by_keyfile(cd, NULL, CRYPT_ANY_SLOT, KEYFILE2, 0, 0));
+ EQ_(3, crypt_activate_by_keyfile_offset(cd, NULL, CRYPT_ANY_SLOT, KEYFILE2, 0, 1, 0));
+ EQ_(4, crypt_activate_by_keyfile_offset(cd, NULL, CRYPT_ANY_SLOT, KEYFILE1, 0, 1, 0));
+ FAIL_(crypt_activate_by_keyfile_offset(cd, CDEVICE_2, CRYPT_ANY_SLOT, KEYFILE2, strlen(KEY2), 2, 0), "not enough data");
+ FAIL_(crypt_activate_by_keyfile_offset(cd, CDEVICE_2, CRYPT_ANY_SLOT, KEYFILE2, 0, strlen(KEY2) + 1, 0), "cannot seek");
+ FAIL_(crypt_activate_by_keyfile_offset(cd, CDEVICE_2, CRYPT_ANY_SLOT, KEYFILE2, 0, 2, 0), "wrong key");
+ EQ_(2, crypt_activate_by_keyfile(cd, CDEVICE_2, CRYPT_ANY_SLOT, KEYFILE2, 0, 0));
+ OK_(crypt_keyslot_destroy(cd, 1));
+ OK_(crypt_keyslot_destroy(cd, 2));
+ OK_(crypt_keyslot_destroy(cd, 3));
+ OK_(crypt_keyslot_destroy(cd, 4));
+ OK_(crypt_deactivate(cd, CDEVICE_2));
+ _remove_keyfiles();
+
+ FAIL_(crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase)), "slot used");
+ key[1] = ~key[1];
+ FAIL_(crypt_keyslot_add_by_volume_key(cd, 6, key, key_size, passphrase, strlen(passphrase)), "key mismatch");
+ key[1] = ~key[1];
+ EQ_(6, crypt_keyslot_add_by_volume_key(cd, 6, key, key_size, passphrase, strlen(passphrase)));
+ EQ_(CRYPT_SLOT_ACTIVE, crypt_keyslot_status(cd, 6));
+
+ FAIL_(crypt_keyslot_destroy(cd, 8), "invalid keyslot");
+ FAIL_(crypt_keyslot_destroy(cd, CRYPT_ANY_SLOT), "invalid keyslot");
+ FAIL_(crypt_keyslot_destroy(cd, 0), "keyslot not used");
+ OK_(crypt_keyslot_destroy(cd, 7));
+ EQ_(CRYPT_SLOT_INACTIVE, crypt_keyslot_status(cd, 7));
+ EQ_(CRYPT_SLOT_ACTIVE_LAST, crypt_keyslot_status(cd, 6));
+
+ EQ_(7, crypt_keyslot_change_by_passphrase(cd, 6, 7, passphrase, strlen(passphrase), passphrase2, strlen(passphrase2)));
+ EQ_(CRYPT_SLOT_ACTIVE_LAST, crypt_keyslot_status(cd, 7));
+ EQ_(7, crypt_activate_by_passphrase(cd, NULL, 7, passphrase2, strlen(passphrase2), 0));
+ EQ_(6, crypt_keyslot_change_by_passphrase(cd, CRYPT_ANY_SLOT, 6, passphrase2, strlen(passphrase2), passphrase, strlen(passphrase)));
+
+ if (!_fips_mode) {
+ EQ_(6, crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key2, &key_size, passphrase, strlen(passphrase)));
+ OK_(crypt_volume_key_verify(cd, key2, key_size));
+
+ OK_(memcmp(key, key2, key_size));
+ }
+ OK_(strcmp(cipher, crypt_get_cipher(cd)));
+ OK_(strcmp(cipher_mode, crypt_get_cipher_mode(cd)));
+ EQ_((int)key_size, crypt_get_volume_key_size(cd));
+ EQ_(r_payload_offset, crypt_get_data_offset(cd));
+ OK_(strcmp(DEVICE_2, crypt_get_device_name(cd)));
+
+ reset_log();
+ OK_(crypt_dump(cd));
+ OK_(!(global_lines != 0));
+ reset_log();
+
+ FAIL_(crypt_set_uuid(cd, "blah"), "wrong UUID format");
+ OK_(crypt_set_uuid(cd, DEVICE_TEST_UUID));
+ OK_(strcmp(DEVICE_TEST_UUID, crypt_get_uuid(cd)));
+
+ FAIL_(crypt_deactivate(cd, CDEVICE_2), "not active");
+ crypt_free(cd);
+ _cleanup_dmdevices();
+
+ /* LUKSv2 format tests */
+
+ /* very basic test */
+ OK_(crypt_init(&cd, DEVICE_2));
+ crypt_set_iteration_time(cd, 1);
+ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, 0, NULL), "Wrong key size");
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, NULL));
+ crypt_free(cd);
+ /* some invalid parameters known to cause troubles */
+ OK_(crypt_init(&cd, DEVICE_2));
+ crypt_set_iteration_time(cd, 0); /* wrong for argon2 but we don't know the pbkdf type yet, ignored */
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, NULL));
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DEVICE_2));
+ crypt_set_iteration_time(cd, 1);
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, NULL));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 0, NULL, key_size, PASSPHRASE, strlen(PASSPHRASE)), 0);
+ crypt_free(cd);
+
+ OK_(crypt_init(&cd, DEVICE_2));
+ crypt_set_iteration_time(cd, 1);
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, key_size, NULL));
+ FAIL_(crypt_keyslot_add_by_volume_key(cd, CRYPT_ANY_SLOT, key, key_size, PASSPHRASE, strlen(PASSPHRASE)), "VK doesn't match any digest");
+ FAIL_(crypt_keyslot_add_by_volume_key(cd, 1, key, key_size, PASSPHRASE, strlen(PASSPHRASE)), "VK doesn't match any digest");
+ crypt_free(cd);
+
+ OK_(create_dmdevice_over_loop(L_DEVICE_1S, r_payload_offset + 1));
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ crypt_set_iteration_time(cd, 1);
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, NULL));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 3, NULL, key_size, PASSPHRASE, strlen(PASSPHRASE)), 3);
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key3, key_size, 0), "VK doesn't match any digest assigned to segment 0");
+ crypt_free(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void UseTempVolumes(void)
+{
+ struct crypt_device *cd;
+ char tmp[256];
+
+ // Tepmporary device without keyslot but with on-disk LUKS header
+ OK_(crypt_init(&cd, DEVICE_2));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_2, NULL, 0, 0), "not yet formatted");
+ OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 16, NULL));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_2, NULL, 0, 0));
+ EQ_(crypt_status(cd, CDEVICE_2), CRYPT_ACTIVE);
+ crypt_free(cd);
+
+ OK_(crypt_init_by_name(&cd, CDEVICE_2));
+ OK_(crypt_deactivate(cd, CDEVICE_2));
+ crypt_free(cd);
+
+ // Dirty checks: device without UUID
+ // we should be able to remove it but not manuipulate with it
+ snprintf(tmp, sizeof(tmp), "dmsetup create %s --table \""
+ "0 100 crypt aes-cbc-essiv:sha256 deadbabedeadbabedeadbabedeadbabe 0 "
+ "%s 2048\"", CDEVICE_2, DEVICE_2);
+ _system(tmp, 1);
+ OK_(crypt_init_by_name(&cd, CDEVICE_2));
+ OK_(crypt_deactivate(cd, CDEVICE_2));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_2, NULL, 0, 0), "No known device type");
+ crypt_free(cd);
+
+ // Dirty checks: device with UUID but LUKS header key fingerprint must fail)
+ snprintf(tmp, sizeof(tmp), "dmsetup create %s --table \""
+ "0 100 crypt aes-cbc-essiv:sha256 deadbabedeadbabedeadbabedeadbabe 0 "
+ "%s 2048\" -u CRYPT-LUKS2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-ctest1",
+ CDEVICE_2, DEVICE_2);
+ _system(tmp, 1);
+ OK_(crypt_init_by_name(&cd, CDEVICE_2));
+ OK_(crypt_deactivate(cd, CDEVICE_2));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_2, NULL, 0, 0), "wrong volume key");
+ crypt_free(cd);
+
+ // No slots
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_2, NULL, 0, 0), "volume key is lost");
+ crypt_free(cd);
+}
+
+static void Luks2HeaderRestore(void)
+{
+ char key[128];
+ struct crypt_device *cd;
+ struct crypt_pbkdf_type pbkdf = {
+ .type = CRYPT_KDF_ARGON2I,
+ .hash = "sha256",
+ .parallel_threads = 4,
+ .max_memory_kb = 1024,
+ .time_ms = 1
+ };
+ struct crypt_params_luks2 params = {
+ .pbkdf = &pbkdf,
+ .data_alignment = 8192, // 4M, data offset will be 4096
+ .sector_size = 512
+ };
+ struct crypt_params_plain pl_params = {
+ .hash = "sha1",
+ .skip = 0,
+ .offset = 0,
+ .size = 0
+ };
+ struct crypt_params_luks1 luks1 = {
+ .data_alignment = 8192, // 4M offset to pass alignment test
+ };
+ uint32_t flags = 0;
+
+ const char *mk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(mk_hex) / 2;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+ uint64_t r_payload_offset;
+
+ /* Cannot use Argon2 in FIPS */
+ if (_fips_mode) {
+ pbkdf.type = CRYPT_KDF_PBKDF2;
+ pbkdf.parallel_threads = 0;
+ pbkdf.max_memory_kb = 0;
+ }
+
+ crypt_decode_key(key, mk_hex, key_size);
+
+ OK_(get_luks2_offsets(1, params.data_alignment, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 5000));
+
+ // do not restore header over plain device
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &pl_params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ FAIL_(crypt_header_restore(cd, CRYPT_PLAIN, NO_REQS_LUKS2_HEADER), "Cannot restore header to PLAIN type device");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS2, NO_REQS_LUKS2_HEADER), "Cannot restore header over PLAIN type device");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ // FIXME: does following test make a sense in LUKS2?
+ // volume key_size mismatch
+ // OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ // memcpy(key2, key, key_size / 2);
+ // OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key2, key_size / 2, &params));
+ // FAIL_(crypt_header_restore(cd, CRYPT_LUKS2, VALID_LUKS2_HEADER), "Volume keysize mismatch");
+ // crypt_free(cd);
+
+ // payload offset mismatch
+ params.data_alignment = 8193;
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS2, NO_REQS_LUKS2_HEADER), "Payload offset mismatch");
+ crypt_free(cd);
+ params.data_alignment = 4096;
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ // FIXME: either format has to fail or next line must be true
+ // EQ_(crypt_get_data_offset(cd), params.data_alignment);
+ // FAIL_(crypt_header_restore(cd, CRYPT_LUKS2, VALID_LUKS2_HEADER), "Payload offset mismatch");
+ crypt_free(cd);
+
+ // do not allow restore over LUKS1 header on device
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ crypt_set_iteration_time(cd, 1);
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, NULL, 32, &luks1));
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS2, NO_REQS_LUKS2_HEADER), "LUKS1 format detected");
+ crypt_free(cd);
+
+ /* check crypt_header_restore() properly loads crypt_device context */
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_wipe(cd, NULL, CRYPT_WIPE_ZERO, 0, 1*1024*1024, 1*1024*1024, 0, NULL, NULL));
+ OK_(crypt_header_restore(cd, CRYPT_LUKS2, NO_REQS_LUKS2_HEADER));
+ /* check LUKS2 specific API call returns non-error code */
+ OK_(crypt_persistent_flags_get(cd, CRYPT_FLAGS_REQUIREMENTS, &flags));
+ EQ_(flags, 0);
+ /* same test, any LUKS */
+ OK_(crypt_wipe(cd, NULL, CRYPT_WIPE_ZERO, 0, 1*1024*1024, 1*1024*1024, 0, NULL, NULL));
+ OK_(crypt_header_restore(cd, CRYPT_LUKS, NO_REQS_LUKS2_HEADER));
+ /* check LUKS2 specific API call returns non-error code */
+ OK_(crypt_persistent_flags_get(cd, CRYPT_FLAGS_REQUIREMENTS, &flags));
+ EQ_(flags, 0);
+
+ crypt_free(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void Luks2HeaderLoad(void)
+{
+ struct crypt_device *cd;
+ struct crypt_pbkdf_type pbkdf = {
+ .type = CRYPT_KDF_ARGON2I,
+ .hash = "sha256",
+ .parallel_threads = 4,
+ .max_memory_kb = 1024,
+ .time_ms = 1
+ };
+ struct crypt_params_luks2 params = {
+ .pbkdf = &pbkdf,
+ .data_alignment = 8192, // 4M, data offset will be 4096
+ .data_device = DEVICE_2,
+ .sector_size = 512
+ };
+ struct crypt_params_plain pl_params = {
+ .hash = "sha1",
+ .skip = 0,
+ .offset = 0,
+ .size = 0
+ };
+ char key[128], cmd[256];
+
+ const char *mk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(mk_hex) / 2;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+ uint64_t r_payload_offset, r_header_size, img_size;
+
+ /* Cannot use Argon2 in FIPS */
+ if (_fips_mode) {
+ pbkdf.type = CRYPT_KDF_PBKDF2;
+ pbkdf.parallel_threads = 0;
+ pbkdf.max_memory_kb = 0;
+ }
+
+ crypt_decode_key(key, mk_hex, key_size);
+
+ // hardcoded values for existing image IMAGE1
+ img_size = 8192;
+ // prepare test env
+ OK_(get_luks2_offsets(1, 0, 0, &r_header_size, &r_payload_offset));
+ // external header device
+ OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size));
+ // prepared header on a device too small to contain header and payload
+ //OK_(create_dmdevice_over_loop(H_DEVICE_WRONG, r_payload_offset - 1));
+ OK_(create_dmdevice_over_loop(H_DEVICE_WRONG, img_size - 1));
+ snprintf(cmd, sizeof(cmd), "dd if=" IMAGE1 " of=" DMDIR H_DEVICE_WRONG " bs=%" PRIu32 " count=%" PRIu64 " 2>/dev/null", params.sector_size, img_size - 1);
+ OK_(_system(cmd, 1));
+ // some device
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 1000));
+ // 1 sector device
+ OK_(create_dmdevice_over_loop(L_DEVICE_1S, r_header_size + 1));
+ // 0 sectors device for payload
+ OK_(create_dmdevice_over_loop(L_DEVICE_0S, r_header_size));
+
+ // valid metadata and device size
+ params.data_alignment = 0;
+ params.data_device = DMDIR L_DEVICE_OK;
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(!crypt_get_metadata_device_name(cd));
+ EQ_(strcmp(DMDIR H_DEVICE, crypt_get_metadata_device_name(cd)), 0);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ // repeat with init with two devices
+ OK_(crypt_init_data_device(&cd, DMDIR H_DEVICE, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ crypt_free(cd);
+ OK_(crypt_init_data_device(&cd, DMDIR H_DEVICE, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(!crypt_get_metadata_device_name(cd));
+ EQ_(strcmp(DMDIR H_DEVICE, crypt_get_metadata_device_name(cd)), 0);
+ crypt_free(cd);
+
+ // bad header: device too small (payloadOffset > device_size)
+ OK_(crypt_init(&cd, DMDIR H_DEVICE_WRONG));
+ FAIL_(crypt_load(cd, CRYPT_LUKS2, NULL), "Device too small");
+ NULL_(crypt_get_type(cd));
+ crypt_free(cd);
+
+ // 0 secs for encrypted data area
+ params.data_alignment = 8192;
+ params.data_device = NULL;
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_0S));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ crypt_free(cd);
+ // load should be ok
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_0S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Device too small");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ crypt_free(cd);
+
+ // damaged header
+ OK_(_system("dd if=/dev/zero of=" DMDIR L_DEVICE_OK " bs=512 count=8 2>/dev/null", 1));
+ OK_(_system("dd if=/dev/zero of=" DMDIR L_DEVICE_OK " bs=512 seek=32 count=8 2>/dev/null", 1));
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ FAIL_(crypt_load(cd, CRYPT_LUKS2, NULL), "Header not found");
+ crypt_free(cd);
+
+ // plain device
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ FAIL_(crypt_load(cd, CRYPT_PLAIN, NULL), "Can't load nonLUKS device type");
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, key, key_size, &pl_params));
+ FAIL_(crypt_load(cd, CRYPT_LUKS2, NULL), "Can't load over nonLUKS device type");
+ crypt_free(cd);
+
+ //LUKSv2 device
+ OK_(crypt_init(&cd, DEVICE_4));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DEVICE_4));
+ crypt_set_iteration_time(cd, 0); /* invalid for argon2 pbkdf, ignored */
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ crypt_free(cd);
+
+ /* check load sets proper device type */
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_0S));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ EQ_(strcmp(CRYPT_LUKS2, crypt_get_type(cd)), 0);
+ crypt_free(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void Luks2HeaderBackup(void)
+{
+ struct crypt_device *cd;
+ struct crypt_pbkdf_type pbkdf = {
+ .type = CRYPT_KDF_ARGON2I,
+ .hash = "sha256",
+ .parallel_threads = 4,
+ .max_memory_kb = 1024,
+ .time_ms = 1
+ };
+ struct crypt_params_luks2 params = {
+ .pbkdf = &pbkdf,
+ .data_alignment = 8192, // 4M, data offset will be 4096
+ .data_device = DEVICE_2,
+ .sector_size = 512
+ };
+ char key[128];
+ int fd, ro = O_RDONLY;
+
+ const char *mk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(mk_hex) / 2;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+ uint64_t r_payload_offset;
+
+ const char *passphrase = PASSPHRASE;
+
+ /* Cannot use Argon2 in FIPS */
+ if (_fips_mode) {
+ pbkdf.type = CRYPT_KDF_PBKDF2;
+ pbkdf.parallel_threads = 0;
+ pbkdf.max_memory_kb = 0;
+ }
+
+ crypt_decode_key(key, mk_hex, key_size);
+
+ OK_(get_luks2_offsets(0, params.data_alignment, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 1));
+
+ // create LUKS device and backup the header
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase)), 7);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 0, key, key_size, passphrase, strlen(passphrase)), 0);
+ OK_(crypt_header_backup(cd, CRYPT_LUKS2, BACKUP_FILE));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ // restore header from backup
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_header_restore(cd, CRYPT_LUKS2, BACKUP_FILE));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ // exercise luksOpen using backup header in file
+ OK_(crypt_init(&cd, BACKUP_FILE));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_OK));
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, passphrase, strlen(passphrase), 0), 0);
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ OK_(crypt_init(&cd, BACKUP_FILE));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_OK));
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 7, passphrase, strlen(passphrase), 0), 7);
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ // exercise luksOpen using backup header on block device
+ fd = loop_attach(&DEVICE_3, BACKUP_FILE, 0, 0, &ro);
+ close(fd);
+ OK_(fd < 0);
+ OK_(crypt_init(&cd, DEVICE_3));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_OK));
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, passphrase, strlen(passphrase), 0), 0);
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ OK_(crypt_init(&cd, DEVICE_3));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_OK));
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 7, passphrase, strlen(passphrase), 0), 7);
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void ResizeDeviceLuks2(void)
+{
+ struct crypt_device *cd, *cd2;
+ struct crypt_pbkdf_type pbkdf = {
+ .type = CRYPT_KDF_ARGON2I,
+ .hash = "sha256",
+ .parallel_threads = 4,
+ .max_memory_kb = 1024,
+ .time_ms = 1
+ };
+ struct crypt_params_luks2 params = {
+ .pbkdf = &pbkdf,
+ .data_alignment = 8192, // 4M, data offset will be 4096
+ .sector_size = 512
+ };
+ char key[128];
+
+ const char *mk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(mk_hex) / 2;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+ uint64_t r_payload_offset, r_header_size, r_size;
+
+ /* Cannot use Argon2 in FIPS */
+ if (_fips_mode) {
+ pbkdf.type = CRYPT_KDF_PBKDF2;
+ pbkdf.parallel_threads = 0;
+ pbkdf.max_memory_kb = 0;
+ }
+
+ crypt_decode_key(key, mk_hex, key_size);
+
+ // prepare env
+ OK_(get_luks2_offsets(1, params.data_alignment, 0, NULL, &r_payload_offset));
+ OK_(get_luks2_offsets(1, 0, 0, &r_header_size, NULL));
+ OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size));
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 1000));
+ OK_(create_dmdevice_over_loop(L_DEVICE_0S, 1000));
+ OK_(create_dmdevice_over_loop(L_DEVICE_WRONG, r_payload_offset + 1000));
+
+ // test header and encrypted payload all in one device
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ // disable loading VKs in kernel keyring (compatible mode)
+ OK_(crypt_volume_key_keyring(cd, 0));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ OK_(crypt_resize(cd, CDEVICE_1, 0));
+ OK_(crypt_resize(cd, CDEVICE_1, 42));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(42, r_size >> SECTOR_SHIFT);
+ OK_(crypt_resize(cd, CDEVICE_1, 0));
+ // autodetect encrypted device area size
+ OK_(crypt_resize(cd, CDEVICE_1, 0));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(1000, r_size >> SECTOR_SHIFT);
+ FAIL_(crypt_resize(cd, CDEVICE_1, 1001), "Device too small");
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(1000, r_size >> SECTOR_SHIFT);
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ params.data_alignment = 0;
+ params.data_device = DMDIR L_DEVICE_0S;
+ // test case for external header
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ OK_(crypt_resize(cd, CDEVICE_1, 666));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(666, r_size >> SECTOR_SHIFT);
+ // autodetect encrypted device size
+ OK_(crypt_resize(cd, CDEVICE_1, 0));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(1000, r_size >> SECTOR_SHIFT);
+ FAIL_(crypt_resize(cd, CDEVICE_1, 1001), "Device too small");
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(1000, r_size >> SECTOR_SHIFT);
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+#ifdef KERNEL_KEYRING
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ // enable loading VKs in kernel keyring (default mode)
+ OK_(crypt_volume_key_keyring(cd, 1));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ // erase volume key from kernel keyring
+ if (t_dm_crypt_keyring_support())
+ OK_(_drop_keyring_key(cd, 0));
+ else
+ FAIL_(_drop_keyring_key(cd, 0), "key not found");
+ // same size is ok
+ OK_(crypt_resize(cd, CDEVICE_1, 0));
+ // kernel fails to find the volume key in keyring
+ if (t_dm_crypt_keyring_support())
+ FAIL_(crypt_resize(cd, CDEVICE_1, 42), "Unable to find volume key in keyring");
+ else
+ OK_(crypt_resize(cd, CDEVICE_1, 42));
+ // test mode must not load vk in keyring
+ OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0));
+ if (t_dm_crypt_keyring_support())
+ FAIL_(crypt_resize(cd, CDEVICE_1, 44), "VK must be in keyring to perform resize");
+ else
+ OK_(crypt_resize(cd, CDEVICE_1, 44));
+ // reinstate the volume key in keyring
+ OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, t_dm_crypt_keyring_support() ? CRYPT_ACTIVATE_KEYRING_KEY : 0));
+ OK_(crypt_resize(cd, CDEVICE_1, 43));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(43, r_size >> SECTOR_SHIFT);
+ crypt_free(cd);
+
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ // check userspace gets hint volume key must be properly loaded in kernel keyring
+ if (t_dm_crypt_keyring_support())
+ EQ_(crypt_resize(cd, CDEVICE_1, 0), -EPERM);
+ else
+ OK_(crypt_resize(cd, CDEVICE_1, 0));
+ crypt_free(cd);
+
+ // same as above for handles initialised by name
+ OK_(crypt_init_by_name(&cd, CDEVICE_1));
+ if (t_dm_crypt_keyring_support())
+ EQ_(crypt_resize(cd, CDEVICE_1, 0), -EPERM);
+ else
+ OK_(crypt_resize(cd, CDEVICE_1, 0));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+#endif
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, NULL, NULL));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+
+ /* create second LUKS2 device */
+ OK_(crypt_init(&cd2, DMDIR L_DEVICE_WRONG));
+ OK_(crypt_format(cd2, CRYPT_LUKS2, cipher, cipher_mode, crypt_get_uuid(cd), key, key_size, &params));
+ OK_(crypt_activate_by_volume_key(cd2, CDEVICE_2, key, key_size, 0));
+ /* do not allow resize of other device */
+ FAIL_(crypt_resize(cd2, CDEVICE_1, 1), "Device got resized by wrong device context.");
+ OK_(crypt_deactivate(cd2, CDEVICE_2));
+ crypt_free(cd2);
+
+ OK_(crypt_init(&cd2, DMDIR L_DEVICE_WRONG));
+ crypt_set_iteration_time(cd2, 1);
+ OK_(crypt_format(cd2, CRYPT_LUKS1, cipher, cipher_mode, crypt_get_uuid(cd), key, key_size, NULL));
+ OK_(crypt_activate_by_volume_key(cd2, CDEVICE_2, key, key_size, 0));
+ FAIL_(crypt_resize(cd2, CDEVICE_1, 1), "Device got resized by wrong device context.");
+ OK_(crypt_deactivate(cd2, CDEVICE_2));
+ crypt_free(cd2);
+
+ OK_(crypt_init(&cd2, DMDIR L_DEVICE_WRONG));
+ OK_(crypt_format(cd2, CRYPT_PLAIN, cipher, cipher_mode, NULL, key, key_size, NULL));
+ OK_(crypt_activate_by_volume_key(cd2, CDEVICE_2, key, key_size, 0));
+ FAIL_(crypt_resize(cd2, CDEVICE_1, 1), "Device got resized by wrong device context.");
+ OK_(crypt_deactivate(cd2, CDEVICE_2));
+ crypt_free(cd2);
+
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void TokenActivationByKeyring(void)
+{
+#ifdef KERNEL_KEYRING
+ key_serial_t kid, kid1;
+ struct crypt_device *cd;
+
+ const char *cipher = "aes";
+ const char *cipher_mode = "xts-plain64";
+
+ const struct crypt_token_params_luks2_keyring params = {
+ .key_description = KEY_DESC_TEST0
+ }, params2 = {
+ .key_description = KEY_DESC_TEST1
+ };
+ uint64_t r_payload_offset;
+
+ kid = add_key("user", KEY_DESC_TEST0, PASSPHRASE, strlen(PASSPHRASE), KEY_SPEC_THREAD_KEYRING);
+ if (kid < 0) {
+ printf("Test or kernel keyring are broken.\n");
+ exit(1);
+ }
+
+ OK_(get_luks2_offsets(1, 0, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_1S, r_payload_offset + 1));
+
+ // prepare the device
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ crypt_set_iteration_time(cd, 1);
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, 32, NULL));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 0);
+ EQ_(crypt_token_luks2_keyring_set(cd, 3, &params), 3);
+ EQ_(crypt_token_assign_keyslot(cd, 3, 0), 3);
+ crypt_free(cd);
+
+ // test thread keyring key in token 0
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 3, NULL, 0), 0);
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 3, NULL, 0), "already open");
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ if (keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING)) {
+ printf("Test or kernel keyring are broken.\n");
+ exit(1);
+ }
+
+ kid = add_key("user", KEY_DESC_TEST0, PASSPHRASE, strlen(PASSPHRASE), KEY_SPEC_PROCESS_KEYRING);
+ if (kid < 0) {
+ printf("Test or kernel keyring are broken.\n");
+ exit(1);
+ }
+
+ // add token 1 with process keyring key
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_token_json_set(cd, 3, NULL), 3);
+ EQ_(crypt_token_luks2_keyring_set(cd, 1, &params), 1);
+ EQ_(crypt_token_assign_keyslot(cd, 1, 0), 1);
+ crypt_free(cd);
+
+ // test process keyring key in token 1
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 1, NULL, 0), 0);
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 1, NULL, 0), "already open");
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ if (keyctl_unlink(kid, KEY_SPEC_PROCESS_KEYRING)) {
+ printf("Test or kernel keyring are broken.\n");
+ exit(1);
+ }
+
+ // create two tokens and let the cryptsetup unlock the volume with the valid one
+ kid = add_key("user", KEY_DESC_TEST0, PASSPHRASE, strlen(PASSPHRASE), KEY_SPEC_THREAD_KEYRING);
+ if (kid < 0) {
+ printf("Test or kernel keyring are broken.\n");
+ exit(1);
+ }
+
+ kid1 = add_key("user", KEY_DESC_TEST1, PASSPHRASE1, strlen(PASSPHRASE1), KEY_SPEC_THREAD_KEYRING);
+ if (kid1 < 0) {
+ printf("Test or kernel keyring are broken.\n");
+ exit(1);
+ }
+
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_token_luks2_keyring_set(cd, 0, &params), 0);
+ EQ_(crypt_token_assign_keyslot(cd, 0, 0), 0);
+ EQ_(crypt_token_luks2_keyring_set(cd, 1, &params2), 1);
+ FAIL_(crypt_token_assign_keyslot(cd, 1, 1), "Keyslot 1 doesn't exist");
+ crypt_set_iteration_time(cd, 1);
+ EQ_(crypt_keyslot_add_by_passphrase(cd, 1, PASSPHRASE, strlen(PASSPHRASE), PASSPHRASE1, strlen(PASSPHRASE1)), 1);
+ EQ_(crypt_token_assign_keyslot(cd, 1, 1), 1);
+ crypt_free(cd);
+
+ // activate by specific token
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 0, NULL, 0), 0);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 1, NULL, 0), 1);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ if (keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING)) {
+ printf("Test or kernel keyring are broken.\n");
+ exit(1);
+ }
+
+ // activate by any token with token 0 having absent pass from keyring
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, CRYPT_ANY_TOKEN, NULL, 0), 1);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ kid = add_key("user", KEY_DESC_TEST0, PASSPHRASE, strlen(PASSPHRASE), KEY_SPEC_THREAD_KEYRING);
+ if (kid < 0) {
+ printf("Test or kernel keyring are broken.\n");
+ exit(1);
+ }
+
+ // replace pass for keyslot 0 making token 0 invalid
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_keyslot_destroy(cd, 0));
+ crypt_set_iteration_time(cd, 1);
+ EQ_(crypt_keyslot_add_by_passphrase(cd, 0, PASSPHRASE1, strlen(PASSPHRASE1), PASSPHRASE1, strlen(PASSPHRASE1)), 0);
+ crypt_free(cd);
+
+ // activate by any token with token 0 having wrong pass for keyslot 0
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, CRYPT_ANY_TOKEN, NULL, 0), 1);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ // create new device, with two tokens:
+ // 1st token being invalid (missing key in keyring)
+ // 2nd token can activate keyslot 1 after failing to do so w/ keyslot 0 (wrong pass)
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ crypt_set_iteration_time(cd, 1);
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, 32, NULL));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 0);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 1, NULL, 32, PASSPHRASE1, strlen(PASSPHRASE1)), 1);
+ EQ_(crypt_token_luks2_keyring_set(cd, 0, &params), 0);
+ EQ_(crypt_token_assign_keyslot(cd, 0, 0), 0);
+ EQ_(crypt_token_luks2_keyring_set(cd, 2, &params2), 2);
+ EQ_(crypt_token_assign_keyslot(cd, 2, 1), 2);
+ crypt_free(cd);
+
+ if (keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING)) {
+ printf("Test or kernel keyring are broken.\n");
+ exit(1);
+ }
+
+ kid1 = add_key("user", KEY_DESC_TEST1, PASSPHRASE1, strlen(PASSPHRASE1), KEY_SPEC_THREAD_KEYRING);
+ if (kid1 < 0) {
+ printf("Test or kernel keyring are broken.\n");
+ exit(1);
+ }
+
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, CRYPT_ANY_TOKEN, NULL, 0), 1);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+ _cleanup_dmdevices();
+#else
+ printf("WARNING: cryptsetup compiled with kernel keyring service disabled, skipping test.\n");
+#endif
+}
+
+static void Tokens(void)
+{
+#define TEST_TOKEN_JSON(x) "{\"type\":\"test_token\",\"keyslots\":[" x "]," \
+ "\"key_length\":32,\"a_field\":\"magic_string\"}"
+
+#define TEST_TOKEN_JSON_INVALID(x) "{\"type\":\"test_token\",\"keyslots\":[" x "]," \
+ "\"key_length\":32}"
+
+#define TEST_TOKEN1_JSON(x) "{\"type\":\"test_token1\",\"keyslots\":[" x "]," \
+ "\"key_length\":32,\"a_field\":\"magic_string\"}"
+
+#define TEST_TOKEN1_JSON_INVALID(x) "{\"type\":\"test_token1\",\"keyslots\":[" x "]," \
+ "\"key_length\":32}"
+
+#define BOGUS_TOKEN0_JSON "{\"type\":\"luks2-\",\"keyslots\":[]}"
+#define BOGUS_TOKEN1_JSON "{\"type\":\"luks2-a\",\"keyslots\":[]}"
+
+#define LUKS2_KEYRING_TOKEN_JSON(x, y) "{\"type\":\"luks2-keyring\",\"keyslots\":[" x "]," \
+ "\"key_description\":" y "}"
+
+#define LUKS2_KEYRING_TOKEN_JSON_BAD(x, y) "{\"type\":\"luks2-keyring\",\"keyslots\":[" x "]," \
+ "\"key_description\":" y ", \"some_field\":\"some_value\"}"
+
+ struct crypt_device *cd;
+
+ const char *dummy;
+ const char *cipher = "aes";
+ const char *cipher_mode = "xts-plain64";
+ char passptr[] = PASSPHRASE;
+ char passptr1[] = PASSPHRASE1;
+
+ static const crypt_token_handler th = {
+ .name = "test_token",
+ .open = test_open,
+ .validate = test_validate
+ }, th2 = {
+ .name = "test_token",
+ .open = test_open
+ }, th3 = {
+ .name = "test_token1",
+ .open = test_open,
+ .validate = test_validate
+ }, th_reserved = {
+ .name = "luks2-prefix",
+ .open = test_open
+ };
+
+ struct crypt_token_params_luks2_keyring params = {
+ .key_description = "desc"
+ };
+ uint64_t r_payload_offset;
+
+ OK_(crypt_token_register(&th));
+ FAIL_(crypt_token_register(&th2), "Token handler with the name already registered.");
+
+ FAIL_(crypt_token_register(&th_reserved), "luks2- is reserved prefix");
+
+ OK_(get_luks2_offsets(1, 0, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_1S, r_payload_offset + 1));
+
+ // basic token API tests
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ crypt_set_iteration_time(cd, 1);
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, 32, NULL));
+ EQ_(crypt_token_status(cd, -1, NULL), CRYPT_TOKEN_INVALID);
+ EQ_(crypt_token_status(cd, 32, NULL), CRYPT_TOKEN_INVALID);
+ EQ_(crypt_token_status(cd, 0, NULL), CRYPT_TOKEN_INACTIVE);
+ EQ_(crypt_token_status(cd, 31, NULL), CRYPT_TOKEN_INACTIVE);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 0);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 1, NULL, 32, PASSPHRASE1, strlen(PASSPHRASE1)), 1);
+ FAIL_(crypt_token_json_set(cd, CRYPT_ANY_TOKEN, TEST_TOKEN_JSON_INVALID("\"0\"")), "Token validation failed");
+ EQ_(crypt_token_json_set(cd, CRYPT_ANY_TOKEN, TEST_TOKEN_JSON("\"0\"")), 0);
+ EQ_(crypt_token_status(cd, 0, NULL), CRYPT_TOKEN_EXTERNAL);
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 0, passptr, 0), 0);
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 0, passptr, 0), "already active");
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ // write invalid token and verify that validate() can detect it after handler being registered
+ EQ_(crypt_token_json_set(cd, CRYPT_ANY_TOKEN, TEST_TOKEN1_JSON_INVALID("\"1\"")), 1);
+ EQ_(crypt_token_status(cd, 1, NULL), CRYPT_TOKEN_EXTERNAL_UNKNOWN);
+ EQ_(crypt_token_json_set(cd, CRYPT_ANY_TOKEN, TEST_TOKEN1_JSON("\"1\"")), 2);
+ EQ_(crypt_token_status(cd, 2, &dummy), CRYPT_TOKEN_EXTERNAL_UNKNOWN);
+ OK_(strcmp(dummy, "test_token1"));
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 1, passptr1, 0), "Unknown token handler");
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 2, passptr1, 0), "Unknown token handler");
+ OK_(crypt_token_register(&th3));
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 1, passptr1, 0), "Token validation failed");
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 2, passptr1, 0), 1);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ // test crypt_token_json_get returns correct token id
+ EQ_(crypt_token_json_get(cd, 2, &dummy), 2);
+
+ // exercise assign/unassign keyslots API
+ EQ_(crypt_token_unassign_keyslot(cd, 2, 1), 2);
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 2, passptr1, 0), "Token assigned to no keyslot");
+ EQ_(crypt_token_assign_keyslot(cd, 2, 0), 2);
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 2, passptr1, 0), "Wrong passphrase");
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 2, passptr, 0), 0);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(crypt_token_json_set(cd, 1, NULL), 1);
+ FAIL_(crypt_token_json_get(cd, 1, &dummy), "Token is not there");
+ EQ_(crypt_token_unassign_keyslot(cd, 2, CRYPT_ANY_SLOT), 2);
+ EQ_(crypt_token_unassign_keyslot(cd, 0, CRYPT_ANY_SLOT), 0);
+
+ // various tests related to unassigned keyslot to volume segment
+ EQ_(crypt_keyslot_add_by_key(cd, 3, NULL, 32, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT), 3);
+ EQ_(crypt_token_assign_keyslot(cd, 2, 0), 2);
+ EQ_(crypt_token_assign_keyslot(cd, 0, 3), 0);
+
+ EQ_(crypt_activate_by_token(cd, NULL, 2, passptr, 0), 0);
+ EQ_(crypt_activate_by_token(cd, NULL, 0, passptr1, CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY), 3);
+ // FIXME: useless error message here (or missing one to be specific)
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 0, passptr1, 0), "No volume key available in token keyslots");
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 2, passptr, 0), 0);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(crypt_token_assign_keyslot(cd, 0, 1), 0);
+ OK_(crypt_token_is_assigned(cd, 0, 1));
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 0, passptr1, 0), 1);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ EQ_(crypt_token_assign_keyslot(cd, 2, 3), 2);
+ OK_(crypt_token_is_assigned(cd, 2, 3));
+ EQ_(crypt_activate_by_token(cd, NULL, 2, passptr, 0), 0);
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 2, passptr, 0), 0);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+#ifdef KERNEL_KEYRING
+ if (t_dm_crypt_keyring_support()) {
+ EQ_(crypt_activate_by_token(cd, NULL, 2, passptr, CRYPT_ACTIVATE_KEYRING_KEY), 0);
+ OK_(_volume_key_in_keyring(cd, 0));
+ }
+ OK_(crypt_volume_key_keyring(cd, 0));
+#endif
+ FAIL_(crypt_activate_by_token(cd, NULL, 2, passptr, CRYPT_ACTIVATE_KEYRING_KEY), "Can't use keyring when disabled in library");
+ OK_(crypt_volume_key_keyring(cd, 1));
+
+ EQ_(crypt_token_luks2_keyring_set(cd, 5, &params), 5);
+ EQ_(crypt_token_status(cd, 5, &dummy), CRYPT_TOKEN_INTERNAL);
+ OK_(strcmp(dummy, "luks2-keyring"));
+
+ FAIL_(crypt_token_luks2_keyring_get(cd, 2, &params), "Token is not luks2-keyring type");
+
+ FAIL_(crypt_token_json_set(cd, CRYPT_ANY_TOKEN, BOGUS_TOKEN0_JSON), "luks2- reserved prefix.");
+ FAIL_(crypt_token_json_set(cd, CRYPT_ANY_TOKEN, BOGUS_TOKEN1_JSON), "luks2- reserved prefix.");
+
+ // test we can use crypt_token_json_set for valid luks2-keyring token
+ FAIL_(crypt_token_json_set(cd, 12, LUKS2_KEYRING_TOKEN_JSON_BAD("\"0\"", "\"my_desc_x\"")), "Strict luks2-keyring token validation failed");
+ EQ_(crypt_token_status(cd, 12, NULL), CRYPT_TOKEN_INACTIVE);
+ FAIL_(crypt_token_json_set(cd, 12, LUKS2_KEYRING_TOKEN_JSON("\"5\"", "\"my_desc\"")), "Missing keyslot 5.");
+ EQ_(crypt_token_json_set(cd, 10, LUKS2_KEYRING_TOKEN_JSON("\"1\"", "\"my_desc\"")), 10);
+ EQ_(crypt_token_status(cd, 10, &dummy), CRYPT_TOKEN_INTERNAL);
+ OK_(strcmp(dummy, "luks2-keyring"));
+ params.key_description = NULL;
+ EQ_(crypt_token_luks2_keyring_get(cd, 10, &params), 10);
+ OK_(strcmp(params.key_description, "my_desc"));
+
+ OK_(crypt_token_is_assigned(cd, 10, 1));
+ // unassigned tests
+ EQ_(crypt_token_is_assigned(cd, 10, 21), -ENOENT);
+ EQ_(crypt_token_is_assigned(cd, 21, 1), -ENOENT);
+ // wrong keyslot or token id tests
+ EQ_(crypt_token_is_assigned(cd, -1, 1), -EINVAL);
+ EQ_(crypt_token_is_assigned(cd, 32, 1), -EINVAL);
+ EQ_(crypt_token_is_assigned(cd, 10, -1), -EINVAL);
+ EQ_(crypt_token_is_assigned(cd, 10, 32), -EINVAL);
+ EQ_(crypt_token_is_assigned(cd, -1, -1), -EINVAL);
+ EQ_(crypt_token_is_assigned(cd, 32, 32), -EINVAL);
+
+ crypt_free(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void LuksConvert(void)
+{
+ struct crypt_device *cd;
+ uint64_t offset, r_payload_offset;
+
+ const char *json = "{\"type\":\"convert_block\",\"keyslots\":[]}";
+ const struct crypt_pbkdf_type argon = {
+ .type = CRYPT_KDF_ARGON2I,
+ .hash = "sha512",
+ .time_ms = 1,
+ .max_memory_kb = 1024,
+ .parallel_threads = 1
+ }, pbkdf2 = {
+ .type = CRYPT_KDF_PBKDF2,
+ .hash = "sha1",
+ .time_ms = 1
+ };
+
+ struct crypt_params_luks2 luks2 = {
+ .pbkdf = &pbkdf2,
+ .sector_size = 512
+ };
+
+ const char *cipher = "aes";
+ const char *cipher_mode = "xts-plain64";
+
+ // prepare the device
+ OK_(crypt_init(&cd, DEVICE_1));
+ crypt_set_iteration_time(cd, 1);
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, NULL, 32, NULL));
+ offset = crypt_get_data_offset(cd);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 0);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 7, NULL, 32, PASSPHRASE1, strlen(PASSPHRASE1)), 7);
+ crypt_free(cd);
+
+ // convert LUKSv1 -> LUKSv2
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ FAIL_(crypt_convert(cd, CRYPT_LUKS1, NULL), "format is already LUKSv1");
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0), 0);
+ FAIL_(crypt_convert(cd, CRYPT_LUKS2, NULL), "device is active");
+ OK_(strcmp(crypt_get_type(cd), CRYPT_LUKS1));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ OK_(crypt_convert(cd, CRYPT_LUKS2, NULL));
+ OK_(strcmp(crypt_get_type(cd), CRYPT_LUKS2));
+ crypt_free(cd);
+
+ // check result
+ OK_(crypt_init(&cd, DEVICE_1));
+ FAIL_(crypt_load(cd, CRYPT_LUKS1, NULL), "wrong luks format");
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ OK_(strcmp(crypt_get_type(cd), CRYPT_LUKS2));
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0), 0);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE1, strlen(PASSPHRASE1), 0), 7);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ FAIL_(crypt_convert(cd, CRYPT_LUKS2, NULL), "format is already LUKSv2");
+ OK_(strcmp(crypt_get_type(cd), CRYPT_LUKS2));
+ crypt_free(cd);
+
+ // convert LUKSv2 -> LUKSv1
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0), 0);
+ FAIL_(crypt_convert(cd, CRYPT_LUKS1, NULL), "device is active");
+ OK_(strcmp(crypt_get_type(cd), CRYPT_LUKS2));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ OK_(crypt_convert(cd, CRYPT_LUKS1, NULL));
+ OK_(strcmp(crypt_get_type(cd), CRYPT_LUKS1));
+ crypt_free(cd);
+
+ // check result
+ OK_(crypt_init(&cd, DEVICE_1));
+ FAIL_(crypt_load(cd, CRYPT_LUKS2, NULL), "wrong luks format");
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ OK_(strcmp(crypt_get_type(cd), CRYPT_LUKS1));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0), 0);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE1, strlen(PASSPHRASE1), 0), 7);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ FAIL_(crypt_convert(cd, CRYPT_LUKS1, NULL), "format is already LUKSv1");
+ OK_(strcmp(crypt_get_type(cd), CRYPT_LUKS1));
+ crypt_free(cd);
+
+ // exercice non-pbkdf2 LUKSv2 conversion
+ if (!_fips_mode) {
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_set_data_offset(cd, offset));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, 32, NULL));
+ OK_(crypt_set_pbkdf_type(cd, &argon));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 0);
+ FAIL_(crypt_convert(cd, CRYPT_LUKS1, NULL), "Incompatible pbkdf with LUKSv1 format");
+ crypt_free(cd);
+ }
+
+ // exercice non LUKS1 compatible keyslot
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_set_data_offset(cd, offset));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, 32, &luks2));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 0);
+ EQ_(crypt_keyslot_add_by_key(cd, 1, NULL, 32, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT), 1);
+ // FIXME: following test fails as expected but for a different reason
+ FAIL_(crypt_convert(cd, CRYPT_LUKS1, NULL), "Unassigned keyslots are incompatible with LUKSv1 format");
+ crypt_free(cd);
+
+ // exercice LUKSv2 conversion with single pbkdf2 keyslot being active
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_set_data_offset(cd, offset));
+ OK_(crypt_set_pbkdf_type(cd, &pbkdf2));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, 32, NULL));
+ offset = crypt_get_data_offset(cd);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 0);
+ if (!_fips_mode) {
+ OK_(crypt_set_pbkdf_type(cd, &argon));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 1, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 1);
+ FAIL_(crypt_convert(cd, CRYPT_LUKS1, NULL), "Different hash for digest and keyslot.");
+ OK_(crypt_keyslot_destroy(cd, 1));
+ }
+ OK_(crypt_convert(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASSPHRASE, strlen(PASSPHRASE), 0), 0);
+ crypt_free(cd);
+
+ // do not allow conversion on keyslot No > 7
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_set_data_offset(cd, offset));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, 32, &luks2));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 0);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 8, NULL, 32, PASSPHRASE1, strlen(PASSPHRASE1)), 8);
+ FAIL_(crypt_convert(cd, CRYPT_LUKS1, NULL), "Can't convert keyslot No 8");
+ crypt_free(cd);
+
+ // do not allow conversion with token
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_set_data_offset(cd, offset));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, 32, &luks2));
+ OK_(crypt_token_json_set(cd, CRYPT_ANY_TOKEN, json));
+ FAIL_(crypt_convert(cd, CRYPT_LUKS1, NULL), "Can't convert header with token.");
+ crypt_free(cd);
+
+ // should be enough for both luks1 and luks2 devices with all vk lengths
+ OK_(get_luks2_offsets(1, 0, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_1S, r_payload_offset + 1));
+
+ // do not allow conversion for legacy luks1 device (non-aligned keyslot offset)
+ OK_(_system("dd if=" CONV_DIR "/" CONV_L1_256_LEGACY " of=" DMDIR L_DEVICE_1S " bs=1M count=2 oflag=direct 2>/dev/null", 1));
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ FAIL_(crypt_convert(cd, CRYPT_LUKS2, NULL), "Can't convert device with unaligned keyslot offset");
+ crypt_free(cd);
+
+ /*
+ * do not allow conversion on images if there's not enough space between
+ * last keyslot and data offset (should not happen on headers created
+ * with cryptsetup)
+ */
+ OK_(_system("dd if=" CONV_DIR "/" CONV_L1_256_UNMOVABLE " of=" DMDIR L_DEVICE_1S " bs=1M count=2 oflag=direct 2>/dev/null", 1));
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ FAIL_(crypt_convert(cd, CRYPT_LUKS2, NULL), "Can't convert device with unaligned keyslot offset");
+ crypt_free(cd);
+
+ // compat conversion tests
+ // LUKS1 -> LUKS2
+
+ // 128b key
+ OK_(_system("dd if=" CONV_DIR "/" CONV_L1_128 " of=" DMDIR L_DEVICE_1S " bs=1M count=2 oflag=direct 2>/dev/null", 1));
+
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ offset = crypt_get_data_offset(cd);
+ OK_(crypt_convert(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS2), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+
+ // 256b key
+ OK_(_system("dd if=" CONV_DIR "/" CONV_L1_256 " of=" DMDIR L_DEVICE_1S " bs=1M count=2 oflag=direct 2>/dev/null", 1));
+
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ offset = crypt_get_data_offset(cd);
+ OK_(crypt_convert(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS2), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+
+ // 512b key
+ OK_(_system("dd if=" CONV_DIR "/" CONV_L1_512 " of=" DMDIR L_DEVICE_1S " bs=1M count=2 oflag=direct 2>/dev/null", 1));
+
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ offset = crypt_get_data_offset(cd);
+ OK_(crypt_convert(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS2), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+
+ // detached LUKS1 header conversion
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L1_128_DET));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ offset = crypt_get_data_offset(cd);
+ OK_(crypt_convert(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS2), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L1_128_DET));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+
+ // 256b key
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L1_256_DET));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ offset = crypt_get_data_offset(cd);
+ OK_(crypt_convert(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS2), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L1_256_DET));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+
+ // 512b key
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L1_512_DET));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ offset = crypt_get_data_offset(cd);
+ OK_(crypt_convert(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS2), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L1_512_DET));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+
+ // LUKS2 -> LUKS1
+ // 128b key
+ OK_(_system("dd if=" CONV_DIR "/" CONV_L2_128 " of=" DMDIR L_DEVICE_1S " bs=1M count=4 oflag=direct 2>/dev/null", 1));
+
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ offset = crypt_get_data_offset(cd);
+ OK_(crypt_convert(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS1), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+
+ // 128b all LUKS1 keyslots used
+ OK_(_system("dd if=" CONV_DIR "/" CONV_L2_128_FULL " of=" DMDIR L_DEVICE_1S " bs=1M count=4 oflag=direct 2>/dev/null", 1));
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_convert(cd, CRYPT_LUKS1, NULL));
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS1), 0);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 1, PASS1, strlen(PASS1), 0), 1);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 2, PASS2, strlen(PASS2), 0), 2);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 3, PASS3, strlen(PASS3), 0), 3);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 4, PASS4, strlen(PASS4), 0), 4);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 5, PASS5, strlen(PASS5), 0), 5);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 6, PASS6, strlen(PASS6), 0), 6);
+ crypt_free(cd);
+
+ // 256b key
+ OK_(_system("dd if=" CONV_DIR "/" CONV_L2_256 " of=" DMDIR L_DEVICE_1S " bs=1M count=4 oflag=direct 2>/dev/null", 1));
+
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ offset = crypt_get_data_offset(cd);
+ OK_(crypt_convert(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS1), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+
+ // 256b all LUKS1 keyslots used
+ OK_(_system("dd if=" CONV_DIR "/" CONV_L2_256_FULL " of=" DMDIR L_DEVICE_1S " bs=1M count=4 oflag=direct 2>/dev/null", 1));
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_convert(cd, CRYPT_LUKS1, NULL));
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS1), 0);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 1, PASS1, strlen(PASS1), 0), 1);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 2, PASS2, strlen(PASS2), 0), 2);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 3, PASS3, strlen(PASS3), 0), 3);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 4, PASS4, strlen(PASS4), 0), 4);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 5, PASS5, strlen(PASS5), 0), 5);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 6, PASS6, strlen(PASS6), 0), 6);
+ crypt_free(cd);
+
+ // 512b key
+ OK_(_system("dd if=" CONV_DIR "/" CONV_L2_512 " of=" DMDIR L_DEVICE_1S " bs=1M count=4 oflag=direct 2>/dev/null", 1));
+
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ offset = crypt_get_data_offset(cd);
+ OK_(crypt_convert(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS1), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+
+ // 512b all LUKS1 keyslots used
+ OK_(_system("dd if=" CONV_DIR "/" CONV_L2_512_FULL " of=" DMDIR L_DEVICE_1S " bs=1M count=4 oflag=direct 2>/dev/null", 1));
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_convert(cd, CRYPT_LUKS1, NULL));
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS1), 0);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 1, PASS1, strlen(PASS1), 0), 1);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 2, PASS2, strlen(PASS2), 0), 2);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 3, PASS3, strlen(PASS3), 0), 3);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 4, PASS4, strlen(PASS4), 0), 4);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 5, PASS5, strlen(PASS5), 0), 5);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 6, PASS6, strlen(PASS6), 0), 6);
+ crypt_free(cd);
+
+ // detached headers
+ // 128b
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L2_128_DET));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ offset = crypt_get_data_offset(cd);
+ OK_(crypt_convert(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS1), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L2_128_DET));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+
+ // 128b all LUKS1 keyslots used
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L2_128_DET_FULL));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_convert(cd, CRYPT_LUKS1, NULL));
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS1), 0);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L2_128_DET_FULL));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 1, PASS1, strlen(PASS1), 0), 1);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 2, PASS2, strlen(PASS2), 0), 2);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 3, PASS3, strlen(PASS3), 0), 3);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 4, PASS4, strlen(PASS4), 0), 4);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 5, PASS5, strlen(PASS5), 0), 5);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 6, PASS6, strlen(PASS6), 0), 6);
+ crypt_free(cd);
+
+ // 256b key
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L2_256_DET));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ offset = crypt_get_data_offset(cd);
+ OK_(crypt_convert(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS1), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L2_256_DET));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+
+ // 256b all LUKS1 keyslots used
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L2_256_DET_FULL));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_convert(cd, CRYPT_LUKS1, NULL));
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS1), 0);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L2_256_DET_FULL));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 1, PASS1, strlen(PASS1), 0), 1);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 2, PASS2, strlen(PASS2), 0), 2);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 3, PASS3, strlen(PASS3), 0), 3);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 4, PASS4, strlen(PASS4), 0), 4);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 5, PASS5, strlen(PASS5), 0), 5);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 6, PASS6, strlen(PASS6), 0), 6);
+ crypt_free(cd);
+
+ // 512b key
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L2_512_DET));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ offset = crypt_get_data_offset(cd);
+ OK_(crypt_convert(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS1), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L2_512_DET));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_get_data_offset(cd), offset);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ crypt_free(cd);
+
+ // 512b all LUKS1 keyslots used
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L2_512_DET_FULL));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_convert(cd, CRYPT_LUKS1, NULL));
+ EQ_(strcmp(crypt_get_type(cd), CRYPT_LUKS1), 0);
+ crypt_free(cd);
+ OK_(crypt_init(&cd, CONV_DIR "/" CONV_L2_512_DET_FULL));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 0, PASS0, strlen(PASS0), 0), 0);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 7, PASS7, strlen(PASS7), 0), 7);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 1, PASS1, strlen(PASS1), 0), 1);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 2, PASS2, strlen(PASS2), 0), 2);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 3, PASS3, strlen(PASS3), 0), 3);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 4, PASS4, strlen(PASS4), 0), 4);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 5, PASS5, strlen(PASS5), 0), 5);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 6, PASS6, strlen(PASS6), 0), 6);
+ crypt_free(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void Pbkdf(void)
+{
+ struct crypt_device *cd;
+ const struct crypt_pbkdf_type *pbkdf;
+
+ const char *cipher = "aes", *mode="xts-plain64";
+ struct crypt_pbkdf_type argon2 = {
+ .type = CRYPT_KDF_ARGON2I,
+ .hash = DEFAULT_LUKS1_HASH,
+ .time_ms = 6,
+ .max_memory_kb = 1024,
+ .parallel_threads = 1
+ }, pbkdf2 = {
+ .type = CRYPT_KDF_PBKDF2,
+ .hash = DEFAULT_LUKS1_HASH,
+ .time_ms = 9
+ }, bad = {
+ .type = "hamster_pbkdf",
+ .hash = DEFAULT_LUKS1_HASH
+ };
+ struct crypt_params_plain params = {
+ .hash = "sha1",
+ .skip = 0,
+ .offset = 0,
+ .size = 0
+ };
+ struct crypt_params_luks1 luks1 = {
+ .hash = "whirlpool", // test non-standard hash
+ .data_alignment = 2048,
+ };
+
+ uint64_t r_payload_offset;
+
+ /* Only PBKDF2 is allowed in FIPS, these tests cannot be run. */
+ if (_fips_mode)
+ return;
+
+ OK_(get_luks2_offsets(1, 0, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 1));
+
+ NULL_(crypt_get_pbkdf_type_params(NULL));
+ NULL_(crypt_get_pbkdf_type_params("suslik"));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type_params(CRYPT_KDF_PBKDF2));
+ OK_(strcmp(pbkdf->type, CRYPT_KDF_PBKDF2));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type_params(CRYPT_KDF_ARGON2I));
+ OK_(strcmp(pbkdf->type, CRYPT_KDF_ARGON2I));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type_params(CRYPT_KDF_ARGON2ID));
+ OK_(strcmp(pbkdf->type, CRYPT_KDF_ARGON2ID));
+
+ // test empty context
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ NULL_(crypt_get_pbkdf_type(cd));
+ OK_(crypt_set_pbkdf_type(cd, &argon2));
+ NOTNULL_(crypt_get_pbkdf_type(cd));
+ OK_(crypt_set_pbkdf_type(cd, &pbkdf2));
+ NOTNULL_(crypt_get_pbkdf_type(cd));
+ OK_(crypt_set_pbkdf_type(cd, NULL));
+ NOTNULL_(crypt_get_pbkdf_type(cd));
+
+ // test plain device
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, mode, NULL, NULL, 32, &params));
+ OK_(crypt_set_pbkdf_type(cd, &argon2));
+ OK_(crypt_set_pbkdf_type(cd, &pbkdf2));
+ OK_(crypt_set_pbkdf_type(cd, NULL));
+ NOTNULL_(crypt_get_pbkdf_type(cd));
+ crypt_free(cd);
+
+ // test LUKSv1 device
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, mode, NULL, NULL, 32, NULL));
+ FAIL_(crypt_set_pbkdf_type(cd, &argon2), "Unsupported with non-LUKS2 devices");
+ OK_(crypt_set_pbkdf_type(cd, &pbkdf2));
+ OK_(crypt_set_pbkdf_type(cd, NULL));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ EQ_(pbkdf->time_ms, DEFAULT_LUKS1_ITER_TIME);
+ crypt_free(cd);
+ // test value set in crypt_set_iteration_time() can be obtained via following crypt_get_pbkdf_type()
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ crypt_set_iteration_time(cd, 42);
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, mode, NULL, NULL, 32, NULL));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ EQ_(pbkdf->time_ms, 42);
+ // test crypt_get_pbkdf_type() returns expected values for LUKSv1
+ OK_(strcmp(pbkdf->type, CRYPT_KDF_PBKDF2));
+ OK_(strcmp(pbkdf->hash, DEFAULT_LUKS1_HASH));
+ EQ_(pbkdf->max_memory_kb, 0);
+ EQ_(pbkdf->parallel_threads, 0);
+ crypt_set_iteration_time(cd, 43);
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ EQ_(pbkdf->time_ms, 43);
+ crypt_free(cd);
+ // test whether crypt_get_pbkdf_type() after double crypt_load()
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ crypt_set_iteration_time(cd, 42);
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ EQ_(pbkdf->time_ms, 42);
+ crypt_free(cd);
+ // test whether hash passed via *params in crypt_load() has higher priority
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ crypt_set_iteration_time(cd, 1);
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, mode, NULL, NULL, 32, &luks1));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ OK_(strcmp(pbkdf->hash, luks1.hash));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ OK_(strcmp(pbkdf->hash, luks1.hash));
+ crypt_free(cd);
+
+ // test LUKSv2 device
+ // test default values are set
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, mode, NULL, NULL, 32, NULL));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ OK_(strcmp(pbkdf->type, DEFAULT_LUKS2_PBKDF));
+ OK_(strcmp(pbkdf->hash, DEFAULT_LUKS1_HASH));
+ EQ_(pbkdf->time_ms, DEFAULT_LUKS2_ITER_TIME);
+ EQ_(pbkdf->max_memory_kb, adjusted_pbkdf_memory());
+ EQ_(pbkdf->parallel_threads, _min(cpus_online(), DEFAULT_LUKS2_PARALLEL_THREADS));
+ // set and verify argon2 type
+ OK_(crypt_set_pbkdf_type(cd, &argon2));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ OK_(strcmp(pbkdf->type, argon2.type));
+ OK_(strcmp(pbkdf->hash, argon2.hash));
+ EQ_(pbkdf->time_ms, argon2.time_ms);
+ EQ_(pbkdf->max_memory_kb, argon2.max_memory_kb);
+ EQ_(pbkdf->parallel_threads, argon2.parallel_threads);
+ // set and verify pbkdf2 type
+ OK_(crypt_set_pbkdf_type(cd, &pbkdf2));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ OK_(strcmp(pbkdf->type, pbkdf2.type));
+ OK_(strcmp(pbkdf->hash, pbkdf2.hash));
+ EQ_(pbkdf->time_ms, pbkdf2.time_ms);
+ EQ_(pbkdf->max_memory_kb, pbkdf2.max_memory_kb);
+ EQ_(pbkdf->parallel_threads, pbkdf2.parallel_threads);
+ // reset and verify default values
+ crypt_set_iteration_time(cd, 1); // it's supposed to override this call
+ OK_(crypt_set_pbkdf_type(cd, NULL));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ OK_(strcmp(pbkdf->type, DEFAULT_LUKS2_PBKDF));
+ OK_(strcmp(pbkdf->hash, DEFAULT_LUKS1_HASH));
+ EQ_(pbkdf->time_ms, DEFAULT_LUKS2_ITER_TIME);
+ EQ_(pbkdf->max_memory_kb, adjusted_pbkdf_memory());
+ EQ_(pbkdf->parallel_threads, _min(cpus_online(), DEFAULT_LUKS2_PARALLEL_THREADS));
+ // try to pass illegal values
+ argon2.parallel_threads = 0;
+ FAIL_(crypt_set_pbkdf_type(cd, &argon2), "Parallel threads can't be 0");
+ argon2.parallel_threads = 1;
+ argon2.max_memory_kb = 0;
+ FAIL_(crypt_set_pbkdf_type(cd, &argon2), "Memory can't be 0");
+ argon2.max_memory_kb = 1024;
+ pbkdf2.parallel_threads = 1;
+ FAIL_(crypt_set_pbkdf_type(cd, &pbkdf2), "Parallel threads can't be set with pbkdf2 type");
+ pbkdf2.parallel_threads = 0;
+ pbkdf2.max_memory_kb = 512;
+ FAIL_(crypt_set_pbkdf_type(cd, &pbkdf2), "Memory can't be set with pbkdf2 type");
+ FAIL_(crypt_set_pbkdf_type(cd, &bad), "Unknown type member");
+ bad.type = CRYPT_KDF_PBKDF2;
+ bad.hash = NULL;
+ FAIL_(crypt_set_pbkdf_type(cd, &bad), "Hash member is empty");
+ bad.type = NULL;
+ bad.hash = DEFAULT_LUKS1_HASH;
+ FAIL_(crypt_set_pbkdf_type(cd, &bad), "Pbkdf type member is empty");
+ bad.hash = "hamster_hash";
+ FAIL_(crypt_set_pbkdf_type(cd, &pbkdf2), "Unknown hash member");
+ crypt_free(cd);
+ // test whether crypt_get_pbkdf_type() behaves accordingly after second crypt_load() call
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ OK_(strcmp(pbkdf->type, DEFAULT_LUKS2_PBKDF));
+ OK_(strcmp(pbkdf->hash, DEFAULT_LUKS1_HASH));
+ EQ_(pbkdf->time_ms, DEFAULT_LUKS2_ITER_TIME);
+ EQ_(pbkdf->max_memory_kb, adjusted_pbkdf_memory());
+ EQ_(pbkdf->parallel_threads, _min(cpus_online(), DEFAULT_LUKS2_PARALLEL_THREADS));
+ crypt_set_iteration_time(cd, 1);
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ OK_(strcmp(pbkdf->type, DEFAULT_LUKS2_PBKDF));
+ OK_(strcmp(pbkdf->hash, DEFAULT_LUKS1_HASH));
+ EQ_(pbkdf->time_ms, 1);
+ EQ_(pbkdf->max_memory_kb, adjusted_pbkdf_memory());
+ EQ_(pbkdf->parallel_threads, _min(cpus_online(), DEFAULT_LUKS2_PARALLEL_THREADS));
+ crypt_free(cd);
+
+ // test crypt_set_pbkdf_type() overwrites invalid value set by crypt_set_iteration_time()
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ crypt_set_iteration_time(cd, 0);
+ OK_(crypt_set_pbkdf_type(cd, &argon2));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ OK_(strcmp(pbkdf->type, argon2.type));
+ EQ_(pbkdf->time_ms, argon2.time_ms);
+
+ // force iterations
+ argon2.iterations = 33;
+ argon2.flags = CRYPT_PBKDF_NO_BENCHMARK;
+ OK_(crypt_set_pbkdf_type(cd, &argon2));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ EQ_(pbkdf->iterations, 33);
+ EQ_(pbkdf->flags, CRYPT_PBKDF_NO_BENCHMARK);
+
+ // time may be unset with iterations
+ argon2.time_ms = 0;
+ OK_(crypt_set_pbkdf_type(cd, &argon2));
+ argon2.flags &= ~CRYPT_PBKDF_NO_BENCHMARK;
+ FAIL_(crypt_set_pbkdf_type(cd, &argon2), "Illegal time value.");
+
+ pbkdf2.time_ms = 0;
+ pbkdf2.flags = CRYPT_PBKDF_NO_BENCHMARK;
+ pbkdf2.parallel_threads = 0;
+ pbkdf2.max_memory_kb = 0;
+ pbkdf2.iterations = 1000;
+ OK_(crypt_set_pbkdf_type(cd, &pbkdf2));
+ pbkdf2.flags &= ~CRYPT_PBKDF_NO_BENCHMARK;
+ FAIL_(crypt_set_pbkdf_type(cd, &pbkdf2), "Illegal time value.");
+
+ // hash is relevant only with pbkdf2
+ pbkdf2.time_ms = 9;
+ pbkdf2.hash = NULL;
+ FAIL_(crypt_set_pbkdf_type(cd, &pbkdf2), "Hash is mandatory for pbkdf2");
+ pbkdf2.hash = "sha1";
+ OK_(crypt_set_pbkdf_type(cd, &pbkdf2));
+
+ argon2.time_ms = 9;
+ argon2.hash = "sha1"; // will be ignored
+ OK_(crypt_set_pbkdf_type(cd, &argon2));
+ argon2.hash = NULL;
+ OK_(crypt_set_pbkdf_type(cd, &argon2));
+
+ crypt_free(cd);
+
+ NOTNULL_(pbkdf = crypt_get_pbkdf_default(CRYPT_LUKS1));
+ OK_(strcmp(pbkdf->type, CRYPT_KDF_PBKDF2));
+ EQ_(pbkdf->time_ms, DEFAULT_LUKS1_ITER_TIME);
+ OK_(strcmp(pbkdf->hash, DEFAULT_LUKS1_HASH));
+ EQ_(pbkdf->max_memory_kb, 0);
+ EQ_(pbkdf->parallel_threads, 0);
+
+ NOTNULL_(pbkdf = crypt_get_pbkdf_default(CRYPT_LUKS2));
+ OK_(strcmp(pbkdf->type, DEFAULT_LUKS2_PBKDF));
+ EQ_(pbkdf->time_ms, DEFAULT_LUKS2_ITER_TIME);
+ OK_(strcmp(pbkdf->hash, DEFAULT_LUKS1_HASH));
+ EQ_(pbkdf->max_memory_kb, DEFAULT_LUKS2_MEMORY_KB);
+ EQ_(pbkdf->parallel_threads, DEFAULT_LUKS2_PARALLEL_THREADS);
+
+ NULL_(pbkdf = crypt_get_pbkdf_default(CRYPT_PLAIN));
+
+ _cleanup_dmdevices();
+}
+
+static void Luks2KeyslotAdd(void)
+{
+ char key[128], key2[128], key_ret[128];
+ struct crypt_device *cd;
+ const char *cipher = "aes", *cipher_mode="xts-plain64";
+ const char *mk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ const char *mk_hex2 = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1e";
+ size_t key_ret_len, key_size = strlen(mk_hex) / 2;
+ uint64_t r_payload_offset;
+
+ crypt_decode_key(key, mk_hex, key_size);
+ crypt_decode_key(key2, mk_hex2, key_size);
+
+ OK_(get_luks2_offsets(1, 0, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 1));
+
+ /* test crypt_keyslot_add_by_key */
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ crypt_set_iteration_time(cd, 1);
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, NULL));
+ EQ_(crypt_keyslot_add_by_key(cd, 1, key2, key_size, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT), 1);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 0, key, key_size, PASSPHRASE, strlen(PASSPHRASE)), 0);
+ EQ_(crypt_keyslot_status(cd, 0), CRYPT_SLOT_ACTIVE_LAST);
+ EQ_(crypt_keyslot_status(cd, 1), CRYPT_SLOT_UNBOUND);
+ /* must not activate volume with keyslot unassigned to a segment */
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key2, key_size, 0), "Key doesn't match volume key digest");
+ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, 1, PASSPHRASE1, strlen(PASSPHRASE1), 0), "Keyslot not assigned to volume");
+ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE1, strlen(PASSPHRASE1), 0), "No keyslot assigned to volume with this passphrase");
+ /* unusable for volume activation even in test mode */
+ FAIL_(crypt_activate_by_volume_key(cd, NULL, key2, key_size, 0), "Key doesn't match volume key digest");
+ /* otoh passphrase check should pass */
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 1, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY), 1);
+ EQ_(crypt_activate_by_passphrase(cd, NULL, CRYPT_ANY_SLOT, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY), 1);
+ /* in general crypt_keyslot_add_by_key must allow any reasonable key size
+ * even though such keyslot will not be usable for segment encryption */
+ EQ_(crypt_keyslot_add_by_key(cd, 2, key2, key_size-1, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT), 2);
+ EQ_(crypt_keyslot_add_by_key(cd, 3, key2, 13, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT), 3);
+
+ FAIL_(crypt_keyslot_get_key_size(cd, CRYPT_ANY_SLOT), "Bad keyslot specification.");
+ EQ_(crypt_get_volume_key_size(cd), key_size);
+ EQ_(crypt_keyslot_get_key_size(cd, 0), key_size);
+ EQ_(crypt_keyslot_get_key_size(cd, 1), key_size);
+ EQ_(crypt_keyslot_get_key_size(cd, 2), key_size-1);
+ EQ_(crypt_keyslot_get_key_size(cd, 3), 13);
+
+ key_ret_len = key_size - 1;
+ FAIL_(crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key_ret, &key_ret_len, PASSPHRASE1, strlen(PASSPHRASE1)), "Wrong size or fips");
+
+ key_ret_len = 13;
+ FAIL_(crypt_volume_key_get(cd, 2, key_ret, &key_ret_len, PASSPHRASE1, strlen(PASSPHRASE1)), "wrong size");
+ EQ_(crypt_volume_key_get(cd, 3, key_ret, &key_ret_len, PASSPHRASE1, strlen(PASSPHRASE1)), 3);
+ FAIL_(crypt_activate_by_volume_key(cd, NULL, key_ret, key_ret_len, 0), "Not a volume key");
+ key_ret_len = key_size;
+ EQ_(crypt_volume_key_get(cd, 1, key_ret, &key_ret_len, PASSPHRASE1, strlen(PASSPHRASE1)), 1);
+
+ /* test force volume key change works as expected */
+ EQ_(crypt_keyslot_add_by_key(cd, 1, NULL, 0, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_SET), 1);
+ OK_(crypt_activate_by_volume_key(cd, NULL, key2, key_size, 0));
+ OK_(crypt_activate_by_volume_key(cd, NULL, key_ret, key_ret_len, 0));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key2, key_size, 0));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(crypt_activate_by_passphrase(cd, NULL, 1, PASSPHRASE1, strlen(PASSPHRASE1), 0), 1);
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 1, PASSPHRASE1, strlen(PASSPHRASE1), 0), 1);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ /* old keyslot must be unusable */
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Key doesn't match volume key digest");
+ FAIL_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0), "Key doesn't match volume key digest");
+ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, PASSPHRASE, strlen(PASSPHRASE), 0), "Keyslot not assigned to volume");
+ EQ_(crypt_keyslot_add_by_passphrase(cd, 5, PASSPHRASE1, strlen(PASSPHRASE1), PASSPHRASE1, strlen(PASSPHRASE1)), 5);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 6, key2, key_size, PASSPHRASE1, strlen(PASSPHRASE1)), 6);
+ /* regression test. check new keyslot is properly assigned to new volume key digest */
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 5, PASSPHRASE1, strlen(PASSPHRASE1), 0), 5);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 6, PASSPHRASE1, strlen(PASSPHRASE1), 0), 6);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ crypt_free(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void Luks2KeyslotParams(void)
+{
+ char key[128], key2[128];
+ struct crypt_device *cd;
+ const char *cipher = "aes", *cipher_mode="xts-plain64";
+ const char *cipher_spec = "aes-xts-plain64", *cipher_keyslot = "aes-cbc-essiv:sha256";
+ const char *mk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ const char *mk_hex2 = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1e";
+ size_t key_size_ret, key_size = strlen(mk_hex) / 2, keyslot_key_size = 16;
+ uint64_t r_payload_offset;
+
+ crypt_decode_key(key, mk_hex, key_size);
+ crypt_decode_key(key2, mk_hex2, key_size);
+
+ OK_(prepare_keyfile(KEYFILE1, PASSPHRASE, strlen(PASSPHRASE)));
+ OK_(prepare_keyfile(KEYFILE2, PASSPHRASE1, strlen(PASSPHRASE1)));
+
+ OK_(get_luks2_offsets(1, 0, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 1));
+
+ EQ_(key_size, 2 * keyslot_key_size);
+ /* test crypt_keyslot_add_by_key */
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ crypt_set_iteration_time(cd, 1);
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, NULL));
+ NULL_(crypt_keyslot_get_encryption(cd, 0, &key_size_ret));
+ OK_(strcmp(crypt_keyslot_get_encryption(cd, CRYPT_ANY_SLOT, &key_size_ret), cipher_spec));
+ EQ_(key_size_ret, key_size);
+
+ // Normal slots
+ EQ_(0, crypt_keyslot_add_by_volume_key(cd, 0, key, key_size, PASSPHRASE, strlen(PASSPHRASE)));
+ EQ_(1, crypt_keyslot_add_by_passphrase(cd, 1, PASSPHRASE, strlen(PASSPHRASE), PASSPHRASE1,strlen(PASSPHRASE1)));
+ EQ_(2, crypt_keyslot_add_by_key(cd, 2, key2, key_size, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT));
+ EQ_(6, crypt_keyslot_add_by_keyfile(cd, 6, KEYFILE1, 0, KEYFILE2, 0));
+
+ // Slots with different encryption type
+ OK_(crypt_keyslot_set_encryption(cd, cipher_keyslot, keyslot_key_size));
+ OK_(strcmp(crypt_keyslot_get_encryption(cd, CRYPT_ANY_SLOT, &key_size_ret), cipher_keyslot));
+ EQ_(key_size_ret, keyslot_key_size);
+
+ EQ_(3, crypt_keyslot_add_by_volume_key(cd, 3, key, key_size, PASSPHRASE, strlen(PASSPHRASE)));
+ EQ_(4, crypt_keyslot_add_by_passphrase(cd, 4, PASSPHRASE, strlen(PASSPHRASE), PASSPHRASE1,strlen(PASSPHRASE1)));
+ EQ_(5, crypt_keyslot_add_by_key(cd, 5, key2, key_size, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT));
+ EQ_(7, crypt_keyslot_add_by_keyfile(cd, 7, KEYFILE1, 0, KEYFILE2, 0));
+
+ crypt_free(cd);
+
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+
+ EQ_(crypt_keyslot_status(cd, 0), CRYPT_SLOT_ACTIVE);
+ OK_(strcmp(crypt_keyslot_get_encryption(cd, 0, &key_size_ret), cipher_spec));
+ EQ_(key_size_ret, key_size);
+
+ EQ_(crypt_keyslot_status(cd, 1), CRYPT_SLOT_ACTIVE);
+ OK_(strcmp(crypt_keyslot_get_encryption(cd, 1, &key_size_ret), cipher_spec));
+ EQ_(key_size_ret, key_size);
+
+ EQ_(crypt_keyslot_status(cd, 2), CRYPT_SLOT_UNBOUND);
+ OK_(strcmp(crypt_keyslot_get_encryption(cd, 2, &key_size_ret), cipher_spec));
+ EQ_(key_size_ret, key_size);
+
+ EQ_(crypt_keyslot_status(cd, 6), CRYPT_SLOT_ACTIVE);
+ OK_(strcmp(crypt_keyslot_get_encryption(cd, 6, &key_size_ret), cipher_spec));
+ EQ_(key_size_ret, key_size);
+
+ EQ_(crypt_keyslot_status(cd, 3), CRYPT_SLOT_ACTIVE);
+ OK_(strcmp(crypt_keyslot_get_encryption(cd, 3, &key_size_ret), cipher_keyslot));
+ EQ_(key_size_ret, keyslot_key_size);
+
+ EQ_(crypt_keyslot_status(cd, 4), CRYPT_SLOT_ACTIVE);
+ OK_(strcmp(crypt_keyslot_get_encryption(cd, 4, &key_size_ret), cipher_keyslot));
+ EQ_(key_size_ret, keyslot_key_size);
+
+ EQ_(crypt_keyslot_status(cd, 5), CRYPT_SLOT_UNBOUND);
+ OK_(strcmp(crypt_keyslot_get_encryption(cd, 5, &key_size_ret), cipher_keyslot));
+ EQ_(key_size_ret, keyslot_key_size);
+
+ EQ_(crypt_keyslot_status(cd, 7), CRYPT_SLOT_ACTIVE);
+ OK_(strcmp(crypt_keyslot_get_encryption(cd, 7, &key_size_ret), cipher_keyslot));
+ EQ_(key_size_ret, keyslot_key_size);
+
+ crypt_set_iteration_time(cd, 1);
+ EQ_(8, crypt_keyslot_change_by_passphrase(cd, 1, 8, PASSPHRASE1, strlen(PASSPHRASE1), PASSPHRASE, strlen(PASSPHRASE)));
+ OK_(strcmp(crypt_keyslot_get_encryption(cd, 8, &key_size_ret), cipher_spec));
+ EQ_(key_size_ret, key_size);
+
+ /* Revert to default */
+ EQ_(9, crypt_keyslot_change_by_passphrase(cd, 5, 9, PASSPHRASE1, strlen(PASSPHRASE1), PASSPHRASE, strlen(PASSPHRASE)));
+ OK_(strcmp(crypt_keyslot_get_encryption(cd, 9, &key_size_ret), cipher_spec));
+ EQ_(key_size_ret, key_size);
+
+ /* Set new encryption params */
+ OK_(crypt_keyslot_set_encryption(cd, cipher_keyslot, keyslot_key_size));
+
+ EQ_(1, crypt_keyslot_change_by_passphrase(cd, 8, 1, PASSPHRASE, strlen(PASSPHRASE), PASSPHRASE1, strlen(PASSPHRASE1)));
+ OK_(strcmp(crypt_keyslot_get_encryption(cd, 1, &key_size_ret), cipher_keyslot));
+ EQ_(key_size_ret, keyslot_key_size);
+
+ EQ_(10, crypt_keyslot_change_by_passphrase(cd, 2, 10, PASSPHRASE1, strlen(PASSPHRASE1), PASSPHRASE, strlen(PASSPHRASE)));
+ OK_(strcmp(crypt_keyslot_get_encryption(cd, 10, &key_size_ret), cipher_keyslot));
+ EQ_(key_size_ret, keyslot_key_size);
+
+ EQ_(0, crypt_keyslot_change_by_passphrase(cd, 0, 0, PASSPHRASE, strlen(PASSPHRASE), PASSPHRASE1, strlen(PASSPHRASE1)));
+ OK_(strcmp(crypt_keyslot_get_encryption(cd, 0, &key_size_ret), cipher_keyslot));
+ EQ_(key_size_ret, keyslot_key_size);
+
+ crypt_free(cd);
+
+ /* LUKS1 compatible calls */
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ crypt_set_iteration_time(cd, 1);
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, NULL));
+ NULL_(crypt_keyslot_get_encryption(cd, 0, &key_size_ret));
+ OK_(strcmp(crypt_keyslot_get_encryption(cd, CRYPT_ANY_SLOT, &key_size_ret), cipher_spec));
+ EQ_(key_size_ret, key_size);
+ EQ_(0, crypt_keyslot_add_by_volume_key(cd, 0, key, key_size, PASSPHRASE, strlen(PASSPHRASE)));
+ OK_(strcmp(crypt_keyslot_get_encryption(cd, 0, &key_size_ret), cipher_spec));
+ EQ_(key_size_ret, key_size);
+ crypt_free(cd);
+
+ _cleanup_dmdevices();
+ _remove_keyfiles();
+}
+
+static void Luks2ActivateByKeyring(void)
+{
+#ifdef KERNEL_KEYRING
+
+ key_serial_t kid, kid1;
+ struct crypt_device *cd;
+ uint64_t r_payload_offset;
+
+ const char *cipher = "aes";
+ const char *cipher_mode = "xts-plain64";
+
+ kid = add_key("user", KEY_DESC_TEST0, PASSPHRASE, strlen(PASSPHRASE), KEY_SPEC_THREAD_KEYRING);
+ kid1 = add_key("user", KEY_DESC_TEST1, PASSPHRASE1, strlen(PASSPHRASE1), KEY_SPEC_THREAD_KEYRING);
+ if (kid < 0 || kid1 < 0) {
+ printf("Test or kernel keyring are broken.\n");
+ exit(1);
+ }
+
+ OK_(get_luks2_offsets(1, 0, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 1));
+
+ // prepare the device
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ crypt_set_iteration_time(cd, 1);
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, 32, NULL));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 0);
+ EQ_(crypt_keyslot_add_by_key(cd, 1, NULL, 32, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT), 1);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 2, NULL, 32, PASSPHRASE1, strlen(PASSPHRASE1)), 2);
+ crypt_free(cd);
+
+ // FIXME: all following tests work as expected but most error messages are missing
+ // check activate by keyring works exactly same as by passphrase
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_activate_by_keyring(cd, NULL, KEY_DESC_TEST0, 0, 0), 0);
+ EQ_(crypt_activate_by_keyring(cd, CDEVICE_1, KEY_DESC_TEST0, 0, 0), 0);
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ FAIL_(crypt_activate_by_keyring(cd, CDEVICE_1, KEY_DESC_TEST0, 0, 0), "already open");
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ EQ_(crypt_activate_by_keyring(cd, NULL, KEY_DESC_TEST1, 1, CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY), 1);
+ EQ_(crypt_activate_by_keyring(cd, NULL, KEY_DESC_TEST1, 2, 0), 2);
+ FAIL_(crypt_activate_by_keyring(cd, CDEVICE_1, KEY_DESC_TEST1, 1, 0), "Keyslot not assigned to volume");
+ EQ_(crypt_activate_by_keyring(cd, CDEVICE_1, KEY_DESC_TEST1, 2, 0), 2);
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(crypt_activate_by_keyring(cd, CDEVICE_1, KEY_DESC_TEST1, CRYPT_ANY_SLOT, 0), 2);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ FAIL_(crypt_activate_by_keyring(cd, NULL, KEY_DESC_TEST0, 2, 0), "Failed to unclock keyslot");
+ FAIL_(crypt_activate_by_keyring(cd, NULL, KEY_DESC_TEST1, 0, 0), "Failed to unclock keyslot");
+ crypt_free(cd);
+
+ if (keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING)) {
+ printf("Test or kernel keyring are broken.\n");
+ exit(1);
+ }
+
+ if (keyctl_unlink(kid1, KEY_SPEC_THREAD_KEYRING)) {
+ printf("Test or kernel keyring are broken.\n");
+ exit(1);
+ }
+
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ FAIL_(crypt_activate_by_keyring(cd, NULL, KEY_DESC_TEST0, CRYPT_ANY_SLOT, 0), "no such key in keyring");
+ FAIL_(crypt_activate_by_keyring(cd, CDEVICE_1, KEY_DESC_TEST0, CRYPT_ANY_SLOT, 0), "no such key in keyring");
+ FAIL_(crypt_activate_by_keyring(cd, CDEVICE_1, KEY_DESC_TEST1, 2, 0), "no such key in keyring");
+ FAIL_(crypt_activate_by_keyring(cd, NULL, KEY_DESC_TEST1, 1, 0), "no such key in keyring");
+ crypt_free(cd);
+ _cleanup_dmdevices();
+#else
+ printf("WARNING: cryptsetup compiled with kernel keyring service disabled, skipping test.\n");
+#endif
+}
+
+static void Luks2Requirements(void)
+{
+ int r;
+ struct crypt_device *cd;
+ char key[128];
+ size_t key_size = 128;
+ const struct crypt_pbkdf_type *pbkdf;
+#ifdef KERNEL_KEYRING
+ key_serial_t kid;
+#endif
+ uint32_t flags;
+ uint64_t dummy, r_payload_offset;
+ struct crypt_active_device cad;
+
+ const char *token, *json = "{\"type\":\"test_token\",\"keyslots\":[]}";
+ struct crypt_pbkdf_type argon2 = {
+ .type = CRYPT_KDF_ARGON2I,
+ .hash = DEFAULT_LUKS1_HASH,
+ .time_ms = 6,
+ .max_memory_kb = 1024,
+ .parallel_threads = 1
+ }, pbkdf2 = {
+ .type = CRYPT_KDF_PBKDF2,
+ .hash = DEFAULT_LUKS1_HASH,
+ .time_ms = 9
+ };
+ struct crypt_token_params_luks2_keyring params_get, params = {
+ .key_description = KEY_DESC_TEST0
+ };
+
+ OK_(prepare_keyfile(KEYFILE1, "aaa", 3));
+ OK_(prepare_keyfile(KEYFILE2, "xxx", 3));
+
+ /* crypt_load (unrestricted) */
+ OK_(crypt_init(&cd, DEVICE_5));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ crypt_free(cd);
+
+ OK_(crypt_init(&cd, DEVICE_5));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+
+ /* crypt_dump (unrestricted) */
+ reset_log();
+ OK_(crypt_dump(cd));
+ OK_(!(global_lines != 0));
+ reset_log();
+
+ /* get & set pbkdf params (unrestricted) */
+ if (!_fips_mode) {
+ OK_(crypt_set_pbkdf_type(cd, &argon2));
+ NOTNULL_(crypt_get_pbkdf_type(cd));
+ }
+
+ OK_(crypt_set_pbkdf_type(cd, &pbkdf2));
+ NOTNULL_(crypt_get_pbkdf_type(cd));
+
+ /* crypt_set_iteration_time (unrestricted) */
+ crypt_set_iteration_time(cd, 1);
+ pbkdf = crypt_get_pbkdf_type(cd);
+ NOTNULL_(pbkdf);
+ EQ_(pbkdf->time_ms, 1);
+
+ /* crypt_convert (restricted) */
+ FAIL_((r = crypt_convert(cd, CRYPT_LUKS1, NULL)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_set_uuid (restricted) */
+ FAIL_((r = crypt_set_uuid(cd, NULL)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_set_label (restricted) */
+ FAIL_((r = crypt_set_label(cd, "label", "subsystem")), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_repair (with current repair capabilities it's unrestricted) */
+ OK_(crypt_repair(cd, CRYPT_LUKS2, NULL));
+
+ /* crypt_keyslot_add_passphrase (restricted) */
+ FAIL_((r = crypt_keyslot_add_by_passphrase(cd, CRYPT_ANY_SLOT, "aaa", 3, "bbb", 3)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_keyslot_change_by_passphrase (restricted) */
+ FAIL_((r = crypt_keyslot_change_by_passphrase(cd, CRYPT_ANY_SLOT, 9, "aaa", 3, "bbb", 3)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_keyslot_add_by_keyfile (restricted) */
+ FAIL_((r = crypt_keyslot_add_by_keyfile(cd, CRYPT_ANY_SLOT, KEYFILE1, 0, KEYFILE2, 0)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_keyslot_add_by_keyfile_offset (restricted) */
+ FAIL_((r = crypt_keyslot_add_by_keyfile_offset(cd, CRYPT_ANY_SLOT, KEYFILE1, 0, 0, KEYFILE2, 0, 0)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_volume_key_get (unrestricted, but see below) */
+ /* FIXME: FIPS requirement stop this, restructure the whole order of calls */
+ if (_fips_mode)
+ goto out;
+
+ OK_(crypt_volume_key_get(cd, 0, key, &key_size, "aaa", 3));
+
+ /* crypt_keyslot_add_by_volume_key (restricted) */
+ FAIL_((r = crypt_keyslot_add_by_volume_key(cd, CRYPT_ANY_SLOT, key, key_size, "xxx", 3)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_keyslot_add_by_key (restricted) */
+ FAIL_((r = crypt_keyslot_add_by_key(cd, CRYPT_ANY_SLOT, NULL, key_size, "xxx", 3, CRYPT_VOLUME_KEY_NO_SEGMENT)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_keyslot_add_by_key (restricted) */
+ FAIL_((r = crypt_keyslot_add_by_key(cd, CRYPT_ANY_SLOT, key, key_size, "xxx", 3, 0)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_persistent_flasgs_set (restricted) */
+ FAIL_((r = crypt_persistent_flags_set(cd, CRYPT_FLAGS_ACTIVATION, CRYPT_ACTIVATE_ALLOW_DISCARDS)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_persistent_flasgs_get (unrestricted) */
+ OK_(crypt_persistent_flags_get(cd, CRYPT_FLAGS_REQUIREMENTS, &flags));
+ EQ_(flags, (uint32_t) CRYPT_REQUIREMENT_UNKNOWN);
+
+ /* crypt_activate_by_passphrase (restricted for activation only) */
+ FAIL_((r = crypt_activate_by_passphrase(cd, CDEVICE_1, 0, "aaa", 3, 0)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+ OK_(crypt_activate_by_passphrase(cd, NULL, 0, "aaa", 3, 0));
+ OK_(crypt_activate_by_passphrase(cd, NULL, 0, "aaa", 3, t_dm_crypt_keyring_support() ? CRYPT_ACTIVATE_KEYRING_KEY : 0));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+
+ /* crypt_activate_by_keyfile (restricted for activation only) */
+ FAIL_((r = crypt_activate_by_keyfile(cd, CDEVICE_1, 0, KEYFILE1, 0, 0)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+ OK_(crypt_activate_by_keyfile(cd, NULL, 0, KEYFILE1, 0, 0));
+ OK_(crypt_activate_by_keyfile(cd, NULL, 0, KEYFILE1, 0, t_dm_crypt_keyring_support() ? CRYPT_ACTIVATE_KEYRING_KEY : 0));
+
+ /* crypt_activate_by_volume_key (restricted for activation only) */
+ FAIL_((r = crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+ OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0));
+ OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, t_dm_crypt_keyring_support() ? CRYPT_ACTIVATE_KEYRING_KEY : 0));
+
+#ifdef KERNEL_KEYRING
+ kid = add_key("user", KEY_DESC_TEST0, "aaa", 3, KEY_SPEC_THREAD_KEYRING);
+ if (kid < 0) {
+ printf("Test or kernel keyring are broken.\n");
+ exit(1);
+ }
+
+ /* crypt_activate_by_keyring (restricted for activation only) */
+ FAIL_((r = crypt_activate_by_keyring(cd, CDEVICE_1, KEY_DESC_TEST0, 0, 0)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+ OK_(crypt_activate_by_keyring(cd, NULL, KEY_DESC_TEST0, 0, 0));
+ OK_(crypt_activate_by_keyring(cd, NULL, KEY_DESC_TEST0, 0, t_dm_crypt_keyring_support() ? CRYPT_ACTIVATE_KEYRING_KEY : 0));
+#endif
+
+ /* crypt_volume_key_verify (unrestricted) */
+ OK_(crypt_volume_key_verify(cd, key, key_size));
+
+ /* crypt_get_cipher (unrestricted) */
+ OK_(strcmp(crypt_get_cipher(cd)?:"", "aes"));
+
+ /* crypt_get_cipher_mode (unrestricted) */
+ OK_(strcmp(crypt_get_cipher_mode(cd)?:"", "xts-plain64"));
+
+ /* crypt_get_uuid (unrestricted) */
+ NOTNULL_(crypt_get_uuid(cd));
+
+ /* crypt_get_device_name (unrestricted) */
+ NOTNULL_(crypt_get_device_name(cd));
+
+ /* crypt_get_data_offset (unrestricted) */
+ OK_(!crypt_get_data_offset(cd));
+
+ /* crypt_get_iv_offset (unrestricted, nothing to test) */
+
+ /* crypt_get_volume_key_size (unrestricted) */
+ EQ_(crypt_get_volume_key_size(cd), key_size);
+
+ /* crypt_keyslot_status (unrestricted) */
+ EQ_(crypt_keyslot_status(cd, 0), CRYPT_SLOT_ACTIVE_LAST);
+ EQ_(crypt_keyslot_status(cd, 1), CRYPT_SLOT_INACTIVE);
+
+ /* crypt_keyslot_get_priority (unrestricted) */
+ EQ_(crypt_keyslot_get_priority(cd, 0), CRYPT_SLOT_PRIORITY_NORMAL);
+
+ /* crypt_keyslot_set_priority (restricted) */
+ FAIL_((r = crypt_keyslot_set_priority(cd, 0, CRYPT_SLOT_PRIORITY_PREFER)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_keyslot_area (unrestricted) */
+ OK_(crypt_keyslot_area(cd, 0, &dummy, &dummy));
+ OK_(!dummy);
+
+ /* crypt_header_backup (unrestricted) */
+ remove(BACKUP_FILE);
+ OK_(crypt_header_backup(cd, CRYPT_LUKS, BACKUP_FILE));
+
+ /* crypt_header_restore (restricted, do not drop the test until we have safe option) */
+ FAIL_((r = crypt_header_restore(cd, CRYPT_LUKS2, BACKUP_FILE)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+ remove(BACKUP_FILE);
+
+ /* crypt_token_json_set (restricted) */
+ FAIL_((r = crypt_token_json_set(cd, CRYPT_ANY_TOKEN, json)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_token_json_get (unrestricted) */
+ OK_(crypt_token_json_get(cd, 0, &token));
+ NOTNULL_(strstr(token, "user_type"));
+
+ /* crypt_token_status (unrestricted) */
+ EQ_(crypt_token_status(cd, 0, &token), CRYPT_TOKEN_EXTERNAL_UNKNOWN);
+ OK_(strcmp(token, "user_type"));
+ EQ_(crypt_token_status(cd, 1, &token), CRYPT_TOKEN_INTERNAL);
+ OK_(strcmp(token, "luks2-keyring"));
+ EQ_(crypt_token_status(cd, 2, NULL), CRYPT_TOKEN_INACTIVE);
+ EQ_(crypt_token_status(cd, 6, &token), CRYPT_TOKEN_INTERNAL_UNKNOWN);
+
+ /* crypt_token_luks2_keyring_set (restricted) */
+ FAIL_((r = crypt_token_luks2_keyring_set(cd, CRYPT_ANY_TOKEN, &params)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_token_luks2_keyring_get (unrestricted) */
+ EQ_(crypt_token_luks2_keyring_get(cd, 1, &params_get), 1);
+ OK_(strcmp(params_get.key_description, KEY_DESC_TEST0));
+
+ /* crypt_token_assign_keyslot (unrestricted) */
+ FAIL_((r = crypt_token_assign_keyslot(cd, 0, 1)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_token_unassign_keyslot (unrestricted) */
+ FAIL_((r = crypt_token_unassign_keyslot(cd, CRYPT_ANY_TOKEN, CRYPT_ANY_SLOT)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_activate_by_token (restricted for activation only) */
+#ifdef KERNEL_KEYRING
+ FAIL_((r = crypt_activate_by_token(cd, CDEVICE_1, 1, NULL, 0)), ""); // supposed to be silent
+ EQ_(r, -ETXTBSY);
+ OK_(crypt_activate_by_token(cd, NULL, 1, NULL, 0));
+ OK_(crypt_activate_by_token(cd, NULL, 1, NULL, t_dm_crypt_keyring_support() ? CRYPT_ACTIVATE_KEYRING_KEY : 0));
+#endif
+ OK_(get_luks2_offsets(1, 8192, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 2));
+ //OK_(_system("dd if=" NO_REQS_LUKS2_HEADER " of=" NO_REQS_LUKS2_HEADER " bs=4096 2>/dev/null", 1));
+ OK_(_system("dd if=" NO_REQS_LUKS2_HEADER " of=" DMDIR L_DEVICE_OK " bs=1M count=4 oflag=direct 2>/dev/null", 1));
+
+ /* need to fake activated LUKSv2 device with requirements features */
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, "aaa", 3, 0));
+ OK_(crypt_header_backup(cd, CRYPT_LUKS2, BACKUP_FILE));
+ /* replace header with no requirements */
+ OK_(_system("dd if=" REQS_LUKS2_HEADER " of=" DMDIR L_DEVICE_OK " bs=1M count=4 oflag=direct 2>/dev/null", 1));
+ crypt_free(cd);
+
+ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, DEVICE_5));
+ crypt_free(cd);
+ OK_(crypt_init_by_name(&cd, CDEVICE_1));
+
+ /* crypt_header_restore (restricted with confirmation required) */
+ /* allow force restore over device header w/ requirements */
+ OK_(crypt_header_restore(cd, CRYPT_LUKS2, BACKUP_FILE));
+ remove(BACKUP_FILE);
+ OK_(_system("dd if=" REQS_LUKS2_HEADER " of=" DMDIR L_DEVICE_OK " bs=1M count=4 oflag=direct 2>/dev/null", 1));
+ OK_(crypt_header_backup(cd, CRYPT_LUKS2, BACKUP_FILE)); /* create backup with requirements */
+
+ /* crypt_suspend (restricted) */
+ FAIL_((r = crypt_suspend(cd, CDEVICE_1)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+ crypt_free(cd);
+
+ /* replace header again to suspend the device */
+ OK_(_system("dd if=" NO_REQS_LUKS2_HEADER " of=" DMDIR L_DEVICE_OK " bs=1M count=4 oflag=direct 2>/dev/null", 1));
+ OK_(crypt_init_by_name(&cd, CDEVICE_1));
+ OK_(crypt_suspend(cd, CDEVICE_1));
+
+ /* crypt_header_restore (restricted, do not drop the test until we have safe option) */
+ /* refuse to overwrite header w/ backup including requirements */
+ FAIL_((r = crypt_header_restore(cd, CRYPT_LUKS2, BACKUP_FILE)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ crypt_free(cd);
+
+ OK_(_system("dd if=" REQS_LUKS2_HEADER " of=" DMDIR L_DEVICE_OK " bs=1M count=4 oflag=direct 2>/dev/null", 1));
+ OK_(crypt_init_by_name(&cd, CDEVICE_1));
+
+ /* crypt_resume_by_passphrase (restricted) */
+ FAIL_((r = crypt_resume_by_passphrase(cd, CDEVICE_1, 0, "aaa", 3)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_resume_by_keyfile (restricted) */
+ FAIL_((r = crypt_resume_by_keyfile(cd, CDEVICE_1, 0, KEYFILE1, 0)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_resume_by_keyfile_offset (restricted) */
+ FAIL_((r = crypt_resume_by_keyfile_offset(cd, CDEVICE_1, 0, KEYFILE1, 0, 0)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+ crypt_free(cd);
+
+ OK_(_system("dd if=" NO_REQS_LUKS2_HEADER " of=" DMDIR L_DEVICE_OK " bs=1M count=4 oflag=direct 2>/dev/null", 1));
+ OK_(crypt_init_by_name(&cd, CDEVICE_1));
+ OK_(crypt_resume_by_passphrase(cd, CDEVICE_1, 0, "aaa", 3));
+ crypt_free(cd);
+ OK_(_system("dd if=" REQS_LUKS2_HEADER " of=" DMDIR L_DEVICE_OK " bs=1M count=4 oflag=direct 2>/dev/null", 1));
+
+ OK_(crypt_init_by_name(&cd, CDEVICE_1));
+ /* load VK in keyring */
+ OK_(crypt_activate_by_passphrase(cd, NULL, 0, "aaa", 3, t_dm_crypt_keyring_support() ? CRYPT_ACTIVATE_KEYRING_KEY : 0));
+ /* crypt_resize (restricted) */
+ FAIL_((r = crypt_resize(cd, CDEVICE_1, 1)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+
+ /* crypt_get_active_device (unrestricted) */
+ OK_(crypt_get_active_device(cd, CDEVICE_1, &cad));
+#ifdef KERNEL_KEYRING
+ if (t_dm_crypt_keyring_support())
+ EQ_(cad.flags & CRYPT_ACTIVATE_KEYRING_KEY, CRYPT_ACTIVATE_KEYRING_KEY);
+#endif
+
+ /* crypt_deactivate (unrestricted) */
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ /* crypt_token_is_assigned (unrestricted) */
+ OK_(crypt_token_is_assigned(cd, 1, 0));
+ OK_(crypt_token_is_assigned(cd, 6, 0));
+ EQ_(crypt_token_is_assigned(cd, 0, 0), -ENOENT);
+
+ /* crypt_keyslot_destroy (unrestricted) */
+ OK_(crypt_keyslot_destroy(cd, 0));
+out:
+ crypt_free(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void Luks2Integrity(void)
+{
+ struct crypt_device *cd;
+ struct crypt_params_integrity ip = {};
+ struct crypt_params_luks2 params = {
+ .sector_size = 512,
+ .integrity = "hmac(sha256)"
+ };
+ size_t key_size = 32 + 32;
+ const char *passphrase = "blabla";
+ const char *cipher = "aes";
+ const char *cipher_mode = "xts-random";
+ int ret;
+
+ // FIXME: This is just a stub
+ OK_(crypt_init(&cd, DEVICE_2));
+ ret = crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, key_size, &params);
+ if (ret < 0) {
+ printf("WARNING: cannot format integrity device, skipping test.\n");
+ crypt_free(cd);
+ return;
+ }
+
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 7, NULL, key_size, passphrase, strlen(passphrase)), 7);
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_2, 7, passphrase, strlen(passphrase) ,0), 7);
+ EQ_(crypt_status(cd, CDEVICE_2), CRYPT_ACTIVE);
+ crypt_free(cd);
+
+ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_2, NULL));
+ OK_(crypt_get_integrity_info(cd, &ip));
+ OK_(strcmp(cipher, crypt_get_cipher(cd)));
+ OK_(strcmp(cipher_mode, crypt_get_cipher_mode(cd)));
+ OK_(strcmp("hmac(sha256)", ip.integrity));
+ EQ_(32, ip.integrity_key_size);
+ EQ_(32+16, ip.tag_size);
+ OK_(crypt_deactivate(cd, CDEVICE_2));
+ crypt_free(cd);
+
+ OK_(crypt_init(&cd, DEVICE_2));
+ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, key_size - 32, &params), "Wrong key size.");
+ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, "xts-plainx", NULL, NULL, key_size, &params), "Wrong cipher.");
+ crypt_free(cd);
+}
+
+static int set_fast_pbkdf(struct crypt_device *cd)
+{
+ struct crypt_pbkdf_type pbkdf = {
+ .type = "argon2id",
+ .hash = "sha256",
+ .iterations = 4,
+ .max_memory_kb = 32,
+ .parallel_threads = 1,
+ .flags = CRYPT_PBKDF_NO_BENCHMARK
+ };
+
+ /* Cannot use Argon2 in FIPS */
+ if (_fips_mode) {
+ pbkdf.type = CRYPT_KDF_PBKDF2;
+ pbkdf.parallel_threads = 0;
+ pbkdf.max_memory_kb = 0;
+ pbkdf.iterations = 1000;
+ }
+ return crypt_set_pbkdf_type(cd, &pbkdf);
+}
+
+static int check_flag(uint32_t flags, uint32_t flag)
+{
+ return (flags & flag) ? 0 : -1;
+}
+
+static void Luks2Refresh(void)
+{
+ uint64_t r_payload_offset;
+ struct crypt_device *cd1, *cd2;
+ char key[128], key1[128];
+ const char *cipher = "aes", *mode = "xts-plain64";
+ const char *mk_hex = "bb21158c733229347bd4e681891e213d94c645be6a5b84818afe7a78a6de7a1a";
+ const char *mk_hex2 = "bb22158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1e";
+ size_t key_size = strlen(mk_hex) / 2;
+ struct crypt_params_luks2 params = {
+ .sector_size = 512,
+ .integrity = "aead"
+ };
+ struct crypt_active_device cad = {};
+
+ crypt_decode_key(key, mk_hex, key_size);
+ crypt_decode_key(key1, mk_hex2, key_size);
+
+ OK_(get_luks2_offsets(1, 0, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 1000));
+ OK_(create_dmdevice_over_loop(L_DEVICE_WRONG, r_payload_offset + 5000));
+ OK_(create_dmdevice_over_loop(L_DEVICE_1S, r_payload_offset + 1));
+ OK_(create_dmdevice_over_loop(H_DEVICE, r_payload_offset));
+
+ /* prepare test device */
+ OK_(crypt_init(&cd1, DMDIR L_DEVICE_OK));
+ OK_(set_fast_pbkdf(cd1));
+ OK_(crypt_format(cd1, CRYPT_LUKS2, cipher, mode, NULL, key, 32, NULL));
+ OK_(crypt_keyslot_add_by_volume_key(cd1, CRYPT_ANY_SLOT, key, 32, "aaa", 3));
+ OK_(crypt_activate_by_passphrase(cd1, CDEVICE_1, 0, "aaa", 3, 0));
+
+ /* check we can refresh significant flags */
+ if (t_dm_crypt_discard_support()) {
+ OK_(crypt_activate_by_passphrase(cd1, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH | CRYPT_ACTIVATE_ALLOW_DISCARDS));
+ OK_(crypt_get_active_device(cd1, CDEVICE_1, &cad));
+ OK_(check_flag(cad.flags, CRYPT_ACTIVATE_ALLOW_DISCARDS));
+ cad.flags = 0;
+ }
+
+ if (t_dm_crypt_cpu_switch_support()) {
+ OK_(crypt_activate_by_passphrase(cd1, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH | CRYPT_ACTIVATE_SAME_CPU_CRYPT));
+ OK_(crypt_get_active_device(cd1, CDEVICE_1, &cad));
+ OK_(check_flag(cad.flags, CRYPT_ACTIVATE_SAME_CPU_CRYPT));
+ cad.flags = 0;
+
+ OK_(crypt_activate_by_passphrase(cd1, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH | CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS));
+ OK_(crypt_get_active_device(cd1, CDEVICE_1, &cad));
+ OK_(check_flag(cad.flags, CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS));
+ cad.flags = 0;
+
+ OK_(crypt_activate_by_passphrase(cd1, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH | CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS));
+ OK_(crypt_get_active_device(cd1, CDEVICE_1, &cad));
+ OK_(check_flag(cad.flags, CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS));
+ cad.flags = 0;
+ }
+
+ OK_(crypt_volume_key_keyring(cd1, 0));
+ OK_(crypt_activate_by_passphrase(cd1, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH));
+ OK_(crypt_get_active_device(cd1, CDEVICE_1, &cad));
+ FAIL_(check_flag(cad.flags, CRYPT_ACTIVATE_KEYRING_KEY), "Unexpected flag raised.");
+ cad.flags = 0;
+
+#ifdef KERNEL_KEYRING
+ if (t_dm_crypt_keyring_support()) {
+ OK_(crypt_volume_key_keyring(cd1, 1));
+ OK_(crypt_activate_by_passphrase(cd1, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH));
+ OK_(crypt_get_active_device(cd1, CDEVICE_1, &cad));
+ OK_(check_flag(cad.flags, CRYPT_ACTIVATE_KEYRING_KEY));
+ cad.flags = 0;
+ }
+#endif
+
+ /* multiple flags at once */
+ if (t_dm_crypt_discard_support() && t_dm_crypt_cpu_switch_support()) {
+ OK_(crypt_activate_by_passphrase(cd1, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH | CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS | CRYPT_ACTIVATE_ALLOW_DISCARDS));
+ OK_(crypt_get_active_device(cd1, CDEVICE_1, &cad));
+ OK_(check_flag(cad.flags, CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS | CRYPT_ACTIVATE_ALLOW_DISCARDS));
+ cad.flags = 0;
+ }
+
+ /* do not allow reactivation with read-only (and drop flag silently because activation behaves exactly same) */
+ OK_(crypt_activate_by_passphrase(cd1, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH | CRYPT_ACTIVATE_READONLY));
+ OK_(crypt_get_active_device(cd1, CDEVICE_1, &cad));
+ FAIL_(check_flag(cad.flags, CRYPT_ACTIVATE_READONLY), "Reactivated with read-only flag.");
+ cad.flags = 0;
+
+ /* reload flag is dropped silently */
+ OK_(crypt_deactivate(cd1, CDEVICE_1));
+ OK_(crypt_activate_by_passphrase(cd1, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH));
+
+ /* check read-only flag is not lost after reload */
+ OK_(crypt_deactivate(cd1, CDEVICE_1));
+ OK_(crypt_activate_by_passphrase(cd1, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_READONLY));
+ OK_(crypt_activate_by_passphrase(cd1, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH));
+ OK_(crypt_get_active_device(cd1, CDEVICE_1, &cad));
+ OK_(check_flag(cad.flags, CRYPT_ACTIVATE_READONLY));
+ cad.flags = 0;
+
+ /* check LUKS2 with auth. enc. reload */
+ OK_(crypt_init(&cd2, DMDIR L_DEVICE_WRONG));
+ if (!crypt_format(cd2, CRYPT_LUKS2, "aes", "gcm-random", crypt_get_uuid(cd1), key, 32, &params)) {
+ OK_(crypt_keyslot_add_by_volume_key(cd2, 0, key, 32, "aaa", 3));
+ OK_(crypt_activate_by_volume_key(cd2, CDEVICE_2, key, 32, 0));
+ OK_(crypt_activate_by_volume_key(cd2, CDEVICE_2, key, 32, CRYPT_ACTIVATE_REFRESH | CRYPT_ACTIVATE_NO_JOURNAL));
+ OK_(crypt_get_active_device(cd2, CDEVICE_2, &cad));
+ OK_(check_flag(cad.flags, CRYPT_ACTIVATE_NO_JOURNAL));
+ cad.flags = 0;
+ OK_(crypt_activate_by_volume_key(cd2, CDEVICE_2, key, 32, CRYPT_ACTIVATE_REFRESH | CRYPT_ACTIVATE_NO_JOURNAL | CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS));
+ OK_(crypt_get_active_device(cd2, CDEVICE_2, &cad));
+ OK_(check_flag(cad.flags, CRYPT_ACTIVATE_NO_JOURNAL | CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS));
+ cad.flags = 0;
+ OK_(crypt_activate_by_passphrase(cd2, CDEVICE_2, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH));
+ OK_(crypt_get_active_device(cd2, CDEVICE_2, &cad));
+ FAIL_(check_flag(cad.flags, CRYPT_ACTIVATE_NO_JOURNAL), "");
+ FAIL_(check_flag(cad.flags, CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS), "");
+ FAIL_(crypt_activate_by_passphrase(cd2, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH), "Refreshed LUKS2 device with LUKS2/aead context");
+ OK_(crypt_deactivate(cd2, CDEVICE_2));
+ } else {
+ printf("WARNING: cannot format integrity device, skipping few reload tests.\n");
+ }
+ crypt_free(cd2);
+
+ /* Use LUKS1 context on LUKS2 device */
+ OK_(crypt_init(&cd2, DMDIR L_DEVICE_1S));
+ OK_(crypt_format(cd2, CRYPT_LUKS1, cipher, mode, crypt_get_uuid(cd1), key, 32, NULL));
+ OK_(crypt_keyslot_add_by_volume_key(cd2, CRYPT_ANY_SLOT, NULL, 32, "aaa", 3));
+ FAIL_(crypt_activate_by_passphrase(cd2, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH), "Refreshed LUKS2 device with LUKS1 context");
+ crypt_free(cd2);
+
+ /* Use PLAIN context on LUKS2 device */
+ OK_(crypt_init(&cd2, DMDIR L_DEVICE_1S));
+ OK_(crypt_format(cd2, CRYPT_PLAIN, cipher, mode, NULL, key, 32, NULL));
+ OK_(crypt_activate_by_volume_key(cd2, CDEVICE_2, key, key_size, 0));
+ FAIL_(crypt_activate_by_volume_key(cd2, CDEVICE_1, key, key_size, CRYPT_ACTIVATE_REFRESH), "Refreshed LUKS2 device with PLAIN context");
+ OK_(crypt_deactivate(cd2, CDEVICE_2));
+ crypt_free(cd2);
+
+ /* (snapshot-like case) */
+ /* try to refresh almost identical device (differs only in major:minor of data device) */
+ OK_(crypt_init(&cd2, DMDIR L_DEVICE_WRONG));
+ OK_(set_fast_pbkdf(cd2));
+ OK_(crypt_format(cd2, CRYPT_LUKS2, cipher, mode, crypt_get_uuid(cd1), key, 32, NULL));
+ OK_(crypt_keyslot_add_by_volume_key(cd2, CRYPT_ANY_SLOT, key, 32, "aaa", 3));
+ FAIL_(crypt_activate_by_passphrase(cd2, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH), "Refreshed dm-crypt mapped over mismatching data device");
+
+ OK_(crypt_deactivate(cd1, CDEVICE_1));
+
+ crypt_free(cd1);
+ crypt_free(cd2);
+
+ _cleanup_dmdevices();
+}
+
+static void Luks2Flags(void)
+{
+ struct crypt_device *cd;
+ uint32_t flags = 42;
+
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+
+ /* check library erase passed variable on success when no flags set */
+ OK_(crypt_persistent_flags_get(cd, CRYPT_FLAGS_ACTIVATION, &flags));
+ EQ_(flags, 0);
+
+ /* check set and get behave as expected */
+ flags = CRYPT_ACTIVATE_ALLOW_DISCARDS;
+ OK_(crypt_persistent_flags_set(cd, CRYPT_FLAGS_ACTIVATION, flags));
+ flags = 0;
+ OK_(crypt_persistent_flags_get(cd, CRYPT_FLAGS_ACTIVATION, &flags));
+ EQ_(flags, CRYPT_ACTIVATE_ALLOW_DISCARDS);
+
+ flags = CRYPT_ACTIVATE_ALLOW_DISCARDS | CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS;
+ OK_(crypt_persistent_flags_set(cd, CRYPT_FLAGS_ACTIVATION, flags));
+ flags = (uint32_t)~0;
+ OK_(crypt_persistent_flags_get(cd, CRYPT_FLAGS_ACTIVATION, &flags));
+ EQ_(flags,CRYPT_ACTIVATE_ALLOW_DISCARDS | CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS);
+
+ crypt_free(cd);
+}
+
+static void Luks2Repair(void)
+{
+ struct crypt_device *cd;
+ char rollback[256];
+
+ snprintf(rollback, sizeof(rollback),
+ "dd if=" IMAGE_PV_LUKS2_SEC ".bcp of=%s bs=1M 2>/dev/null",
+ DEVICE_6);
+
+ OK_(crypt_init(&cd, DEVICE_6));
+
+ FAIL_(crypt_load(cd, CRYPT_LUKS, NULL), "Ambiguous signature detected");
+ FAIL_(crypt_repair(cd, CRYPT_LUKS1, NULL), "Not a LUKS2 device");
+
+ /* check explicit LUKS2 repair works */
+ OK_(crypt_repair(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DEVICE_6));
+
+ /* rollback */
+ OK_(_system(rollback, 1));
+ FAIL_(crypt_load(cd, CRYPT_LUKS, NULL), "Ambiguous signature detected");
+
+ /* check repair with type detection works */
+ OK_(crypt_repair(cd, CRYPT_LUKS, NULL));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ crypt_free(cd);
+
+ /* repeat with locking disabled (must not have any effect) */
+ OK_(_system(rollback, 1));
+ OK_(crypt_init(&cd, DEVICE_6));
+ OK_(crypt_metadata_locking(cd, 0));
+
+ FAIL_(crypt_load(cd, CRYPT_LUKS, NULL), "Ambiguous signature detected");
+ FAIL_(crypt_repair(cd, CRYPT_LUKS1, NULL), "Not a LUKS2 device");
+
+ /* check explicit LUKS2 repair works */
+ OK_(crypt_repair(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DEVICE_6));
+
+ /* rollback */
+ OK_(_system(rollback, 1));
+ FAIL_(crypt_load(cd, CRYPT_LUKS, NULL), "Ambiguous signature detected");
+
+ /* check repair with type detection works */
+ OK_(crypt_repair(cd, CRYPT_LUKS, NULL));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ crypt_free(cd);
+}
+
+static void int_handler(int sig __attribute__((__unused__)))
+{
+ _quit++;
+}
+
+int main(int argc, char *argv[])
+{
+ struct sigaction sa = { .sa_handler = int_handler };
+ int i;
+
+ if (getuid() != 0) {
+ printf("You must be root to run this test.\n");
+ exit(77);
+ }
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp("-v", argv[i]) || !strcmp("--verbose", argv[i]))
+ _verbose = 1;
+ else if (!strcmp("--debug", argv[i]))
+ _debug = _verbose = 1;
+ }
+
+ /* Handle interrupt properly */
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+
+ register_cleanup(_cleanup);
+
+ _cleanup();
+ if (_setup())
+ goto out;
+
+ crypt_set_debug_level(_debug ? CRYPT_DEBUG_JSON : CRYPT_DEBUG_NONE);
+
+ RUN_(AddDeviceLuks2, "Format and use LUKS2 device");
+ RUN_(Luks2HeaderLoad, "LUKS2 header load");
+ RUN_(Luks2HeaderRestore, "LUKS2 header restore");
+ RUN_(Luks2HeaderBackup, "LUKS2 header backup");
+ RUN_(ResizeDeviceLuks2, "LUKS2 device resize tests");
+ RUN_(UseLuks2Device, "Use pre-formated LUKS2 device");
+ RUN_(SuspendDevice, "LUKS2 Suspend/Resume");
+ RUN_(UseTempVolumes, "Format and use temporary encrypted device");
+ RUN_(Tokens, "General tokens API");
+ RUN_(TokenActivationByKeyring, "Builtin kernel keyring token");
+ RUN_(LuksConvert, "LUKS1 <-> LUKS2 conversions");
+ RUN_(Pbkdf, "Default PBKDF manipulation routines");
+ RUN_(Luks2KeyslotParams, "Add a new keyslot with different encryption");
+ RUN_(Luks2KeyslotAdd, "Add a new keyslot by unused key");
+ RUN_(Luks2ActivateByKeyring, "LUKS2 activation by passphrase in keyring");
+ RUN_(Luks2Requirements, "LUKS2 requirements flags");
+ RUN_(Luks2Integrity, "LUKS2 with data integrity");
+ RUN_(Luks2Refresh, "Active device table refresh");
+ RUN_(Luks2Flags, "LUKS2 persistent flags");
+ RUN_(Luks2Repair, "LUKS2 repair"); // test disables metadata locking. Run always last!
+out:
+ _cleanup();
+ return 0;
+}
diff --git a/tests/api-test.c b/tests/api-test.c
new file mode 100644
index 0000000..e41e48e
--- /dev/null
+++ b/tests/api-test.c
@@ -0,0 +1,1917 @@
+/*
+ * cryptsetup library API check functions
+ *
+ * Copyright (C) 2009-2019 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2019 Milan Broz
+ * Copyright (C) 2016-2019 Ondrej Kozina
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "api_test.h"
+#include "luks.h"
+#include "libcryptsetup.h"
+
+#define DMDIR "/dev/mapper/"
+
+#define DEVICE_1_UUID "28632274-8c8a-493f-835b-da802e1c576b"
+#define DEVICE_EMPTY_name "crypt_zero"
+#define DEVICE_EMPTY DMDIR DEVICE_EMPTY_name
+#define DEVICE_ERROR_name "crypt_error"
+#define DEVICE_ERROR DMDIR DEVICE_ERROR_name
+
+#define CDEVICE_1 "ctest1"
+#define CDEVICE_2 "ctest2"
+#define CDEVICE_WRONG "O_o"
+#define H_DEVICE "head_ok"
+#define H_DEVICE_WRONG "head_wr"
+#define L_DEVICE_1S "luks_onesec"
+#define L_DEVICE_0S "luks_zerosec"
+#define L_DEVICE_WRONG "luks_wr"
+#define L_DEVICE_OK "luks_ok"
+#define EVL_HEADER_1 "evil_hdr-luks_hdr_damage"
+#define EVL_HEADER_2 "evil_hdr-payload_overwrite"
+#define EVL_HEADER_3 "evil_hdr-stripes_payload_dmg"
+#define EVL_HEADER_4 "evil_hdr-small_luks_device"
+#define EVL_HEADER_5 "evil_hdr-keyslot_overlap"
+#define VALID_HEADER "valid_header_file"
+#define BACKUP_FILE "csetup_backup_file"
+#define IMAGE1 "compatimage.img"
+#define IMAGE_EMPTY "empty.img"
+
+#define KEYFILE1 "key1.file"
+#define KEY1 "compatkey"
+
+#define KEYFILE2 "key2.file"
+#define KEY2 "0123456789abcdef"
+
+#define PASSPHRASE "blabla"
+#define PASSPHRASE1 "albalb"
+
+#define DEVICE_TEST_UUID "12345678-1234-1234-1234-123456789abc"
+
+#define DEVICE_WRONG "/dev/Ooo_"
+#define DEVICE_CHAR "/dev/zero"
+#define THE_LFILE_TEMPLATE "cryptsetup-tstlp.XXXXXX"
+
+#define LUKS_PHDR_SIZE_B 1024
+
+static int _fips_mode = 0;
+
+static char *DEVICE_1 = NULL;
+static char *DEVICE_2 = NULL;
+static char *DEVICE_3 = NULL;
+
+static char *tmp_file_1 = NULL;
+static char *test_loop_file = NULL;
+
+// Helpers
+
+static int get_luks_offsets(int metadata_device,
+ size_t keylength,
+ unsigned int alignpayload_sec,
+ unsigned int alignoffset_sec,
+ uint64_t *r_header_size,
+ uint64_t *r_payload_offset)
+{
+ int i;
+ uint64_t current_sector;
+ uint32_t sectors_per_stripes_set;
+
+ if (!keylength) {
+ if (r_header_size)
+ *r_header_size = 0;
+ if (r_payload_offset)
+ *r_payload_offset = 0;
+ return -1;
+ }
+
+ sectors_per_stripes_set = DIV_ROUND_UP(keylength*LUKS_STRIPES, SECTOR_SIZE);
+ current_sector = DIV_ROUND_UP_MODULO(DIV_ROUND_UP(LUKS_PHDR_SIZE_B, SECTOR_SIZE),
+ LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE);
+ for (i=0; i < (LUKS_NUMKEYS - 1); i++)
+ current_sector = DIV_ROUND_UP_MODULO(current_sector + sectors_per_stripes_set,
+ LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE);
+ if (r_header_size)
+ *r_header_size = current_sector + sectors_per_stripes_set;
+
+ current_sector = DIV_ROUND_UP_MODULO(current_sector + sectors_per_stripes_set,
+ LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE);
+
+ if (r_payload_offset) {
+ if (metadata_device)
+ *r_payload_offset = alignpayload_sec;
+ else
+ *r_payload_offset = DIV_ROUND_UP_MODULO(current_sector, alignpayload_sec)
+ + alignoffset_sec;
+ }
+
+ return 0;
+}
+
+static void _remove_keyfiles(void)
+{
+ remove(KEYFILE1);
+ remove(KEYFILE2);
+}
+
+#if HAVE_DECL_DM_TASK_RETRY_REMOVE
+#define DM_RETRY "--retry "
+#else
+#define DM_RETRY ""
+#endif
+
+static void _cleanup_dmdevices(void)
+{
+ struct stat st;
+
+ if (!stat(DMDIR H_DEVICE, &st))
+ _system("dmsetup remove " DM_RETRY H_DEVICE, 0);
+
+ if (!stat(DMDIR H_DEVICE_WRONG, &st))
+ _system("dmsetup remove " DM_RETRY H_DEVICE_WRONG, 0);
+
+ if (!stat(DMDIR L_DEVICE_0S, &st))
+ _system("dmsetup remove " DM_RETRY L_DEVICE_0S, 0);
+
+ if (!stat(DMDIR L_DEVICE_1S, &st))
+ _system("dmsetup remove " DM_RETRY L_DEVICE_1S, 0);
+
+ if (!stat(DMDIR L_DEVICE_WRONG, &st))
+ _system("dmsetup remove " DM_RETRY L_DEVICE_WRONG, 0);
+
+ if (!stat(DMDIR L_DEVICE_OK, &st))
+ _system("dmsetup remove " DM_RETRY L_DEVICE_OK, 0);
+
+ t_dev_offset = 0;
+}
+
+static void _cleanup(void)
+{
+ struct stat st;
+
+ //_system("udevadm settle", 0);
+
+ if (!stat(DMDIR CDEVICE_1, &st))
+ _system("dmsetup remove " DM_RETRY CDEVICE_1, 0);
+
+ if (!stat(DMDIR CDEVICE_2, &st))
+ _system("dmsetup remove " DM_RETRY CDEVICE_2, 0);
+
+ if (!stat(DEVICE_EMPTY, &st))
+ _system("dmsetup remove " DM_RETRY DEVICE_EMPTY_name, 0);
+
+ if (!stat(DEVICE_ERROR, &st))
+ _system("dmsetup remove " DM_RETRY DEVICE_ERROR_name, 0);
+
+ _cleanup_dmdevices();
+
+ if (loop_device(THE_LOOP_DEV))
+ loop_detach(THE_LOOP_DEV);
+
+ if (loop_device(DEVICE_1))
+ loop_detach(DEVICE_1);
+
+ if (loop_device(DEVICE_2))
+ loop_detach(DEVICE_2);
+
+ if (loop_device(DEVICE_3))
+ loop_detach(DEVICE_3);
+
+ _system("rm -f " IMAGE_EMPTY, 0);
+ _system("rm -f " IMAGE1, 0);
+
+ if (test_loop_file)
+ remove(test_loop_file);
+ if (tmp_file_1)
+ remove(tmp_file_1);
+
+ remove(EVL_HEADER_1);
+ remove(EVL_HEADER_2);
+ remove(EVL_HEADER_3);
+ remove(EVL_HEADER_4);
+ remove(EVL_HEADER_5);
+ remove(VALID_HEADER);
+ remove(BACKUP_FILE);
+
+ _remove_keyfiles();
+
+ free(tmp_file_1);
+ free(test_loop_file);
+ free(THE_LOOP_DEV);
+ free(DEVICE_1);
+ free(DEVICE_2);
+ free(DEVICE_3);
+}
+
+static int _setup(void)
+{
+ int fd, ro = 0;
+ char cmd[128];
+
+ test_loop_file = strdup(THE_LFILE_TEMPLATE);
+ if ((fd=mkstemp(test_loop_file)) == -1) {
+ printf("cannot create temporary file with template %s\n", test_loop_file);
+ return 1;
+ }
+ close(fd);
+ snprintf(cmd, sizeof(cmd), "dd if=/dev/zero of=%s bs=%d count=%d 2>/dev/null",
+ test_loop_file, SECTOR_SIZE, TST_LOOP_FILE_SIZE);
+ if (_system(cmd, 1))
+ return 1;
+
+ fd = loop_attach(&THE_LOOP_DEV, test_loop_file, 0, 0, &ro);
+ close(fd);
+
+ tmp_file_1 = strdup(THE_LFILE_TEMPLATE);
+ if ((fd=mkstemp(tmp_file_1)) == -1) {
+ printf("cannot create temporary file with template %s\n", tmp_file_1);
+ return 1;
+ }
+ close(fd);
+ snprintf(cmd, sizeof(cmd), "dd if=/dev/zero of=%s bs=%d count=%d 2>/dev/null",
+ tmp_file_1, SECTOR_SIZE, 10);
+ if (_system(cmd, 1))
+ return 1;
+
+ _system("dmsetup create " DEVICE_EMPTY_name " --table \"0 10000 zero\"", 1);
+ _system("dmsetup create " DEVICE_ERROR_name " --table \"0 10000 error\"", 1);
+
+ _system(" [ ! -e " IMAGE1 " ] && xz -dk " IMAGE1 ".xz", 1);
+ fd = loop_attach(&DEVICE_1, IMAGE1, 0, 0, &ro);
+ close(fd);
+
+ _system("dd if=/dev/zero of=" IMAGE_EMPTY " bs=1M count=10 2>/dev/null", 1);
+ fd = loop_attach(&DEVICE_2, IMAGE_EMPTY, 0, 0, &ro);
+ close(fd);
+
+ /* Keymaterial offset is less than 8 sectors */
+ _system(" [ ! -e " EVL_HEADER_1 " ] && xz -dk " EVL_HEADER_1 ".xz", 1);
+ /* keymaterial offset aims into payload area */
+ _system(" [ ! -e " EVL_HEADER_2 " ] && xz -dk " EVL_HEADER_2 ".xz", 1);
+ /* keymaterial offset is valid, number of stripes causes payload area to be overwritten */
+ _system(" [ ! -e " EVL_HEADER_3 " ] && xz -dk " EVL_HEADER_3 ".xz", 1);
+ /* luks device header for data and header on same device. payloadOffset is greater than
+ * device size (crypt_load() test) */
+ _system(" [ ! -e " EVL_HEADER_4 " ] && xz -dk " EVL_HEADER_4 ".xz", 1);
+ /* two keyslots with same offset (overlapping keyslots) */
+ _system(" [ ! -e " EVL_HEADER_5 " ] && xz -dk " EVL_HEADER_5 ".xz", 1);
+ /* valid header: payloadOffset=4096, key_size=32,
+ * volume_key = bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a */
+ _system(" [ ! -e " VALID_HEADER " ] && xz -dk " VALID_HEADER ".xz", 1);
+
+ /* Prepare tcrypt images */
+ _system("tar xJf tcrypt-images.tar.xz 2>/dev/null", 1);
+
+ _system("modprobe dm-crypt", 0);
+ _system("modprobe dm-verity", 0);
+
+ _fips_mode = fips_mode();
+ if (_debug)
+ printf("FIPS MODE: %d\n", _fips_mode);
+
+ /* Use default log callback */
+ crypt_set_log_callback(NULL, &global_log_callback, NULL);
+
+ return 0;
+}
+
+static void AddDevicePlain(void)
+{
+ struct crypt_device *cd;
+ struct crypt_params_plain params = {
+ .hash = "sha1",
+ .skip = 0,
+ .offset = 0,
+ .size = 0
+ };
+ int fd;
+ char key[128], key2[128], path[128];
+
+ const char *passphrase = PASSPHRASE;
+ // hashed hex version of PASSPHRASE
+ const char *mk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(mk_hex) / 2;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+
+ uint64_t size, r_size;
+
+ crypt_decode_key(key, mk_hex, key_size);
+ FAIL_(crypt_init(&cd, ""), "empty device string");
+ FAIL_(crypt_init(&cd, DEVICE_WRONG), "nonexistent device name ");
+ FAIL_(crypt_init(&cd, DEVICE_CHAR), "character device as backing device");
+ OK_(crypt_init(&cd, tmp_file_1));
+ crypt_free(cd);
+
+ // test crypt_format, crypt_get_cipher, crypt_get_cipher_mode, crypt_get_volume_key_size
+ OK_(crypt_init(&cd,DEVICE_1));
+ params.skip = 3;
+ params.offset = 42;
+ FAIL_(crypt_format(cd,CRYPT_PLAIN,NULL,cipher_mode,NULL,NULL,key_size,&params),"cipher param is null");
+ FAIL_(crypt_format(cd,CRYPT_PLAIN,cipher,NULL,NULL,NULL,key_size,&params),"cipher_mode param is null");
+ OK_(crypt_format(cd,CRYPT_PLAIN,cipher,cipher_mode,NULL,NULL,key_size,&params));
+ OK_(strcmp(cipher_mode,crypt_get_cipher_mode(cd)));
+ OK_(strcmp(cipher,crypt_get_cipher(cd)));
+ EQ_((int)key_size, crypt_get_volume_key_size(cd));
+ EQ_(params.skip, crypt_get_iv_offset(cd));
+ EQ_(params.offset, crypt_get_data_offset(cd));
+ params.skip = 0;
+ params.offset = 0;
+
+ // crypt_set_uuid()
+ FAIL_(crypt_set_uuid(cd,DEVICE_1_UUID),"can't set uuid to plain device");
+
+ crypt_free(cd);
+
+ // default is "plain" hash - no password hash
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, NULL));
+ FAIL_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0), "cannot verify key with plain");
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ // test boundaries in offset parameter
+ t_device_size(DEVICE_1,&size);
+ params.hash = NULL;
+ // zero sectors length
+ params.offset = size >> SECTOR_SHIFT;
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ EQ_(crypt_get_data_offset(cd),params.offset);
+ // device size is 0 sectors
+ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0), "invalid device size (0 blocks)");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ // data part of crypt device is of 1 sector size
+ params.offset = (size >> SECTOR_SHIFT) - 1;
+ crypt_free(cd);
+
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ snprintf(path, sizeof(path), "%s/%s", crypt_get_dir(), CDEVICE_1);
+ if (t_device_size(path, &r_size) >= 0)
+ EQ_(r_size>>SECTOR_SHIFT, 1);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ // size > device_size
+ params.offset = 0;
+ params.size = (size >> SECTOR_SHIFT) + 1;
+ crypt_init(&cd, DEVICE_1);
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0),"Device too small");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ crypt_free(cd);
+
+ // offset == device_size (autodetect size)
+ params.offset = (size >> SECTOR_SHIFT);
+ params.size = 0;
+ crypt_init(&cd, DEVICE_1);
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0),"Device too small");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ crypt_free(cd);
+
+ // offset == device_size (user defined size)
+ params.offset = (size >> SECTOR_SHIFT);
+ params.size = 123;
+ crypt_init(&cd, DEVICE_1);
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0),"Device too small");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ crypt_free(cd);
+
+ // offset+size > device_size
+ params.offset = 42;
+ params.size = (size >> SECTOR_SHIFT) - params.offset + 1;
+ crypt_init(&cd, DEVICE_1);
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0),"Offset and size are beyond device real size");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ crypt_free(cd);
+
+ // offset+size == device_size
+ params.offset = 42;
+ params.size = (size >> SECTOR_SHIFT) - params.offset;
+ crypt_init(&cd, DEVICE_1);
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ if (!t_device_size(path, &r_size))
+ EQ_((r_size >> SECTOR_SHIFT),params.size);
+ OK_(crypt_deactivate(cd,CDEVICE_1));
+
+ crypt_free(cd);
+ params.hash = "sha1";
+ params.offset = 0;
+ params.size = 0;
+ params.skip = 0;
+
+ // Now use hashed password
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ FAIL_(crypt_activate_by_passphrase(cd, NULL, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0),
+ "cannot verify passphrase with plain" );
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0));
+
+ // device status check
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ snprintf(path, sizeof(path), "%s/%s", crypt_get_dir(), CDEVICE_1);
+ fd = open(path, O_RDONLY);
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_BUSY);
+ FAIL_(crypt_deactivate(cd, CDEVICE_1), "Device is busy");
+ close(fd);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ crypt_free(cd);
+
+ // crypt_init_by_name_and_header
+ OK_(crypt_init(&cd,DEVICE_1));
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ crypt_free(cd);
+
+ // init with detached header is not supported
+ OK_(crypt_init_data_device(&cd, DEVICE_2, DEVICE_1));
+ FAIL_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params),
+ "can't use plain with separate metadata device");
+ crypt_free(cd);
+
+ FAIL_(crypt_init_by_name_and_header(&cd, CDEVICE_1, H_DEVICE),"can't init plain device by header device");
+ OK_(crypt_init_by_name(&cd, CDEVICE_1));
+ OK_(strcmp(cipher_mode,crypt_get_cipher_mode(cd)));
+ OK_(strcmp(cipher,crypt_get_cipher(cd)));
+ EQ_((int)key_size, crypt_get_volume_key_size(cd));
+ EQ_(params.skip, crypt_get_iv_offset(cd));
+ EQ_(params.offset, crypt_get_data_offset(cd));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ OK_(crypt_init(&cd,DEVICE_1));
+ OK_(crypt_format(cd,CRYPT_PLAIN,cipher,cipher_mode,NULL,NULL,key_size,&params));
+ params.size = 0;
+ params.offset = 0;
+
+ // crypt_set_data_device
+ FAIL_(crypt_set_data_device(cd,H_DEVICE),"can't set data device for plain device");
+ NULL_(crypt_get_metadata_device_name(cd));
+
+ // crypt_get_type
+ OK_(strcmp(crypt_get_type(cd),CRYPT_PLAIN));
+
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+
+ // crypt_resize()
+ OK_(crypt_resize(cd,CDEVICE_1,size>>SECTOR_SHIFT)); // same size
+ if (!t_device_size(path,&r_size))
+ EQ_(r_size, size);
+
+ // size overlaps
+ FAIL_(crypt_resize(cd, CDEVICE_1, (uint64_t)-1),"Backing device is too small");
+ FAIL_(crypt_resize(cd, CDEVICE_1, (size>>SECTOR_SHIFT)+1),"crypt device overlaps backing device");
+
+ // resize ok
+ OK_(crypt_resize(cd,CDEVICE_1, 123));
+ if (!t_device_size(path,&r_size))
+ EQ_(r_size>>SECTOR_SHIFT, 123);
+ OK_(crypt_resize(cd,CDEVICE_1,0)); // full size (autodetect)
+ if (!t_device_size(path,&r_size))
+ EQ_(r_size, size);
+ OK_(crypt_deactivate(cd,CDEVICE_1));
+ EQ_(crypt_status(cd,CDEVICE_1),CRYPT_INACTIVE);
+ crypt_free(cd);
+
+ // offset tests
+ OK_(crypt_init(&cd,DEVICE_1));
+ params.offset = 42;
+ params.size = (size>>SECTOR_SHIFT) - params.offset - 10;
+ OK_(crypt_format(cd,CRYPT_PLAIN,cipher,cipher_mode,NULL,NULL,key_size,&params));
+ OK_(crypt_activate_by_volume_key(cd,CDEVICE_1,key,key_size,0));
+ if (!t_device_size(path,&r_size))
+ EQ_(r_size>>SECTOR_SHIFT, params.size);
+ // resize to fill remaining capacity
+ OK_(crypt_resize(cd,CDEVICE_1,params.size + 10));
+ if (!t_device_size(path,&r_size))
+ EQ_(r_size>>SECTOR_SHIFT, params.size + 10);
+
+ // 1 sector beyond real size
+ FAIL_(crypt_resize(cd,CDEVICE_1,params.size + 11), "new device size overlaps backing device"); // with respect to offset
+ if (!t_device_size(path,&r_size))
+ EQ_(r_size>>SECTOR_SHIFT, params.size + 10);
+ EQ_(crypt_status(cd,CDEVICE_1),CRYPT_ACTIVE);
+ fd = open(path, O_RDONLY);
+ close(fd);
+ OK_(fd < 0);
+
+ // resize to minimal size
+ OK_(crypt_resize(cd,CDEVICE_1, 1)); // minimal device size
+ if (!t_device_size(path,&r_size))
+ EQ_(r_size>>SECTOR_SHIFT, 1);
+ // use size of backing device (autodetect with respect to offset)
+ OK_(crypt_resize(cd,CDEVICE_1,0));
+ if (!t_device_size(path,&r_size))
+ EQ_(r_size>>SECTOR_SHIFT, (size >> SECTOR_SHIFT)- 42);
+ OK_(crypt_deactivate(cd,CDEVICE_1));
+ crypt_free(cd);
+
+ params.size = 0;
+ params.offset = 0;
+ OK_(crypt_init(&cd,DEVICE_1));
+ OK_(crypt_format(cd,CRYPT_PLAIN,cipher,cipher_mode,NULL,NULL,key_size,&params));
+ OK_(crypt_activate_by_volume_key(cd,CDEVICE_1,key,key_size,0));
+
+ // suspend/resume tests
+ FAIL_(crypt_suspend(cd,CDEVICE_1),"cannot suspend plain device");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ FAIL_(crypt_resume_by_passphrase(cd,CDEVICE_1,CRYPT_ANY_SLOT,passphrase, strlen(passphrase)),"cannot resume plain device");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+
+ // retrieve volume key check
+ if (!_fips_mode) {
+ memset(key2, 0, key_size);
+ key_size--;
+ // small buffer
+ FAIL_(crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key2, &key_size, passphrase, strlen(passphrase)), "small buffer");
+ key_size++;
+ OK_(crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key2, &key_size, passphrase, strlen(passphrase)));
+
+ OK_(memcmp(key, key2, key_size));
+ }
+ OK_(strcmp(cipher, crypt_get_cipher(cd)));
+ OK_(strcmp(cipher_mode, crypt_get_cipher_mode(cd)));
+ EQ_((int)key_size, crypt_get_volume_key_size(cd));
+ EQ_(0, crypt_get_data_offset(cd));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ // now with keyfile
+ OK_(prepare_keyfile(KEYFILE1, KEY1, strlen(KEY1)));
+ OK_(prepare_keyfile(KEYFILE2, KEY2, strlen(KEY2)));
+ FAIL_(crypt_activate_by_keyfile(cd, NULL, CRYPT_ANY_SLOT, KEYFILE1, 0, 0), "cannot verify key with plain");
+ EQ_(0, crypt_activate_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 0, 0));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ FAIL_(crypt_activate_by_keyfile_offset(cd, NULL, CRYPT_ANY_SLOT, KEYFILE1, 0, strlen(KEY1) + 1, 0), "cannot seek");
+ FAIL_(crypt_activate_by_keyfile_device_offset(cd, NULL, CRYPT_ANY_SLOT, KEYFILE1, 0, strlen(KEY1) + 1, 0), "cannot seek");
+ EQ_(0, crypt_activate_by_keyfile_offset(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 0, 0, 0));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(0, crypt_activate_by_keyfile_device_offset(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 0, 0, 0));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ _remove_keyfiles();
+ crypt_free(cd);
+
+ OK_(crypt_init(&cd,DEVICE_1));
+ OK_(crypt_format(cd,CRYPT_PLAIN,cipher,cipher_mode,NULL,NULL,key_size,&params));
+
+ // crypt_keyslot_*()
+ FAIL_(crypt_keyslot_add_by_passphrase(cd,CRYPT_ANY_SLOT,passphrase,strlen(passphrase),passphrase,strlen(passphrase)), "can't add keyslot to plain device");
+ FAIL_(crypt_keyslot_add_by_volume_key(cd,CRYPT_ANY_SLOT ,key,key_size,passphrase,strlen(passphrase)),"can't add keyslot to plain device");
+ FAIL_(crypt_keyslot_add_by_keyfile(cd,CRYPT_ANY_SLOT,KEYFILE1,strlen(KEY1),KEYFILE2,strlen(KEY2)),"can't add keyslot to plain device");
+ FAIL_(crypt_keyslot_destroy(cd,1),"can't manipulate keyslots on plain device");
+ EQ_(crypt_keyslot_status(cd, 0), CRYPT_SLOT_INVALID);
+ _remove_keyfiles();
+
+ crypt_free(cd);
+}
+
+static int new_messages = 0;
+static void new_log(int level, const char *msg, void *usrptr)
+{
+ if (level == CRYPT_LOG_ERROR)
+ new_messages++;
+ global_log_callback(level, msg, usrptr);
+}
+
+static void CallbacksTest(void)
+{
+ struct crypt_device *cd;
+ struct crypt_params_plain params = {
+ .hash = "sha1",
+ .skip = 0,
+ .offset = 0,
+ };
+
+ size_t key_size = 256 / 8;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+ const char *passphrase = PASSPHRASE;
+
+ OK_(crypt_init(&cd, DEVICE_1));
+ new_messages = 0;
+ crypt_set_log_callback(cd, &new_log, NULL);
+ EQ_(new_messages, 0);
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ EQ_(new_messages, 0);
+ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0), "already exists");
+ EQ_(new_messages, 1);
+ crypt_set_log_callback(cd, NULL, NULL);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+}
+
+static void UseLuksDevice(void)
+{
+ struct crypt_device *cd;
+ char key[128];
+ size_t key_size;
+
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ OK_(crypt_activate_by_passphrase(cd, NULL, CRYPT_ANY_SLOT, KEY1, strlen(KEY1), 0));
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1), 0));
+ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1), 0), "already open");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ FAIL_(crypt_deactivate(cd, CDEVICE_1), "no such device");
+
+ key_size = 16;
+ OK_(strcmp("aes", crypt_get_cipher(cd)));
+ OK_(strcmp("cbc-essiv:sha256", crypt_get_cipher_mode(cd)));
+ OK_(strcmp(DEVICE_1_UUID, crypt_get_uuid(cd)));
+ EQ_((int)key_size, crypt_get_volume_key_size(cd));
+ EQ_(1032, crypt_get_data_offset(cd));
+
+ if (!_fips_mode) {
+ EQ_(0, crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key, &key_size, KEY1, strlen(KEY1)));
+ OK_(crypt_volume_key_verify(cd, key, key_size));
+ OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ key[1] = ~key[1];
+ FAIL_(crypt_volume_key_verify(cd, key, key_size), "key mismatch");
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "key mismatch");
+ }
+ crypt_free(cd);
+}
+
+static void SuspendDevice(void)
+{
+ int suspend_status;
+ struct crypt_device *cd;
+
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1), 0));
+
+ suspend_status = crypt_suspend(cd, CDEVICE_1);
+ if (suspend_status == -ENOTSUP) {
+ printf("WARNING: Suspend/Resume not supported, skipping test.\n");
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+ return;
+ }
+
+ OK_(suspend_status);
+ FAIL_(crypt_suspend(cd, CDEVICE_1), "already suspended");
+
+ FAIL_(crypt_resume_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1)-1), "wrong key");
+ OK_(crypt_resume_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1)));
+ FAIL_(crypt_resume_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1)), "not suspended");
+
+ OK_(prepare_keyfile(KEYFILE1, KEY1, strlen(KEY1)));
+ OK_(crypt_suspend(cd, CDEVICE_1));
+ FAIL_(crypt_resume_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1 "blah", 0), "wrong keyfile");
+ FAIL_(crypt_resume_by_keyfile_offset(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 1, 0), "wrong key");
+ FAIL_(crypt_resume_by_keyfile_device_offset(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 1, 0), "wrong key");
+ OK_(crypt_resume_by_keyfile_device_offset(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 0, 0));
+ FAIL_(crypt_resume_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 0), "not suspended");
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ /* create LUKS device with detached header */
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ OK_(crypt_set_data_device(cd, DEVICE_2));
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1), 0));
+ crypt_free(cd);
+
+ /* Should be able to suspend but not resume if not header specified */
+ OK_(crypt_init_by_name(&cd, CDEVICE_1));
+ OK_(crypt_suspend(cd, CDEVICE_1));
+ FAIL_(crypt_suspend(cd, CDEVICE_1), "already suspended");
+ FAIL_(crypt_resume_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1)-1), "no header");
+ crypt_free(cd);
+
+ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, DEVICE_1));
+ OK_(crypt_resume_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEY1, strlen(KEY1)));
+
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ _remove_keyfiles();
+}
+
+static void AddDeviceLuks(void)
+{
+ enum { OFFSET_1M = 2048 , OFFSET_2M = 4096, OFFSET_4M = 8192, OFFSET_8M = 16384 };
+ struct crypt_device *cd;
+ struct crypt_params_luks1 params = {
+ .hash = "sha512",
+ .data_alignment = OFFSET_1M, // 4M, data offset will be 4096
+ .data_device = DEVICE_2
+ };
+ char key[128], key2[128], key3[128];
+
+ const char *passphrase = "blabla", *passphrase2 = "nsdkFI&Y#.sd";
+ const char *mk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ const char *mk_hex2 = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1e";
+ size_t key_size = strlen(mk_hex) / 2;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+ uint64_t r_payload_offset, r_header_size, r_size_1;
+ struct crypt_pbkdf_type pbkdf;
+
+ crypt_decode_key(key, mk_hex, key_size);
+ crypt_decode_key(key3, mk_hex2, key_size);
+
+ // init test devices
+ OK_(get_luks_offsets(1, key_size, 0, 0, &r_header_size, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size));
+ OK_(create_dmdevice_over_loop(H_DEVICE_WRONG, r_header_size - 1));
+
+ // format
+ OK_(crypt_init(&cd, DMDIR H_DEVICE_WRONG));
+ params.data_alignment = 0;
+ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params), "Not enough space for keyslots material");
+ crypt_free(cd);
+
+ // test payload_offset = 0 for encrypted device with external header device
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_get_data_offset(cd), 0);
+ crypt_free(cd);
+
+ params.data_alignment = 0;
+ params.data_device = NULL;
+
+ // test payload_offset = 0. format() should look up alignment offset from device topology
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(!(crypt_get_data_offset(cd) > 0));
+ crypt_free(cd);
+
+ // set_data_offset has priority, alignment must be 0 or must be compatible
+ params.data_alignment = 0;
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(crypt_set_data_offset(cd, OFFSET_8M));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_get_data_offset(cd), OFFSET_8M);
+ crypt_free(cd);
+
+ // Load gets the value from metadata
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(crypt_set_data_offset(cd, OFFSET_2M));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_get_data_offset(cd), OFFSET_8M);
+ crypt_free(cd);
+
+ params.data_alignment = OFFSET_4M;
+ OK_(crypt_init(&cd, DEVICE_2));
+ FAIL_(crypt_set_data_offset(cd, OFFSET_2M + 1), "Not aligned to 4096"); // must be aligned to 4k
+ OK_(crypt_set_data_offset(cd, OFFSET_2M));
+ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params), "Alignment not compatible");
+ OK_(crypt_set_data_offset(cd, OFFSET_4M));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_get_data_offset(cd), OFFSET_4M);
+ crypt_free(cd);
+
+ /*
+ * test limit values for backing device size
+ */
+ params.data_alignment = OFFSET_2M;
+ OK_(get_luks_offsets(0, key_size, params.data_alignment, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_0S, r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_1S, r_payload_offset + 1));
+ //OK_(create_dmdevice_over_loop(L_DEVICE_WRONG, r_payload_offset - 1));
+ OK_(create_dmdevice_over_loop(L_DEVICE_WRONG, 2050 - 1)); //FIXME last keyslot - 1 sector
+
+ // 1 sector less than required
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_WRONG));
+ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params), "Device too small");
+ crypt_free(cd);
+
+ // 0 sectors for encrypted area
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_0S));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Encrypted area too small");
+ crypt_free(cd);
+
+ // 1 sector for encrypted area
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_get_data_offset(cd), params.data_alignment);
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(t_device_size(DMDIR CDEVICE_1, &r_size_1));
+ EQ_(r_size_1, SECTOR_SIZE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ // restrict format only to empty context
+ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params), "Context is already formatted");
+ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, NULL), "Context is already formatted");
+ // change data device to wrong one
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_0S));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Device too small");
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ params.data_alignment = 0;
+ params.data_device = DEVICE_2;
+
+ // generate keyslot material at the end of luks header
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase)), 7);
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 7, passphrase, strlen(passphrase) ,0), 7);
+ crypt_free(cd);
+ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, DMDIR H_DEVICE));
+ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params), "Context is already formatted");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ crypt_free(cd);
+ // check active status without header
+ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, NULL));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ NULL_(crypt_get_type(cd));
+ OK_(strcmp(cipher, crypt_get_cipher(cd)));
+ OK_(strcmp(cipher_mode, crypt_get_cipher_mode(cd)));
+ EQ_((int)key_size, crypt_get_volume_key_size(cd));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ params.data_alignment = OFFSET_1M;
+ params.data_device = NULL;
+
+ // test uuid mismatch and _init_by_name_and_header
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ crypt_free(cd);
+ params.data_alignment = 0;
+ params.data_device = DEVICE_2;
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ crypt_free(cd);
+ // there we've got uuid mismatch
+ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, DMDIR H_DEVICE));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ NULL_(crypt_get_type(cd));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Device is active");
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_2, key, key_size, 0), "Device is active");
+ EQ_(crypt_status(cd, CDEVICE_2), CRYPT_INACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ params.data_device = NULL;
+
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+
+ // even with no keyslots defined it can be activated by volume key
+ OK_(crypt_volume_key_verify(cd, key, key_size));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_2, key, key_size, 0));
+ EQ_(crypt_status(cd, CDEVICE_2), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_2));
+
+ // now with keyslot
+ EQ_(7, crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase)));
+ EQ_(CRYPT_SLOT_ACTIVE_LAST, crypt_keyslot_status(cd, 7));
+ EQ_(7, crypt_activate_by_passphrase(cd, CDEVICE_2, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0));
+ EQ_(crypt_status(cd, CDEVICE_2), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_2));
+
+ crypt_set_iteration_time(cd, 1);
+ EQ_(1, crypt_keyslot_add_by_volume_key(cd, 1, key, key_size, KEY1, strlen(KEY1)));
+
+ // PBKDF info (in LUKS1 slots are ther same)
+ FAIL_(crypt_keyslot_get_pbkdf(cd, 1, NULL), "PBKDF struct required");
+ OK_(crypt_keyslot_get_pbkdf(cd, 1, &pbkdf));
+ OK_(strcmp(pbkdf.type, CRYPT_KDF_PBKDF2));
+ OK_(strcmp(pbkdf.hash, params.hash));
+ EQ_(1000, pbkdf.iterations); /* set by minimum iterations above */
+ EQ_(0, pbkdf.max_memory_kb);
+ EQ_(0, pbkdf.parallel_threads);
+
+ OK_(prepare_keyfile(KEYFILE1, KEY1, strlen(KEY1)));
+ OK_(prepare_keyfile(KEYFILE2, KEY2, strlen(KEY2)));
+ EQ_(2, crypt_keyslot_add_by_keyfile(cd, 2, KEYFILE1, 0, KEYFILE2, 0));
+ FAIL_(crypt_keyslot_add_by_keyfile_offset(cd, 3, KEYFILE1, 0, 1, KEYFILE2, 0, 1), "wrong key");
+ EQ_(3, crypt_keyslot_add_by_keyfile_offset(cd, 3, KEYFILE1, 0, 0, KEYFILE2, 0, 1));
+ EQ_(4, crypt_keyslot_add_by_keyfile_offset(cd, 4, KEYFILE2, 0, 1, KEYFILE1, 0, 1));
+ FAIL_(crypt_activate_by_keyfile(cd, CDEVICE_2, CRYPT_ANY_SLOT, KEYFILE2, strlen(KEY2)-1, 0), "key mismatch");
+ EQ_(2, crypt_activate_by_keyfile(cd, NULL, CRYPT_ANY_SLOT, KEYFILE2, 0, 0));
+ EQ_(3, crypt_activate_by_keyfile_offset(cd, NULL, CRYPT_ANY_SLOT, KEYFILE2, 0, 1, 0));
+ EQ_(4, crypt_activate_by_keyfile_offset(cd, NULL, CRYPT_ANY_SLOT, KEYFILE1, 0, 1, 0));
+ FAIL_(crypt_activate_by_keyfile_offset(cd, CDEVICE_2, CRYPT_ANY_SLOT, KEYFILE2, strlen(KEY2), 2, 0), "not enough data");
+ FAIL_(crypt_activate_by_keyfile_offset(cd, CDEVICE_2, CRYPT_ANY_SLOT, KEYFILE2, 0, strlen(KEY2) + 1, 0), "cannot seek");
+ FAIL_(crypt_activate_by_keyfile_offset(cd, CDEVICE_2, CRYPT_ANY_SLOT, KEYFILE2, 0, 2, 0), "wrong key");
+ EQ_(2, crypt_activate_by_keyfile(cd, CDEVICE_2, CRYPT_ANY_SLOT, KEYFILE2, 0, 0));
+ OK_(crypt_keyslot_destroy(cd, 1));
+ OK_(crypt_keyslot_destroy(cd, 2));
+ OK_(crypt_keyslot_destroy(cd, 3));
+ OK_(crypt_keyslot_destroy(cd, 4));
+ OK_(crypt_deactivate(cd, CDEVICE_2));
+ _remove_keyfiles();
+
+ FAIL_(crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase)), "slot used");
+ key[1] = ~key[1];
+ FAIL_(crypt_keyslot_add_by_volume_key(cd, 6, key, key_size, passphrase, strlen(passphrase)), "key mismatch");
+ key[1] = ~key[1];
+ EQ_(6, crypt_keyslot_add_by_volume_key(cd, 6, key, key_size, passphrase, strlen(passphrase)));
+ EQ_(CRYPT_SLOT_ACTIVE, crypt_keyslot_status(cd, 6));
+
+ FAIL_(crypt_keyslot_destroy(cd, 8), "invalid keyslot");
+ FAIL_(crypt_keyslot_destroy(cd, CRYPT_ANY_SLOT), "invalid keyslot");
+ FAIL_(crypt_keyslot_destroy(cd, 0), "keyslot not used");
+ OK_(crypt_keyslot_destroy(cd, 7));
+ EQ_(CRYPT_SLOT_INACTIVE, crypt_keyslot_status(cd, 7));
+ EQ_(CRYPT_SLOT_ACTIVE_LAST, crypt_keyslot_status(cd, 6));
+
+ EQ_(7, crypt_keyslot_change_by_passphrase(cd, 6, 7, passphrase, strlen(passphrase), passphrase2, strlen(passphrase2)));
+ EQ_(CRYPT_SLOT_ACTIVE_LAST, crypt_keyslot_status(cd, 7));
+ EQ_(7, crypt_activate_by_passphrase(cd, NULL, 7, passphrase2, strlen(passphrase2), 0));
+ EQ_(6, crypt_keyslot_change_by_passphrase(cd, CRYPT_ANY_SLOT, 6, passphrase2, strlen(passphrase2), passphrase, strlen(passphrase)));
+
+ if (!_fips_mode) {
+ EQ_(6, crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key2, &key_size, passphrase, strlen(passphrase)));
+ OK_(crypt_volume_key_verify(cd, key2, key_size));
+
+ OK_(memcmp(key, key2, key_size));
+ }
+ OK_(strcmp(cipher, crypt_get_cipher(cd)));
+ OK_(strcmp(cipher_mode, crypt_get_cipher_mode(cd)));
+ EQ_((int)key_size, crypt_get_volume_key_size(cd));
+ EQ_(OFFSET_2M, crypt_get_data_offset(cd));
+ OK_(strcmp(DEVICE_2, crypt_get_device_name(cd)));
+
+ reset_log();
+ OK_(crypt_dump(cd));
+ OK_(!(global_lines != 0));
+ reset_log();
+
+ FAIL_(crypt_set_uuid(cd, "blah"), "wrong UUID format");
+ OK_(crypt_set_uuid(cd, DEVICE_TEST_UUID));
+ OK_(strcmp(DEVICE_TEST_UUID, crypt_get_uuid(cd)));
+
+ FAIL_(crypt_deactivate(cd, CDEVICE_2), "not active");
+ crypt_free(cd);
+ _cleanup_dmdevices();
+}
+
+static void UseTempVolumes(void)
+{
+ struct crypt_device *cd;
+ char tmp[256];
+
+ // Tepmporary device without keyslot but with on-disk LUKS header
+ OK_(crypt_init(&cd, DEVICE_2));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_2, NULL, 0, 0), "not yet formatted");
+ OK_(crypt_format(cd, CRYPT_LUKS1, "aes", "cbc-essiv:sha256", NULL, NULL, 16, NULL));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_2, NULL, 0, 0));
+ EQ_(crypt_status(cd, CDEVICE_2), CRYPT_ACTIVE);
+ crypt_free(cd);
+
+ OK_(crypt_init_by_name(&cd, CDEVICE_2));
+ OK_(crypt_deactivate(cd, CDEVICE_2));
+ crypt_free(cd);
+
+ // Dirty checks: device without UUID
+ // we should be able to remove it but not manuipulate with it
+ snprintf(tmp, sizeof(tmp), "dmsetup create %s --table \""
+ "0 100 crypt aes-cbc-essiv:sha256 deadbabedeadbabedeadbabedeadbabe 0 "
+ "%s 2048\"", CDEVICE_2, DEVICE_2);
+ _system(tmp, 1);
+ OK_(crypt_init_by_name(&cd, CDEVICE_2));
+ OK_(crypt_deactivate(cd, CDEVICE_2));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_2, NULL, 0, 0), "No known device type");
+ crypt_free(cd);
+
+ // Dirty checks: device with UUID but LUKS header key fingerprint must fail)
+ snprintf(tmp, sizeof(tmp), "dmsetup create %s --table \""
+ "0 100 crypt aes-cbc-essiv:sha256 deadbabedeadbabedeadbabedeadbabe 0 "
+ "%s 2048\" -u CRYPT-LUKS1-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-ctest1",
+ CDEVICE_2, DEVICE_2);
+ _system(tmp, 1);
+ OK_(crypt_init_by_name(&cd, CDEVICE_2));
+ OK_(crypt_deactivate(cd, CDEVICE_2));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_2, NULL, 0, 0), "wrong volume key");
+ crypt_free(cd);
+
+ // No slots
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_2, NULL, 0, 0), "volume key is lost");
+ crypt_free(cd);
+
+ // Plain device
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(crypt_format(cd, CRYPT_PLAIN, "aes", "cbc-essiv:sha256", NULL, NULL, 16, NULL));
+ FAIL_(crypt_activate_by_volume_key(cd, NULL, "xxx", 3, 0), "cannot verify key with plain");
+ FAIL_(crypt_volume_key_verify(cd, "xxx", 3), "cannot verify key with plain");
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_2, "xxx", 3, 0), "wrong key length");
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_2, "volumekeyvolumek", 16, 0));
+ EQ_(crypt_status(cd, CDEVICE_2), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_2));
+ crypt_free(cd);
+}
+
+static void LuksHeaderRestore(void)
+{
+ struct crypt_device *cd;
+ struct crypt_params_luks1 params = {
+ .hash = "sha512",
+ .data_alignment = 2048, // 4M, data offset will be 4096
+ };
+ struct crypt_params_plain pl_params = {
+ .hash = "sha1",
+ .skip = 0,
+ .offset = 0,
+ .size = 0
+ };
+ char key[128], key2[128], cmd[256];
+
+ const char *mk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(mk_hex) / 2;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+ uint64_t r_payload_offset;
+
+ crypt_decode_key(key, mk_hex, key_size);
+
+ OK_(get_luks_offsets(0, key_size, params.data_alignment, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 5000));
+
+ // do not restore header over plain device
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &pl_params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ FAIL_(crypt_header_restore(cd, CRYPT_PLAIN, VALID_HEADER), "Cannot restore header to PLAIN type device");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, VALID_HEADER), "Cannot restore header over PLAIN type device");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ // invalid headers
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_1), "Header corrupted");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_2), "Header corrupted");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_3), "Header corrupted");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_4), "Header too small");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_5), "Header corrupted");
+ OK_(crypt_header_restore(cd, CRYPT_LUKS1, VALID_HEADER));
+ // wipe valid luks header
+ snprintf(cmd, sizeof(cmd), "dd if=/dev/zero of=" DMDIR L_DEVICE_OK " bs=512 count=%" PRIu64 " 2>/dev/null", r_payload_offset);
+ OK_(_system(cmd, 1));
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_1), "Header corrupted");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_2), "Header corrupted");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_3), "Header corrupted");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_4), "Header too small");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_5), "Header corrupted");
+ OK_(crypt_header_restore(cd, CRYPT_LUKS1, VALID_HEADER));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ // volume key_size mismatch
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ memcpy(key2, key, key_size / 2);
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key2, key_size / 2, &params));
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, VALID_HEADER), "Volume keysize mismatch");
+ crypt_free(cd);
+
+ // payload offset mismatch
+ params.data_alignment = 8192;
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, VALID_HEADER), "Payload offset mismatch");
+ //_system("dmsetup table;sleep 1",1);
+ crypt_free(cd);
+
+ /* check crypt_header_restore() properly loads crypt_device context */
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_wipe(cd, NULL, CRYPT_WIPE_ZERO, 0, 1*1024*1024, 1*1024*1024, 0, NULL, NULL));
+ OK_(crypt_header_restore(cd, CRYPT_LUKS1, VALID_HEADER));
+ OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0));
+ /* same test, any LUKS */
+ OK_(crypt_wipe(cd, NULL, CRYPT_WIPE_ZERO, 0, 1*1024*1024, 1*1024*1024, 0, NULL, NULL));
+ OK_(crypt_header_restore(cd, CRYPT_LUKS, VALID_HEADER));
+ OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0));
+
+ crypt_free(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void LuksHeaderLoad(void)
+{
+ struct crypt_device *cd;
+ struct crypt_params_luks1 params = {
+ .hash = "sha512",
+ .data_alignment = 2048,
+ };
+ struct crypt_params_plain pl_params = {
+ .hash = "sha1",
+ .skip = 0,
+ .offset = 0,
+ .size = 0
+ };
+ char key[128], cmd[256];
+
+ const char *mk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(mk_hex) / 2;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+ uint64_t r_payload_offset, r_header_size;
+ uint64_t mdata_size, keyslots_size;
+
+ crypt_decode_key(key, mk_hex, key_size);
+
+ // prepare test env
+ OK_(get_luks_offsets(0, key_size, params.data_alignment, 0, &r_header_size, &r_payload_offset));
+ // external header device
+ OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size));
+ // prepared header on a device too small to contain header and payload
+ //OK_(create_dmdevice_over_loop(H_DEVICE_WRONG, r_payload_offset - 1));
+ OK_(create_dmdevice_over_loop(H_DEVICE_WRONG, 2050 - 1)); //FIXME
+ //snprintf(cmd, sizeof(cmd), "dd if=" EVL_HEADER_4 " of=" DMDIR H_DEVICE_WRONG " bs=512 count=%" PRIu64, r_payload_offset - 1);
+ snprintf(cmd, sizeof(cmd), "dd if=" EVL_HEADER_4 " of=" DMDIR H_DEVICE_WRONG " bs=512 count=%d 2>/dev/null", 2050 - 1);
+ OK_(_system(cmd, 1));
+ // some device
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 1000));
+ // 1 sector device
+ OK_(create_dmdevice_over_loop(L_DEVICE_1S, r_payload_offset + 1));
+ // 0 sectors device for payload
+ OK_(create_dmdevice_over_loop(L_DEVICE_0S, r_payload_offset));
+
+ // valid metadata and device size
+ params.data_alignment = 0;
+ params.data_device = DMDIR L_DEVICE_OK;
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(!crypt_get_metadata_device_name(cd));
+ EQ_(strcmp(DMDIR H_DEVICE, crypt_get_metadata_device_name(cd)), 0);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ // repeat with init with two devices
+ OK_(crypt_init_data_device(&cd, DMDIR H_DEVICE, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ crypt_free(cd);
+ OK_(crypt_init_data_device(&cd, DMDIR H_DEVICE, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ OK_(!crypt_get_metadata_device_name(cd));
+ EQ_(strcmp(DMDIR H_DEVICE, crypt_get_metadata_device_name(cd)), 0);
+ crypt_free(cd);
+
+ // bad header: device too small (payloadOffset > device_size)
+ OK_(crypt_init(&cd, DMDIR H_DEVICE_WRONG));
+ FAIL_(crypt_load(cd, CRYPT_LUKS1, NULL), "Device too small");
+ NULL_(crypt_get_type(cd));
+ crypt_free(cd);
+
+ // 0 secs for encrypted data area
+ params.data_alignment = 2048;
+ params.data_device = NULL;
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_0S));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ FAIL_(crypt_set_metadata_size(cd, 0x004000, 0x004000), "Wrong context type");
+ OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size));
+ EQ_(mdata_size, LUKS_ALIGN_KEYSLOTS);
+ EQ_(keyslots_size, r_header_size * SECTOR_SIZE - mdata_size);
+ crypt_free(cd);
+ // load should be ok
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_0S));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Device too small");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ crypt_free(cd);
+
+ // damaged header
+ OK_(_system("dd if=/dev/zero of=" DMDIR L_DEVICE_OK " bs=512 count=8 2>/dev/null", 1));
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ FAIL_(crypt_load(cd, CRYPT_LUKS1, NULL), "Header not found");
+ crypt_free(cd);
+
+ // plain device
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ FAIL_(crypt_load(cd, CRYPT_PLAIN, NULL), "Can't load nonLUKS device type");
+ crypt_free(cd);
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, key, key_size, &pl_params));
+ FAIL_(crypt_load(cd, CRYPT_LUKS1, NULL), "Can't load over nonLUKS device type");
+ FAIL_(crypt_set_metadata_size(cd, 0x004000, 0x004000), "Wrong context type");
+ FAIL_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size), "Wrong context type");
+ crypt_free(cd);
+
+ /* check load sets proper device type */
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_0S));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ EQ_(strcmp(CRYPT_LUKS1, crypt_get_type(cd)), 0);
+ crypt_free(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void LuksHeaderBackup(void)
+{
+ struct crypt_device *cd;
+ struct crypt_params_luks1 params = {
+ .hash = "sha512",
+ .data_alignment = 2048,
+ };
+ char key[128];
+ int fd, ro = O_RDONLY;
+
+ const char *mk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(mk_hex) / 2;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+ uint64_t r_payload_offset;
+
+ const char *passphrase = PASSPHRASE;
+
+ crypt_decode_key(key, mk_hex, key_size);
+
+ OK_(get_luks_offsets(0, key_size, params.data_alignment, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 1));
+
+ // create LUKS device and backup the header
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase)), 7);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 0, key, key_size, passphrase, strlen(passphrase)), 0);
+ OK_(crypt_header_backup(cd, CRYPT_LUKS1, BACKUP_FILE));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ // restore header from backup
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_header_restore(cd, CRYPT_LUKS1, BACKUP_FILE));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ // exercise luksOpen using backup header in file
+ OK_(crypt_init(&cd, BACKUP_FILE));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_OK));
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, passphrase, strlen(passphrase), 0), 0);
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ OK_(crypt_init(&cd, BACKUP_FILE));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_OK));
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 7, passphrase, strlen(passphrase), 0), 7);
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ // exercise luksOpen using backup header on block device
+ fd = loop_attach(&DEVICE_3, BACKUP_FILE, 0, 0, &ro);
+ close(fd);
+ OK_(fd < 0);
+ OK_(crypt_init(&cd, DEVICE_3));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_OK));
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, passphrase, strlen(passphrase), 0), 0);
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ OK_(crypt_init(&cd, DEVICE_3));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_OK));
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 7, passphrase, strlen(passphrase), 0), 7);
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void ResizeDeviceLuks(void)
+{
+ struct crypt_device *cd, *cd2;
+ struct crypt_params_luks1 params = {
+ .hash = "sha512",
+ .data_alignment = 2048,
+ };
+ char key[128];
+
+ const char *mk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(mk_hex) / 2;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+ uint64_t r_payload_offset, r_header_size, r_size;
+
+ crypt_decode_key(key, mk_hex, key_size);
+
+ // prepare env
+ OK_(get_luks_offsets(0, key_size, params.data_alignment, 0, NULL, &r_payload_offset));
+ OK_(get_luks_offsets(1, key_size, 0, 0, &r_header_size, NULL));
+ OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size));
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 1000));
+ OK_(create_dmdevice_over_loop(L_DEVICE_0S, 1000));
+ OK_(create_dmdevice_over_loop(L_DEVICE_WRONG, r_payload_offset + 1000));
+
+ // test header and encrypted payload all in one device
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ OK_(crypt_resize(cd, CDEVICE_1, 42));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(42, r_size >> SECTOR_SHIFT);
+ // autodetect encrypted device area size
+ OK_(crypt_resize(cd, CDEVICE_1, 0));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(1000, r_size >> SECTOR_SHIFT);
+ FAIL_(crypt_resize(cd, CDEVICE_1, 1001), "Device too small");
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(1000, r_size >> SECTOR_SHIFT);
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ params.data_alignment = 0;
+ params.data_device = DMDIR L_DEVICE_0S;
+ // test case for external header
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ OK_(crypt_resize(cd, CDEVICE_1, 666));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(666, r_size >> SECTOR_SHIFT);
+ // autodetect encrypted device size
+ OK_(crypt_resize(cd, CDEVICE_1, 0));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(1000, r_size >> SECTOR_SHIFT);
+ FAIL_(crypt_resize(cd, CDEVICE_1, 1001), "Device too small");
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(1000, r_size >> SECTOR_SHIFT);
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, NULL, NULL));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+
+ /* do not allow resize of other device */
+ OK_(crypt_init(&cd2, DMDIR L_DEVICE_WRONG));
+ OK_(crypt_format(cd2, CRYPT_LUKS1, cipher, cipher_mode, crypt_get_uuid(cd), key, key_size, &params));
+ OK_(crypt_activate_by_volume_key(cd2, CDEVICE_2, key, key_size, 0));
+ FAIL_(crypt_resize(cd2, CDEVICE_1, 1), "Device got resized by wrong device context.");
+ OK_(crypt_deactivate(cd2, CDEVICE_2));
+ crypt_free(cd2);
+
+ OK_(crypt_init(&cd2, DMDIR L_DEVICE_WRONG));
+ OK_(crypt_format(cd2, CRYPT_PLAIN, cipher, cipher_mode, NULL, key, key_size, NULL));
+ OK_(crypt_activate_by_volume_key(cd2, CDEVICE_2, key, key_size, 0));
+ FAIL_(crypt_resize(cd2, CDEVICE_1, 1), "Device got resized by wrong device context.");
+ OK_(crypt_deactivate(cd2, CDEVICE_2));
+ crypt_free(cd2);
+
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void HashDevicePlain(void)
+{
+ struct crypt_device *cd;
+ struct crypt_params_plain params = {
+ .hash = NULL,
+ .skip = 0,
+ .offset = 0,
+ };
+
+ size_t key_size;
+ const char *mk_hex, *keystr;
+ char key[256];
+
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_format(cd, CRYPT_PLAIN, "aes", "cbc-essiv:sha256", NULL, NULL, 16, &params));
+
+ // hash PLAIN, short key
+ OK_(prepare_keyfile(KEYFILE1, "tooshort", 8));
+ FAIL_(crypt_activate_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 16, 0), "not enough data in keyfile");
+ _remove_keyfiles();
+
+ // hash PLAIN, exact key
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ mk_hex = "caffeecaffeecaffeecaffeecaffee88";
+ key_size = 16;
+ crypt_decode_key(key, mk_hex, key_size);
+ OK_(prepare_keyfile(KEYFILE1, key, key_size));
+ OK_(crypt_activate_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, key_size, 0));
+ OK_(get_key_dm(CDEVICE_1, key, sizeof(key)));
+ OK_(strcmp(key, mk_hex));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ // Limit plain key
+ mk_hex = "caffeecaffeecaffeecaffeeca000000";
+ OK_(crypt_activate_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, key_size - 3, 0));
+ OK_(get_key_dm(CDEVICE_1, key, sizeof(key)));
+ OK_(strcmp(key, mk_hex));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ _remove_keyfiles();
+
+ // hash PLAIN, long key
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ mk_hex = "caffeecaffeecaffeecaffeecaffee88babebabe";
+ key_size = 16;
+ crypt_decode_key(key, mk_hex, key_size);
+ OK_(prepare_keyfile(KEYFILE1, key, strlen(mk_hex) / 2));
+ OK_(crypt_activate_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, key_size, 0));
+ OK_(get_key_dm(CDEVICE_1, key, sizeof(key)));
+ FAIL_(strcmp(key, mk_hex), "only key length used");
+ OK_(strncmp(key, mk_hex, key_size));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ // Now without explicit limit
+ OK_(crypt_activate_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 0, 0));
+ OK_(get_key_dm(CDEVICE_1, key, sizeof(key)));
+ FAIL_(strcmp(key, mk_hex), "only key length used");
+ OK_(strncmp(key, mk_hex, key_size));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ _remove_keyfiles();
+
+ // Handling of legacy "plain" hash (no hash)
+ params.hash = "plain";
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ mk_hex = "aabbcaffeecaffeecaffeecaffeecaff";
+ key_size = 16;
+ crypt_decode_key(key, mk_hex, key_size);
+ OK_(prepare_keyfile(KEYFILE1, key, strlen(mk_hex) / 2));
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_format(cd, CRYPT_PLAIN, "aes", "cbc-essiv:sha256", NULL, NULL, 16, &params));
+ OK_(crypt_activate_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, key_size, 0));
+ OK_(get_key_dm(CDEVICE_1, key, sizeof(key)));
+ OK_(strcmp(key, mk_hex));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ _remove_keyfiles();
+
+ // hash sha256
+ params.hash = "sha256";
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_format(cd, CRYPT_PLAIN, "aes", "cbc-essiv:sha256", NULL, NULL, 16, &params));
+
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ mk_hex = "c62e4615bd39e222572f3a1bf7c2132e";
+ keystr = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+ key_size = strlen(keystr); // 32
+ OK_(prepare_keyfile(KEYFILE1, keystr, strlen(keystr)));
+ OK_(crypt_activate_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, key_size, 0));
+ OK_(get_key_dm(CDEVICE_1, key, sizeof(key)));
+ OK_(strcmp(key, mk_hex));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ // Read full keyfile
+ OK_(crypt_activate_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 0, 0));
+ OK_(get_key_dm(CDEVICE_1, key, sizeof(key)));
+ OK_(strcmp(key, mk_hex));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ _remove_keyfiles();
+
+ // Limit keyfile read
+ keystr = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxAAAAAAAA";
+ OK_(prepare_keyfile(KEYFILE1, keystr, strlen(keystr)));
+ OK_(crypt_activate_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, key_size, 0));
+ OK_(get_key_dm(CDEVICE_1, key, sizeof(key)));
+ OK_(strcmp(key, mk_hex));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ // Full keyfile
+ OK_(crypt_activate_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 0, 0));
+ OK_(get_key_dm(CDEVICE_1, key, sizeof(key)));
+ OK_(strcmp(key, "0e49cb34a1dee1df33f6505e4de44a66"));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ _remove_keyfiles();
+
+ // FIXME: add keyfile="-" tests somehow
+
+ crypt_free(cd);
+}
+
+static void VerityTest(void)
+{
+ struct crypt_device *cd;
+ const char *salt_hex = "20c28ffc129c12360ba6ceea2b6cf04e89c2b41cfe6b8439eb53c1897f50df7b";
+ const char *root_hex = "ab018b003a967fc782effb293b6dccb60b4f40c06bf80d16391acf686d28b5d6";
+ char salt[256], root_hash[256];
+ struct crypt_active_device cad;
+ struct crypt_params_verity params = {
+ .data_device = DEVICE_EMPTY,
+ .salt = salt,
+ .data_size = 0, /* whole device */
+ .hash_area_offset = 0,
+ .flags = CRYPT_VERITY_CREATE_HASH,
+ };
+
+ crypt_decode_key(salt, salt_hex, strlen(salt_hex) / 2);
+ crypt_decode_key(root_hash, root_hex, strlen(root_hex) / 2);
+
+ /* Format */
+ OK_(crypt_init(&cd, DEVICE_2));
+
+ /* block size */
+ params.data_block_size = 333;
+ FAIL_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, &params),
+ "Unsupppored block size.");
+ params.data_block_size = 4096;
+ params.hash_block_size = 333;
+ FAIL_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, &params),
+ "Unsupppored block size.");
+ params.hash_block_size = 4096;
+
+ /* salt size */
+ params.salt_size = 257;
+ FAIL_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, &params),
+ "Too large salt.");
+ params.salt_size = 32;
+
+ /* hash_type */
+ params.hash_type = 3;
+ FAIL_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, &params),
+ "Unsupported hash type.");
+ params.hash_type = 1;
+ params.hash_name = "blah";
+ FAIL_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, &params),
+ "Unsupported hash name.");
+ params.hash_name = "sha256";
+
+ OK_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, &params));
+ crypt_free(cd);
+
+ params.data_device = NULL;
+ OK_(crypt_init_data_device(&cd, DEVICE_2, DEVICE_EMPTY));
+ OK_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, &params));
+ EQ_(strcmp(DEVICE_2, crypt_get_metadata_device_name(cd)), 0);
+ crypt_free(cd);
+
+ /* Verify */
+ OK_(crypt_init(&cd, DEVICE_2));
+ memset(&params, 0, sizeof(params));
+ params.data_device = DEVICE_EMPTY;
+ params.flags = CRYPT_VERITY_CHECK_HASH;
+ OK_(crypt_load(cd, CRYPT_VERITY, &params));
+
+ /* check verity params */
+ EQ_(crypt_get_volume_key_size(cd), 32);
+ OK_(strcmp(CRYPT_VERITY, crypt_get_type(cd)));
+ memset(&params, 0, sizeof(params));
+ OK_(crypt_get_verity_info(cd, &params));
+ OK_(strcmp("sha256", params.hash_name));
+ EQ_(strlen(salt_hex) / 2, params.salt_size);
+ OK_(memcmp(salt, params.salt, params.salt_size));
+ EQ_(4096, params.data_block_size);
+ EQ_(4096, params.hash_block_size);
+ EQ_(1, params.hash_type);
+ EQ_(crypt_get_volume_key_size(cd), 32);
+
+ OK_(crypt_activate_by_volume_key(cd, NULL, root_hash, 32, 0));
+ OK_(crypt_set_data_device(cd, DEVICE_1));
+ FAIL_(crypt_activate_by_volume_key(cd, NULL, root_hash, 32, 0), "Data corrupted");;
+
+ OK_(crypt_set_data_device(cd, DEVICE_EMPTY));
+ if (crypt_activate_by_volume_key(cd, CDEVICE_1, root_hash, 32,
+ CRYPT_ACTIVATE_READONLY) == -ENOTSUP) {
+ printf("WARNING: kernel dm-verity not supported, skipping test.\n");
+ crypt_free(cd);
+ return;
+ }
+ OK_(crypt_get_active_device(cd, CDEVICE_1, &cad));
+ EQ_(CRYPT_ACTIVATE_READONLY, cad.flags);
+ crypt_free(cd);
+
+ OK_(crypt_init_by_name(&cd, CDEVICE_1));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ /* hash fail */
+ root_hash[1] = ~root_hash[1];
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, root_hash, 32, CRYPT_ACTIVATE_READONLY));
+ /* Be sure there was some read activity to mark device corrupted. */
+ _system("blkid " DMDIR CDEVICE_1, 0);
+ OK_(crypt_get_active_device(cd, CDEVICE_1, &cad));
+ EQ_(CRYPT_ACTIVATE_READONLY|CRYPT_ACTIVATE_CORRUPTED, cad.flags);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ root_hash[1] = ~root_hash[1];
+
+ /* data fail */
+ OK_(crypt_set_data_device(cd, DEVICE_1));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, root_hash, 32, CRYPT_ACTIVATE_READONLY));
+ _system("blkid " DMDIR CDEVICE_1, 0);
+ OK_(crypt_get_active_device(cd, CDEVICE_1, &cad));
+ EQ_(CRYPT_ACTIVATE_READONLY|CRYPT_ACTIVATE_CORRUPTED, cad.flags);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ crypt_free(cd);
+}
+
+static void TcryptTest(void)
+{
+ struct crypt_device *cd = NULL;
+ struct crypt_active_device cad;
+ const char *passphrase = "aaaaaaaaaaaa";
+ const char *kf1 = "tcrypt-images/keyfile1";
+ const char *kf2 = "tcrypt-images/keyfile2";
+ const char *keyfiles[] = { kf1, kf2 };
+ struct crypt_params_tcrypt params = {
+ .passphrase = passphrase,
+ .passphrase_size = strlen(passphrase),
+ .keyfiles = keyfiles,
+ .keyfiles_count = 2,
+ };
+ double enc_mbr = 0, dec_mbr = 0;
+ const char *tcrypt_dev = "tcrypt-images/tck_5-sha512-xts-aes";
+ const char *tcrypt_dev2 = "tcrypt-images/tc_5-sha512-xts-serpent-twofish-aes";
+ size_t key_size = 64;
+ char key[key_size], key_def[key_size];
+ const char *key_hex =
+ "98dee64abe44bbf41d171c1f7b3e8eacda6d6b01f459097459a167f8c2872a96"
+ "3979531d1cdc18af62757cf22286f16f8583d848524f128d7594ac2082668c73";
+ int r;
+
+ crypt_decode_key(key_def, key_hex, strlen(key_hex) / 2);
+
+ // First ensure we can use af_alg skcipher interface
+ r = crypt_benchmark(NULL, "aes", "xts", 512, 16, 1024, &enc_mbr, &dec_mbr);
+ if (r == -ENOTSUP || r == -ENOENT) {
+ printf("WARNING: algif_skcipher interface not present, skipping test.\n");
+ return;
+ }
+
+ OK_(crypt_init(&cd, tcrypt_dev));
+ params.passphrase_size--;
+ FAIL_(crypt_load(cd, CRYPT_TCRYPT, &params), "Wrong passphrase");
+ params.passphrase_size++;
+ OK_(crypt_load(cd, CRYPT_TCRYPT, &params));
+
+ // check params after load
+ OK_(strcmp("xts-plain64", crypt_get_cipher_mode(cd)));
+ OK_(strcmp("aes", crypt_get_cipher(cd)));
+ EQ_(key_size, crypt_get_volume_key_size(cd));
+ EQ_(256, crypt_get_iv_offset(cd));
+ EQ_(256, crypt_get_data_offset(cd));
+
+ memset(key, 0, key_size);
+ if (!_fips_mode) {
+ key_size--;
+ // small buffer
+ FAIL_(crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key, &key_size, NULL, 0), "small buffer");
+ key_size++;
+ OK_(crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key, &key_size, NULL, 0));
+ OK_(memcmp(key, key_def, key_size));
+ }
+
+ reset_log();
+ OK_(crypt_dump(cd));
+ OK_(!(global_lines != 0));
+ reset_log();
+
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, NULL, 0, CRYPT_ACTIVATE_READONLY));
+ NULL_(crypt_get_metadata_device_name(cd));
+ crypt_free(cd);
+
+ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, NULL));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+
+ FAIL_(crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key, &key_size, NULL, 0), "Need crypt_load");
+
+ // check params after init_by_name
+ OK_(strcmp("xts-plain64", crypt_get_cipher_mode(cd)));
+ OK_(strcmp("aes", crypt_get_cipher(cd)));
+ EQ_(key_size, crypt_get_volume_key_size(cd));
+ EQ_(256, crypt_get_iv_offset(cd));
+ EQ_(256, crypt_get_data_offset(cd));
+
+ OK_(crypt_get_active_device(cd, CDEVICE_1, &cad));
+ EQ_(CRYPT_ACTIVATE_READONLY, cad.flags);
+ EQ_(256, cad.offset);
+ EQ_(256, cad.iv_offset);
+ EQ_(72, cad.size);
+
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+
+ // init with detached header is not supported
+ OK_(crypt_init_data_device(&cd, tcrypt_dev2, DEVICE_2));
+ FAIL_(crypt_load(cd, CRYPT_TCRYPT, &params), "can't use tcrypt with separate metadata device");
+ crypt_free(cd);
+
+ // Following test uses non-FIPS algorithms in the cipher chain
+ if(_fips_mode)
+ return;
+
+ OK_(crypt_init(&cd, tcrypt_dev2));
+ params.keyfiles = NULL;
+ params.keyfiles_count = 0;
+ r = crypt_load(cd, CRYPT_TCRYPT, &params);
+ if (r < 0) {
+ printf("WARNING: cannot use non-AES encryption, skipping test.\n");
+ crypt_free(cd);
+ return;
+ }
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, NULL, 0, CRYPT_ACTIVATE_READONLY));
+ crypt_free(cd);
+
+ // Deactivate the whole chain
+ EQ_(crypt_status(NULL, CDEVICE_1 "_1"), CRYPT_BUSY);
+ OK_(crypt_deactivate(NULL, CDEVICE_1));
+ EQ_(crypt_status(NULL, CDEVICE_1 "_1"), CRYPT_INACTIVE);
+}
+
+static void IntegrityTest(void)
+{
+ struct crypt_device *cd;
+ struct crypt_params_integrity params = {
+ .tag_size = 4,
+ .integrity = "crc32c",
+ .sector_size = 4096,
+ }, ip = {};
+ int ret;
+
+ // FIXME: this should be more detailed
+
+ OK_(crypt_init(&cd,DEVICE_1));
+ FAIL_(crypt_format(cd,CRYPT_INTEGRITY,NULL,NULL,NULL,NULL,0,NULL), "params field required");
+ ret = crypt_format(cd,CRYPT_INTEGRITY,NULL,NULL,NULL,NULL,0,&params);
+ if (ret < 0) {
+ printf("WARNING: cannot format integrity device, skipping test.\n");
+ crypt_free(cd);
+ return;
+ }
+ OK_(crypt_get_integrity_info(cd, &ip));
+ EQ_(ip.tag_size, params.tag_size);
+ EQ_(ip.sector_size, params.sector_size);
+ EQ_(crypt_get_sector_size(cd), params.sector_size);
+ EQ_(ip.interleave_sectors, params.interleave_sectors);
+ EQ_(ip.journal_size, params.journal_size);
+ EQ_(ip.journal_watermark, params.journal_watermark);
+ OK_(strcmp(ip.integrity,params.integrity));
+ FAIL_(crypt_set_uuid(cd,DEVICE_1_UUID),"can't set uuid to integrity device");
+ crypt_free(cd);
+
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_load(cd, CRYPT_INTEGRITY, NULL));
+ crypt_free(cd);
+
+ OK_(crypt_init(&cd, DEVICE_1));
+ //params.tag_size = 8;
+ //FAIL_(crypt_load(cd, CRYPT_INTEGRITY, &params), "tag size mismatch");
+ params.tag_size = 4;
+ OK_(crypt_load(cd, CRYPT_INTEGRITY, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, NULL, 0, 0));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ crypt_free(cd);
+
+ memset(&ip, 0, sizeof(ip));
+ OK_(crypt_init_by_name(&cd, CDEVICE_1));
+ OK_(crypt_get_integrity_info(cd, &ip));
+ EQ_(ip.tag_size, params.tag_size);
+ OK_(strcmp(ip.integrity,params.integrity));
+ OK_(strcmp(CRYPT_INTEGRITY,crypt_get_type(cd)));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ crypt_free(cd);
+}
+
+// Check that gcrypt is properly initialised in format
+static void NonFIPSAlg(void)
+{
+ struct crypt_device *cd;
+ struct crypt_params_luks1 params = {0};
+ char key[128] = "";
+ size_t key_size = 128 / 8;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+ int ret;
+
+ OK_(crypt_init(&cd, DEVICE_2));
+ params.hash = "sha256";
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params),
+ "Already formatted.");
+ crypt_free(cd);
+
+ params.hash = "whirlpool";
+ OK_(crypt_init(&cd, DEVICE_2));
+ ret = crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params);
+ if (ret < 0) {
+ printf("WARNING: whirlpool not supported, skipping test.\n");
+ crypt_free(cd);
+ return;
+ }
+ crypt_free(cd);
+
+ params.hash = "md5";
+ OK_(crypt_init(&cd, DEVICE_2));
+ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params),
+ "MD5 unsupported, too short");
+ crypt_free(cd);
+}
+
+static void int_handler(int sig __attribute__((__unused__)))
+{
+ _quit++;
+}
+
+int main(int argc, char *argv[])
+{
+ struct sigaction sa = { .sa_handler = int_handler };
+ int i;
+
+ if (getuid() != 0) {
+ printf("You must be root to run this test.\n");
+ exit(77);
+ }
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp("-v", argv[i]) || !strcmp("--verbose", argv[i]))
+ _verbose = 1;
+ else if (!strcmp("--debug", argv[i]))
+ _debug = _verbose = 1;
+ }
+
+ /* Handle interrupt properly */
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+
+ register_cleanup(_cleanup);
+
+ _cleanup();
+ if (_setup())
+ goto out;
+
+ crypt_set_debug_level(_debug ? CRYPT_DEBUG_ALL : CRYPT_DEBUG_NONE);
+
+ RUN_(NonFIPSAlg, "Crypto is properly initialised in format"); //must be the first!
+ RUN_(AddDevicePlain, "A plain device API creation");
+ RUN_(HashDevicePlain, "A plain device API hash");
+ RUN_(AddDeviceLuks, "Format and use LUKS device");
+ RUN_(LuksHeaderLoad, "Header load");
+ RUN_(LuksHeaderRestore, "LUKS header restore");
+ RUN_(LuksHeaderBackup, "LUKS header backup");
+ RUN_(ResizeDeviceLuks, "LUKS device resize");
+ RUN_(UseLuksDevice, "Use pre-formated LUKS device");
+ RUN_(SuspendDevice, "Suspend/Resume");
+ RUN_(UseTempVolumes, "Format and use temporary encrypted device");
+ RUN_(CallbacksTest, "API callbacks");
+ RUN_(VerityTest, "DM verity");
+ RUN_(TcryptTest, "Tcrypt API");
+ RUN_(IntegrityTest, "Integrity API");
+out:
+ _cleanup();
+ return 0;
+}
diff --git a/tests/api_test.h b/tests/api_test.h
new file mode 100644
index 0000000..21154dd
--- /dev/null
+++ b/tests/api_test.h
@@ -0,0 +1,116 @@
+/*
+ * cryptsetup library API check functions
+ *
+ * Copyright (C) 2009-2019 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2019 Milan Broz
+ * Copyright (C) 2016-2019 Ondrej Kozina
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef API_TEST_H
+#define API_TEST_H
+
+#include <stdio.h>
+#include <stdint.h>
+
+extern char *THE_LOOP_DEV;
+extern int _debug;
+extern int global_lines;
+extern int _quit;
+extern int _verbose;
+extern uint64_t t_dev_offset;
+
+int t_device_size(const char *device, uint64_t *size);
+int t_dm_check_versions(void);
+int t_dm_crypt_keyring_support(void);
+int t_dm_crypt_cpu_switch_support(void);
+int t_dm_crypt_discard_support(void);
+
+int fips_mode(void);
+
+int create_dmdevice_over_loop(const char *dm_name, const uint64_t size);
+
+int get_key_dm(const char *name, char *buffer, unsigned int buffer_size);
+
+int prepare_keyfile(const char *name, const char *passphrase, int size);
+
+int crypt_decode_key(char *key, const char *hex, unsigned int size);
+
+void global_log_callback(int level, const char *msg, void *usrptr);
+
+void reset_log(void);
+
+int _system(const char *command, int warn);
+
+void register_cleanup(void (*cleanup)(void));
+
+void check_ok(int status, int line, const char *func);
+void check_ko(int status, int line, const char *func);
+void check_equal(int line, const char *func, int64_t x, int64_t y);
+void check_null(int line, const char *func, const void *x);
+void check_notnull(int line, const char *func, const void *x);
+void xlog(const char *msg, const char *tst, const char *func, int line, const char *txt);
+
+/* crypt_device context must be "cd" to parse error properly here */
+#define OK_(x) do { xlog("(success)", #x, __FUNCTION__, __LINE__, NULL); \
+ check_ok((x), __LINE__, __FUNCTION__); \
+ } while(0)
+#define FAIL_(x, y) do { xlog("(fail) ", #x, __FUNCTION__, __LINE__, y); \
+ check_ko((x), __LINE__, __FUNCTION__); \
+ } while(0)
+#define EQ_(x, y) do { int64_t _x = (x), _y = (y); \
+ xlog("(equal) ", #x " == " #y, __FUNCTION__, __LINE__, NULL); \
+ if (_x != _y) check_equal(__LINE__, __FUNCTION__, _x, _y); \
+ } while(0)
+#define NULL_(x) do { xlog("(null) ", #x, __FUNCTION__, __LINE__, NULL); \
+ check_null(__LINE__, __FUNCTION__, (x)); \
+ } while(0)
+#define NOTNULL_(x) do { xlog("(notnull)", #x, __FUNCTION__, __LINE__, NULL); \
+ check_notnull(__LINE__, __FUNCTION__, (x)); \
+ } while(0)
+#define RUN_(x, y) do { reset_log(); \
+ printf("%s: %s\n", #x, (y)); x(); \
+ } while (0)
+
+#define SECTOR_SHIFT 9L
+#define SECTOR_SIZE 512
+#define TST_LOOP_FILE_SIZE (((1<<20)*100)>>SECTOR_SHIFT)
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+#define DIV_ROUND_UP_MODULO(n,d) (DIV_ROUND_UP(n,d)*(d))
+
+/* Device mapper backend - kernel support flags */
+#define T_DM_KEY_WIPE_SUPPORTED (1 << 0) /* key wipe message */
+#define T_DM_LMK_SUPPORTED (1 << 1) /* lmk mode */
+#define T_DM_SECURE_SUPPORTED (1 << 2) /* wipe (secure) buffer flag */
+#define T_DM_PLAIN64_SUPPORTED (1 << 3) /* plain64 IV */
+#define T_DM_DISCARDS_SUPPORTED (1 << 4) /* discards/TRIM option is supported */
+#define T_DM_VERITY_SUPPORTED (1 << 5) /* dm-verity target supported */
+#define T_DM_TCW_SUPPORTED (1 << 6) /* tcw (TCRYPT CBC with whitening) */
+#define T_DM_SAME_CPU_CRYPT_SUPPORTED (1 << 7) /* same_cpu_crypt */
+#define T_DM_SUBMIT_FROM_CRYPT_CPUS_SUPPORTED (1 << 8) /* submit_from_crypt_cpus */
+#define T_DM_VERITY_ON_CORRUPTION_SUPPORTED (1 << 9) /* ignore/restart_on_corruption, ignore_zero_block */
+#define T_DM_VERITY_FEC_SUPPORTED (1 << 10) /* Forward Error Correction (FEC) */
+#define T_DM_KERNEL_KEYRING_SUPPORTED (1 << 11) /* dm-crypt allows loading kernel keyring keys */
+#define T_DM_INTEGRITY_SUPPORTED (1 << 12) /* dm-integrity target supported */
+//FIXME add T_DM_SECTOR_SIZE once we have version
+
+/* loop helpers */
+int loop_device(const char *loop);
+int loop_attach(char **loop, const char *file, int offset,
+ int autoclear, int *readonly);
+int loop_detach(const char *loop);
+
+#endif
diff --git a/tests/blkid-luks2-pv.img.xz b/tests/blkid-luks2-pv.img.xz
new file mode 100644
index 0000000..c9d0e57
--- /dev/null
+++ b/tests/blkid-luks2-pv.img.xz
Binary files differ
diff --git a/tests/blockwise-compat b/tests/blockwise-compat
new file mode 100755
index 0000000..c9b08b7
--- /dev/null
+++ b/tests/blockwise-compat
@@ -0,0 +1,375 @@
+#!/bin/bash
+
+# set _FORCE_LOCAL environment variable to run blockwise unit tests even on local
+# nfs. Some tests will fail because nfs is eager to write for example 4095 bytes
+# in O_DIRECT mode.
+
+BW_UNIT=./unit-utils-io
+STRACE=strace
+MNT_DIR=./mnt_bwunit
+LOCAL_FILE=./blockwise_localfile
+
+# $1 path to scsi debug bdev
+scsi_debug_teardown() {
+ local _tries=15;
+
+ while [ -b "$1" -a $_tries -gt 0 ]; do
+ rmmod scsi_debug 2> /dev/null
+ if [ -b "$1" ]; then
+ sleep .1
+ _tries=$((_tries-1))
+ fi
+ done
+
+ test ! -b "$1" || rmmod scsi_debug
+}
+
+cleanup() {
+ if [ -d "$MNT_DIR" ] ; then
+ umount -f $MNT_DIR 2>/dev/null
+ rmdir $MNT_DIR 2>/dev/null
+ fi
+ rm -f $LOCAL_FILE 2> /dev/null
+ scsi_debug_teardown "$DEV" || exit 100
+}
+
+fail()
+{
+ if [ -n "$1" ] ; then echo "FAIL $1" ; else echo "FAIL" ; fi
+ cleanup
+ exit 100
+}
+
+fail_count()
+{
+ echo "$MSG[FAIL]"
+ FAILS=$((FAILS+1))
+}
+
+warn_count()
+{
+ echo "$MSG[WARNING]"
+ WARNS=$((WARNS+1))
+}
+
+skip()
+{
+ echo "TEST SKIPPED: $1"
+ cleanup
+ exit 0
+}
+
+add_device() {
+ modprobe scsi_debug $@ delay=0
+ if [ $? -ne 0 ] ; then
+ echo "This kernel seems to not support proper scsi_debug module, test skipped."
+ exit 77
+ fi
+ DEV=$(grep -l -e scsi_debug /sys/block/*/device/model | cut -f4 -d /)
+ DEV="/dev/$DEV"
+ [ -b $DEV ] || fail "Cannot find $DEV."
+}
+
+falloc() {
+ dd if=/dev/zero of=$2 bs=1M count=$1 2> /dev/null
+}
+
+run_all_in_fs() {
+ for file in $(ls img_fs_*.img.xz) ; do
+ echo "Run tests in $file put on top block device."
+ xz -d -c $file | dd of=$DEV bs=1M 2>/dev/null || fail "bad image"
+ [ ! -d $MNT_DIR ] && mkdir $MNT_DIR
+ mount $DEV $MNT_DIR
+ if [ $? -ne 0 ]; then
+ echo "Mounting image $file failed, skipped."
+ continue;
+ fi
+ rm -rf $MNT_DIR/* 2>/dev/null
+ local tfile=$MNT_DIR/bwunit_tstfile
+ falloc $DEVSIZEMB $tfile || fail "enospc?"
+ local iobsize=$(stat -c "%o" $tfile)
+ test -n "$iobsize" -a $iobsize -gt 0 || fail
+ local oldbsize=$BSIZE
+ BSIZE=$iobsize
+ run_all $tfile
+ BSIZE=$oldbsize
+ umount $MNT_DIR
+ done
+}
+
+trunc_file() {
+ test $1 -eq 0 || truncate -c -s $1 $2 2>/dev/null || dd if=/dev/zero of=$2 bs=$1 count=1 2>/dev/null || fail "Failed to truncate test file $2."
+}
+
+RUN() {
+ local _res=$1
+ shift
+ local _dev=$1
+ shift
+ local _fn=$1
+ shift
+ local _type="bdev"
+ local _fsize=0
+
+ test -b $_dev || {
+ _type="file"
+ _fsize=$(stat -c "%s" $_dev)
+ }
+
+ case "$_res" in
+ P)
+ MSG="Testing $_fn on $_type with params $@ [expecting TRUE]..."
+ $BW_UNIT $_dev $_fn $@
+ if [ $? -ne 0 ]; then
+ if [ $_type = "file" ]; then
+ warn_count
+ else
+ fail_count
+ fi
+ trunc_file $_fsize $_dev
+ test -z "$STRACE" || $STRACE -o ./$BW_UNIT-fail-$FAILS-should-pass.log $BW_UNIT $_dev $_fn $@ 2> /dev/null
+ else
+ MSG="$MSG[OK]"
+ fi
+ ;;
+ F)
+ MSG="Testing $_fn on $_type with params $@ [expecting FALSE]..."
+ $BW_UNIT $_dev $_fn $@ 2> /dev/null
+ if [ $? -eq 0 ]; then
+ if [ $_type = "file" ]; then
+ warn_count
+ else
+ fail_count
+ fi
+ trunc_file $_fsize $_dev
+ test -z "$STRACE" || $STRACE -o ./$BW_UNIT-fail-$FAILS-should-fail.log $BW_UNIT $_dev $_fn $@ 2> /dev/null
+ else
+ MSG="$MSG[OK]"
+ fi
+ ;;
+ *)
+ fail "Internal test error"
+ ;;
+ esac
+
+ trunc_file $_fsize $_dev
+}
+
+run_all() {
+ if [ -b "$1" ]; then
+ BD_FAIL="F"
+ else
+ BD_FAIL="P"
+ fi
+
+ # buffer io support only blocksize aligned ios
+ # device/file fn_name length
+ RUN "P" $1 read_buffer $BSIZE
+ RUN "P" $1 read_buffer $((2*BSIZE))
+ RUN "F" $1 read_buffer $((BSIZE-1))
+ RUN "F" $1 read_buffer $((BSIZE+1))
+ RUN "P" $1 read_buffer 0
+
+ RUN "P" $1 write_buffer $BSIZE
+ RUN "P" $1 write_buffer $((2*BSIZE))
+
+ RUN "F" $1 write_buffer $((BSIZE-1))
+ RUN "F" $1 write_buffer $((BSIZE+1))
+ RUN "F" $1 write_buffer 0
+
+ # basic blockwise functions
+ # device/file fn_name length bsize
+ RUN "P" $1 read_blockwise 0 $BSIZE
+ RUN "P" $1 read_blockwise $((BSIZE)) $BSIZE
+ RUN "P" $1 read_blockwise $((BSIZE-1)) $BSIZE
+ RUN "P" $1 read_blockwise $((BSIZE+1)) $BSIZE
+ RUN "P" $1 read_blockwise $((DEVSIZE)) $BSIZE
+ RUN "P" $1 read_blockwise $((DEVSIZE-1)) $BSIZE
+ RUN "F" $1 read_blockwise $((DEVSIZE+1)) $BSIZE
+
+ RUN "P" $1 write_blockwise 0 $BSIZE
+ RUN "P" $1 write_blockwise $((BSIZE)) $BSIZE
+ RUN "P" $1 write_blockwise $((BSIZE-1)) $BSIZE
+ RUN "P" $1 write_blockwise $((BSIZE+1)) $BSIZE
+ RUN "P" $1 write_blockwise $((DEVSIZE)) $BSIZE
+ RUN "P" $1 write_blockwise $((DEVSIZE-1)) $BSIZE
+ RUN "$BD_FAIL" $1 write_blockwise $((DEVSIZE+1)) $BSIZE
+
+ # seek variant blockwise functions
+ # device/file fn_name length bsize offset
+ RUN "P" $1 read_lseek_blockwise 0 $BSIZE 0
+ RUN "P" $1 read_lseek_blockwise 0 $BSIZE 1
+ RUN "P" $1 read_lseek_blockwise 0 $BSIZE $((DEVSIZE))
+ # length = 0 is significant here
+ RUN "P" $1 read_lseek_blockwise 0 $BSIZE $((DEVSIZE+1))
+
+ # beginning of device
+ RUN "P" $1 read_lseek_blockwise 1 $BSIZE 0
+ RUN "P" $1 read_lseek_blockwise 1 $BSIZE 1
+ RUN "P" $1 read_lseek_blockwise 1 $BSIZE $((BSIZE-1))
+ RUN "P" $1 read_lseek_blockwise 1 $BSIZE $((BSIZE/2))
+
+ # somewhere in the 'middle'
+ RUN "P" $1 read_lseek_blockwise 1 $BSIZE $BSIZE
+ RUN "P" $1 read_lseek_blockwise 1 $BSIZE $((BSIZE+1))
+ RUN "P" $1 read_lseek_blockwise 1 $BSIZE $((2*BSIZE-1))
+ RUN "P" $1 read_lseek_blockwise 1 $BSIZE $((BSIZE+BSIZE/2-1))
+
+ # cross-sector tests
+ RUN "P" $1 read_lseek_blockwise 2 $BSIZE $((BSIZE-1))
+ RUN "P" $1 read_lseek_blockwise $((BSIZE+1)) $BSIZE $((BSIZE-1))
+ RUN "P" $1 read_lseek_blockwise $((BSIZE+2)) $BSIZE $((BSIZE-1))
+ RUN "P" $1 read_lseek_blockwise 2 $BSIZE $((2*BSIZE-1))
+ RUN "P" $1 read_lseek_blockwise $((BSIZE+1)) $BSIZE $((2*BSIZE-1))
+ RUN "P" $1 read_lseek_blockwise $((BSIZE+2)) $BSIZE $((2*BSIZE-1))
+
+ # including one whole sector
+ RUN "P" $1 read_lseek_blockwise $((BSIZE+2)) $BSIZE $((BSIZE))
+ RUN "P" $1 read_lseek_blockwise $((2*BSIZE)) $BSIZE $((BSIZE+1))
+ RUN "P" $1 read_lseek_blockwise $((2*BSIZE)) $BSIZE $((BSIZE-1))
+ RUN "P" $1 read_lseek_blockwise $((BSIZE+2)) $BSIZE $((BSIZE-1))
+ RUN "P" $1 read_lseek_blockwise $((2*BSIZE)) $BSIZE $((BSIZE+1))
+ RUN "P" $1 read_lseek_blockwise $((3*BSIZE-2)) $BSIZE $((BSIZE+1))
+
+ # hiting exactly the sector boundary
+ RUN "P" $1 read_lseek_blockwise $((BSIZE-1)) $BSIZE 1
+ RUN "P" $1 read_lseek_blockwise $((BSIZE-1)) $BSIZE $((BSIZE+1))
+ RUN "P" $1 read_lseek_blockwise $((BSIZE+1)) $BSIZE $((BSIZE-1))
+ RUN "P" $1 read_lseek_blockwise $((BSIZE+1)) $BSIZE $((2*BSIZE-1))
+
+ # device end
+ RUN "P" $1 read_lseek_blockwise 1 $BSIZE $((DEVSIZE-1))
+ RUN "P" $1 read_lseek_blockwise $((BSIZE-1)) $BSIZE $((DEVSIZE-BSIZE+1))
+ RUN "P" $1 read_lseek_blockwise $((BSIZE)) $BSIZE $((DEVSIZE-BSIZE))
+ RUN "P" $1 read_lseek_blockwise $((BSIZE+1)) $BSIZE $((DEVSIZE-BSIZE-1))
+
+ # this must fail on both device and file
+ RUN "F" $1 read_lseek_blockwise 1 $BSIZE $((DEVSIZE))
+ RUN "F" $1 read_lseek_blockwise $((BSIZE-1)) $BSIZE $((DEVSIZE-BSIZE+2))
+ RUN "F" $1 read_lseek_blockwise $((BSIZE)) $BSIZE $((DEVSIZE-BSIZE+1))
+ RUN "F" $1 read_lseek_blockwise $((BSIZE+1)) $BSIZE $((DEVSIZE-BSIZE))
+
+ RUN "P" $1 write_lseek_blockwise 0 $BSIZE 0
+ # TODO: this may pass but must not write a byte (write(0) is undefined).
+ # Test it with underlying dm-error or phony read/write syscalls.
+ # Skipping read is optimization.
+ # HINT: currently it performs useless write and read as well
+ RUN "P" $1 write_lseek_blockwise 0 $BSIZE 1
+ RUN "P" $1 write_lseek_blockwise 0 $BSIZE $BSIZE
+
+ # beginning of device
+ RUN "P" $1 write_lseek_blockwise 1 $BSIZE 0
+ RUN "P" $1 write_lseek_blockwise 1 $BSIZE 1
+ RUN "P" $1 write_lseek_blockwise 1 $BSIZE $((BSIZE-1))
+ RUN "P" $1 write_lseek_blockwise 1 $BSIZE $((BSIZE/2))
+
+ # somewhere in the 'middle'
+ RUN "P" $1 write_lseek_blockwise 1 $BSIZE $BSIZE
+ RUN "P" $1 write_lseek_blockwise 1 $BSIZE $((BSIZE+1))
+ RUN "P" $1 write_lseek_blockwise 1 $BSIZE $((2*BSIZE-1))
+ RUN "P" $1 write_lseek_blockwise 1 $BSIZE $((BSIZE+BSIZE/2-1))
+
+ # cross-sector tests
+ RUN "P" $1 write_lseek_blockwise 2 $BSIZE $((BSIZE-1))
+ RUN "P" $1 write_lseek_blockwise $((BSIZE+1)) $BSIZE $((BSIZE-1))
+ RUN "P" $1 write_lseek_blockwise $((BSIZE+2)) $BSIZE $((BSIZE-1))
+ RUN "P" $1 write_lseek_blockwise 2 $BSIZE $((2*BSIZE-1))
+ RUN "P" $1 write_lseek_blockwise $((BSIZE+1)) $BSIZE $((2*BSIZE-1))
+ RUN "P" $1 write_lseek_blockwise $((BSIZE+2)) $BSIZE $((2*BSIZE-1))
+
+ # including one whole sector
+ RUN "P" $1 write_lseek_blockwise $((BSIZE+2)) $BSIZE $((BSIZE))
+ RUN "P" $1 write_lseek_blockwise $((2*BSIZE)) $BSIZE $((BSIZE+1))
+ RUN "P" $1 write_lseek_blockwise $((2*BSIZE)) $BSIZE $((BSIZE-1))
+ RUN "P" $1 write_lseek_blockwise $((BSIZE+2)) $BSIZE $((BSIZE-1))
+ RUN "P" $1 write_lseek_blockwise $((2*BSIZE)) $BSIZE $((BSIZE+1))
+ RUN "P" $1 write_lseek_blockwise $((3*BSIZE-2)) $BSIZE $((BSIZE+1))
+
+ # hiting exactly the sector boundary
+ RUN "P" $1 write_lseek_blockwise $((BSIZE-1)) $BSIZE 1
+ RUN "P" $1 write_lseek_blockwise $((BSIZE-1)) $BSIZE $((BSIZE+1))
+ RUN "P" $1 write_lseek_blockwise $((BSIZE+1)) $BSIZE $((BSIZE-1))
+ RUN "P" $1 write_lseek_blockwise $((BSIZE+1)) $BSIZE $((2*BSIZE-1))
+
+ # device end
+ RUN "P" $1 write_lseek_blockwise 1 $BSIZE $((DEVSIZE-1))
+ RUN "P" $1 write_lseek_blockwise $((BSIZE-1)) $BSIZE $((DEVSIZE-BSIZE+1))
+ RUN "P" $1 write_lseek_blockwise $((BSIZE)) $BSIZE $((DEVSIZE-BSIZE))
+ RUN "P" $1 write_lseek_blockwise $((BSIZE+1)) $BSIZE $((DEVSIZE-BSIZE-1))
+
+ # this must fail on device, but pass on file (which is unfortunate and maybe design mistake)
+ RUN "$BD_FAIL" $1 write_lseek_blockwise 1 $BSIZE $((DEVSIZE))
+ RUN "$BD_FAIL" $1 write_lseek_blockwise $((BSIZE-1)) $BSIZE $((DEVSIZE-BSIZE+2))
+ RUN "$BD_FAIL" $1 write_lseek_blockwise $((BSIZE)) $BSIZE $((DEVSIZE-BSIZE+1))
+ RUN "$BD_FAIL" $1 write_lseek_blockwise $((BSIZE+1)) $BSIZE $((DEVSIZE-BSIZE))
+}
+
+which $STRACE > /dev/null 2>&1 || unset STRACE
+test -x $BW_UNIT || skip "Run \"make `basename $BW_UNIT`\" first"
+
+FAILS=0
+WARNS=0
+DEVSIZEMB=2
+DEVSIZE=$((DEVSIZEMB*1024*1024))
+
+PAGE_SIZE=$(getconf PAGE_SIZE)
+echo "System PAGE_SIZE=$PAGE_SIZE"
+
+echo "Run tests in local filesystem"
+falloc $DEVSIZEMB $LOCAL_FILE || fail "Failed to create file in local filesystem."
+BSIZE=$(stat -c "%o" $LOCAL_FILE)
+if [ $BSIZE -gt $((512*1024)) ]; then
+ echo "Detected file block size: $BSIZE bytes"
+ echo "Tuning it down to system page size ($PAGE_SIZE bytes)"
+ BSIZE=$PAGE_SIZE
+fi
+run_all $LOCAL_FILE
+
+[ $(id -u) -eq 0 ] || {
+ echo "WARNING: You must be root to run remaining tests."
+ test $FAILS -eq 0 || fail "($FAILS wrong result(s) in total)"
+ cleanup
+ exit 0
+}
+
+DEVBSIZE=512
+BSIZE=$DEVBSIZE
+EXP=0
+DEVSIZEMBIMG=32
+
+echo "# Create classic 512B drive"
+echo "# (logical_block_size=$DEVBSIZE, physical_block_size=$((DEVBSIZE*(1<<EXP))))"
+add_device dev_size_mb=$DEVSIZEMB sector_size=$DEVBSIZE physblk_exp=$EXP num_tgts=1
+run_all $DEV
+cleanup
+add_device dev_size_mb=$DEVSIZEMBIMG sector_size=$DEVBSIZE physblk_exp=$EXP num_tgts=1
+run_all_in_fs
+cleanup
+
+EXP=3
+echo "# Create desktop-class 4K drive"
+echo "# (logical_block_size=$DEVBSIZE, physical_block_size=$((DEVBSIZE*(1<<EXP))))"
+add_device dev_size_mb=$DEVSIZEMB physblk_exp=$EXP sector_size=$DEVBSIZE num_tgts=1
+run_all $DEV
+BSIZE=$((DEVBSIZE*(1<<EXP)))
+run_all $DEV
+cleanup
+
+add_device dev_size_mb=$DEVSIZEMBIMG physblk_exp=$EXP sector_size=$DEVBSIZE num_tgts=1
+run_all_in_fs
+cleanup
+
+DEVBSIZE=4096
+BSIZE=$DEVBSIZE
+EXP=0
+echo "# Create enterprise-class 4K drive"
+echo "# (logical_block_size=$DEVBSIZE, physical_block_size=$((DEVBSIZE*(1<<EXP))))"
+add_device dev_size_mb=$DEVSIZEMB physblk_exp=$EXP sector_size=$DEVBSIZE num_tgts=1
+run_all $DEV
+cleanup
+add_device dev_size_mb=$DEVSIZEMBIMG sector_size=$DEVBSIZE physblk_exp=$EXP num_tgts=1
+run_all_in_fs
+cleanup
+
+test $WARNS -eq 0 || echo "(WARNING: $WARNS suspicious result(s) in total)"
+test $FAILS -eq 0 || fail "($FAILS wrong result(s) in total)"
diff --git a/tests/compat-test b/tests/compat-test
new file mode 100755
index 0000000..c905f7e
--- /dev/null
+++ b/tests/compat-test
@@ -0,0 +1,952 @@
+#!/bin/bash
+
+PS4='$LINENO:'
+CRYPTSETUP=../cryptsetup
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+DEV_NAME=dummy
+DEV_NAME2=dummy2
+DEV_NAME3=dummy3
+ORIG_IMG=luks-test-orig
+IMG=luks-test
+IMG10=luks-test-v10
+HEADER_IMG=luks-header
+KEY1=key1
+KEY2=key2
+KEY5=key5
+KEYE=keye
+PWD0="compatkey"
+PWD1="93R4P4pIqAH8"
+PWD2="mymJeD8ivEhE"
+PWD3="ocMakf3fAcQO"
+PWDW="rUkL4RUryBom"
+VK_FILE="compattest_vkfile"
+
+FAST_PBKDF_OPT="--pbkdf pbkdf2 --pbkdf-force-iterations 1000"
+
+LUKS_HEADER="S0-5 S6-7 S8-39 S40-71 S72-103 S104-107 S108-111 R112-131 R132-163 S164-167 S168-207 A0-591"
+KEY_SLOT0="S208-211 S212-215 R216-247 A248-251 A251-255"
+KEY_MATERIAL0="R4096-68096"
+KEY_MATERIAL0_EXT="R4096-68096"
+
+KEY_SLOT1="S256-259 S260-263 R264-295 A296-299 A300-303"
+KEY_MATERIAL1="R69632-133632"
+KEY_MATERIAL1_EXT="S69632-133632"
+
+KEY_SLOT5="S448-451 S452-455 R456-487 A488-491 A492-495"
+KEY_MATERIAL5="R331776-395264"
+KEY_MATERIAL5_EXT="S331776-395264"
+
+TEST_UUID="12345678-1234-1234-1234-123456789abc"
+
+LOOPDEV=$(losetup -f 2>/dev/null)
+[ -f /etc/system-fips ] && FIPS_MODE=$(cat /proc/sys/crypto/fips_enabled 2>/dev/null)
+
+function remove_mapping()
+{
+ [ -b /dev/mapper/$DEV_NAME3 ] && dmsetup remove $DEV_NAME3 >/dev/null 2>&1
+ [ -b /dev/mapper/$DEV_NAME2 ] && dmsetup remove $DEV_NAME2 >/dev/null 2>&1
+ [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove $DEV_NAME >/dev/null 2>&1
+ losetup -d $LOOPDEV >/dev/null 2>&1
+ rm -f $ORIG_IMG $IMG $IMG10 $KEY1 $KEY2 $KEY5 $KEYE $HEADER_IMG $VK_FILE missing-file >/dev/null 2>&1
+}
+
+function force_uevent()
+{
+ DNAME=$(echo $LOOPDEV | cut -f3 -d /)
+ echo "change" >/sys/block/$DNAME/uevent
+}
+
+function fail()
+{
+ [ -n "$1" ] && echo "$1"
+ remove_mapping
+ echo "FAILED at line $(caller)"
+ exit 2
+}
+
+function fips_mode()
+{
+ [ -n "$FIPS_MODE" ] && [ "$FIPS_MODE" -gt 0 ]
+}
+
+function can_fail_fips()
+{
+ # Ignore this fail if running in FIPS mode
+ fips_mode || fail $1
+}
+
+function skip()
+{
+ [ -n "$1" ] && echo "$1"
+ remove_mapping
+ [ -z "$2" ] && exit $2
+ exit 77
+}
+
+function prepare()
+{
+ [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove $DEV_NAME >/dev/null 2>&1
+
+ case "$2" in
+ file)
+ remove_mapping
+ dd if=/dev/zero of=$IMG bs=1k count=10000 >/dev/null 2>&1
+ sync
+ ;;
+ wipe)
+ remove_mapping
+ dd if=/dev/zero of=$IMG bs=1k count=10000 >/dev/null 2>&1
+ sync
+ losetup $LOOPDEV $IMG
+ ;;
+ new)
+ remove_mapping
+ xz -cd compatimage.img.xz > $IMG
+ # FIXME: switch to internal loop (no losetup at all)
+ echo "bad" | $CRYPTSETUP luksOpen --key-slot 0 --test-passphrase $IMG 2>&1 | \
+ grep "autoclear flag" && skip "WARNING: Too old kernel, test skipped."
+ losetup $LOOPDEV $IMG
+ xz -cd compatv10image.img.xz > $IMG10
+ ;;
+ reuse | *)
+ if [ ! -e $IMG ]; then
+ xz -cd compatimage.img.xz > $IMG
+ losetup $LOOPDEV $IMG
+ fi
+ [ ! -e $IMG10 ] && xz -cd compatv10image.img.xz > $IMG10
+ ;;
+ esac
+
+ if [ ! -e $KEY1 ]; then
+ #dd if=/dev/urandom of=$KEY1 count=1 bs=32 >/dev/null 2>&1
+ echo -n $'\x48\xc6\x74\x4f\x41\x4e\x50\xc0\x79\xc2\x2d\x5b\x5f\x68\x84\x17' >$KEY1
+ echo -n $'\x9c\x03\x5e\x1b\x4d\x0f\x9a\x75\xb3\x90\x70\x32\x0a\xf8\xae\xc4'>>$KEY1
+ fi
+
+ if [ ! -e $KEY2 ]; then
+ dd if=/dev/urandom of=$KEY2 count=1 bs=16 >/dev/null 2>&1
+ fi
+
+ if [ ! -e $KEY5 ]; then
+ dd if=/dev/urandom of=$KEY5 count=1 bs=16 >/dev/null 2>&1
+ fi
+
+ if [ ! -e $KEYE ]; then
+ touch $KEYE
+ fi
+
+ cp $IMG $ORIG_IMG
+ [ -n "$1" ] && echo "CASE: $1"
+}
+
+function check()
+{
+ sync
+ [ -z "$1" ] && return
+ ./differ $ORIG_IMG $IMG $1 || fail
+}
+
+function check_exists()
+{
+ [ -b /dev/mapper/$DEV_NAME ] || fail
+ check $1
+}
+
+function valgrind_setup()
+{
+ which valgrind >/dev/null 2>&1 || fail "Cannot find valgrind."
+ [ ! -f $CRYPTSETUP_VALGRIND ] && fail "Unable to get location of cryptsetup executable."
+ export LD_LIBRARY_PATH="$CRYPTSETUP_LIB_VALGRIND:$LD_LIBRARY_PATH"
+}
+
+function valgrind_run()
+{
+ INFOSTRING="$(basename ${BASH_SOURCE[1]})-line-${BASH_LINENO[0]}" ./valg.sh ${CRYPTSETUP_VALGRIND} "$@"
+}
+
+export LANG=C
+
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+
+# LUKS non-root-tests
+if [ $(id -u) != 0 ]; then
+ $CRYPTSETUP benchmark -c aes-xts-plain64 >/dev/null 2>&1 || \
+ skip "WARNING: Cannot run test without kernel userspace crypto API, test skipped."
+fi
+
+prepare "Image in file tests (root capabilities not required)" file
+echo "[1] format"
+echo $PWD1 | $CRYPTSETUP luksFormat --type luks1 $IMG $FAST_PBKDF_OPT || fail
+echo "[2] open"
+echo $PWD0 | $CRYPTSETUP luksOpen $IMG --test-passphrase 2>/dev/null && fail
+[ $? -ne 2 ] && fail "luksOpen should return EPERM exit code"
+echo $PWD1 | $CRYPTSETUP luksOpen $IMG --test-passphrase || fail
+echo "[3] add key"
+echo $PWD1 | $CRYPTSETUP luksAddKey $IMG $FAST_PBKDF_OPT 2>/dev/null && fail
+echo -e "$PWD1\n$PWD2" | $CRYPTSETUP luksAddKey $IMG $FAST_PBKDF_OPT || fail
+echo -e "$PWD0\n$PWD1" | $CRYPTSETUP luksAddKey $IMG $FAST_PBKDF_OPT 2>/dev/null && fail
+echo "[4] change key"
+echo -e "$PWD1\n$PWD0\n" | $CRYPTSETUP luksChangeKey $FAST_PBKDF_OPT $IMG || fail
+echo -e "$PWD1\n$PWD2\n" | $CRYPTSETUP luksChangeKey $FAST_PBKDF_OPT $IMG 2>/dev/null && fail
+[ $? -ne 2 ] && fail "luksChangeKey should return EPERM exit code"
+echo "[5] remove key"
+# delete active keys PWD0, PWD2
+echo $PWD1 | $CRYPTSETUP luksRemoveKey $IMG 2>/dev/null && fail
+[ $? -ne 2 ] && fail "luksRemove should return EPERM exit code"
+echo $PWD0 | $CRYPTSETUP luksRemoveKey $IMG || fail
+echo $PWD2 | $CRYPTSETUP luksRemoveKey $IMG || fail
+# check if keys were deleted
+echo $PWD0 | $CRYPTSETUP luksOpen $IMG --test-passphrase 2>/dev/null && fail
+[ $? -ne 2 ] && fail "luksOpen should return EPERM exit code"
+echo $PWD2 | $CRYPTSETUP luksOpen $IMG --test-passphrase 2>/dev/null && fail
+[ $? -ne 2 ] && fail "luksOpen should return EPERM exit code"
+echo "[6] kill slot"
+# format new luks device with active keys PWD1, PWD2
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $IMG $FAST_PBKDF_OPT || fail
+echo -e "$PWD1\n$PWD2" | $CRYPTSETUP luksAddKey $IMG $FAST_PBKDF_OPT || fail
+# deactivate keys by killing slots
+$CRYPTSETUP luksDump $IMG | grep -q "Key Slot 0: ENABLED" || fail
+$CRYPTSETUP luksDump $IMG | grep -q "Key Slot 1: ENABLED" || fail
+$CRYPTSETUP luksDump $IMG | grep -q "Key Slot 2: DISABLED" || fail
+echo $PWD1 | $CRYPTSETUP -q luksKillSlot $IMG 0 2>/dev/null && fail
+echo $PWD2 | $CRYPTSETUP -q luksKillSlot $IMG 0 || fail
+$CRYPTSETUP luksDump $IMG | grep -q "Key Slot 0: DISABLED" || fail
+echo $PWD1 | $CRYPTSETUP -q luksKillSlot $IMG 1 2>/dev/null && fail
+[ $? -ne 2 ] && fail "luksKill should return EPERM exit code"
+echo $PWD2 | $CRYPTSETUP -q luksKillSlot $IMG 1 || fail
+$CRYPTSETUP luksDump $IMG | grep -q "Key Slot 1: DISABLED" || fail
+# check if keys were deactivated
+echo $PWD1 | $CRYPTSETUP luksOpen $IMG --test-passphrase 2>/dev/null && fail
+echo $PWD2 | $CRYPTSETUP luksOpen $IMG --test-passphrase 2>/dev/null && fail
+echo "[7] header backup"
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $IMG $FAST_PBKDF_OPT || fail
+$CRYPTSETUP luksHeaderBackup $IMG --header-backup-file $HEADER_IMG || fail
+echo $PWD1 | $CRYPTSETUP luksRemoveKey $IMG || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $IMG --test-passphrase 2>/dev/null && fail
+echo "[8] header restore"
+$CRYPTSETUP luksHeaderRestore -q $IMG --header-backup-file $HEADER_IMG || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $IMG --test-passphrase || fail
+echo "[9] luksDump"
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT --uuid $TEST_UUID $IMG $KEY1 || fail
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $IMG -d $KEY1 || fail
+$CRYPTSETUP luksDump $IMG | grep -q "Key Slot 0: ENABLED" || fail
+$CRYPTSETUP luksDump $IMG | grep -q $TEST_UUID || fail
+echo $PWDW | $CRYPTSETUP luksDump $IMG --dump-master-key 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksDump $IMG --dump-master-key | grep -q "MK dump:" || can_fail_fips
+$CRYPTSETUP luksDump -q $IMG --dump-master-key -d $KEY1 | grep -q "MK dump:" || can_fail_fips
+echo $PWD1 | $CRYPTSETUP luksDump -q $IMG --dump-master-key --master-key-file $VK_FILE >/dev/null || can_fail_fips
+echo $PWD1 | $CRYPTSETUP luksDump -q $IMG --dump-master-key --master-key-file $VK_FILE 2>/dev/null && fail
+fips_mode || {
+ echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT --master-key-file $VK_FILE $IMG || fail
+}
+echo "[10] uuid"
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT --uuid $TEST_UUID $IMG || fail
+$CRYPTSETUP -q luksUUID $IMG | grep -q $TEST_UUID || fail
+
+[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
+[ -z "$LOOPDEV" ] && skip "WARNING: Cannot find free loop device, test skipped."
+
+# LUKS root-tests
+prepare "[1] open - compat image - acceptance check" new
+echo $PWD0 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail
+check_exists
+ORG_SHA1=$(sha1sum -b /dev/mapper/$DEV_NAME | cut -f 1 -d' ')
+[ "$ORG_SHA1" = 676062b66ebf36669dab705442ea0762dfc091b0 ] || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+
+# Check it can be opened from header backup as well
+$CRYPTSETUP luksHeaderBackup $IMG --header-backup-file $HEADER_IMG || fail
+echo $PWD0 | $CRYPTSETUP luksOpen $IMG10 $DEV_NAME --header $HEADER_IMG || fail
+check_exists
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+# Check restore
+$CRYPTSETUP luksHeaderRestore -q $IMG --header-backup-file $HEADER_IMG || fail
+
+# Repeat for V1.0 header - not aligned first keyslot
+echo $PWD0 | $CRYPTSETUP luksOpen $IMG10 $DEV_NAME || fail
+check_exists
+ORG_SHA1=$(sha1sum -b /dev/mapper/$DEV_NAME | cut -f 1 -d' ')
+[ "$ORG_SHA1" = 51b48c2471a7593ceaf14dc5e66bca86ed05f6cc ] || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+
+rm -f $HEADER_IMG
+$CRYPTSETUP luksHeaderBackup $IMG10 --header-backup-file $HEADER_IMG
+echo $PWD0 | $CRYPTSETUP luksOpen $IMG10 $DEV_NAME --header $HEADER_IMG || fail
+check_exists
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+
+prepare "[2] open - compat image - denial check" new
+echo $PWDW | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+echo $PWDW | $CRYPTSETUP luksOpen $IMG10 $DEV_NAME 2>/dev/null && fail
+check
+
+# All headers items and first key material section must change
+prepare "[3] format" wipe
+echo $PWD1 | $CRYPTSETUP -i 1000 -c aes-cbc-essiv:sha256 -s 128 luksFormat --type luks1 $LOOPDEV || fail
+check "$LUKS_HEADER $KEY_SLOT0 $KEY_MATERIAL0"
+
+prepare "[4] format using hash sha512" wipe
+echo $PWD1 | $CRYPTSETUP -i 1000 -h sha512 -c aes-cbc-essiv:sha256 -s 128 luksFormat --type luks1 $LOOPDEV || fail
+check "$LUKS_HEADER $KEY_SLOT0 $KEY_MATERIAL0"
+
+prepare "[5] open"
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME --test-passphrase || fail
+echo $PWDW | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME --test-passphrase 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail
+check_exists
+
+# Key Slot 1 and key material section 1 must change, the rest must not.
+prepare "[6] add key"
+echo -e "$PWD1\n$PWD2" | $CRYPTSETUP luksAddKey $LOOPDEV || fail
+check "$KEY_SLOT1 $KEY_MATERIAL1"
+echo $PWD2 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail
+
+# Unsuccessful Key Delete - nothing may change
+prepare "[7] unsuccessful delete"
+echo $PWDW | $CRYPTSETUP luksKillSlot $LOOPDEV 1 2>/dev/null && fail
+$CRYPTSETUP -q luksKillSlot $LOOPDEV 8 2>/dev/null && fail
+$CRYPTSETUP -q luksKillSlot $LOOPDEV 7 2>/dev/null && fail
+check
+
+# Delete Key Test
+# Key Slot 1 and key material section 1 must change, the rest must not
+prepare "[8] successful delete"
+$CRYPTSETUP -q luksKillSlot $LOOPDEV 1 || fail
+check "$KEY_SLOT1 $KEY_MATERIAL1_EXT"
+echo $PWD2 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME 2> /dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail
+
+# Key Slot 1 and key material section 1 must change, the rest must not
+prepare "[9] add key test for key files"
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV $KEY1 || fail
+check "$KEY_SLOT1 $KEY_MATERIAL1"
+$CRYPTSETUP -d $KEY1 luksOpen $LOOPDEV $DEV_NAME || fail
+
+# Key Slot 1 and key material section 1 must change, the rest must not
+prepare "[10] delete key test with key1 as remaining key"
+$CRYPTSETUP -d $KEY1 luksKillSlot $LOOPDEV 0 || fail
+check "$KEY_SLOT0 $KEY_MATERIAL0_EXT"
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP luksOpen -d $KEY1 $LOOPDEV $DEV_NAME || fail
+
+# Delete last slot
+prepare "[11] delete last key" wipe
+echo $PWD1 | $CRYPTSETUP luksFormat --type luks1 $LOOPDEV $FAST_PBKDF_OPT || fail
+echo $PWD1 | $CRYPTSETUP luksKillSlot $LOOPDEV 0 || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+
+# Format test for ESSIV, and some other parameters.
+prepare "[12] parameter variation test" wipe
+$CRYPTSETUP -q -i 1000 -c aes-cbc-essiv:sha256 -s 128 luksFormat --type luks1 $LOOPDEV $KEY1 || fail
+check "$LUKS_HEADER $KEY_SLOT0 $KEY_MATERIAL0"
+$CRYPTSETUP -d $KEY1 luksOpen $LOOPDEV $DEV_NAME || fail
+
+prepare "[13] open/close - stacked devices" wipe
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $LOOPDEV $FAST_PBKDF_OPT || fail
+echo $PWD1 | $CRYPTSETUP -q luksOpen $LOOPDEV $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 /dev/mapper/$DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP -q luksOpen /dev/mapper/$DEV_NAME $DEV_NAME2 || fail
+$CRYPTSETUP -q luksClose $DEV_NAME2 || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+
+prepare "[14] format/open - passphrase on stdin & new line" wipe
+# stdin defined by "-" must take even newline
+#echo -n -e "$PWD1\n$PWD2" | $CRYPTSETUP -q luksFormat $LOOPDEV - || fail
+echo -n -e "$PWD1\n$PWD2" | $CRYPTSETUP $FAST_PBKDF_OPT -q --key-file=- luksFormat --type luks1 $LOOPDEV || fail
+echo -n -e "$PWD1\n$PWD2" | $CRYPTSETUP -q --key-file=- luksOpen $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+echo -n -e "$PWD1\n$PWD2" | $CRYPTSETUP -q luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+# now also try --key-file
+echo -n -e "$PWD1\n$PWD2" | $CRYPTSETUP $FAST_PBKDF_OPT -q luksFormat --type luks1 $LOOPDEV --key-file=- || fail
+echo -n -e "$PWD1\n$PWD2" | $CRYPTSETUP -q --key-file=- luksOpen $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+# process newline if from stdin
+echo -n -e "$PWD1\n$PWD2" | $CRYPTSETUP $FAST_PBKDF_OPT -q luksFormat --type luks1 $LOOPDEV || fail
+echo "$PWD1" | $CRYPTSETUP -q luksOpen $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+
+prepare "[15] UUID - use and report provided UUID" wipe
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT --uuid blah $LOOPDEV 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT --uuid $TEST_UUID $LOOPDEV || fail
+tst=$($CRYPTSETUP -q luksUUID $LOOPDEV)
+[ "$tst"x = "$TEST_UUID"x ] || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV || fail
+$CRYPTSETUP -q luksUUID --uuid $TEST_UUID $LOOPDEV || fail
+tst=$($CRYPTSETUP -q luksUUID $LOOPDEV)
+[ "$tst"x = "$TEST_UUID"x ] || fail
+
+prepare "[16] luksFormat" wipe
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT --master-key-file /dev/urandom $LOOPDEV || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT --master-key-file /dev/urandom $LOOPDEV -d $KEY1 || fail
+$CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT --master-key-file /dev/urandom -s 256 --uuid $TEST_UUID $LOOPDEV $KEY1 || fail
+$CRYPTSETUP luksOpen -d $KEY1 $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+# open by UUID
+force_uevent # some systems do not update loop by-uuid
+$CRYPTSETUP luksOpen -d $KEY1 UUID=X$TEST_UUID $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP luksOpen -d $KEY1 UUID=$TEST_UUID $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+# empty keyfile
+$CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV $KEYE || fail
+$CRYPTSETUP luksOpen -d $KEYE $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+# open by volume key
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT -s 256 --master-key-file $KEY1 $LOOPDEV || fail
+$CRYPTSETUP luksOpen --master-key-file /dev/urandom $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP luksOpen --master-key-file $KEY1 $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+# unsupported pe-keyslot encryption
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT -s 128 --keyslot-cipher "aes-cbc-plain" $LOOPDEV 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT -s 128 --keyslot-key-size 256 $LOOPDEV 2>/dev/null && fail
+
+prepare "[17] AddKey volume key, passphrase and keyfile" wipe
+# masterkey
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV --master-key-file /dev/zero --key-slot 3 || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV --test-passphrase || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 3: ENABLED" || fail
+echo $PWD2 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV --master-key-file /dev/zero --key-slot 4 || fail
+echo $PWD2 | $CRYPTSETUP luksOpen $LOOPDEV --test-passphrase --key-slot 4 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 4: ENABLED" || fail
+echo $PWD3 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV --master-key-file /dev/null --key-slot 5 2>/dev/null && fail
+$CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV --master-key-file /dev/zero --key-slot 5 $KEY1 || fail
+$CRYPTSETUP luksOpen $LOOPDEV --test-passphrase --key-slot 5 -d $KEY1 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 5: ENABLED" || fail
+
+# special "-" handling
+$CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV $KEY1 --key-slot 3 || fail
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV -d $KEY1 - || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV --test-passphrase 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV -d - --test-passphrase || fail
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV -d - $KEY2 || fail
+$CRYPTSETUP luksOpen $LOOPDEV -d $KEY2 --test-passphrase || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV -d - -d $KEY1 --test-passphrase 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV -d $KEY1 -d $KEY1 --test-passphrase 2>/dev/null && fail
+
+# [0]PWD1 [1]PWD2 [2]$KEY1/1 [3]$KEY1 [4]$KEY2
+$CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV $KEY1 --key-slot 3 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 3: ENABLED" || fail
+$CRYPTSETUP luksAddKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY1 $KEY2 --key-slot 3 2>/dev/null && fail
+# keyfile/keyfile
+$CRYPTSETUP luksAddKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY1 $KEY2 --key-slot 4 || fail
+$CRYPTSETUP luksOpen $LOOPDEV -d $KEY2 --test-passphrase --key-slot 4 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 4: ENABLED" || fail
+# passphrase/keyfile
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV -d $KEY1 --key-slot 0 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 0: ENABLED" || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV --test-passphrase --key-slot 0 || fail
+# passphrase/passphrase
+echo -e "$PWD1\n$PWD2\n" | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV --key-slot 1 || fail
+echo $PWD2 | $CRYPTSETUP luksOpen $LOOPDEV --test-passphrase --key-slot 1 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 1: ENABLED" || fail
+# keyfile/passphrase
+echo -e "$PWD2\n" | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV $KEY1 --key-slot 2 --new-keyfile-size 3 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 2: ENABLED" || fail
+
+prepare "[18] RemoveKey passphrase and keyfile" reuse
+$CRYPTSETUP luksRemoveKey $LOOPDEV $KEY1 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 3: DISABLED" || fail
+$CRYPTSETUP luksRemoveKey $LOOPDEV $KEY1 2>/dev/null && fail
+$CRYPTSETUP luksAddKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY2 $KEY1 --key-slot 3 2>/dev/null || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 3: ENABLED" || fail
+$CRYPTSETUP luksRemoveKey $LOOPDEV $KEY2 --keyfile-size 1 2>/dev/null && fail
+$CRYPTSETUP luksRemoveKey $LOOPDEV $KEY2 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 4: DISABLED" || fail
+# if password or keyfile is provided, batch mode must not suppress it
+echo "badpw" | $CRYPTSETUP luksKillSlot $LOOPDEV 2 2>/dev/null && fail
+echo "badpw" | $CRYPTSETUP luksKillSlot $LOOPDEV 2 -q 2>/dev/null && fail
+echo "badpw" | $CRYPTSETUP luksKillSlot $LOOPDEV 2 --key-file=- 2>/dev/null && fail
+echo "badpw" | $CRYPTSETUP luksKillSlot $LOOPDEV 2 --key-file=- -q 2>/dev/null && fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 2: ENABLED" || fail
+# kill slot using passphrase from 1
+echo $PWD2 | $CRYPTSETUP luksKillSlot $LOOPDEV 2 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 2: DISABLED" || fail
+# kill slot with redirected stdin
+$CRYPTSETUP luksKillSlot $LOOPDEV 3 </dev/null 2>/dev/null || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 3: DISABLED" || fail
+# remove key0 / slot 0
+echo $PWD1 | $CRYPTSETUP luksRemoveKey $LOOPDEV || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 0: DISABLED" || fail
+# last keyslot, in batch mode no passphrase needed...
+$CRYPTSETUP luksKillSlot -q $LOOPDEV 1 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 1: DISABLED" || fail
+
+prepare "[19] create & status & resize" wipe
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME $LOOPDEV --hash xxx 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME $LOOPDEV --hash sha1 --cipher aes-cbc-essiv:sha256 --offset 3 --skip 4 --readonly || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "offset:" | grep -q "3 sectors" || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "skipped:" | grep -q "4 sectors" || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "mode:" | grep -q "readonly" || fail
+$CRYPTSETUP -q resize $DEV_NAME --size 100 || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "100 sectors" || fail
+$CRYPTSETUP -q resize $DEV_NAME || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "19997 sectors" || fail
+# Resize underlying loop device as well
+truncate -s 16M $IMG || fail
+$CRYPTSETUP -q resize $DEV_NAME || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "32765 sectors" || fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+$CRYPTSETUP -q status $DEV_NAME >/dev/null && fail
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME --hash sha1 $LOOPDEV || fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP -q create $DEV_NAME --hash sha1 $LOOPDEV || fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP -q create $DEV_NAME --hash sha1 --size 100 $LOOPDEV || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "100 sectors" || fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+# 4k sector resize (if kernel supports it)
+echo $PWD1 | $CRYPTSETUP -q open --type plain $LOOPDEV $DEV_NAME --sector-size 4096 --size 8 >/dev/null 2>&1
+if [ $? -eq 0 ] ; then
+ $CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "8 sectors"
+ $CRYPTSETUP -q resize $DEV_NAME --size 16 || fail
+ $CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "16 sectors"
+ $CRYPTSETUP -q resize $DEV_NAME --size 9 2>/dev/null && fail
+ $CRYPTSETUP -q remove $DEV_NAME || fail
+fi
+# verify is ignored on non-tty input
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME $LOOPDEV --hash sha1 --verify-passphrase 2>/dev/null || fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 --key-size 255 2>/dev/null && fail
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 --key-size -1 2>/dev/null && fail
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 -l -1 2>/dev/null && fail
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 || fail
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 2>/dev/null && fail
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d blah 2>/dev/null && fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d /dev/urandom || fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+
+prepare "[20] Disallow open/create if already mapped." wipe
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 || fail
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 2>/dev/null && fail
+$CRYPTSETUP create $DEV_NAME2 $LOOPDEV -d $KEY1 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $LOOPDEV 2>/dev/null && fail
+$CRYPTSETUP remove $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $LOOPDEV || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME2 2>/dev/null && fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+
+prepare "[21] luksDump" wipe
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT --uuid $TEST_UUID $LOOPDEV $KEY1 || fail
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV -d $KEY1 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 0: ENABLED" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q $TEST_UUID || fail
+echo $PWDW | $CRYPTSETUP luksDump $LOOPDEV --dump-master-key 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksDump $LOOPDEV --dump-master-key | grep -q "MK dump:" || can_fail_fips
+$CRYPTSETUP luksDump -q $LOOPDEV --dump-master-key -d $KEY1 | grep -q "MK dump:" || can_fail_fips
+echo $PWD1 | $CRYPTSETUP luksDump -q $LOOPDEV --dump-master-key --master-key-file $VK_FILE > /dev/null || can_fail_fips
+fips_mode || {
+ echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT --master-key-file $VK_FILE $LOOPDEV || fail
+}
+
+prepare "[22] remove disappeared device" wipe
+dmsetup create $DEV_NAME --table "0 5000 linear $LOOPDEV 2" || fail
+echo $PWD1 | $CRYPTSETUP -q $FAST_PBKDF_OPT luksFormat --type luks1 /dev/mapper/$DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP -q luksOpen /dev/mapper/$DEV_NAME $DEV_NAME2 || fail
+# underlying device now returns error but node is still present
+dmsetup load $DEV_NAME --table "0 5000 error" || fail
+dmsetup resume $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME2 || fail
+dmsetup remove $DEV_NAME || fail
+
+prepare "[23] ChangeKey passphrase and keyfile" wipe
+# [0]$KEY1 [1]key0
+$CRYPTSETUP -q luksFormat --type luks1 $LOOPDEV $KEY1 $FAST_PBKDF_OPT --key-slot 0 || fail
+echo $PWD1 | $CRYPTSETUP luksAddKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY1 --key-slot 1 || fail
+# keyfile [0] / keyfile [0]
+$CRYPTSETUP luksChangeKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY1 $KEY2 --key-slot 0 || fail
+# passphrase [1] / passphrase [1]
+echo -e "$PWD1\n$PWD2\n" | $CRYPTSETUP luksChangeKey $LOOPDEV $FAST_PBKDF_OPT --key-slot 1 || fail
+# keyfile [0] / keyfile [new]
+$CRYPTSETUP luksChangeKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY2 $KEY1 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 0: DISABLED" || fail
+# passphrase [1] / passphrase [new]
+echo -e "$PWD2\n$PWD1\n" | $CRYPTSETUP luksChangeKey $FAST_PBKDF_OPT $LOOPDEV || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 1: DISABLED" || fail
+# use all slots
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT || fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT || fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT || fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT || fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT || fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT || fail
+# still allows replace
+$CRYPTSETUP luksChangeKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY1 $KEY2 || fail
+$CRYPTSETUP luksChangeKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY1 $KEY2 2>/dev/null && fail
+
+prepare "[24] Keyfile limit" wipe
+$CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV $KEY1 --key-slot 0 -l 13 || fail
+$CRYPTSETUP --key-file=$KEY1 luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP --key-file=$KEY1 -l 0 luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP --key-file=$KEY1 -l -1 luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP --key-file=$KEY1 -l 14 luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP --key-file=$KEY1 -l 13 --keyfile-offset 1 luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP --key-file=$KEY1 -l 13 --keyfile-offset -1 luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP --key-file=$KEY1 -l 13 luksOpen $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT 2>/dev/null && fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT -l 14 2>/dev/null && fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT -l -1 2>/dev/null && fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT -l 13 --new-keyfile-size 12 || fail
+$CRYPTSETUP luksRemoveKey $LOOPDEV $KEY2 2>/dev/null && fail
+$CRYPTSETUP luksRemoveKey $LOOPDEV $KEY2 -l 12 || fail
+$CRYPTSETUP luksChangeKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT 2>/dev/null && fail
+$CRYPTSETUP luksChangeKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT -l 14 2>/dev/null && fail
+$CRYPTSETUP luksChangeKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT -l 13 || fail
+# -l is ignored for stdin if _only_ passphrase is used
+echo $PWD1 | $CRYPTSETUP luksAddKey $LOOPDEV -d $KEY2 $FAST_PBKDF_OPT || fail
+# this is stupid, but expected
+echo $PWD1 | $CRYPTSETUP luksRemoveKey $LOOPDEV -l 11 2>/dev/null && fail
+echo $PWDW"0" | $CRYPTSETUP luksRemoveKey $LOOPDEV -l 12 2>/dev/null && fail
+echo -e "$PWD1\n" | $CRYPTSETUP luksRemoveKey $LOOPDEV -d- -l 12 || fail
+# offset
+$CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV $KEY1 --key-slot 0 -l 13 --keyfile-offset 16 || fail
+$CRYPTSETUP --key-file=$KEY1 -l 13 --keyfile-offset 15 luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP --key-file=$KEY1 -l 13 --keyfile-offset 16 luksOpen $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+$CRYPTSETUP luksAddKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY1 -l 13 --keyfile-offset 16 $KEY2 --new-keyfile-offset 1 || fail
+$CRYPTSETUP --key-file=$KEY2 --keyfile-offset 11 luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP --key-file=$KEY2 --keyfile-offset 1 luksOpen $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+$CRYPTSETUP luksChangeKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY2 --keyfile-offset 1 $KEY2 --new-keyfile-offset 0 || fail
+$CRYPTSETUP luksOpen -d $KEY2 $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+# large device with keyfile
+echo -e '0 10000000 error'\\n'10000000 1000000 zero' | dmsetup create $DEV_NAME2 || fail
+$CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV /dev/mapper/$DEV_NAME2 -l 13 --keyfile-offset 5120000000 || fail
+$CRYPTSETUP --key-file=/dev/mapper/$DEV_NAME2 -l 13 --keyfile-offset 5119999999 luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP --key-file=/dev/mapper/$DEV_NAME2 -l 13 --keyfile-offset 5120000000 luksOpen $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+$CRYPTSETUP luksChangeKey $LOOPDEV $FAST_PBKDF_OPT -d /dev/mapper/$DEV_NAME2 \
+ --keyfile-offset 5120000000 -l 13 /dev/mapper/$DEV_NAME2 --new-keyfile-offset 5120000001 --new-keyfile-size 15 || fail
+dmsetup remove $DEV_NAME2
+
+prepare "[25] Create shared segments" wipe
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME $LOOPDEV --hash sha1 --offset 0 --size 256 || fail
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME2 $LOOPDEV --hash sha1 --offset 512 --size 256 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME2 $LOOPDEV --hash sha1 --offset 512 --size 256 --shared || fail
+$CRYPTSETUP -q remove $DEV_NAME2 || fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+
+prepare "[26] Suspend/Resume" wipe
+# only LUKS is supported
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME --hash sha1 $LOOPDEV || fail
+$CRYPTSETUP luksSuspend $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP luksResume $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+$CRYPTSETUP luksSuspend $DEV_NAME 2>/dev/null && fail
+# LUKS
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV || fail
+echo $PWD1 | $CRYPTSETUP -q luksOpen $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP luksSuspend $DEV_NAME || fail
+$CRYPTSETUP -q resize $DEV_NAME 2>/dev/null && fail
+echo $PWDW | $CRYPTSETUP luksResume $DEV_NAME 2>/dev/null && fail
+[ $? -ne 2 ] && fail "luksResume should return EPERM exit code"
+echo $PWD1 | $CRYPTSETUP luksResume $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+
+prepare "[27] luksOpen with specified key slot number" wipe
+# first, let's try passphrase option
+echo $PWD3 | $CRYPTSETUP luksFormat --type luks1 $FAST_PBKDF_OPT -S 5 $LOOPDEV || fail
+check $LUKS_HEADER $KEY_SLOT5 $KEY_MATERIAL5
+echo $PWD3 | $CRYPTSETUP luksOpen -S 4 $LOOPDEV $DEV_NAME && fail
+[ -b /dev/mapper/$DEV_NAME ] && fail
+echo $PWD3 | $CRYPTSETUP luksOpen -S 5 $LOOPDEV $DEV_NAME || fail
+check_exists
+$CRYPTSETUP luksClose $DEV_NAME || fail
+echo -e "$PWD3\n$PWD1" | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT -S 0 $LOOPDEV || fail
+check $LUKS_HEADER $KEY_SLOT0 $KEY_MATERIAL0
+echo $PWD3 | $CRYPTSETUP luksOpen -S 0 $LOOPDEV $DEV_NAME 2>/dev/null && fail
+[ -b /dev/mapper/$DEV_NAME ] && fail
+echo $PWD1 | $CRYPTSETUP luksOpen -S 5 $LOOPDEV $DEV_NAME 2>/dev/null && fail
+[ -b /dev/mapper/$DEV_NAME ] && fail
+# second, try it with keyfiles
+$CRYPTSETUP luksFormat --type luks1 -q -S 5 -d $KEY5 $LOOPDEV || fail
+check $LUKS_HEADER $KEY_SLOT5 $KEY_MATERIAL5
+$CRYPTSETUP luksAddKey $FAST_PBKDF_OPT -S 1 -d $KEY5 $LOOPDEV $KEY1 || fail
+check $LUKS_HEADER $KEY_SLOT1 $KEY_MATERIAL1
+$CRYPTSETUP luksOpen -S 5 -d $KEY5 $LOOPDEV $DEV_NAME || fail
+check_exists
+$CRYPTSETUP luksClose $DEV_NAME || fail
+$CRYPTSETUP luksOpen -S 1 -d $KEY5 $LOOPDEV $DEV_NAME 2>/dev/null && fail
+[ -b /dev/mapper/$DEV_NAME ] && fail
+$CRYPTSETUP luksOpen -S 5 -d $KEY1 $LOOPDEV $DEV_NAME 2>/dev/null && fail
+[ -b /dev/mapper/$DEV_NAME ] && fail
+
+prepare "[28] Detached LUKS header" wipe
+echo $PWD1 | $CRYPTSETUP luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV --header $HEADER_IMG || fail
+echo $PWD1 | $CRYPTSETUP luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV --header $HEADER_IMG --align-payload 1 >/dev/null 2>&1 && fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV --header $HEADER_IMG --align-payload 8192 || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV --header $HEADER_IMG --align-payload 0 || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV --header $HEADER_IMG --align-payload 8192 --offset 8192 >/dev/null 2>&1 && fail
+truncate -s 4096 $HEADER_IMG
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV --header $HEADER_IMG -S7 >/dev/null 2>&1 || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV --header $HEADER_IMG --offset 80000 >/dev/null 2>&1 || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV --header $HEADER_IMG --offset 8192 || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV --header $HEADER_IMG --offset 0 || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV --header $HEADER_IMG $DEV_NAME || fail
+$CRYPTSETUP -q resize $DEV_NAME --size 100 --header $HEADER_IMG || fail
+$CRYPTSETUP -q status $DEV_NAME --header $HEADER_IMG | grep "size:" | grep -q "100 sectors" || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "type:" | grep -q "n/a" || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "100 sectors" || fail
+$CRYPTSETUP luksSuspend $DEV_NAME --header $HEADER_IMG || fail
+echo $PWD1 | $CRYPTSETUP luksResume $DEV_NAME --header $HEADER_IMG || fail
+$CRYPTSETUP luksSuspend $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP luksResume $DEV_NAME && fail
+echo $PWD1 | $CRYPTSETUP luksResume $DEV_NAME --header $HEADER_IMG || fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT -S 5 _fakedev_ --header $HEADER_IMG $KEY5 || fail
+$CRYPTSETUP luksDump _fakedev_ --header $HEADER_IMG | grep -q "Key Slot 5: ENABLED" || fail
+$CRYPTSETUP luksKillSlot -q _fakedev_ --header $HEADER_IMG 5 || fail
+$CRYPTSETUP luksDump _fakedev_ --header $HEADER_IMG | grep -q "Key Slot 5: DISABLED" || fail
+
+prepare "[29] Repair metadata" wipe
+$CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV $KEY1 --key-slot 0 || fail
+# second sector overwrite should corrupt keyslot 6+7
+dd if=/dev/urandom of=$LOOPDEV bs=512 seek=1 count=1 >/dev/null 2>&1
+$CRYPTSETUP luksOpen -d $KEY1 $LOOPDEV $DEV_NAME >/dev/null 2>&1 && fail
+$CRYPTSETUP -q repair $LOOPDEV >/dev/null 2>&1 || fail
+$CRYPTSETUP luksOpen -d $KEY1 $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+
+prepare "[30] LUKS erase" wipe
+$CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV $KEY5 --key-slot 5 || fail
+$CRYPTSETUP luksAddKey $FAST_PBKDF_OPT -S 1 -d $KEY5 $LOOPDEV $KEY1 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 1: ENABLED" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 5: ENABLED" || fail
+$CRYPTSETUP luksErase -q $LOOPDEV || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 1: DISABLED" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 5: DISABLED" || fail
+
+prepare "[31] Deferred removal of device" wipe
+echo $PWD1 | $CRYPTSETUP open --type plain --hash sha256 $LOOPDEV $DEV_NAME || fail
+echo $PWD2 | $CRYPTSETUP open --type plain --hash sha256 /dev/mapper/$DEV_NAME $DEV_NAME2 || fail
+$CRYPTSETUP close $DEV_NAME >/dev/null 2>&1 && fail
+$CRYPTSETUP -q status $DEV_NAME >/dev/null 2>&1 || fail
+$CRYPTSETUP close --deferred $DEV_NAME >/dev/null 2>&1
+if [ $? -eq 0 ] ; then
+ dmsetup info $DEV_NAME | grep -q "DEFERRED REMOVE" || fail
+ $CRYPTSETUP -q status $DEV_NAME >/dev/null 2>&1 || fail
+ $CRYPTSETUP close $DEV_NAME2 || fail
+ $CRYPTSETUP -q status $DEV_NAME >/dev/null 2>&1 && fail
+else
+ $CRYPTSETUP close $DEV_NAME2 >/dev/null 2>&1
+ $CRYPTSETUP close $DEV_NAME >/dev/null 2>&1
+fi
+
+# Interactive tests
+# Do not remove sleep 0.1 below, the password query flushes TTY buffer (so the code is racy).
+which expect >/dev/null 2>&1 || skip "WARNING: expect tool missing, interactive test will be skipped." 0
+
+prepare "[32] Interactive password retry from terminal." new
+expect - >/dev/null <<EOF
+proc abort {} { send_error "Timeout. "; exit 2 }
+set timeout 10
+eval spawn $CRYPTSETUP luksOpen -v -T 2 $LOOPDEV $DEV_NAME
+expect timeout abort "Enter passphrase for $LOOPDEV:"
+sleep 0.1
+send "$PWD0 x\n"
+expect timeout abort "No key available with this passphrase."
+expect timeout abort "Enter passphrase for $LOOPDEV:"
+sleep 0.1
+send "$PWD0\n"
+expect timeout abort "Key slot 0 unlocked."
+expect timeout abort "Command successful."
+expect timeout abort eof
+exit
+EOF
+[ $? -eq 0 ] || fail "Expect script failed."
+check_exists
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+
+prepare "[33] Interactive unsuccessful password retry from terminal." new
+expect - >/dev/null <<EOF
+proc abort {} { send_error "Timeout. "; exit 2 }
+set timeout 10
+eval spawn $CRYPTSETUP luksOpen -v -T 2 $LOOPDEV $DEV_NAME
+expect timeout abort "Enter passphrase for $LOOPDEV:"
+sleep 0.1
+send "$PWD0 x\n"
+expect timeout abort "No key available with this passphrase."
+expect timeout abort "Enter passphrase for $LOOPDEV:"
+sleep 0.1
+send "$PWD0 y\n"
+expect timeout abort "No key available with this passphrase."
+expect timeout abort eof
+exit
+EOF
+[ $? -eq 0 ] || fail "Expect script failed."
+
+prepare "[34] Interactive kill of last key slot." new
+expect - >/dev/null <<EOF
+proc abort {} { send_error "Timeout. "; exit 2 }
+set timeout 10
+eval spawn $CRYPTSETUP luksKillSlot -v $LOOPDEV 0
+expect timeout abort "Are you sure? (Type uppercase yes):"
+send "YES\n"
+expect timeout abort "Enter any remaining passphrase:"
+sleep 0.1
+send "$PWD0\n"
+expect timeout abort "Command successful."
+expect timeout abort eof
+eval spawn $CRYPTSETUP luksKillSlot -v $LOOPDEV 0
+expect timeout abort "Keyslot 0 is not active."
+expect timeout abort eof
+exit
+EOF
+[ $? -eq 0 ] || fail "Expect script failed."
+
+prepare "[35] Interactive format of device." wipe
+expect - >/dev/null <<EOF
+proc abort {} { send_error "Timeout. "; exit 2 }
+set timeout 10
+eval spawn $CRYPTSETUP luksFormat --type luks1 $FAST_PBKDF_OPT -v $LOOPDEV
+expect timeout abort "Are you sure? (Type uppercase yes):"
+send "YES\n"
+expect timeout abort "Enter passphrase for $LOOPDEV:"
+sleep 0.1
+send "$PWD0\n"
+expect timeout abort "Verify passphrase:"
+sleep 0.1
+send "$PWD0\n"
+expect timeout abort "Command successful."
+expect timeout abort eof
+eval spawn $CRYPTSETUP luksOpen -v $LOOPDEV --test-passphrase
+expect timeout abort "Enter passphrase for $LOOPDEV:"
+sleep 0.1
+send "$PWD0\n"
+expect timeout abort "Command successful."
+expect timeout abort eof
+exit
+EOF
+[ $? -eq 0 ] || fail "Expect script failed."
+
+prepare "[36] Interactive unsuccessful format of device." new
+expect - >/dev/null <<EOF
+proc abort {} { send_error "Timeout. "; exit 2 }
+set timeout 10
+eval spawn $CRYPTSETUP erase -v $LOOPDEV
+expect timeout abort "Are you sure? (Type uppercase yes):"
+send "YES\n"
+expect timeout abort "Command successful."
+expect timeout abort eof
+eval spawn $CRYPTSETUP luksFormat --type luks1 $FAST_PBKDF_OPT -v $LOOPDEV
+expect timeout abort "Are you sure? (Type uppercase yes):"
+send "YES\n"
+expect timeout abort "Enter passphrase for $LOOPDEV:"
+sleep 0.1
+send "$PWD0\n"
+expect timeout abort "Verify passphrase:"
+sleep 0.1
+send "$PWD0 x\n"
+expect timeout abort "Passphrases do not match."
+expect timeout abort eof
+eval spawn $CRYPTSETUP luksOpen -v $LOOPDEV -T 1 --test-passphrase
+expect timeout abort "Enter passphrase for $LOOPDEV:"
+sleep 0.1
+send "$PWD0\n"
+expect timeout abort "No key available with this passphrase."
+expect timeout abort eof
+exit
+EOF
+[ $? -eq 0 ] || fail "Expect script failed."
+
+prepare "[37] Interactive add key." new
+expect - >/dev/null <<EOF
+proc abort {} { send_error "Timeout. "; exit 2 }
+set timeout 10
+eval spawn $CRYPTSETUP luksAddKey -S 2 $FAST_PBKDF_OPT -v $LOOPDEV
+expect timeout abort "Enter any existing passphrase:"
+sleep 0.1
+send "$PWD0\n"
+expect timeout abort "Enter new passphrase for key slot:"
+sleep 0.1
+send "$PWD1\n"
+expect timeout abort "Verify passphrase:"
+sleep 0.1
+send "$PWD1\n"
+expect timeout abort "Command successful."
+expect timeout abort eof
+eval spawn $CRYPTSETUP luksOpen $FAST_PBKDF_OPT -v $LOOPDEV --test-passphrase
+expect timeout abort "Enter passphrase"
+sleep 0.1
+send "$PWD1\n"
+expect timeout abort "Command successful."
+expect timeout abort eof
+eval spawn $CRYPTSETUP luksKillSlot -v $LOOPDEV 1
+expect timeout abort "Keyslot 1 is not active."
+expect timeout abort eof
+eval spawn $CRYPTSETUP luksKillSlot -v $LOOPDEV 2
+expect timeout abort "Enter any remaining passphrase:"
+sleep 0.1
+send "$PWD0\n"
+expect timeout abort "Key slot 2 removed."
+expect timeout abort eof
+exit
+EOF
+[ $? -eq 0 ] || fail "Expect script failed."
+
+prepare "[38] Interactive change key." new
+expect - >/dev/null <<EOF
+proc abort {} { send_error "Timeout. "; exit 2 }
+set timeout 10
+eval spawn $CRYPTSETUP luksChangeKey $FAST_PBKDF_OPT -v $LOOPDEV
+expect timeout abort "Enter passphrase to be changed:"
+sleep 0.1
+send "$PWD0\n"
+expect timeout abort "Enter new passphrase:"
+sleep 0.1
+send "$PWD1\n"
+expect timeout abort "Verify passphrase:"
+sleep 0.1
+send "$PWD1\n"
+expect timeout abort "Command successful."
+expect timeout abort eof
+eval spawn $CRYPTSETUP luksOpen -v $LOOPDEV --test-passphrase
+expect timeout abort "Enter passphrase for $LOOPDEV:"
+sleep 0.1
+send "$PWD1\n"
+expect timeout abort "Command successful."
+expect timeout abort eof
+exit
+EOF
+[ $? -eq 0 ] || fail "Expect script failed."
+
+prepare "[39] Interactive suspend and resume." new
+echo $PWD0 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail
+expect - >/dev/null <<EOF
+proc abort {} { send_error "Timeout. "; exit 2 }
+set timeout 10
+eval spawn $CRYPTSETUP luksSuspend -v $DEV_NAME
+expect timeout abort "Command successful."
+expect timeout abort eof
+eval spawn $CRYPTSETUP luksResume -v -T 3 $DEV_NAME
+expect timeout abort "Enter passphrase for $LOOPDEV:"
+sleep 0.1
+send "$PWD0 x\n"
+expect timeout abort "No key available with this passphrase."
+expect timeout abort "Enter passphrase for $LOOPDEV:"
+sleep 0.1
+send "$PWD1\n"
+expect timeout abort "No key available with this passphrase."
+expect timeout abort "Enter passphrase for $LOOPDEV:"
+sleep 0.1
+send "$PWD0 y\n"
+expect timeout abort "No key available with this passphrase."
+expect timeout abort eof
+eval spawn $CRYPTSETUP luksResume -v $DEV_NAME
+expect timeout abort "Enter passphrase for $LOOPDEV:"
+sleep 0.1
+send "$PWD0\n"
+expect timeout abort "Command successful."
+expect timeout abort eof
+exit
+EOF
+[ $? -eq 0 ] || fail "Expect script failed."
+$CRYPTSETUP remove $DEV_NAME || fail
+
+remove_mapping
+exit 0
diff --git a/tests/compat-test2 b/tests/compat-test2
new file mode 100755
index 0000000..893367f
--- /dev/null
+++ b/tests/compat-test2
@@ -0,0 +1,954 @@
+#!/bin/bash
+
+PS4='$LINENO:'
+CRYPTSETUP=../cryptsetup
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+DEV_NAME=dummy
+DEV_NAME2=dummy2
+DEV_NAME3=dummy3
+ORIG_IMG=luks-test-orig
+IMG=luks-test
+IMG10=luks-test-v10
+HEADER_IMG=luks-header
+HEADER_KEYU=luks2_keyslot_unassigned.img
+HEADER_LUKS2_PV=blkid-luks2-pv.img
+KEY1=key1
+KEY2=key2
+KEY5=key5
+KEYE=keye
+PWD0="compatkey"
+PWD1="93R4P4pIqAH8"
+PWD2="mymJeD8ivEhE"
+PWD3="ocMakf3fAcQO"
+PWD4="Qx3qn46vq0v"
+PWDW="rUkL4RUryBom"
+TEST_KEYRING_NAME="compattest2_keyring"
+TEST_TOKEN0="compattest2_desc0"
+TEST_TOKEN1="compattest2_desc1"
+VK_FILE="compattest2_vkfile"
+IMPORT_TOKEN="{\"type\":\"some_type\",\"keyslots\":[],\"base64_data\":\"zxI7vKB1Qwl4VPB4D-N-OgcC14hPCG0IDu8O7eCqaQ\"}"
+TOKEN_FILE0=test-token-file0
+TOKEN_FILE1=test-token-file1
+
+FAST_PBKDF_OPT="--pbkdf pbkdf2 --pbkdf-force-iterations 1000"
+
+TEST_UUID="12345678-1234-1234-1234-123456789abc"
+
+LOOPDEV=$(losetup -f 2>/dev/null)
+[ -f /etc/system-fips ] && FIPS_MODE=$(cat /proc/sys/crypto/fips_enabled 2>/dev/null)
+
+LOCK_DIR=$(grep DEFAULT_LUKS2_LOCK_PATH ../config.h | cut -d\" -f 2)
+HAVE_KEYRING=$(grep -e "#define KERNEL_KEYRING" ../config.h)
+test -n "$HAVE_KEYRING" || HAVE_KEYRING=0
+HAVE_KEYRING=${HAVE_KEYRING: -1}
+
+function remove_mapping()
+{
+ [ -b /dev/mapper/$DEV_NAME3 ] && dmsetup remove $DEV_NAME3
+ [ -b /dev/mapper/$DEV_NAME2 ] && dmsetup remove $DEV_NAME2
+ [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove $DEV_NAME
+ losetup -d $LOOPDEV >/dev/null 2>&1
+ rm -f $ORIG_IMG $IMG $IMG10 $KEY1 $KEY2 $KEY5 $KEYE $HEADER_IMG $HEADER_KEYU $VK_FILE $HEADER_LUKS2_PV missing-file $TOKEN_FILE0 $TOKEN_FILE1 test_image_* >/dev/null 2>&1
+
+ # unlink whole test keyring
+ [ -n "$TEST_KEYRING" ] && keyctl unlink $TEST_KEYRING "@u" >/dev/null
+ unset TEST_KEYRING
+}
+
+function force_uevent()
+{
+ DNAME=$(echo $LOOPDEV | cut -f3 -d /)
+ echo "change" >/sys/block/$DNAME/uevent
+}
+
+function fail()
+{
+ [ -n "$1" ] && echo "$1"
+ remove_mapping
+ echo "FAILED at line $(caller)"
+ exit 2
+}
+
+function fips_mode()
+{
+ [ -n "$FIPS_MODE" ] && [ "$FIPS_MODE" -gt 0 ]
+}
+
+function can_fail_fips()
+{
+ # Ignore this fail if running in FIPS mode
+ fips_mode || fail $1
+}
+
+function skip()
+{
+ [ -n "$1" ] && echo "$1"
+ remove_mapping
+ exit 77
+}
+
+function prepare()
+{
+ [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove $DEV_NAME
+
+ case "$2" in
+ wipe)
+ remove_mapping
+ dd if=/dev/zero of=$IMG bs=1M count=40 >/dev/null 2>&1
+ sync
+ losetup $LOOPDEV $IMG
+ ;;
+ new)
+ remove_mapping
+ xz -cd compatimage.img.xz > $IMG
+ xz -dk $HEADER_KEYU.xz
+ # FIXME: switch to internal loop (no losetup at all)
+ echo "bad" | $CRYPTSETUP luksOpen --key-slot 0 --test-passphrase $IMG 2>&1 | \
+ grep "autoclear flag" && skip "WARNING: Too old kernel, test skipped."
+ losetup $LOOPDEV $IMG
+ xz -cd compatv10image.img.xz > $IMG10
+ ;;
+ reuse | *)
+ if [ ! -e $IMG ]; then
+ xz -cd compatimage.img.xz > $IMG
+ losetup $LOOPDEV $IMG
+ fi
+ [ ! -e $IMG10 ] && xz -cd compatv10image.img.xz > $IMG10
+ ;;
+ esac
+
+ if [ ! -e $KEY1 ]; then
+ #dd if=/dev/urandom of=$KEY1 count=1 bs=32 >/dev/null 2>&1
+ echo -n $'\x48\xc6\x74\x4f\x41\x4e\x50\xc0\x79\xc2\x2d\x5b\x5f\x68\x84\x17' >$KEY1
+ echo -n $'\x9c\x03\x5e\x1b\x4d\x0f\x9a\x75\xb3\x90\x70\x32\x0a\xf8\xae\xc4'>>$KEY1
+ fi
+
+ if [ ! -e $KEY2 ]; then
+ dd if=/dev/urandom of=$KEY2 count=1 bs=16 >/dev/null 2>&1
+ fi
+
+ if [ ! -e $KEY5 ]; then
+ dd if=/dev/urandom of=$KEY5 count=1 bs=16 >/dev/null 2>&1
+ fi
+
+ if [ ! -e $KEYE ]; then
+ touch $KEYE
+ fi
+
+ cp $IMG $ORIG_IMG
+ [ -n "$1" ] && echo "CASE: $1"
+}
+
+function check_exists()
+{
+ [ -b /dev/mapper/$DEV_NAME ] || fail
+}
+
+function valgrind_setup()
+{
+ which valgrind >/dev/null 2>&1 || fail "Cannot find valgrind."
+ [ ! -f $CRYPTSETUP_VALGRIND ] && fail "Unable to get location of cryptsetup executable."
+ export LD_LIBRARY_PATH="$CRYPTSETUP_LIB_VALGRIND:$LD_LIBRARY_PATH"
+}
+
+function valgrind_run()
+{
+ INFOSTRING="$(basename ${BASH_SOURCE[1]})-line-${BASH_LINENO[0]}" ./valg.sh ${CRYPTSETUP_VALGRIND} "$@"
+}
+
+function dm_crypt_keyring_support()
+{
+ 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 -gt 1 ] && return 0
+ [ $VER_MAJ -eq 1 -a $VER_MIN -gt 18 ] && return 0
+ [ $VER_MAJ -eq 1 -a $VER_MIN -eq 18 -a $VER_PTC -ge 1 ] && return 0
+ return 1
+}
+
+function dm_crypt_keyring_flawed()
+{
+ dm_crypt_keyring_support && return 1;
+
+ [ $VER_MAJ -gt 1 ] && return 0
+ [ $VER_MAJ -eq 1 -a $VER_MIN -ge 15 ] && return 0
+ return 1
+}
+
+function dm_crypt_keyring_new_kernel()
+{
+ KER_STR=$(uname -r)
+ [ -z "$KER_STR" ] && fail "Failed to parse kernel version."
+ KER_MAJ=$(echo $KER_STR | cut -f 1 -d.)
+ KER_MIN=$(echo $KER_STR | cut -f 2 -d.)
+
+ [ $KER_MAJ -ge 5 ] && return 0
+ [ $KER_MAJ -eq 4 -a $KER_MIN -ge 15 ] && return 0
+ return 1
+}
+
+function test_and_prepare_keyring() {
+ which keyctl > /dev/null 2>&1 || skip "Cannot find keyctl, test skipped"
+ keyctl list "@s" > /dev/null || skip "Current session keyring is unreachable, test skipped"
+ TEST_KEYRING=$(keyctl newring $TEST_KEYRING_NAME "@u" 2> /dev/null)
+ test -n "$TEST_KEYRING" || skip "Failed to create keyring in user keyring"
+ keyctl search "@s" keyring "$TEST_KEYRING" > /dev/null 2>&1 || keyctl link "@u" "@s" > /dev/null 2>&1
+ load_key user test_key test_data "$TEST_KEYRING" || skip "Kernel keyring service is useless on this system, test skipped."
+}
+
+# $1 type
+# $2 description
+# $3 payload
+# $4 keyring
+function load_key()
+{
+ keyctl add $@ >/dev/null
+}
+
+export LANG=C
+
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+
+[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
+[ -z "$LOOPDEV" ] && skip "WARNING: Cannot find free loop device, test skipped."
+[ -d "$LOCK_DIR" ] || skip "WARNING: LUKS2 locking directory ($LOCK_DIR) is missing, test skipped."
+
+prepare "[1] Data offset" wipe
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -q --offset 1 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -q --offset 16385 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -q --offset 32 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -q --align-payload 16384 --offset 16384 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -q --offset 16384 || fail
+$CRYPTSETUP -q luksDump $LOOPDEV | grep -q "offset: $((512 * 16384)) \[bytes\]" || fail
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -q --sector-size 1024 --offset 16384 >/dev/null || fail
+$CRYPTSETUP -q luksDump $LOOPDEV | grep -q "offset: $((512 * 16384)) \[bytes\]" || fail
+truncate -s 4096 $HEADER_IMG
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --header $HEADER_IMG -q --offset 80000 >/dev/null 2>&1 || fail
+
+prepare "[2] Sector size and old payload alignment" wipe
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -q --sector-size 511 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -q --sector-size 256 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -q --sector-size 8192 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -q --sector-size 512 || fail
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -q --align-payload 5 || fail
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -q --sector-size 512 --align-payload 5 || fail
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -q --sector-size 2048 --align-payload 32 >/dev/null || fail
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -q --sector-size 4096 >/dev/null || fail
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -q --sector-size 2048 --align-payload 32768 >/dev/null || fail
+$CRYPTSETUP -q luksDump $LOOPDEV | grep -q "offset: $((512 * 32768)) \[bytes\]" || fail
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -q --sector-size 2048 >/dev/null || fail
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -q --sector-size 4096 --align-payload 32768 >/dev/null || fail
+$CRYPTSETUP -q luksDump $LOOPDEV | grep -q "offset: $((512 * 32768)) \[bytes\]" || fail
+
+prepare "[3] format" wipe
+echo $PWD1 | $CRYPTSETUP -q $FAST_PBKDF_OPT -c aes-cbc-essiv:sha256 -s 128 luksFormat --type luks2 $LOOPDEV || fail
+prepare "[4] format using hash sha512" wipe
+echo $PWD1 | $CRYPTSETUP $FAST_PBKDF_OPT -h sha512 -c aes-cbc-essiv:sha256 -s 128 luksFormat --type luks2 $LOOPDEV || fail
+$CRYPTSETUP -q luksDump $LOOPDEV | grep "0: pbkdf2" -A2 | grep "Hash:" | grep -qe sha512 || fail
+
+prepare "[5] open"
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME --test-passphrase || fail
+echo $PWDW | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME --test-passphrase 2>/dev/null && fail
+[ $? -ne 2 ] && fail "luksOpen should return EPERM exit code"
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail
+check_exists
+
+# Key Slot 1 and key material section 1 must change, the rest must not.
+prepare "[6] add key"
+echo -e "$PWD1\n$PWD2" | $CRYPTSETUP luksAddKey $LOOPDEV $FAST_PBKDF_OPT || fail
+echo $PWD2 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail
+
+# Unsuccessful Key Delete - nothing may change
+prepare "[7] unsuccessful delete"
+echo $PWDW | $CRYPTSETUP luksKillSlot $LOOPDEV 1 2>/dev/null && fail
+[ $? -ne 2 ] && fail "luksKillSlot should return EPERM exit code"
+#FIXME
+#$CRYPTSETUP -q luksKillSlot $LOOPDEV 8 2>/dev/null && fail
+#$CRYPTSETUP -q luksKillSlot $LOOPDEV 7 2>/dev/null && fail
+
+# Delete Key Test
+# Key Slot 1 and key material section 1 must change, the rest must not
+prepare "[8] successful delete"
+$CRYPTSETUP -q luksKillSlot $LOOPDEV 1 || fail
+echo $PWD2 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME 2> /dev/null && fail
+[ $? -ne 2 ] && fail "luksOpen should return EPERM exit code"
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail
+
+# Key Slot 1 and key material section 1 must change, the rest must not
+prepare "[9] add key test for key files"
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV $KEY1 || fail
+$CRYPTSETUP -d $KEY1 luksOpen $LOOPDEV $DEV_NAME || fail
+
+# Key Slot 1 and key material section 1 must change, the rest must not
+prepare "[10] delete key test with key1 as remaining key"
+$CRYPTSETUP -d $KEY1 luksKillSlot $LOOPDEV 0 || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP luksOpen -d $KEY1 $LOOPDEV $DEV_NAME || fail
+
+# Delete last slot
+prepare "[11] delete last key" wipe
+echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 $LOOPDEV $FAST_PBKDF_OPT || fail
+echo $PWD1 | $CRYPTSETUP luksKillSlot $LOOPDEV 0 || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+
+# Format test for ESSIV, and some other parameters.
+prepare "[12] parameter variation test" wipe
+$CRYPTSETUP -q $FAST_PBKDF_OPT -c aes-cbc-essiv:sha256 -s 128 luksFormat --type luks2 $LOOPDEV $KEY1 || fail
+$CRYPTSETUP -d $KEY1 luksOpen $LOOPDEV $DEV_NAME || fail
+
+prepare "[13] open/close - stacked devices" wipe
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $LOOPDEV $FAST_PBKDF_OPT || fail
+echo $PWD1 | $CRYPTSETUP -q luksOpen $LOOPDEV $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 /dev/mapper/$DEV_NAME $FAST_PBKDF_OPT || fail
+echo $PWD1 | $CRYPTSETUP -q luksOpen /dev/mapper/$DEV_NAME $DEV_NAME2 || fail
+$CRYPTSETUP -q luksClose $DEV_NAME2 || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+
+prepare "[14] format/open - passphrase on stdin & new line" wipe
+# stdin defined by "-" must take even newline
+#echo -n -e "$PWD1\n$PWD2" | $CRYPTSETUP -q luksFormat $LOOPDEV - || fail
+echo -n -e "$PWD1\n$PWD2" | $CRYPTSETUP $FAST_PBKDF_OPT -q --key-file=- luksFormat --type luks2 $LOOPDEV || fail
+echo -n -e "$PWD1\n$PWD2" | $CRYPTSETUP -q --key-file=- luksOpen $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+echo -n -e "$PWD1\n$PWD2" | $CRYPTSETUP -q luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+# now also try --key-file
+echo -n -e "$PWD1\n$PWD2" | $CRYPTSETUP $FAST_PBKDF_OPT -q luksFormat --type luks2 $LOOPDEV --key-file=- || fail
+echo -n -e "$PWD1\n$PWD2" | $CRYPTSETUP -q --key-file=- luksOpen $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+# process newline if from stdin
+echo -n -e "$PWD1\n$PWD2" | $CRYPTSETUP $FAST_PBKDF_OPT -q luksFormat --type luks2 $LOOPDEV || fail
+echo "$PWD1" | $CRYPTSETUP -q luksOpen $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+
+prepare "[15] UUID - use and report provided UUID" wipe
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --uuid blah --type luks2 $LOOPDEV 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --uuid $TEST_UUID --type luks2 $LOOPDEV || fail
+tst=$($CRYPTSETUP -q luksUUID $LOOPDEV)
+[ "$tst"x = "$TEST_UUID"x ] || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV || fail
+$CRYPTSETUP -q luksUUID --uuid $TEST_UUID $LOOPDEV || fail
+tst=$($CRYPTSETUP -q luksUUID $LOOPDEV)
+[ "$tst"x = "$TEST_UUID"x ] || fail
+
+prepare "[16] luksFormat" wipe
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --master-key-file /dev/urandom --type luks2 $LOOPDEV || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --master-key-file /dev/urandom --type luks2 $LOOPDEV -d $KEY1 || fail
+$CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --master-key-file /dev/urandom -s 256 --uuid $TEST_UUID --type luks2 $LOOPDEV $KEY1 || fail
+$CRYPTSETUP luksOpen -d $KEY1 $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+# open by UUID
+force_uevent # some systems do not update loop by-uuid
+$CRYPTSETUP luksOpen -d $KEY1 UUID=X$TEST_UUID $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP luksOpen -d $KEY1 UUID=$TEST_UUID $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+# empty keyfile
+$CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV $KEYE || fail
+$CRYPTSETUP luksOpen -d $KEYE $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+# open by volume key
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT -s 256 --master-key-file $KEY1 --type luks2 $LOOPDEV || fail
+$CRYPTSETUP luksOpen --master-key-file /dev/urandom $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP luksOpen --master-key-file $KEY1 $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+
+prepare "[17] AddKey volume key, passphrase and keyfile" wipe
+# masterkey
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --master-key-file /dev/zero --key-slot 3 || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV --test-passphrase || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "3: luks2" || fail
+echo $PWD2 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV --master-key-file /dev/zero --key-slot 4 || fail
+echo $PWD2 | $CRYPTSETUP luksOpen $LOOPDEV --test-passphrase --key-slot 4 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "4: luks2" || fail
+echo $PWD3 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV --master-key-file /dev/null --key-slot 5 2>/dev/null && fail
+$CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV --master-key-file /dev/zero --key-slot 5 $KEY1 || fail
+$CRYPTSETUP luksOpen $LOOPDEV --test-passphrase --key-slot 5 -d $KEY1 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "5: luks2" || fail
+
+# special "-" handling
+$CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV $KEY1 --key-slot 3 || fail
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV -d $KEY1 - || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV --test-passphrase 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV -d - --test-passphrase || fail
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV -d - $KEY2 || fail
+$CRYPTSETUP luksOpen $LOOPDEV -d $KEY2 --test-passphrase || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV -d - -d $KEY1 --test-passphrase 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV -d $KEY1 -d $KEY1 --test-passphrase 2>/dev/null && fail
+
+# [0]PWD1 [1]PWD2 [2]$KEY1/1 [3]$KEY1 [4]$KEY2
+$CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV $KEY1 --key-slot 3 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "3: luks2" || fail
+$CRYPTSETUP luksAddKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY1 $KEY2 --key-slot 3 2>/dev/null && fail
+# keyfile/keyfile
+$CRYPTSETUP luksAddKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY1 $KEY2 --key-slot 4 || fail
+$CRYPTSETUP luksOpen $LOOPDEV -d $KEY2 --test-passphrase --key-slot 4 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "4: luks2" || fail
+# passphrase/keyfile
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV -d $KEY1 --key-slot 0 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "0: luks2" || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV --test-passphrase --key-slot 0 || fail
+# passphrase/passphrase
+echo -e "$PWD1\n$PWD2\n" | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV --key-slot 1 || fail
+echo $PWD2 | $CRYPTSETUP luksOpen $LOOPDEV --test-passphrase --key-slot 1 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "1: luks2" || fail
+# keyfile/passphrase
+echo -e "$PWD2\n" | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV $KEY1 --key-slot 2 --new-keyfile-size 3 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "2: luks2" || fail
+
+prepare "[18] RemoveKey passphrase and keyfile" reuse
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "3: luks2" || fail
+$CRYPTSETUP luksRemoveKey $LOOPDEV $KEY1 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "3: luks2" && fail
+$CRYPTSETUP luksRemoveKey $LOOPDEV $KEY1 2>/dev/null && fail
+[ $? -ne 2 ] && fail "luksRemoveKey should return EPERM exit code"
+$CRYPTSETUP luksRemoveKey $LOOPDEV $KEY2 --keyfile-size 1 2>/dev/null && fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "4: luks2" || fail
+$CRYPTSETUP luksRemoveKey $LOOPDEV $KEY2 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "4: luks2" && fail
+# if password or keyfile is provided, batch mode must not suppress it
+echo "badpw" | $CRYPTSETUP luksKillSlot $LOOPDEV 2 2>/dev/null && fail
+echo "badpw" | $CRYPTSETUP luksKillSlot $LOOPDEV 2 -q 2>/dev/null && fail
+echo "badpw" | $CRYPTSETUP luksKillSlot $LOOPDEV 2 --key-file=- 2>/dev/null && fail
+echo "badpw" | $CRYPTSETUP luksKillSlot $LOOPDEV 2 --key-file=- -q 2>/dev/null && fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "2: luks2" || fail
+# kill slot using passphrase from 1
+echo $PWD2 | $CRYPTSETUP luksKillSlot $LOOPDEV 2 2>/dev/null || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "2: luks2" && fail
+# remove key0 / slot 0
+echo $PWD1 | $CRYPTSETUP luksRemoveKey $LOOPDEV || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "0: luks2" && fail
+# last keyslot, in batch mode no passphrase needed...
+$CRYPTSETUP luksKillSlot -q $LOOPDEV 1 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "1: luks2" && fail
+
+prepare "[19] create & status & resize" wipe
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME $LOOPDEV --hash xxx 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME $LOOPDEV --hash sha1 --cipher aes-cbc-essiv:sha256 --offset 3 --skip 4 --readonly || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "offset:" | grep -q "3 sectors" || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "skipped:" | grep -q "4 sectors" || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "mode:" | grep -q "readonly" || fail
+$CRYPTSETUP -q resize $DEV_NAME --size 100 || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "100 sectors" || fail
+$CRYPTSETUP -q resize $DEV_NAME || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "81917 sectors" || fail
+# Resize underlying loop device as well
+truncate -s 20M $IMG || fail
+$CRYPTSETUP -q resize $DEV_NAME || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "40957 sectors" || fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+$CRYPTSETUP -q status $DEV_NAME >/dev/null && fail
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME --hash sha1 $LOOPDEV || fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP -q create $DEV_NAME --hash sha1 $LOOPDEV || fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP -q create $DEV_NAME --hash sha1 --size 100 $LOOPDEV || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "100 sectors" || fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+# verify is ignored on non-tty input
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME $LOOPDEV --hash sha1 --verify-passphrase 2>/dev/null || fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 --key-size 255 2>/dev/null && fail
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 --key-size -1 2>/dev/null && fail
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 -l -1 2>/dev/null && fail
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 || fail
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 2>/dev/null && fail
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d blah 2>/dev/null && fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d /dev/urandom || fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail
+if dm_crypt_keyring_support; then
+ echo | $CRYPTSETUP -q resize --size 100 $DEV_NAME 2>/dev/null && fail
+fi
+echo $PWD1 | $CRYPTSETUP -q resize --size 100 $DEV_NAME || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "100 sectors" || fail
+$CRYPTSETUP close $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP luksOpen --disable-keyring $LOOPDEV $DEV_NAME || fail
+echo | $CRYPTSETUP -q resize --size 100 $DEV_NAME || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "100 sectors" || fail
+$CRYPTSETUP close $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail
+if dm_crypt_keyring_support; then
+ $CRYPTSETUP -q resize --disable-keyring --size 100 $DEV_NAME 2>/dev/null && fail
+fi
+
+prepare "[20] Disallow open/create if already mapped." wipe
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 || fail
+$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 2>/dev/null && fail
+$CRYPTSETUP create $DEV_NAME2 $LOOPDEV -d $KEY1 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV 2>/dev/null && fail
+$CRYPTSETUP remove $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME2 2>/dev/null && fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+
+prepare "[21] luksDump" wipe
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --uuid $TEST_UUID --type luks2 $LOOPDEV $KEY1 || fail
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV -d $KEY1 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "0: luks2" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q $TEST_UUID || fail
+echo $PWDW | $CRYPTSETUP luksDump $LOOPDEV --dump-master-key 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksDump $LOOPDEV --dump-master-key | grep -q "MK dump:" || can_fail_fips
+$CRYPTSETUP luksDump -q $LOOPDEV --dump-master-key -d $KEY1 | grep -q "MK dump:" || can_fail_fips
+echo $PWD1 | $CRYPTSETUP luksDump -q $LOOPDEV --dump-master-key --master-key-file $VK_FILE >/dev/null || can_fail_fips
+echo $PWD1 | $CRYPTSETUP luksDump -q $LOOPDEV --dump-master-key --master-key-file $VK_FILE 2>/dev/null && fail
+fips_mode || {
+ echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT --master-key-file $VK_FILE $LOOPDEV || fail
+}
+
+prepare "[22] remove disappeared device" wipe
+dmsetup create $DEV_NAME --table "0 39998 linear $LOOPDEV 2" || fail
+echo $PWD1 | $CRYPTSETUP -q $FAST_PBKDF_OPT luksFormat --type luks2 /dev/mapper/$DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP -q luksOpen /dev/mapper/$DEV_NAME $DEV_NAME2 || fail
+# underlying device now returns error but node is still present
+dmsetup load $DEV_NAME --table "0 40000 error" || fail
+dmsetup resume $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME2 || fail
+dmsetup remove $DEV_NAME || fail
+
+prepare "[23] ChangeKey passphrase and keyfile" wipe
+# [0]$KEY1 [1]key0
+$CRYPTSETUP -q luksFormat --type luks2 $LOOPDEV $KEY1 $FAST_PBKDF_OPT --key-slot 0 || fail
+echo $PWD1 | $CRYPTSETUP luksAddKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY1 --key-slot 1 || fail
+# keyfile [0] / keyfile [0]
+$CRYPTSETUP luksChangeKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY1 $KEY2 --key-slot 0 || fail
+# passphrase [1] / passphrase [1]
+echo -e "$PWD1\n$PWD2\n" | $CRYPTSETUP luksChangeKey $LOOPDEV $FAST_PBKDF_OPT --key-slot 1 || fail
+# keyfile [0] / keyfile [new]
+$CRYPTSETUP luksChangeKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY2 $KEY1 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "0: luks2" && fail
+# passphrase [1] / passphrase [new]
+echo -e "$PWD2\n$PWD1\n" | $CRYPTSETUP luksChangeKey $FAST_PBKDF_OPT $LOOPDEV || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "1: luks2" && fail
+# use all slots
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT || fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT || fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT || fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT || fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT || fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT || fail
+# still allows replace
+#FIXME
+#$CRYPTSETUP luksChangeKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY1 $KEY2 || fail
+#$CRYPTSETUP luksChangeKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY1 $KEY2 2>/dev/null && fail
+
+prepare "[24] Keyfile limit" wipe
+$CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV $KEY1 --key-slot 0 -l 13 || fail
+$CRYPTSETUP --key-file=$KEY1 luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP --key-file=$KEY1 -l 0 luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP --key-file=$KEY1 -l -1 luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP --key-file=$KEY1 -l 14 luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP --key-file=$KEY1 -l 13 --keyfile-offset 1 luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP --key-file=$KEY1 -l 13 --keyfile-offset -1 luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP --key-file=$KEY1 -l 13 luksOpen $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 2>/dev/null && fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 -l 14 2>/dev/null && fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 -l -1 2>/dev/null && fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT -l 13 --new-keyfile-size 12 || fail
+$CRYPTSETUP luksRemoveKey $LOOPDEV $KEY2 2>/dev/null && fail
+$CRYPTSETUP luksRemoveKey $LOOPDEV $KEY2 -l 12 || fail
+$CRYPTSETUP luksChangeKey $LOOPDEV -d $KEY1 $KEY2 2>/dev/null && fail
+[ $? -ne 2 ] && fail "luksChangeKey should return EPERM exit code"
+$CRYPTSETUP luksChangeKey $LOOPDEV -d $KEY1 $KEY2 -l 14 2>/dev/null && fail
+$CRYPTSETUP luksChangeKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT -l 13 || fail
+# -l is ignored for stdin if _only_ passphrase is used
+echo $PWD1 | $CRYPTSETUP luksAddKey $LOOPDEV -d $KEY2 $FAST_PBKDF_OPT || fail
+# this is stupid, but expected
+echo $PWD1 | $CRYPTSETUP luksRemoveKey $LOOPDEV -l 11 2>/dev/null && fail
+echo $PWDW"0" | $CRYPTSETUP luksRemoveKey $LOOPDEV -l 12 2>/dev/null && fail
+echo -e "$PWD1\n" | $CRYPTSETUP luksRemoveKey $LOOPDEV -d- -l 12 || fail
+# offset
+$CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV $KEY1 --key-slot 0 -l 13 --keyfile-offset 16 || fail
+$CRYPTSETUP --key-file=$KEY1 -l 13 --keyfile-offset 15 luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP --key-file=$KEY1 -l 13 --keyfile-offset 16 luksOpen $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+$CRYPTSETUP luksAddKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY1 -l 13 --keyfile-offset 16 $KEY2 --new-keyfile-offset 1 || fail
+$CRYPTSETUP --key-file=$KEY2 --keyfile-offset 11 luksOpen $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP --key-file=$KEY2 --keyfile-offset 1 luksOpen $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+$CRYPTSETUP luksChangeKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY2 --keyfile-offset 1 $KEY2 --new-keyfile-offset 0 || fail
+$CRYPTSETUP luksOpen -d $KEY2 $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+
+prepare "[25] Create shared segments" wipe
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME $LOOPDEV --hash sha1 --offset 0 --size 256 || fail
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME2 $LOOPDEV --hash sha1 --offset 512 --size 256 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME2 $LOOPDEV --hash sha1 --offset 512 --size 256 --shared || fail
+$CRYPTSETUP -q remove $DEV_NAME2 || fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+
+prepare "[26] Suspend/Resume" wipe
+# only LUKS is supported
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME --hash sha1 $LOOPDEV || fail
+$CRYPTSETUP luksSuspend $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP luksResume $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+$CRYPTSETUP luksSuspend $DEV_NAME 2>/dev/null && fail
+# LUKS
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV || fail
+echo $PWD1 | $CRYPTSETUP -q luksOpen $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP luksSuspend $DEV_NAME || fail
+$CRYPTSETUP -q resize $DEV_NAME 2>/dev/null && fail
+echo $PWDW | $CRYPTSETUP luksResume $DEV_NAME 2>/dev/null && fail
+[ $? -ne 2 ] && fail "luksResume should return EPERM exit code"
+echo $PWD1 | $CRYPTSETUP luksResume $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+
+prepare "[27] luksOpen with specified key slot number" wipe
+# first, let's try passphrase option
+echo $PWD3 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT -S 5 --type luks2 $LOOPDEV || fail
+echo $PWD3 | $CRYPTSETUP luksOpen -S 4 $LOOPDEV $DEV_NAME && fail
+[ -b /dev/mapper/$DEV_NAME ] && fail
+echo $PWD3 | $CRYPTSETUP luksOpen -S 5 $LOOPDEV $DEV_NAME || fail
+check_exists
+$CRYPTSETUP luksClose $DEV_NAME || fail
+echo -e "$PWD3\n$PWD1" | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT -S 0 $LOOPDEV || fail
+echo $PWD3 | $CRYPTSETUP luksOpen -S 0 $LOOPDEV $DEV_NAME 2>/dev/null && fail
+[ -b /dev/mapper/$DEV_NAME ] && fail
+echo $PWD1 | $CRYPTSETUP luksOpen -S 5 $LOOPDEV $DEV_NAME 2>/dev/null && fail
+[ -b /dev/mapper/$DEV_NAME ] && fail
+# second, try it with keyfiles
+$CRYPTSETUP -q luksFormat -q -S 5 $FAST_PBKDF_OPT -d $KEY5 --type luks2 $LOOPDEV || fail
+$CRYPTSETUP luksAddKey $FAST_PBKDF_OPT -S 1 -d $KEY5 $LOOPDEV $KEY1 || fail
+$CRYPTSETUP luksOpen -S 5 -d $KEY5 $LOOPDEV $DEV_NAME || fail
+check_exists
+$CRYPTSETUP luksClose $DEV_NAME || fail
+$CRYPTSETUP luksOpen -S 1 -d $KEY5 $LOOPDEV $DEV_NAME 2>/dev/null && fail
+[ -b /dev/mapper/$DEV_NAME ] && fail
+$CRYPTSETUP luksOpen -S 5 -d $KEY1 $LOOPDEV $DEV_NAME 2>/dev/null && fail
+[ -b /dev/mapper/$DEV_NAME ] && fail
+# test keyslot not assigned to segment is unable to unlock volume
+# otoh it should be allowed to test for proper passphrase
+prepare "" new
+echo $PWD1 | $CRYPTSETUP open -S1 --test-passphrase $HEADER_KEYU || fail
+echo $PWD1 | $CRYPTSETUP open --test-passphrase $HEADER_KEYU || fail
+echo $PWD1 | $CRYPTSETUP open -S1 $HEADER_KEYU $DEV_NAME && fail
+[ -b /dev/mapper/$DEV_NAME ] && fail
+echo $PWD1 | $CRYPTSETUP open $HEADER_KEYU $DEV_NAME && fail
+[ -b /dev/mapper/$DEV_NAME ] && fail
+echo $PWD0 | $CRYPTSETUP open -S1 --test-passphrase $HEADER_KEYU $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP luksKillSlot -q $HEADER_KEYU 0
+$CRYPTSETUP luksDump $HEADER_KEYU | grep -q "0: luks2" && fail
+echo $PWD1 | $CRYPTSETUP open -S1 --test-passphrase $HEADER_KEYU || fail
+echo $PWD1 | $CRYPTSETUP open --test-passphrase $HEADER_KEYU || fail
+echo $PWD1 | $CRYPTSETUP open -S1 $HEADER_KEYU $DEV_NAME && fail
+
+prepare "[28] Detached LUKS header" wipe
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --header $HEADER_IMG || fail
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --header $HEADER_IMG --align-payload 1 >/dev/null 2>&1 && fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --header $HEADER_IMG --align-payload 8192 || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --header $HEADER_IMG --align-payload 4096 >/dev/null || fail
+$CRYPTSETUP luksDump $HEADER_IMG | grep -e "0: crypt" -A1 | grep -qe $((4096*512)) || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --header $HEADER_IMG --align-payload 0 || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV --header $HEADER_IMG $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP -q resize $DEV_NAME --size 100 --header $HEADER_IMG || fail
+$CRYPTSETUP -q status $DEV_NAME --header $HEADER_IMG | grep "size:" | grep -q "100 sectors" || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "type:" | grep -q "n/a" || fail
+$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "100 sectors" || fail
+$CRYPTSETUP luksSuspend $DEV_NAME --header $HEADER_IMG || fail
+echo $PWD1 | $CRYPTSETUP luksResume $DEV_NAME --header $HEADER_IMG || fail
+$CRYPTSETUP luksSuspend $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP luksResume $DEV_NAME && fail
+echo $PWD1 | $CRYPTSETUP luksResume $DEV_NAME --header $HEADER_IMG || fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT -S 5 _fakedev_ --header $HEADER_IMG $KEY5 || fail
+$CRYPTSETUP luksDump _fakedev_ --header $HEADER_IMG | grep -q "5: luks2" || fail
+$CRYPTSETUP luksKillSlot -q _fakedev_ --header $HEADER_IMG 5 || fail
+$CRYPTSETUP luksDump _fakedev_ --header $HEADER_IMG | grep -q "5: luks2" && fail
+
+prepare "[29] Repair metadata" wipe
+xz -dk $HEADER_LUKS2_PV.xz
+$CRYPTSETUP isLuks --disable-locks $HEADER_LUKS2_PV && fail
+$CRYPTSETUP isLuks $HEADER_LUKS2_PV && fail
+$CRYPTSETUP isLuks --disable-locks --type luks2 $HEADER_LUKS2_PV && fail
+$CRYPTSETUP isLuks --type luks2 $HEADER_LUKS2_PV && fail
+$CRYPTSETUP -q repair $HEADER_LUKS2_PV || fail
+$CRYPTSETUP isLuks $HEADER_LUKS2_PV || fail
+$CRYPTSETUP isLuks --type luks2 $HEADER_LUKS2_PV || fail
+$CRYPTSETUP isLuks --type luks1 $HEADER_LUKS2_PV && fail
+
+prepare "[30] LUKS erase" wipe
+$CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV $KEY5 --key-slot 5 || fail
+$CRYPTSETUP luksAddKey $FAST_PBKDF_OPT -S 1 -d $KEY5 $LOOPDEV $KEY1 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "1: luks2" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "5: luks2" || fail
+$CRYPTSETUP luksErase -q $LOOPDEV || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "1: luks2" && fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "5: luks2" && fail
+
+prepare "[31] LUKS convert" wipe
+$CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks1 $LOOPDEV $KEY5 --key-slot 5 || fail
+$CRYPTSETUP luksAddKey $FAST_PBKDF_OPT -S 1 -d $KEY5 $LOOPDEV $KEY1 || fail
+$CRYPTSETUP -q convert --type luks1 $LOOPDEV >/dev/null 2>&1 && fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 1: ENABLED" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 5: ENABLED" || fail
+$CRYPTSETUP -q convert --type luks2 $LOOPDEV || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "1: luks2" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "5: luks2" || fail
+$CRYPTSETUP -q convert --type luks1 $LOOPDEV || fail
+# hash test
+$CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV $KEY5 -S 0 --hash sha1 || fail
+$CRYPTSETUP luksAddKey $FAST_PBKDF_OPT -S 1 -d $KEY5 $LOOPDEV $KEY1 --hash sha256 || fail
+$CRYPTSETUP -q convert --type luks1 $LOOPDEV >/dev/null 2>&1 && fail
+$CRYPTSETUP -q luksKillSlot $LOOPDEV 1 || fail
+$CRYPTSETUP -q convert --type luks1 $LOOPDEV || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 0: ENABLED" || fail
+$CRYPTSETUP luksOpen $LOOPDEV --test-passphrase --key-slot 0 -d $KEY5 || fail
+
+if dm_crypt_keyring_flawed; then
+ prepare "[32a] LUKS2 keyring dm-crypt bug" wipe
+ echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --header $HEADER_IMG || fail
+ echo $PWD1 | $CRYPTSETUP open $LOOPDEV --header $HEADER_IMG $DEV_NAME || fail
+ $CRYPTSETUP -q status $DEV_NAME | grep "key location:" | grep -q "dm-crypt" || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+ # key must not load in kernel key even when dm-crypt module is missing
+ if rmmod dm-crypt > /dev/null 2>&1; then
+ echo $PWD1 | $CRYPTSETUP open $LOOPDEV --header $HEADER_IMG $DEV_NAME || fail
+ $CRYPTSETUP -q status $DEV_NAME | grep "key location:" | grep -q "dm-crypt" || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+ fi
+fi
+
+if dm_crypt_keyring_support && dm_crypt_keyring_new_kernel; then
+ prepare "[32] LUKS2 key in keyring" wipe
+ echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --header $HEADER_IMG || fail
+
+ # check keyring support detection works as expected
+ rmmod dm-crypt > /dev/null 2>&1 || true
+ echo $PWD1 | $CRYPTSETUP open $LOOPDEV --header $HEADER_IMG $DEV_NAME || fail
+ $CRYPTSETUP -q status $DEV_NAME | grep "key location:" | grep -q "keyring" || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+
+ echo $PWD1 | $CRYPTSETUP open $LOOPDEV --disable-keyring --header $HEADER_IMG $DEV_NAME || fail
+ $CRYPTSETUP -q status $DEV_NAME | grep "key location:" | grep -q "dm-crypt" || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+
+ echo $PWD1 | $CRYPTSETUP open $LOOPDEV --disable-keyring --header $HEADER_IMG $DEV_NAME || fail
+ $CRYPTSETUP luksSuspend $DEV_NAME || fail
+ echo $PWD1 | $CRYPTSETUP luksResume $DEV_NAME --header $HEADER_IMG || fail
+ $CRYPTSETUP -q status $DEV_NAME | grep "key location:" | grep -q "keyring" || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+
+ echo $PWD1 | $CRYPTSETUP open $LOOPDEV --header $HEADER_IMG $DEV_NAME || fail
+ $CRYPTSETUP luksSuspend $DEV_NAME || fail
+ echo $PWD1 | $CRYPTSETUP luksResume --disable-keyring $DEV_NAME --header $HEADER_IMG || fail
+ $CRYPTSETUP -q status $DEV_NAME | grep "key location:" | grep -q "dm-crypt" || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+fi
+
+# FIXME: candidate for non-root tests
+prepare "[33] tokens" wipe
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV || fail
+if [ $HAVE_KEYRING -gt 0 ]; then
+
+ test_and_prepare_keyring
+
+ $CRYPTSETUP token add $LOOPDEV --key-description $TEST_TOKEN0 --token-id 3 || fail
+ $CRYPTSETUP luksDump $LOOPDEV | grep -q -e "3: luks2-keyring" || fail
+ # keyslot 5 is inactive
+ $CRYPTSETUP token add $LOOPDEV --key-description $TEST_TOKEN1 --key-slot 5 2> /dev/null && fail
+ # key description is not reachable
+ $CRYPTSETUP open --token-only $LOOPDEV --test-passphrase && fail
+ # wrong passphrase
+ load_key user $TEST_TOKEN0 "blabla" "$TEST_KEYRING" || fail "Cannot load 32 byte user key type"
+ $CRYPTSETUP open --token-only $LOOPDEV --test-passphrase 2>/dev/null && fail
+ load_key user $TEST_TOKEN0 $PWD1 "$TEST_KEYRING" || fail "Cannot load 32 byte user key type"
+ $CRYPTSETUP open --token-only $LOOPDEV --test-passphrase || fail
+ $CRYPTSETUP open --token-only $LOOPDEV $DEV_NAME || fail
+ $CRYPTSETUP status $DEV_NAME > /dev/null || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+ $CRYPTSETUP token remove --token-id 3 $LOOPDEV || fail
+ $CRYPTSETUP luksDump $LOOPDEV | grep -q -e "3: luks2-keyring" && fail
+
+ # test we can remove keyslot with token
+ echo -e "$PWD1\n$PWD2" | $CRYPTSETUP luksAddKey -S4 $FAST_PBKDF_OPT $LOOPDEV || fail
+ $CRYPTSETUP token add $LOOPDEV --key-description $TEST_TOKEN1 --key-slot 4 || fail
+ $CRYPTSETUP -q luksKillSlot $LOOPDEV 4 || fail
+fi
+echo -n "$IMPORT_TOKEN" | $CRYPTSETUP token import $LOOPDEV --token-id 10 || fail
+echo -n "$IMPORT_TOKEN" | $CRYPTSETUP token import $LOOPDEV --token-id 11 --json-file - || fail
+echo -n "$IMPORT_TOKEN" > $TOKEN_FILE0
+$CRYPTSETUP token import $LOOPDEV --token-id 12 --json-file $TOKEN_FILE0 || fail
+$CRYPTSETUP token import $LOOPDEV --token-id 12 --json-file $TOKEN_FILE0 2>/dev/null && fail
+$CRYPTSETUP token export $LOOPDEV --token-id 10 | diff --from-file - $TOKEN_FILE0 || fail
+$CRYPTSETUP token export $LOOPDEV --token-id 11 | diff --from-file - $TOKEN_FILE0 || fail
+$CRYPTSETUP token export $LOOPDEV --token-id 12 | diff --from-file - $TOKEN_FILE0 || fail
+$CRYPTSETUP token export $LOOPDEV --token-id 12 --json-file $TOKEN_FILE1 || fail
+diff $TOKEN_FILE0 $TOKEN_FILE1 || fail
+$CRYPTSETUP token export $LOOPDEV --token-id 12 > $TOKEN_FILE1 || fail
+diff $TOKEN_FILE0 $TOKEN_FILE1 || fail
+
+prepare "[34] LUKS keyslot priority" wipe
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV -S 1 || fail
+echo -e "$PWD1\n$PWD2" | $CRYPTSETUP luksAddKey $LOOPDEV $FAST_PBKDF_OPT -S 5 || fail
+$CRYPTSETUP config $LOOPDEV -S 0 --priority prefer && fail
+$CRYPTSETUP config $LOOPDEV -S 1 --priority bla >/dev/null 2>&1 && fail
+$CRYPTSETUP config $LOOPDEV -S 1 --priority ignore || fail
+echo $PWD1 | $CRYPTSETUP open $LOOPDEV --test-passphrase 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP open $LOOPDEV --test-passphrase -S 1 || fail
+echo $PWD2 | $CRYPTSETUP open $LOOPDEV --test-passphrase || fail
+$CRYPTSETUP config $LOOPDEV -S 1 --priority normal || fail
+echo $PWD1 | $CRYPTSETUP open $LOOPDEV --test-passphrase || fail
+$CRYPTSETUP config $LOOPDEV -S 1 --priority ignore || fail
+echo $PWD1 | $CRYPTSETUP open $LOOPDEV --test-passphrase 2>/dev/null && fail
+
+prepare "[35] LUKS label and subsystem" wipe
+echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep "Subsystem:" | grep -q "(no subsystem)" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep "Label:" | grep -q "(no label)" || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --subsystem SatelliteTwo --label TheLabel || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep "Subsystem:" | grep -q "SatelliteTwo" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep "Label:" | grep -q "TheLabel" || fail
+$CRYPTSETUP config $LOOPDEV --subsystem SatelliteThree
+$CRYPTSETUP luksDump $LOOPDEV | grep "Subsystem:" | grep -q "SatelliteThree" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep "Label:" | grep -q "(no label)" || fail
+$CRYPTSETUP config $LOOPDEV --subsystem SatelliteThree --label TheLabel
+$CRYPTSETUP luksDump $LOOPDEV | grep "Subsystem:" | grep -q "SatelliteThree" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep "Label:" | grep -q "TheLabel" || fail
+
+prepare "[36] LUKS PBKDF setting" wipe
+echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 --pbkdf bla $LOOPDEV >/dev/null 2>&1 && fail
+# Force setting, no benchmark. PBKDF2 has 1000 iterations as a minimum
+echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 --pbkdf pbkdf2 --pbkdf-force-iterations 999 $LOOPDEV 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --pbkdf pbkdf2 --pbkdf-force-iterations 1234 $LOOPDEV || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep "Iterations:" | grep -q "1234" || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --pbkdf argon2id --pbkdf-force-iterations 3 $LOOPDEV 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --pbkdf argon2id --pbkdf-force-iterations 4 --pbkdf-memory 100000 $LOOPDEV || can_fail_fips
+$CRYPTSETUP luksDump $LOOPDEV | grep "PBKDF:" | grep -q "argon2id" || can_fail_fips
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --pbkdf argon2i --pbkdf-force-iterations 4 \
+ --pbkdf-memory 1234 --pbkdf-parallel 1 $LOOPDEV || can_fail_fips
+$CRYPTSETUP luksDump $LOOPDEV | grep "PBKDF:" | grep -q "argon2i" || can_fail_fips
+$CRYPTSETUP luksDump $LOOPDEV | grep "Time cost:" | grep -q "4" || can_fail_fips
+$CRYPTSETUP luksDump $LOOPDEV | grep "Memory:" | grep -q "1234" || can_fail_fips
+$CRYPTSETUP luksDump $LOOPDEV | grep "Threads:" | grep -q "1" || can_fail_fips
+# Benchmark
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --pbkdf argon2i -i 500 --pbkdf-memory 1234 --pbkdf-parallel 1 $LOOPDEV || can_fail_fips
+[ 0"$($CRYPTSETUP luksDump $LOOPDEV | grep "Time cost:" | cut -d: -f 2 | sed -e 's/\ //g')" -gt 0 ] || can_fail_fips
+[ 0"$($CRYPTSETUP luksDump $LOOPDEV | grep "Memory:" | cut -d: -f 2 | sed -e 's/\ //g')" -gt 0 ] || can_fail_fips
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --pbkdf pbkdf2 -i 500 $LOOPDEV || fail
+[ 0"$($CRYPTSETUP luksDump $LOOPDEV | grep -m1 "Iterations:" | cut -d' ' -f 2 | sed -e 's/\ //g')" -gt 1000 ] || fail
+
+prepare "[37] LUKS Keyslot convert" wipe
+$CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks1 $LOOPDEV $KEY5 --key-slot 5 || fail
+$CRYPTSETUP -q luksConvertKey $LOOPDEV --key-file $KEY5 2>/dev/null && fail
+$CRYPTSETUP -q convert --type luks2 $LOOPDEV || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "5: luks2" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep "PBKDF:" | grep -q "pbkdf2" || fail
+$CRYPTSETUP -q luksConvertKey $LOOPDEV -S 5 --key-file $KEY5 --pbkdf argon2i -i1 --pbkdf-memory 32 || can_fail_fips
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "5: luks2" || can_fail_fips
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV -S 1 --key-file $KEY5 || fail
+$CRYPTSETUP -q luksKillSlot $LOOPDEV 5 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "1: luks2" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep "PBKDF:" | grep -q "pbkdf2" || fail
+echo $PWD1 | $CRYPTSETUP -q luksConvertKey $LOOPDEV -S 1 --pbkdf argon2i -i1 --pbkdf-memory 32 || can_fail_fips
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "1: luks2" || can_fail_fips
+echo $PWD3 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT -S 21 --unbound -s 16 $LOOPDEV || fail
+echo $PWD3 | $CRYPTSETUP luksConvertKey --pbkdf-force-iterations 1001 --pbkdf pbkdf2 -S 21 $LOOPDEV || fail
+
+prepare "[38] luksAddKey unbound tests" wipe
+$CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV $KEY5 --key-slot 5 || fail
+# unbound key may have arbitrary size
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT --unbound -s 16 $LOOPDEV || fail
+echo $PWD2 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT --unbound -s 32 -S 2 $LOOPDEV || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "2: luks2 (unbound)" || fail
+echo $PWD3 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT --unbound -s 8 -S 3 --master-key-file /dev/urandom $LOOPDEV || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "3: luks2 (unbound)" || fail
+# unbound key size is required
+echo $PWD1 | $CRYPTSETUP -q luksAddKey --unbound $LOOPDEV 2>/dev/null && fail
+echo $PWD3 | $CRYPTSETUP -q luksAddKey --unbound --master-key-file /dev/urandom $LOOPDEV 2> /dev/null && fail
+# do not allow to replace keyslot by unbound slot
+echo $PWD1 | $CRYPTSETUP -q luksAddKey -S5 --unbound -s 32 $LOOPDEV 2>/dev/null && fail
+echo $PWD2 | $CRYPTSETUP -q open $LOOPDEV $DEV_NAME 2> /dev/null && fail
+echo $PWD2 | $CRYPTSETUP -q open $LOOPDEV --test-passphrase || fail
+echo $PWD2 | $CRYPTSETUP -q open -S2 $LOOPDEV $DEV_NAME 2> /dev/null && fail
+echo $PWD2 | $CRYPTSETUP -q open -S2 $LOOPDEV --test-passphrase || fail
+echo $PWD1 | $CRYPTSETUP -q open $LOOPDEV $DEV_NAME 2> /dev/null && fail
+echo $PWD1 | $CRYPTSETUP -q open $LOOPDEV --test-passphrase || fail
+# check we're able to change passphrase for unbound keyslot
+echo -e "$PWD2\n$PWD3" | $CRYPTSETUP luksChangeKey $FAST_PBKDF_OPT -S 2 $LOOPDEV || fail
+echo $PWD3 | $CRYPTSETUP open --test-passphrase $FAST_PBKDF_OPT -S 2 $LOOPDEV || fail
+echo $PWD3 | $CRYPTSETUP -q open -S 2 $LOOPDEV $DEV_NAME 2> /dev/null && fail
+# do not allow adding keyslot by unbound keyslot
+echo -e "$PWD3\n$PWD1" | $CRYPTSETUP -q luksAddKey $LOOPDEV 2> /dev/null && fail
+# check adding keyslot works when there's unbound keyslot
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $LOOPDEV --key-file $KEY5 -S8 || fail
+echo $PWD1 | $CRYPTSETUP open $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP close $DEV_NAME || fail
+$CRYPTSETUP luksKillSlot -q $LOOPDEV 2
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "2: luks2 (unbound)" && fail
+$CRYPTSETUP luksKillSlot -q $LOOPDEV 3
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "3: luks2 (unbound)" && fail
+
+prepare "[39] LUKS2 metadata variants" wipe
+tar xJf luks2_mda_images.tar.xz
+echo -n "$IMPORT_TOKEN" > $TOKEN_FILE0
+for mda in 16 32 64 128 256 512 1024 2048 4096 ; do
+ echo -n "[$mda KiB]"
+ echo $PWD4 | $CRYPTSETUP open test_image_$mda $DEV_NAME || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+ echo -e "$PWD4\n$PWD3" | $CRYPTSETUP luksAddKey -S9 $FAST_PBKDF_OPT test_image_$mda || fail
+ echo $PWD4 | $CRYPTSETUP open --test-passphrase test_image_$mda || fail
+ echo $PWD3 | $CRYPTSETUP open -S9 --test-passphrase test_image_$mda || fail
+ echo -n "$IMPORT_TOKEN" | $CRYPTSETUP token import test_image_$mda --token-id 10 || fail
+ $CRYPTSETUP token export test_image_$mda --token-id 10 | diff --from-file - $TOKEN_FILE0 || fail
+ echo -n "[OK]"
+done
+echo
+
+prepare "[40] LUKS2 metadata areas" wipe
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV 2> /dev/null || fail
+DEFAULT_OFFSET=$($CRYPTSETUP luksDump $LOOPDEV | grep "offset: " | cut -f 2 -d ' ')
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks1 $LOOPDEV --key-size 256 --luks2-metadata-size=128k --luks2-keyslots-size=128k 2> /dev/null && fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --key-size 256 --luks2-metadata-size=128k --luks2-keyslots-size=127k 2> /dev/null && fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --key-size 256 --luks2-metadata-size=127k --luks2-keyslots-size=128k 2> /dev/null && fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --key-size 256 --luks2-metadata-size=128k --luks2-keyslots-size=128M >/dev/null 2>&1 && fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --key-size 256 --luks2-metadata-size=128k --luks2-keyslots-size=128k >/dev/null || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep "Metadata area:" | grep -q "131072 \[bytes\]" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep "Keyslots area:" | grep -q "131072 \[bytes\]" || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --key-size 256 --luks2-metadata-size=128k || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep "Metadata area:" | grep -q "131072 \[bytes\]" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep "Keyslots area:" | grep -q "$((DEFAULT_OFFSET-2*131072)) \[bytes\]" || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --key-size 256 --luks2-keyslots-size=128k >/dev/null || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep "Metadata area:" | grep -q "16384 \[bytes\]" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep "Keyslots area:" | grep -q "131072 \[bytes\]" || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --key-size 256 --offset 16384 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep "Metadata area:" | grep -q "16384 \[bytes\]" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep "Keyslots area:" | grep -q "8355840 \[bytes\]" || fail
+# data offset vs area size
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --key-size 256 --offset 64 --luks2-keyslots-size=8192 >/dev/null 2>&1 && fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --key-size 256 --offset $((256+56)) >/dev/null 2>&1 && fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --key-size 256 --offset $((256+64)) >/dev/null || fail
+
+prepare "[41] Per-keyslot encryption parameters" wipe
+KEYSLOT_CIPHER="aes-cbc-plain64"
+$CRYPTSETUP -q luksFormat --type luks2 $LOOPDEV $KEY1 $FAST_PBKDF_OPT --key-slot 0 --keyslot-cipher $KEYSLOT_CIPHER --keyslot-key-size 128 || fail
+[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "0: luks2" | grep "Cipher:" | sed -e 's/[[:space:]]\+Cipher:\ \+//g')" = $KEYSLOT_CIPHER ] || fail
+[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "0: luks2" | grep "Cipher key:"| sed -e 's/[[:space:]]\+Cipher\ key:\ \+//g')" = "128 bits" ] || fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT --key-slot 1 --keyslot-cipher $KEYSLOT_CIPHER --keyslot-key-size 128 || fail
+[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "1: luks2" | grep "Cipher:" | sed -e 's/[[:space:]]\+Cipher:\ \+//g')" = $KEYSLOT_CIPHER ] || fail
+[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "1: luks2" | grep "Cipher key:"| sed -e 's/[[:space:]]\+Cipher\ key:\ \+//g')" = "128 bits" ] || fail
+$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 $FAST_PBKDF_OPT --key-slot 2 || fail
+$CRYPTSETUP luksChangeKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY2 $KEY1 --key-slot 2 --keyslot-cipher $KEYSLOT_CIPHER --keyslot-key-size 128 || fail
+[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "2: luks2" | grep "Cipher:" | sed -e 's/[[:space:]]\+Cipher:\ \+//g')" = $KEYSLOT_CIPHER ] || fail
+[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "2: luks2" | grep "Cipher key:"| sed -e 's/[[:space:]]\+Cipher\ key:\ \+//g')" = "128 bits" ] || fail
+# unbound keyslot
+echo $PWD3 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT --key-slot 21 --unbound -s 32 --keyslot-cipher $KEYSLOT_CIPHER --keyslot-key-size 128 $LOOPDEV || fail
+[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "21: luks2" | grep "Cipher:" | sed -e 's/[[:space:]]\+Cipher:\ \+//g')" = $KEYSLOT_CIPHER ] || fail
+[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "21: luks2" | grep "Cipher key:"| sed -e 's/[[:space:]]\+Cipher\ key:\ \+//g')" = "128 bits" ] || fail
+echo $PWD3 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT --key-slot 22 --unbound -s 32 $LOOPDEV || fail
+echo $PWD3 | $CRYPTSETUP luksConvertKey --key-slot 22 $LOOPDEV --keyslot-cipher $KEYSLOT_CIPHER --keyslot-key-size 128 $LOOPDEV || fail
+[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "22: luks2" | grep "Cipher:" | sed -e 's/[[:space:]]\+Cipher:\ \+//g')" = $KEYSLOT_CIPHER ] || fail
+[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "22: luks2" | grep "Cipher key:"| sed -e 's/[[:space:]]\+Cipher\ key:\ \+//g')" = "128 bits" ] || fail
+
+remove_mapping
+exit 0
diff --git a/tests/compatimage.img.xz b/tests/compatimage.img.xz
new file mode 100644
index 0000000..37fe163
--- /dev/null
+++ b/tests/compatimage.img.xz
Binary files differ
diff --git a/tests/compatimage2.img.xz b/tests/compatimage2.img.xz
new file mode 100644
index 0000000..ceaeafc
--- /dev/null
+++ b/tests/compatimage2.img.xz
Binary files differ
diff --git a/tests/compatv10image.img.xz b/tests/compatv10image.img.xz
new file mode 100644
index 0000000..2203626
--- /dev/null
+++ b/tests/compatv10image.img.xz
Binary files differ
diff --git a/tests/conversion_imgs.tar.xz b/tests/conversion_imgs.tar.xz
new file mode 100644
index 0000000..cdeb961
--- /dev/null
+++ b/tests/conversion_imgs.tar.xz
Binary files differ
diff --git a/tests/crypto-vectors.c b/tests/crypto-vectors.c
new file mode 100644
index 0000000..3ee2414
--- /dev/null
+++ b/tests/crypto-vectors.c
@@ -0,0 +1,632 @@
+/*
+ * cryptsetup crypto backend test vectors
+ *
+ * Copyright (C) 2018-2019 Milan Broz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "crypto_backend.h"
+
+#define MAX_BLOCK_SIZE 128
+
+static void printhex(const char *s, const char *buf, size_t len)
+{
+ size_t i;
+
+ printf("%s: ", s);
+ for (i = 0; i < len; i++)
+ printf(" %02x", (unsigned char)buf[i]);
+ printf("\n");
+ fflush(stdout);
+}
+
+/*
+ * KDF tests
+ */
+struct kdf_test_vector {
+ const char *type;
+ const char *hash;
+ unsigned int hash_block_length;
+ unsigned int iterations;
+ unsigned int memory;
+ unsigned int parallelism;
+ const char *password;
+ unsigned int password_length;
+ const char *salt;
+ unsigned int salt_length;
+// const char *key;
+// unsigned int key_length;
+// const char *ad;
+// unsigned int ad_length;
+ const char *output;
+ unsigned int output_length;
+};
+
+struct kdf_test_vector kdf_test_vectors[] = {
+ /* Argon2 RFC (without key and ad values) */
+ {
+ "argon2i", NULL, 0, 3, 32, 4,
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01", 32,
+ "\x02\x02\x02\x02\x02\x02\x02\x02"
+ "\x02\x02\x02\x02\x02\x02\x02\x02", 16,
+// "\x03\x03\x03\x03\x03\x03\x03\x03", 8,
+// "\x04\x04\x04\x04\x04\x04\x04\x04"
+// "\x04\x04\x04\x04", 12,
+ "\xa9\xa7\x51\x0e\x6d\xb4\xd5\x88"
+ "\xba\x34\x14\xcd\x0e\x09\x4d\x48"
+ "\x0d\x68\x3f\x97\xb9\xcc\xb6\x12"
+ "\xa5\x44\xfe\x8e\xf6\x5b\xa8\xe0", 32
+// "\xc8\x14\xd9\xd1\xdc\x7f\x37\xaa"
+// "\x13\xf0\xd7\x7f\x24\x94\xbd\xa1"
+// "\xc8\xde\x6b\x01\x6d\xd3\x88\xd2"
+// "\x99\x52\xa4\xc4\x67\x2b\x6c\xe8", 32
+ },
+ {
+ "argon2id", NULL, 0, 3, 32, 4,
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01", 32,
+ "\x02\x02\x02\x02\x02\x02\x02\x02"
+ "\x02\x02\x02\x02\x02\x02\x02\x02", 16,
+// "\x03\x03\x03\x03\x03\x03\x03\x03", 8,
+// "\x04\x04\x04\x04\x04\x04\x04\x04"
+// "\x04\x04\x04\x04", 12,
+ "\x03\xaa\xb9\x65\xc1\x20\x01\xc9"
+ "\xd7\xd0\xd2\xde\x33\x19\x2c\x04"
+ "\x94\xb6\x84\xbb\x14\x81\x96\xd7"
+ "\x3c\x1d\xf1\xac\xaf\x6d\x0c\x2e", 32
+// "\x0d\x64\x0d\xf5\x8d\x78\x76\x6c"
+// "\x08\xc0\x37\xa3\x4a\x8b\x53\xc9"
+// "\xd0\x1e\xf0\x45\x2d\x75\xb6\x5e"
+// "\xb5\x25\x20\xe9\x6b\x01\xe6\x59", 32
+ },
+ /* RFC 3962 */
+ {
+ "pbkdf2", "sha1", 64, 1, 0, 0,
+ "password", 8,
+ "ATHENA.MIT.EDUraeburn", 21,
+ "\xcd\xed\xb5\x28\x1b\xb2\xf8\x01"
+ "\x56\x5a\x11\x22\xb2\x56\x35\x15"
+ "\x0a\xd1\xf7\xa0\x4b\xb9\xf3\xa3"
+ "\x33\xec\xc0\xe2\xe1\xf7\x08\x37", 32
+ }, {
+ "pbkdf2", "sha1", 64, 2, 0, 0,
+ "password", 8,
+ "ATHENA.MIT.EDUraeburn", 21,
+ "\x01\xdb\xee\x7f\x4a\x9e\x24\x3e"
+ "\x98\x8b\x62\xc7\x3c\xda\x93\x5d"
+ "\xa0\x53\x78\xb9\x32\x44\xec\x8f"
+ "\x48\xa9\x9e\x61\xad\x79\x9d\x86", 32
+ }, {
+ "pbkdf2", "sha1", 64, 1200, 0, 0,
+ "password", 8,
+ "ATHENA.MIT.EDUraeburn", 21,
+ "\x5c\x08\xeb\x61\xfd\xf7\x1e\x4e"
+ "\x4e\xc3\xcf\x6b\xa1\xf5\x51\x2b"
+ "\xa7\xe5\x2d\xdb\xc5\xe5\x14\x2f"
+ "\x70\x8a\x31\xe2\xe6\x2b\x1e\x13", 32
+ }, {
+ "pbkdf2", "sha1", 64, 5, 0, 0,
+ "password", 8,
+ "\0224VxxV4\022", 8, // "\x1234567878563412
+ "\xd1\xda\xa7\x86\x15\xf2\x87\xe6"
+ "\xa1\xc8\xb1\x20\xd7\x06\x2a\x49"
+ "\x3f\x98\xd2\x03\xe6\xbe\x49\xa6"
+ "\xad\xf4\xfa\x57\x4b\x6e\x64\xee", 32
+ }, {
+ "pbkdf2", "sha1", 64, 1200, 0, 0,
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 64,
+ "pass phrase equals block size", 29,
+ "\x13\x9c\x30\xc0\x96\x6b\xc3\x2b"
+ "\xa5\x5f\xdb\xf2\x12\x53\x0a\xc9"
+ "\xc5\xec\x59\xf1\xa4\x52\xf5\xcc"
+ "\x9a\xd9\x40\xfe\xa0\x59\x8e\xd1", 32
+ }, {
+ "pbkdf2", "sha1", 64, 1200, 0, 0,
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 65,
+ "pass phrase exceeds block size", 30,
+ "\x9c\xca\xd6\xd4\x68\x77\x0c\xd5"
+ "\x1b\x10\xe6\xa6\x87\x21\xbe\x61"
+ "\x1a\x8b\x4d\x28\x26\x01\xdb\x3b"
+ "\x36\xbe\x92\x46\x91\x5e\xc8\x2a", 32
+ }, {
+ "pbkdf2", "sha1", 64, 50, 0, 0,
+ "\360\235\204\236", 4, // g-clef ("\xf09d849e)
+ "EXAMPLE.COMpianist", 18,
+ "\x6b\x9c\xf2\x6d\x45\x45\x5a\x43"
+ "\xa5\xb8\xbb\x27\x6a\x40\x3b\x39"
+ "\xe7\xfe\x37\xa0\xc4\x1e\x02\xc2"
+ "\x81\xff\x30\x69\xe1\xe9\x4f\x52", 32
+ }, {
+ /* RFC-6070 */
+ "pbkdf2", "sha1", 64, 1, 0, 0,
+ "password", 8,
+ "salt", 4,
+ "\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9"
+ "\xb5\x24\xaf\x60\x12\x06\x2f\xe0\x37\xa6", 20
+ }, {
+ "pbkdf2", "sha1", 64, 2, 0, 0,
+ "password", 8,
+ "salt", 4,
+ "\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c\xcd\x1e"
+ "\xd9\x2a\xce\x1d\x41\xf0\xd8\xde\x89\x57", 20
+ }, {
+ "pbkdf2", "sha1", 64, 4096, 0, 0,
+ "password", 8,
+ "salt", 4,
+ "\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad"
+ "\x49\xd9\x26\xf7\x21\xd0\x65\xa4\x29\xc1", 20
+ }, {
+ "pbkdf2", "sha1", 64, 16777216, 0, 0,
+ "password", 8,
+ "salt", 4,
+ "\xee\xfe\x3d\x61\xcd\x4d\xa4\xe4\xe9\x94"
+ "\x5b\x3d\x6b\xa2\x15\x8c\x26\x34\xe9\x84", 20
+ }, {
+ "pbkdf2", "sha1", 64, 4096, 0, 0,
+ "passwordPASSWORDpassword", 24,
+ "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36,
+ "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8"
+ "\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96"
+ "\x4c\xf2\xf0\x70\x38", 25
+ }, {
+ "pbkdf2", "sha1", 64, 4096, 0, 0,
+ "pass\0word", 9,
+ "sa\0lt", 5,
+ "\x56\xfa\x6a\xa7\x55\x48\x09\x9d\xcc\x37"
+ "\xd7\xf0\x34\x25\xe0\xc3", 16
+ }, {
+ /* empty password test */
+ "pbkdf2", "sha1", 64, 2, 0, 0,
+ "", 0,
+ "salt", 4,
+ "\x13\x3a\x4c\xe8\x37\xb4\xd2\x52\x1e\xe2"
+ "\xbf\x03\xe1\x1c\x71\xca\x79\x4e\x07\x97", 20
+ }, {
+ /* Password exceeds block size test */
+ "pbkdf2", "sha256", 64, 1200, 0, 0,
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 65,
+ "pass phrase exceeds block size", 30,
+ "\x22\x34\x4b\xc4\xb6\xe3\x26\x75"
+ "\xa8\x09\x0f\x3e\xa8\x0b\xe0\x1d"
+ "\x5f\x95\x12\x6a\x2c\xdd\xc3\xfa"
+ "\xcc\x4a\x5e\x6d\xca\x04\xec\x58", 32
+ }, {
+ "pbkdf2", "sha512", 128, 1200, 0, 0,
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 129,
+ "pass phrase exceeds block size", 30,
+ "\x0f\xb2\xed\x2c\x0e\x6e\xfb\x7d"
+ "\x7d\x8e\xdd\x58\x01\xb4\x59\x72"
+ "\x99\x92\x16\x30\x5e\xa4\x36\x8d"
+ "\x76\x14\x80\xf3\xe3\x7a\x22\xb9", 32
+ }, {
+ "pbkdf2", "whirlpool", 64, 1200, 0, 0,
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 65,
+ "pass phrase exceeds block size", 30,
+ "\x9c\x1c\x74\xf5\x88\x26\xe7\x6a"
+ "\x53\x58\xf4\x0c\x39\xe7\x80\x89"
+ "\x07\xc0\x31\x19\x9a\x50\xa2\x48"
+ "\xf1\xd9\xfe\x78\x64\xe5\x84\x50", 32
+ }
+};
+
+/*
+ * Hash tests
+ */
+
+struct hash_alg {
+ const char *name;
+ int length;
+};
+
+static struct hash_alg hash_algs[] = {
+ { "sha1", 20 },
+ { "sha256", 32 },
+ { "sha512", 64 },
+ { "ripemd160", 20 },
+ { "whirlpool", 64 },
+ { NULL, 0 }
+};
+
+struct hash_in {
+ const char* buffer;
+ unsigned int length;
+};
+
+struct hash_in hash_inputs[]
+ = { { "", 0 },
+ { "a", 1 },
+ { "abc", 3 },
+ { "abcdefghijklmnopqrstuvwxyz", 26 },
+ { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62 },
+ { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56 },
+ { "message digest", 14 } };
+
+struct hash_out {
+ uint32_t crc32_out;
+ const char* sha1_out;
+ const char* sha256_out;
+ const char* sha512_out;
+ const char* rmd160_out;
+ const char* wp512_out;
+};
+
+struct hash_out hash_outputs[] = {
+ {
+ 0x00000000,
+ "\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18\x90\xaf\xd8\x07\x09",
+ "\xe3\xb0\xc4\x42\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99\x6f\xb9\x24\x27\xae\x41\xe4\x64\x9b\x93\x4c\xa4\x95\x99\x1b\x78\x52\xb8\x55",
+ "\xcf\x83\xe1\x35\x7e\xef\xb8\xbd\xf1\x54\x28\x50\xd6\x6d\x80\x07\xd6\x20\xe4\x05\x0b\x57\x15\xdc\x83\xf4\xa9\x21\xd3\x6c\xe9\xce\x47\xd0\xd1\x3c\x5d\x85\xf2\xb0\xff\x83\x18\xd2\x87\x7e\xec\x2f\x63\xb9\x31\xbd\x47\x41\x7a\x81\xa5\x38\x32\x7a\xf9\x27\xda\x3e",
+ "\x9c\x11\x85\xa5\xc5\xe9\xfc\x54\x61\x28\x08\x97\x7e\xe8\xf5\x48\xb2\x25\x8d\x31",
+ "\x19\xfa\x61\xd7\x55\x22\xa4\x66\x9b\x44\xe3\x9c\x1d\x2e\x17\x26\xc5\x30\x23\x21\x30\xd4\x07\xf8\x9a\xfe\xe0\x96\x49\x97\xf7\xa7\x3e\x83\xbe\x69\x8b\x28\x8f\xeb\xcf\x88\xe3\xe0\x3c\x4f\x07\x57\xea\x89\x64\xe5\x9b\x63\xd9\x37\x08\xb1\x38\xcc\x42\xa6\x6e\xb3"
+ },
+ {
+ 0xe8b7be43,
+ "\x86\xf7\xe4\x37\xfa\xa5\xa7\xfc\xe1\x5d\x1d\xdc\xb9\xea\xea\xea\x37\x76\x67\xb8",
+ "\xca\x97\x81\x12\xca\x1b\xbd\xca\xfa\xc2\x31\xb3\x9a\x23\xdc\x4d\xa7\x86\xef\xf8\x14\x7c\x4e\x72\xb9\x80\x77\x85\xaf\xee\x48\xbb",
+ "\x1f\x40\xfc\x92\xda\x24\x16\x94\x75\x09\x79\xee\x6c\xf5\x82\xf2\xd5\xd7\xd2\x8e\x18\x33\x5d\xe0\x5a\xbc\x54\xd0\x56\x0e\x0f\x53\x02\x86\x0c\x65\x2b\xf0\x8d\x56\x02\x52\xaa\x5e\x74\x21\x05\x46\xf3\x69\xfb\xbb\xce\x8c\x12\xcf\xc7\x95\x7b\x26\x52\xfe\x9a\x75",
+ "\x0b\xdc\x9d\x2d\x25\x6b\x3e\xe9\xda\xae\x34\x7b\xe6\xf4\xdc\x83\x5a\x46\x7f\xfe",
+ "\x8a\xca\x26\x02\x79\x2a\xec\x6f\x11\xa6\x72\x06\x53\x1f\xb7\xd7\xf0\xdf\xf5\x94\x13\x14\x5e\x69\x73\xc4\x50\x01\xd0\x08\x7b\x42\xd1\x1b\xc6\x45\x41\x3a\xef\xf6\x3a\x42\x39\x1a\x39\x14\x5a\x59\x1a\x92\x20\x0d\x56\x01\x95\xe5\x3b\x47\x85\x84\xfd\xae\x23\x1a"
+ },
+ {
+ 0x352441c2,
+ "\xa9\x99\x3e\x36\x47\x06\x81\x6a\xba\x3e\x25\x71\x78\x50\xc2\x6c\x9c\xd0\xd8\x9d",
+ "\xba\x78\x16\xbf\x8f\x01\xcf\xea\x41\x41\x40\xde\x5d\xae\x22\x23\xb0\x03\x61\xa3\x96\x17\x7a\x9c\xb4\x10\xff\x61\xf2\x00\x15\xad",
+ "\xdd\xaf\x35\xa1\x93\x61\x7a\xba\xcc\x41\x73\x49\xae\x20\x41\x31\x12\xe6\xfa\x4e\x89\xa9\x7e\xa2\x0a\x9e\xee\xe6\x4b\x55\xd3\x9a\x21\x92\x99\x2a\x27\x4f\xc1\xa8\x36\xba\x3c\x23\xa3\xfe\xeb\xbd\x45\x4d\x44\x23\x64\x3c\xe8\x0e\x2a\x9a\xc9\x4f\xa5\x4c\xa4\x9f",
+ "\x8e\xb2\x08\xf7\xe0\x5d\x98\x7a\x9b\x04\x4a\x8e\x98\xc6\xb0\x87\xf1\x5a\x0b\xfc",
+ "\x4e\x24\x48\xa4\xc6\xf4\x86\xbb\x16\xb6\x56\x2c\x73\xb4\x02\x0b\xf3\x04\x3e\x3a\x73\x1b\xce\x72\x1a\xe1\xb3\x03\xd9\x7e\x6d\x4c\x71\x81\xee\xbd\xb6\xc5\x7e\x27\x7d\x0e\x34\x95\x71\x14\xcb\xd6\xc7\x97\xfc\x9d\x95\xd8\xb5\x82\xd2\x25\x29\x20\x76\xd4\xee\xf5"
+ },
+ {
+ 0x4c2750bd,
+ "\x32\xd1\x0c\x7b\x8c\xf9\x65\x70\xca\x04\xce\x37\xf2\xa1\x9d\x84\x24\x0d\x3a\x89",
+ "\x71\xc4\x80\xdf\x93\xd6\xae\x2f\x1e\xfa\xd1\x44\x7c\x66\xc9\x52\x5e\x31\x62\x18\xcf\x51\xfc\x8d\x9e\xd8\x32\xf2\xda\xf1\x8b\x73",
+ "\x4d\xbf\xf8\x6c\xc2\xca\x1b\xae\x1e\x16\x46\x8a\x05\xcb\x98\x81\xc9\x7f\x17\x53\xbc\xe3\x61\x90\x34\x89\x8f\xaa\x1a\xab\xe4\x29\x95\x5a\x1b\xf8\xec\x48\x3d\x74\x21\xfe\x3c\x16\x46\x61\x3a\x59\xed\x54\x41\xfb\x0f\x32\x13\x89\xf7\x7f\x48\xa8\x79\xc7\xb1\xf1",
+ "\xf7\x1c\x27\x10\x9c\x69\x2c\x1b\x56\xbb\xdc\xeb\x5b\x9d\x28\x65\xb3\x70\x8d\xbc",
+ "\xf1\xd7\x54\x66\x26\x36\xff\xe9\x2c\x82\xeb\xb9\x21\x2a\x48\x4a\x8d\x38\x63\x1e\xad\x42\x38\xf5\x44\x2e\xe1\x3b\x80\x54\xe4\x1b\x08\xbf\x2a\x92\x51\xc3\x0b\x6a\x0b\x8a\xae\x86\x17\x7a\xb4\xa6\xf6\x8f\x67\x3e\x72\x07\x86\x5d\x5d\x98\x19\xa3\xdb\xa4\xeb\x3b"
+ },
+ {
+ 0x1fc2e6d2,
+ "\x76\x1c\x45\x7b\xf7\x3b\x14\xd2\x7e\x9e\x92\x65\xc4\x6f\x4b\x4d\xda\x11\xf9\x40",
+ "\xdb\x4b\xfc\xbd\x4d\xa0\xcd\x85\xa6\x0c\x3c\x37\xd3\xfb\xd8\x80\x5c\x77\xf1\x5f\xc6\xb1\xfd\xfe\x61\x4e\xe0\xa7\xc8\xfd\xb4\xc0",
+ "\x1e\x07\xbe\x23\xc2\x6a\x86\xea\x37\xea\x81\x0c\x8e\xc7\x80\x93\x52\x51\x5a\x97\x0e\x92\x53\xc2\x6f\x53\x6c\xfc\x7a\x99\x96\xc4\x5c\x83\x70\x58\x3e\x0a\x78\xfa\x4a\x90\x04\x1d\x71\xa4\xce\xab\x74\x23\xf1\x9c\x71\xb9\xd5\xa3\xe0\x12\x49\xf0\xbe\xbd\x58\x94",
+ "\xb0\xe2\x0b\x6e\x31\x16\x64\x02\x86\xed\x3a\x87\xa5\x71\x30\x79\xb2\x1f\x51\x89",
+ "\xdc\x37\xe0\x08\xcf\x9e\xe6\x9b\xf1\x1f\x00\xed\x9a\xba\x26\x90\x1d\xd7\xc2\x8c\xde\xc0\x66\xcc\x6a\xf4\x2e\x40\xf8\x2f\x3a\x1e\x08\xeb\xa2\x66\x29\x12\x9d\x8f\xb7\xcb\x57\x21\x1b\x92\x81\xa6\x55\x17\xcc\x87\x9d\x7b\x96\x21\x42\xc6\x5f\x5a\x7a\xf0\x14\x67"
+ },
+ {
+ 0x171a3f5f,
+ "\x84\x98\x3e\x44\x1c\x3b\xd2\x6e\xba\xae\x4a\xa1\xf9\x51\x29\xe5\xe5\x46\x70\xf1",
+ "\x24\x8d\x6a\x61\xd2\x06\x38\xb8\xe5\xc0\x26\x93\x0c\x3e\x60\x39\xa3\x3c\xe4\x59\x64\xff\x21\x67\xf6\xec\xed\xd4\x19\xdb\x06\xc1",
+ "\x20\x4a\x8f\xc6\xdd\xa8\x2f\x0a\x0c\xed\x7b\xeb\x8e\x08\xa4\x16\x57\xc1\x6e\xf4\x68\xb2\x28\xa8\x27\x9b\xe3\x31\xa7\x03\xc3\x35\x96\xfd\x15\xc1\x3b\x1b\x07\xf9\xaa\x1d\x3b\xea\x57\x78\x9c\xa0\x31\xad\x85\xc7\xa7\x1d\xd7\x03\x54\xec\x63\x12\x38\xca\x34\x45",
+ "\x12\xa0\x53\x38\x4a\x9c\x0c\x88\xe4\x05\xa0\x6c\x27\xdc\xf4\x9a\xda\x62\xeb\x2b",
+ "\x52\x6b\x23\x94\xd8\x56\x83\xe2\x4b\x29\xac\xd0\xfd\x37\xf7\xd5\x02\x7f\x61\x36\x6a\x14\x07\x26\x2d\xc2\xa6\xa3\x45\xd9\xe2\x40\xc0\x17\xc1\x83\x3d\xb1\xe6\xdb\x6a\x46\xbd\x44\x4b\x0c\x69\x52\x0c\x85\x6e\x7c\x6e\x9c\x36\x6d\x15\x0a\x7d\xa3\xae\xb1\x60\xd1"
+ },
+ {
+ 0x20159d7f,
+ "\xc1\x22\x52\xce\xda\x8b\xe8\x99\x4d\x5f\xa0\x29\x0a\x47\x23\x1c\x1d\x16\xaa\xe3",
+ "\xf7\x84\x6f\x55\xcf\x23\xe1\x4e\xeb\xea\xb5\xb4\xe1\x55\x0c\xad\x5b\x50\x9e\x33\x48\xfb\xc4\xef\xa3\xa1\x41\x3d\x39\x3c\xb6\x50",
+ "\x10\x7d\xbf\x38\x9d\x9e\x9f\x71\xa3\xa9\x5f\x6c\x05\x5b\x92\x51\xbc\x52\x68\xc2\xbe\x16\xd6\xc1\x34\x92\xea\x45\xb0\x19\x9f\x33\x09\xe1\x64\x55\xab\x1e\x96\x11\x8e\x8a\x90\x5d\x55\x97\xb7\x20\x38\xdd\xb3\x72\xa8\x98\x26\x04\x6d\xe6\x66\x87\xbb\x42\x0e\x7c",
+ "\x5d\x06\x89\xef\x49\xd2\xfa\xe5\x72\xb8\x81\xb1\x23\xa8\x5f\xfa\x21\x59\x5f\x36",
+ "\x37\x8c\x84\xa4\x12\x6e\x2d\xc6\xe5\x6d\xcc\x74\x58\x37\x7a\xac\x83\x8d\x00\x03\x22\x30\xf5\x3c\xe1\xf5\x70\x0c\x0f\xfb\x4d\x3b\x84\x21\x55\x76\x59\xef\x55\xc1\x06\xb4\xb5\x2a\xc5\xa4\xaa\xa6\x92\xed\x92\x00\x52\x83\x8f\x33\x62\xe8\x6d\xbd\x37\xa8\x90\x3e"
+ }
+};
+
+/*
+ * HMAC tests
+ */
+// RFC 4231 - HMAC test vectors for SHA-256, SHA-512
+// RFC 2202 - HMAC test vectors for SHA-1
+
+struct hmac_test_vector {
+ const char *key;
+ unsigned int key_length;
+ const char *data;
+ unsigned int data_length;
+ const char *hmac_sha_1;
+ const char *hmac_sha_256;
+ const char *hmac_sha_512;
+};
+
+struct hmac_test_vector hmac_test_vectors[] = {
+ {
+ "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", 20,
+ "\x48\x69\x20\x54\x68\x65\x72\x65", 8, // "Hi There"
+ "\xb6\x17\x31\x86\x55\x05\x72\x64\xe2\x8b\xc0\xb6\xfb\x37\x8c\x8e\xf1\x46\xbe\x00",
+ "\xb0\x34\x4c\x61\xd8\xdb\x38\x53\x5c\xa8\xaf\xce\xaf\x0b\xf1\x2b\x88\x1d\xc2\x00\xc9\x83\x3d\xa7\x26\xe9\x37\x6c\x2e\x32\xcf\xf7",
+ "\x87\xaa\x7c\xde\xa5\xef\x61\x9d\x4f\xf0\xb4\x24\x1a\x1d\x6c\xb0\x23\x79\xf4\xe2\xce\x4e\xc2\x78\x7a\xd0\xb3\x05\x45\xe1\x7c\xde\xda\xa8\x33\xb7\xd6\xb8\xa7\x02\x03\x8b\x27\x4e\xae\xa3\xf4\xe4\xbe\x9d\x91\x4e\xeb\x61\xf1\x70\x2e\x69\x6c\x20\x3a\x12\x68\x54"
+ },
+ {
+ "\x4a\x65\x66\x65", 4, // "Jefe"
+ "\x77\x68\x61\x74\x20\x64\x6f\x20\x79\x61\x20\x77\x61\x6e\x74\x20\x66\x6f\x72\x20\x6e\x6f\x74\x68\x69\x6e\x67\x3f", 28, // "what do ya want for nothing?"
+ "\xef\xfc\xdf\x6a\xe5\xeb\x2f\xa2\xd2\x74\x16\xd5\xf1\x84\xdf\x9c\x25\x9a\x7c\x79",
+ "\x5b\xdc\xc1\x46\xbf\x60\x75\x4e\x6a\x04\x24\x26\x08\x95\x75\xc7\x5a\x00\x3f\x08\x9d\x27\x39\x83\x9d\xec\x58\xb9\x64\xec\x38\x43",
+ "\x16\x4b\x7a\x7b\xfc\xf8\x19\xe2\xe3\x95\xfb\xe7\x3b\x56\xe0\xa3\x87\xbd\x64\x22\x2e\x83\x1f\xd6\x10\x27\x0c\xd7\xea\x25\x05\x54\x97\x58\xbf\x75\xc0\x5a\x99\x4a\x6d\x03\x4f\x65\xf8\xf0\xe6\xfd\xca\xea\xb1\xa3\x4d\x4a\x6b\x4b\x63\x6e\x07\x0a\x38\xbc\xe7\x37"
+ },
+ {
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 20,
+ "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", 50,
+ "\x12\x5d\x73\x42\xb9\xac\x11\xcd\x91\xa3\x9a\xf4\x8a\xa1\x7b\x4f\x63\xf1\x75\xd3",
+ "\x77\x3e\xa9\x1e\x36\x80\x0e\x46\x85\x4d\xb8\xeb\xd0\x91\x81\xa7\x29\x59\x09\x8b\x3e\xf8\xc1\x22\xd9\x63\x55\x14\xce\xd5\x65\xfe",
+ "\xfa\x73\xb0\x08\x9d\x56\xa2\x84\xef\xb0\xf0\x75\x6c\x89\x0b\xe9\xb1\xb5\xdb\xdd\x8e\xe8\x1a\x36\x55\xf8\x3e\x33\xb2\x27\x9d\x39\xbf\x3e\x84\x82\x79\xa7\x22\xc8\x06\xb4\x85\xa4\x7e\x67\xc8\x07\xb9\x46\xa3\x37\xbe\xe8\x94\x26\x74\x27\x88\x59\xe1\x32\x92\xfb"
+ },
+ {
+ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", 25,
+ "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", 50,
+ "\x4c\x90\x07\xf4\x02\x62\x50\xc6\xbc\x84\x14\xf9\xbf\x50\xc8\x6c\x2d\x72\x35\xda",
+ "\x82\x55\x8a\x38\x9a\x44\x3c\x0e\xa4\xcc\x81\x98\x99\xf2\x08\x3a\x85\xf0\xfa\xa3\xe5\x78\xf8\x07\x7a\x2e\x3f\xf4\x67\x29\x66\x5b",
+ "\xb0\xba\x46\x56\x37\x45\x8c\x69\x90\xe5\xa8\xc5\xf6\x1d\x4a\xf7\xe5\x76\xd9\x7f\xf9\x4b\x87\x2d\xe7\x6f\x80\x50\x36\x1e\xe3\xdb\xa9\x1c\xa5\xc1\x1a\xa2\x5e\xb4\xd6\x79\x27\x5c\xc5\x78\x80\x63\xa5\xf1\x97\x41\x12\x0c\x4f\x2d\xe2\xad\xeb\xeb\x10\xa2\x98\xdd"
+ },
+ {
+ // Long key
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 131,
+ "\x54\x65\x73\x74\x20\x55\x73\x69\x6e\x67\x20\x4c\x61\x72\x67\x65\x72\x20\x54\x68\x61\x6e\x20\x42\x6c\x6f\x63\x6b\x2d\x53\x69\x7a\x65\x20\x4b\x65\x79\x20\x2d\x20\x48\x61\x73\x68\x20\x4b\x65\x79\x20\x46\x69\x72\x73\x74", 54, // "Test Using Larger Than Block-Size Key - Hash Key First"
+ "\x90\xd0\xda\xce\x1c\x1b\xdc\x95\x73\x39\x30\x78\x03\x16\x03\x35\xbd\xe6\xdf\x2b",
+ "\x60\xe4\x31\x59\x1e\xe0\xb6\x7f\x0d\x8a\x26\xaa\xcb\xf5\xb7\x7f\x8e\x0b\xc6\x21\x37\x28\xc5\x14\x05\x46\x04\x0f\x0e\xe3\x7f\x54",
+ "\x80\xb2\x42\x63\xc7\xc1\xa3\xeb\xb7\x14\x93\xc1\xdd\x7b\xe8\xb4\x9b\x46\xd1\xf4\x1b\x4a\xee\xc1\x12\x1b\x01\x37\x83\xf8\xf3\x52\x6b\x56\xd0\x37\xe0\x5f\x25\x98\xbd\x0f\xd2\x21\x5d\x6a\x1e\x52\x95\xe6\x4f\x73\xf6\x3f\x0a\xec\x8b\x91\x5a\x98\x5d\x78\x65\x98"
+ },
+ {
+ // Long key and long data
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 131,
+ "\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74\x20\x75\x73\x69\x6e\x67\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x6b\x65\x79\x20\x61\x6e\x64\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x64\x61\x74\x61\x2e\x20\x54\x68\x65\x20\x6b\x65\x79\x20\x6e\x65\x65\x64\x73\x20\x74\x6f\x20\x62\x65\x20\x68\x61\x73\x68\x65\x64\x20\x62\x65\x66\x6f\x72\x65\x20\x62\x65\x69\x6e\x67\x20\x75\x73\x65\x64\x20\x62\x79\x20\x74\x68\x65\x20\x48\x4d\x41\x43\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2e", 152,
+ "\x21\x7e\x44\xbb\x08\xb6\xe0\x6a\x2d\x6c\x30\xf3\xcb\x9f\x53\x7f\x97\xc6\x33\x56",
+ "\x9b\x09\xff\xa7\x1b\x94\x2f\xcb\x27\x63\x5f\xbc\xd5\xb0\xe9\x44\xbf\xdc\x63\x64\x4f\x07\x13\x93\x8a\x7f\x51\x53\x5c\x3a\x35\xe2",
+ "\xe3\x7b\x6a\x77\x5d\xc8\x7d\xba\xa4\xdf\xa9\xf9\x6e\x5e\x3f\xfd\xde\xbd\x71\xf8\x86\x72\x89\x86\x5d\xf5\xa3\x2d\x20\xcd\xc9\x44\xb6\x02\x2c\xac\x3c\x49\x82\xb1\x0d\x5e\xeb\x55\xc3\xe4\xde\x15\x13\x46\x76\xfb\x6d\xe0\x44\x60\x65\xc9\x74\x40\xfa\x8c\x6a\x58"
+ }
+};
+
+static int pbkdf_test_vectors(void)
+{
+ char result[256];
+ unsigned int i;
+ struct kdf_test_vector *vec;
+
+ for (i = 0; i < (sizeof(kdf_test_vectors) / sizeof(*kdf_test_vectors)); i++) {
+ vec = &kdf_test_vectors[i];
+ printf("PBKDF vector %02d %s ", i, vec->type);
+ if (vec->hash && crypt_hash_size(vec->hash) < 0) {
+ printf("[%s N/A]\n", vec->hash);
+ continue;
+ }
+ if (crypt_pbkdf(vec->type, vec->hash,
+ vec->password, vec->password_length,
+ vec->salt, vec->salt_length,
+ result, vec->output_length,
+ vec->iterations, vec->memory, vec->parallelism)) {
+ printf("crypto backend [FAILED].\n");
+ return -EINVAL;
+ }
+ if (memcmp(result, vec->output, vec->output_length)) {
+ printf("expected output [FAILED].\n");
+ printhex(" got", result, vec->output_length);
+ printhex("want", vec->output, vec->output_length);
+ return -EINVAL;
+ }
+ printf("[OK]\n");
+ memset(result, 0, sizeof(result));
+ }
+ return 0;
+}
+
+
+static const char* get_vec(struct hash_out* out, int i)
+{
+ switch (i) {
+ case 0:
+ return out->sha1_out;
+ case 1:
+ return out->sha256_out;
+ case 2:
+ return out->sha512_out;
+ case 3:
+ return out->rmd160_out;
+ case 4:
+ return out->wp512_out;
+ }
+
+ return NULL;
+}
+
+static int hash_test(void)
+{
+ uint32_t crc32;
+ unsigned int i, j;
+ int r, hash_length;
+ struct hash_in* in_vec;
+ struct hash_out* out_vec;
+ struct hash_alg* hash;
+ struct crypt_hash *h;
+ char result[64];
+
+ for (i = 0; i < (sizeof(hash_inputs) / sizeof(*hash_inputs)); i++) {
+ in_vec = &hash_inputs[i];
+ out_vec = &hash_outputs[i];
+
+ // CRC32 vector test
+ printf("Hash vector %02d: [CRC32]", i);
+ crc32 = crypt_crc32(~0, (const unsigned char*)in_vec->buffer, in_vec->length) ^ ~0;
+ if (crc32 != out_vec->crc32_out) {
+ printf("expected output [FAILED].\n");
+ printf(" got: %x\n", crc32);
+ printf("want: %x\n", out_vec->crc32_out);
+ return -EINVAL;
+ }
+
+ // Other hashes test
+ for (j = 0; j < (sizeof(hash_algs) / sizeof(*hash_algs) - 1); j++) {
+ hash = &hash_algs[j];
+
+ hash_length = crypt_hash_size(hash->name);
+ if (hash_length != hash->length) {
+ if (hash_length < 0) {
+ printf("[%s N/A]", hash->name);
+ continue;
+ }
+ return -EINVAL;
+ }
+
+ printf("[%s]", hash->name);
+ if (crypt_hash_init(&h, hash->name))
+ return -EINVAL;
+
+ r = crypt_hash_write(h, in_vec->buffer, in_vec->length);
+
+ if (!r)
+ r = crypt_hash_final(h, result, hash->length);
+
+ crypt_hash_destroy(h);
+
+ if (r)
+ return r;
+
+ if (memcmp(result, get_vec(out_vec, j), hash->length)) {
+ printf("expected output [FAILED].\n");
+ printhex(" got", result, hash->length);
+ printhex("want", get_vec(out_vec, j), hash->length);
+ return -EINVAL;
+ }
+ }
+
+ printf("\n");
+ }
+
+ return 0;
+}
+
+static const char* get_hmac_res(struct hmac_test_vector* out, int i)
+{
+ switch (i) {
+ case 0:
+ return out->hmac_sha_1;
+ case 1:
+ return out->hmac_sha_256;
+ case 2:
+ return out->hmac_sha_512;
+ }
+
+ return NULL;
+}
+
+
+static int hmac_test(void)
+{
+ struct crypt_hmac *hmac;
+ struct hmac_test_vector *vector;
+ struct crypt_hash *h;
+ unsigned int i, j;
+ int hmac_length, r;
+
+ char result[64];
+ char key[MAX_BLOCK_SIZE];
+
+ for (i = 0; i < (sizeof(hmac_test_vectors) / sizeof(*hmac_test_vectors)); i++) {
+ vector = &hmac_test_vectors[i];
+ printf("HMAC vector %02d: ", i);
+
+ for(j = 0; j < 3; j++) {
+ struct hash_alg* hash = &hash_algs[j];
+ hmac_length = crypt_hmac_size(hash->name);
+ if (hmac_length != hash->length) {
+ if (hmac_length < 0) {
+ printf("[%s N/A]", hash->name);
+ continue;
+ }
+ return -EINVAL;
+ }
+ printf("[%s]", hash->name);
+
+ int key_length = vector->key_length;
+
+ // hash key first if key size is greater than max block size
+ if (key_length > MAX_BLOCK_SIZE) {
+ if (crypt_hash_init(&h, hash->name))
+ return -EINVAL;
+
+ r = crypt_hash_write(h, vector->key, vector->key_length);
+
+ if (!r)
+ r = crypt_hash_final(h, key, hash->length);
+
+ crypt_hash_destroy(h);
+
+ if (r)
+ return r;
+
+ key_length = hash->length;
+ } else {
+ memcpy(key, vector->key, vector->key_length);
+ }
+
+ if (crypt_hmac_init(&hmac, hash->name, key, key_length))
+ return -EINVAL;
+
+ r = crypt_hmac_write(hmac, vector->data, vector->data_length);
+
+ if (!r)
+ r = crypt_hmac_final(hmac, result, hmac_length);
+
+ crypt_hmac_destroy(hmac);
+
+ if (r)
+ return r;
+
+ if (memcmp(result, get_hmac_res(vector, j), hash->length)) {
+ printf("expected output [FAILED].\n");
+ printhex(" got", result, hash->length);
+ printhex("want", get_hmac_res(vector, j), hash->length);
+ return -EINVAL;
+ }
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ if (crypt_backend_init(NULL)) {
+ printf("Crypto backend init error.\n");
+ exit(EXIT_FAILURE);
+ }
+ printf("Test vectors using %s crypto backend.\n", crypt_backend_version());
+
+ if (pbkdf_test_vectors())
+ exit(EXIT_FAILURE);
+
+ if (hash_test())
+ exit(EXIT_FAILURE);
+
+ if (hmac_test())
+ exit(EXIT_FAILURE);
+
+ crypt_backend_destroy();
+ exit(EXIT_SUCCESS);
+}
diff --git a/tests/cryptsetup-valg-supps b/tests/cryptsetup-valg-supps
new file mode 100644
index 0000000..b2530f7
--- /dev/null
+++ b/tests/cryptsetup-valg-supps
@@ -0,0 +1,36 @@
+# Suppresion file for valgrind
+
+# known problem in libgcrypt
+{
+ leak_in_libgcrypt_00
+ Memcheck:Leak
+ fun:malloc
+ obj:/lib64/libgcrypt.so*
+ ...
+ obj:/lib64/libgcrypt.so*
+ fun:crypt_backend_init
+ fun:init_crypto
+ ...
+}
+# following leaks/errors are addressed to libpopt...
+{
+ popt_read_error
+ Memcheck:Addr4
+ obj:/lib*/libpopt.so*
+ fun:poptGetNextOpt
+ fun:main
+}
+{
+ popt_leak_poptGetNextOpt_00
+ Memcheck:Leak
+ fun:realloc
+ fun:poptGetNextOpt
+ fun:main
+}
+{
+ popt_leak_poptGetNextOpt_01
+ Memcheck:Leak
+ fun:malloc
+ fun:poptGetNextOpt
+ fun:main
+}
diff --git a/tests/device-test b/tests/device-test
new file mode 100755
index 0000000..ab5db20
--- /dev/null
+++ b/tests/device-test
@@ -0,0 +1,257 @@
+#!/bin/bash
+
+CRYPTSETUP="../cryptsetup"
+MNT_DIR="./mnt_luks"
+DEV_NAME="dummy"
+DEV_NAME2="ymmud"
+PWD1="93R4P4pIqAH8"
+PWD2="mymJeD8ivEhE"
+FAST_PBKDF_OPT="--pbkdf pbkdf2 --pbkdf-force-iterations 1000"
+SKIP_COUNT=0
+
+cleanup() {
+ [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove $DEV_NAME
+ udevadm settle >/dev/null 2>&1
+ if [ -d "$MNT_DIR" ] ; then
+ umount -f $MNT_DIR 2>/dev/null
+ rmdir $MNT_DIR 2>/dev/null
+ fi
+ sleep 2
+}
+
+fail()
+{
+ if [ -n "$1" ] ; then
+ echo "FAIL $1 at line $(caller)"
+ else
+ echo "FAIL at line $(caller)"
+ fi
+ cleanup
+ exit 100
+}
+
+skip()
+{
+ echo "TEST SKIPPED: $1"
+ cleanup
+ exit 77
+}
+
+function 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
+ DM_KEYRING=1
+ return
+ }
+
+ [ $VER_MIN -lt 14 ] && return
+ DM_PERF_CPU=1
+ if [ $VER_MIN -ge 17 -o \( $VER_MIN -eq 14 -a $VER_PTC -ge 5 \) ]; then
+ DM_SECTOR_SIZE=1
+ fi
+ if [ $VER_MIN -gt 18 -o \( $VER_MIN -eq 18 -a $VER_PTC -ge 1 \) ]; then
+ DM_KEYRING=1
+ fi
+}
+
+function dm_crypt_keyring_support()
+{
+ 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.)
+
+ # run the test with dm-crypt v1.15.0+ on purpose
+ # the fix is in dm-crypt v1.18.1+
+ [ $VER_MAJ -gt 1 ] && return 0
+ [ $VER_MAJ -lt 1 ] && return 1
+ [ $VER_MIN -ge 15 ]
+}
+
+format() # format
+{
+ dd if=/dev/zero of=$DEV bs=1M count=32 >/dev/null 2>&1
+
+ echo $PWD1 | $CRYPTSETUP luksFormat --type $1 $DEV -q $FAST_PBKDF_OPT -c aes-cbc-essiv:sha256
+ [ $? -ne 0 ] && fail "Format failed."
+
+ # test some operation, just in case
+ echo -e "$PWD1\n$PWD2" | $CRYPTSETUP luksAddKey $DEV -i1 --key-slot 1
+ [ $? -ne 0 ] && fail "Keyslot add failed."
+
+ $CRYPTSETUP -q luksKillSlot $DEV 1
+ [ $? -ne 0 ] && fail "Keyslot removal failed."
+}
+
+check_sector_size() # $1 expected sector size
+{
+ $CRYPTSETUP status $DEV_NAME | grep "sector size" | grep -q $1 || fail
+ if [ $S -gt 512 ]; then
+ dmsetup table $DEV_NAME | grep -q "sector_size:$1" || fail
+ fi
+}
+
+if [ $(id -u) != 0 ]; then
+ skip "You must be root to run this test, test skipped."
+fi
+
+dm_crypt_features
+
+[ ! -d $MNT_DIR ] && mkdir $MNT_DIR
+
+echo "[1] Using tmpfs for image"
+DEV="$MNT_DIR/test.img"
+mount -t tmpfs none $MNT_DIR || skip "Mounting tmpfs not available."
+format luks1
+
+echo "[2] Kernel dmcrypt performance options"
+if [ -z "$DM_PERF_CPU" ]; then
+ echo "TEST SKIPPED: dmcrypt options not available"
+ SKIP_COUNT=$((SKIP_COUNT+1))
+else
+ # plain
+ echo -e "$PWD1" | $CRYPTSETUP open -q --type plain --hash sha256 $DEV $DEV_NAME --perf-same_cpu_crypt --perf-submit_from_crypt_cpus || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q submit_from_crypt_cpus || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+ echo -e "$PWD1" | $CRYPTSETUP open -q --type plain --hash sha256 $DEV $DEV_NAME --perf-same_cpu_crypt --allow-discards || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q discards || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+ echo -e "$PWD1" | $CRYPTSETUP open -q --type plain --hash sha256 $DEV $DEV_NAME || fail
+ echo -e "$PWD1" | $CRYPTSETUP refresh --hash sha256 -q $DEV_NAME --perf-same_cpu_crypt --allow-discards || fail
+ # Hash affects volume key for plain device. Check we can detect it
+ echo -e "$PWD1" | $CRYPTSETUP refresh -q $DEV_NAME --hash sha512 --perf-same_cpu_crypt --allow-discards 2>/dev/null && fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q discards || fail
+ echo -e "$PWD1" | $CRYPTSETUP refresh --hash sha256 -q $DEV_NAME --allow-discards || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q discards || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt && fail
+ echo -e "$PWD1" | $CRYPTSETUP refresh --hash sha256 -q $DEV_NAME || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q discards && fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt && fail
+ echo -e "$PWD1" | $CRYPTSETUP refresh --hash sha256 $DEV $DEV_NAME2 2>/dev/null && fail
+ $CRYPTSETUP close $DEV_NAME || fail
+ # LUKS
+ echo -e "$PWD1" | $CRYPTSETUP open --type luks1 $DEV $DEV_NAME --perf-same_cpu_crypt --perf-submit_from_crypt_cpus || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q submit_from_crypt_cpus || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+ echo -e "$PWD1" | $CRYPTSETUP open --type luks1 $DEV $DEV_NAME --perf-same_cpu_crypt --allow-discards || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q discards || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+ echo -e "$PWD1" | $CRYPTSETUP open $DEV $DEV_NAME || fail
+ echo -e "$PWD1" | $CRYPTSETUP refresh $DEV_NAME --allow-discards || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q discards || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt && fail
+ echo -e "$PWD1" | $CRYPTSETUP refresh $DEV_NAME --allow-discards --perf-same_cpu_crypt || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q discards || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt || fail
+ echo -e "$PWD1" | $CRYPTSETUP refresh $DEV_NAME || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q discards && fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt && fail
+ echo -e "$PWD1" | $CRYPTSETUP refresh $DEV $DEV_NAME2 2>/dev/null && fail
+ $CRYPTSETUP close $DEV_NAME || fail
+
+ format luks2
+ echo -e "$PWD1" | $CRYPTSETUP open $DEV $DEV_NAME --perf-same_cpu_crypt --perf-submit_from_crypt_cpus --persistent || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q submit_from_crypt_cpus || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+ # Stored in metadata
+ echo -e "$PWD1" | $CRYPTSETUP open $DEV $DEV_NAME || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q submit_from_crypt_cpus || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+ echo -e "$PWD1" | $CRYPTSETUP open $DEV $DEV_NAME --perf-same_cpu_crypt --allow-discards --persistent || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q discards || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+ echo -e "$PWD1" | $CRYPTSETUP open $DEV $DEV_NAME || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q discards || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+
+ echo -e "$PWD1" | $CRYPTSETUP open $DEV $DEV_NAME --persistent || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt && fail
+ $CRYPTSETUP status $DEV_NAME | grep -q discards && fail
+ echo -e "$PWD1" | $CRYPTSETUP refresh $DEV $DEV_NAME --perf-same_cpu_crypt --perf-submit_from_crypt_cpus --persistent || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q submit_from_crypt_cpus || fail
+ echo -e "$PWD1" | $CRYPTSETUP refresh $DEV_NAME || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q submit_from_crypt_cpus || fail
+ echo -e "$PWD1" | $CRYPTSETUP refresh $DEV $DEV_NAME --perf-same_cpu_crypt --allow-discards --persistent || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q discards || fail
+ echo -e "$PWD1" | $CRYPTSETUP refresh $DEV_NAME || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q discards || fail
+ echo -e "$PWD1" | $CRYPTSETUP refresh $DEV $DEV_NAME --perf-submit_from_crypt_cpus || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q discards || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q submit_from_crypt_cpus || fail
+ echo -e "$PWD1" | $CRYPTSETUP refresh $DEV $DEV_NAME || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q submit_from_crypt_cpus && fail
+ echo -e "$PWD1" | $CRYPTSETUP refresh $DEV $DEV_NAME --persistent || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q same_cpu_crypt && fail
+ $CRYPTSETUP status $DEV_NAME | grep -q discards && fail
+ $CRYPTSETUP status $DEV_NAME | grep -q submit_from_crypt_cpus && fail
+ echo -e "$PWD1" | $CRYPTSETUP refresh $DEV $DEV_NAME --disable-keyring || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q keyring && fail
+ if [ -n "$DM_KEYRING" ]; then
+ echo -e "$PWD1" | $CRYPTSETUP refresh $DEV $DEV_NAME || fail
+ $CRYPTSETUP status $DEV_NAME | grep -q keyring || fail
+ fi
+ echo -e "$PWD1" | $CRYPTSETUP refresh $DEV $DEV_NAME2 2>/dev/null && fail
+ $CRYPTSETUP close $DEV_NAME || fail
+fi
+
+echo "[3] Kernel dmcrypt sector size options"
+echo -e "$PWD1" | $CRYPTSETUP open --type plain --hash sha256 $DEV $DEV_NAME --sector-size 4096 >/dev/null 2>&1
+ret=$?
+[ -z "$DM_SECTOR_SIZE" -a $ret -eq 0 ] && fail "cryptsetup activated device with --sector-size option on incompatible kernel!"
+if [ $ret -ne 0 ] ; then
+ SKIP_COUNT=$((SKIP_COUNT+1))
+ if [ $SKIP_COUNT -ge 2 ]; then
+ skip "dmcrypt sector-size option not available"
+ fi
+ echo "TEST SKIPPED: dmcrypt sector-size option not available"
+else
+ $CRYPTSETUP close $DEV_NAME || fail
+
+ echo -n "PLAIN sector size:"
+ echo -e "$PWD1" | $CRYPTSETUP open --type plain --hash sha256 $DEV $DEV_NAME --sector-size 1234 >/dev/null 2>&1 && fail
+ for S in 512 1024 2048 4096; do
+ echo -n "[$S]"
+ echo -e "$PWD1" | $CRYPTSETUP open -q --type plain --hash sha256 $DEV $DEV_NAME --sector-size $S || fail
+ check_sector_size $S
+ $CRYPTSETUP close $DEV_NAME || fail
+ done
+ echo
+
+ echo -n "LUKS2 sector size:"
+ echo -e "$PWD1" | $CRYPTSETUP luksFormat --type luks2 -$DEV --sector-size 1234 >/dev/null 2>&1 && fail
+ for S in 512 1024 2048 4096; do
+ echo -n "[$S]"
+ echo -e "$PWD1" | $CRYPTSETUP -q luksFormat --type luks2 --pbkdf pbkdf2 --pbkdf-force-iterations 1000 $DEV --sector-size $S || fail
+ echo -e "$PWD1" | $CRYPTSETUP open $DEV $DEV_NAME || fail
+ check_sector_size $S
+ $CRYPTSETUP close $DEV_NAME || fail
+ done
+ echo
+fi
+
+cleanup
diff --git a/tests/differ.c b/tests/differ.c
new file mode 100644
index 0000000..7a94bff
--- /dev/null
+++ b/tests/differ.c
@@ -0,0 +1,166 @@
+/*
+ * cryptsetup file differ check (rewritten Clemens' fileDiffer in Python)
+ *
+ * Copyright (C) 2010-2019 Red Hat, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+struct ffile {
+ char *name;
+ int fd;
+ unsigned char *addr;
+ size_t size;
+};
+
+enum df { OK , FAIL };
+
+static void print_diff(off_t from, int max,
+ const unsigned char *o,
+ const unsigned char *n)
+{
+ int i, len = max;
+
+ if (len > 16)
+ len = 16;
+
+ printf("OLD:");
+ for (i = 0; i < len; i++)
+ printf(" %02x", o[from + i]);
+ printf("%s\n ", max != len ? " ..." : "");
+ for (i = 0; i < len; i++)
+ printf(" %2c", o[from + i] > ' ' ? o[from + i]: '.');
+ printf("\nNEW:");
+ for (i = 0; i < len; i++)
+ printf(" %02x", n[from + i]);
+ printf("%s\n ", max != len ? " ..." : "");
+ for (i = 0; i < len; i++)
+ printf(" %2c", n[from + i] > ' ' ? n[from + i]: '.');
+ printf("\n");
+}
+
+/*
+ * Xfrom-to (e.g. R10-15)
+ * A - change allowed
+ * S - change required, semantic
+ * R - change required, random
+ * F - change forbidden
+ */
+static enum df check(const char *range, unsigned char *o, unsigned char *n)
+{
+ char strict;
+ unsigned long long from, to;
+ enum df ret;
+
+ if (sscanf(range, "%c%llu-%llu", &strict, &from, &to) != 3) {
+ printf("Unknown range format %s.\n", range);
+ return FAIL;
+ }
+
+ switch (toupper(strict)) {
+ case 'A':
+ ret = OK;
+ break;
+ case 'S':
+ ret = memcmp(&o[from], &n[from], to - from + 1) != 0 ? OK : FAIL;
+ break;
+ case 'R': /* FIXME - random test */
+ ret = memcmp(&o[from], &n[from], to - from + 1) != 0 ? OK : FAIL;
+ break;
+ case 'F':
+ ret = memcmp(&o[from], &n[from], to - from + 1) == 0 ? OK : FAIL;
+ break;
+ default:
+ ret = FAIL;
+ break;
+ }
+
+ if (ret == FAIL)
+ print_diff(from, to - from + 1, o, n);
+
+ return ret;
+}
+
+static int open_mmap(struct ffile *f)
+{
+ struct stat st;
+
+ f->fd = open(f->name, O_RDONLY);
+ if (f->fd == -1 || fstat(f->fd, &st) == -1)
+ return 0;
+
+ f->size = st.st_size;
+ f->addr = mmap(NULL, f->size, PROT_READ, MAP_PRIVATE, f->fd, 0);
+
+ return (f->addr == MAP_FAILED) ? 0 : 1;
+}
+
+static void close_mmap(struct ffile *f)
+{
+ if (f->addr != MAP_FAILED && !munmap(f->addr, f->size))
+ f->addr = MAP_FAILED;
+
+ if (f->fd != -1 && !close(f->fd))
+ f->fd = -1;
+}
+
+int main(int argc, char *argv[])
+{
+ int i, r = 1;
+ struct ffile file_old = {
+ .fd = -1,
+ .addr = MAP_FAILED,
+ };
+ struct ffile file_new = {
+ .fd = -1,
+ .addr = MAP_FAILED,
+ };
+
+ if (argc < 3) {
+ printf("Use: differ old_file new_file change_list.\n");
+ goto bad;
+ }
+
+ file_old.name = argv[1];
+ if (!open_mmap(&file_old))
+ goto bad;
+
+ file_new.name = argv[2];
+ if (!open_mmap(&file_new))
+ goto bad;
+
+ for (i = 3; i < argc; i++)
+ if (check(argv[i], file_old.addr, file_new.addr) == FAIL) {
+ printf ("FAILED for %s\n", argv[i]);
+ r = 1;
+ goto bad;
+ }
+
+ r = 0;
+bad:
+ close_mmap(&file_new);
+ close_mmap(&file_old);
+
+ return r;
+}
diff --git a/tests/discards-test b/tests/discards-test
new file mode 100755
index 0000000..dfa7507
--- /dev/null
+++ b/tests/discards-test
@@ -0,0 +1,84 @@
+#!/bin/bash
+
+CRYPTSETUP="../cryptsetup"
+DEV_NAME="discard-t3st"
+DEV=""
+PWD1="93R4P4pIqAH8"
+
+cleanup() {
+ [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove $DEV_NAME
+ udevadm settle >/dev/null 2>&1
+ rmmod scsi_debug 2>/dev/null
+ sleep 2
+}
+
+fail()
+{
+ echo "FAILED at line $(caller)"
+ cleanup
+ exit 100
+}
+
+add_device() {
+ modprobe scsi_debug $@ delay=0
+ if [ $? -ne 0 ] ; then
+ echo "This kernel seems to not support proper scsi_debug module, test skipped."
+ exit 77
+ fi
+
+ sleep 2
+ DEV=$(grep -l -e scsi_debug /sys/block/*/device/model | cut -f4 -d /)
+
+ DEV="/dev/$DEV"
+ [ -b $DEV ] || fail "Cannot find $DEV."
+}
+
+function check_version()
+{
+ VER_STR=$(dmsetup targets | grep crypt | cut -f 2 -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.)
+
+ # option supported in 1.11
+ test $VER_MAJ -gt 1 && return 0
+ test $VER_MIN -ge 11 && return 0
+ return 1
+}
+
+if [ $(id -u) != 0 ]; then
+ echo "WARNING: You must be root to run this test, test skipped."
+ exit 77
+fi
+
+modprobe --dry-run scsi_debug || exit 77
+modprobe dm-crypt >/dev/null 2>&1
+if ! check_version ; then
+ echo "Probably old kernel, test skipped."
+ exit 77
+fi
+
+add_device dev_size_mb=16 sector_size=512 num_tgts=1 lbpu=1
+
+# FIXME test hash of device (unmap -> zero)
+# for now just check that flag is enabled
+
+echo "[1] Allowing discards for LUKS device"
+echo $PWD1 | $CRYPTSETUP luksFormat --type luks1 $DEV -q -i1 || fail
+echo $PWD1 | $CRYPTSETUP luksOpen $DEV $DEV_NAME --allow-discards || fail
+$CRYPTSETUP status $DEV_NAME | grep flags | grep discards >/dev/null || fail
+$CRYPTSETUP resize $DEV_NAME --size 100 || fail
+$CRYPTSETUP status $DEV_NAME | grep flags | grep discards >/dev/null || fail
+dmsetup table $DEV_NAME | grep allow_discards >/dev/null || fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+
+echo "[2] Allowing discards for plain device"
+echo $PWD1 | $CRYPTSETUP create -q $DEV_NAME $DEV --hash sha1 --allow-discards || fail
+$CRYPTSETUP status $DEV_NAME | grep flags | grep discards >/dev/null || fail
+$CRYPTSETUP resize $DEV_NAME --size 100 || fail
+$CRYPTSETUP status $DEV_NAME | grep flags | grep discards >/dev/null || fail
+dmsetup table $DEV_NAME | grep allow_discards >/dev/null || fail
+$CRYPTSETUP remove $DEV_NAME || fail
+
+cleanup
diff --git a/tests/evil_hdr-keyslot_overlap.xz b/tests/evil_hdr-keyslot_overlap.xz
new file mode 100644
index 0000000..6d56368
--- /dev/null
+++ b/tests/evil_hdr-keyslot_overlap.xz
Binary files differ
diff --git a/tests/evil_hdr-luks_hdr_damage.xz b/tests/evil_hdr-luks_hdr_damage.xz
new file mode 100644
index 0000000..b0b5254
--- /dev/null
+++ b/tests/evil_hdr-luks_hdr_damage.xz
Binary files differ
diff --git a/tests/evil_hdr-payload_overwrite.xz b/tests/evil_hdr-payload_overwrite.xz
new file mode 100644
index 0000000..b9e0e68
--- /dev/null
+++ b/tests/evil_hdr-payload_overwrite.xz
Binary files differ
diff --git a/tests/evil_hdr-small_luks_device.xz b/tests/evil_hdr-small_luks_device.xz
new file mode 100644
index 0000000..700392d
--- /dev/null
+++ b/tests/evil_hdr-small_luks_device.xz
Binary files differ
diff --git a/tests/evil_hdr-stripes_payload_dmg.xz b/tests/evil_hdr-stripes_payload_dmg.xz
new file mode 100644
index 0000000..dc91556
--- /dev/null
+++ b/tests/evil_hdr-stripes_payload_dmg.xz
Binary files differ
diff --git a/tests/generators/generate-luks2-area-in-json-hdr-space-json0.img.sh b/tests/generators/generate-luks2-area-in-json-hdr-space-json0.img.sh
new file mode 100755
index 0000000..3938f7b
--- /dev/null
+++ b/tests/generators/generate-luks2-area-in-json-hdr-space-json0.img.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with one area accessing luks
+# header space
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # make area 7 access the luks2 header space
+ OFFS=$((2*LUKS2_HDR_SIZE*512-1))
+ LEN=1
+ json_str=$(jq -c --arg off $OFFS --arg len $LEN \
+ '.keyslots."0".area.offset = $off | .keyslots."0".area.size = $len' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c --arg off $OFFS --arg len $LEN \
+ 'if (.keyslots."0".area.offset != $off) or (.keyslots."0".area.size != $len)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-argon2-leftover-params.img.sh b/tests/generators/generate-luks2-argon2-leftover-params.img.sh
new file mode 100755
index 0000000..7f003a0
--- /dev/null
+++ b/tests/generators/generate-luks2-argon2-leftover-params.img.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with luks2 keyslot kdf object
+# having left over params.
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # add keyslot 1 to second digest
+ obj_len=$(jq -c -M '.keyslots."1".kdf | length' $TMPDIR/json0)
+ json_str=$(jq -r -c -M '.keyslots."1".kdf.type = "pbkdf2" | .keyslots."1".kdf.iterations = 1001 | .keyslots."1".kdf.hash = "sha256"' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ chks_res0=$(read_sha256_checksum $TGT_IMG)
+ test "$chks0" = "$chks_res0" || exit 2
+ new_obj_len=$(jq -c -M '.keyslots."1".kdf | length' $TMPDIR/json_res0)
+ test $((obj_len+2)) -eq $new_obj_len || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-correct-full-json0.img.sh b/tests/generators/generate-luks2-correct-full-json0.img.sh
new file mode 100755
index 0000000..f32f84b
--- /dev/null
+++ b/tests/generators/generate-luks2-correct-full-json0.img.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate header with correct json of maximal size in primary slot.
+# Secondary header is broken on purpose.
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+PATTERN="\"config\":{"
+KEY="\"config_key\":\""
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ read -r json_str < $TMPDIR/json0
+ json_len=${#json_str}
+ pindex=$(strindex $json_str $PATTERN)
+ test $pindex -gt 0 || exit 2
+
+ offset=${#PATTERN}
+ offset=$((offset+pindex))
+ key_len=${#KEY}
+ remain=$((LUKS2_JSON_SIZE*512-json_len-key_len-2)) # -2: closing '"' and terminating '\0'
+ if [ ${json_str:offset:1} = "}" ]; then
+ format_str="%s%s%s"
+ else
+ format_str="%s%s,%s"
+ remain=$((remain-1)) # also count with separating ','
+ fi
+ test $remain -gt 0 || exit 2
+
+ fill=$(repeat_str "X" $remain)"\""
+
+ printf $format_str $KEY $fill ${json_str:$offset} | _dd of=$TMPDIR/json0 bs=1 seek=$offset conv=notrunc
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ chks_res0=$(read_sha256_checksum $TGT_IMG)
+ test "$chks0" = "$chks_res0" || exit 2
+ #json_str_res0=$(< $TMPDIR/json_res0)
+ read -r json_str_res0 < $TMPDIR/json_res0
+ test ${#json_str_res0} -eq $((LUKS2_JSON_SIZE*512-1)) || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-corrupted-hdr0-with-correct-chks.img.sh b/tests/generators/generate-luks2-corrupted-hdr0-with-correct-chks.img.sh
new file mode 100755
index 0000000..3d4f729
--- /dev/null
+++ b/tests/generators/generate-luks2-corrupted-hdr0-with-correct-chks.img.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate header with malformed json but correct checksum in primary header
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+}
+
+function generate()
+{
+ read -r json_str < $TMPDIR/json0
+ json_len=${#json_str}
+ json_len=$((json_len-1)) # to replace json closing '}'
+ json_new_str="${json_str:0:json_len},\""
+
+ while [ ${#json_new_str} -le $((LUKS2_JSON_SIZE*512)) ]; do
+ json_new_str=$json_new_str"all_work_and_no_play_makes_Jack_a_dull_boy_"
+ done
+
+ printf "%s" $json_new_str | _dd of=$TMPDIR/json0 bs=512 count=$LUKS2_JSON_SIZE
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+}
+
+function check()
+{
+ chks_res0=$(read_sha256_checksum $TGT_IMG)
+ test "$chks0" = "$chks_res0" || exit 2
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ read -r json_str_res0 < $TMPDIR/json_res0
+ test ${#json_str_res0} -eq $((LUKS2_JSON_SIZE*512)) || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-corrupted-hdr1-with-correct-chks.img.sh b/tests/generators/generate-luks2-corrupted-hdr1-with-correct-chks.img.sh
new file mode 100755
index 0000000..026393c
--- /dev/null
+++ b/tests/generators/generate-luks2-corrupted-hdr1-with-correct-chks.img.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate header with malformed json but correct checksum in secondary header
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json1 $TGT_IMG $TMPDIR/json1
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ read -r json_str < $TMPDIR/json1
+ json_len=${#json_str}
+ json_len=$((json_len-1)) # to replace json closing '}'
+ json_new_str="${json_str:0:json_len},\""
+
+ while [ ${#json_new_str} -le $((LUKS2_JSON_SIZE*512)) ]; do
+ json_new_str=$json_new_str"all_work_and_no_play_makes_Jack_a_dull_boy_"
+ done
+
+ printf "%s" $json_new_str | _dd of=$TMPDIR/json1 bs=512 count=$LUKS2_JSON_SIZE
+
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json1 $TMPDIR/area1
+ erase_checksum $TMPDIR/area1
+ chks1=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks1 $TMPDIR/area1
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ chks_res1=$(read_sha256_checksum $TMPDIR/hdr_res1)
+ test "$chks1" = "$chks_res1" || exit 2
+ read_luks2_json1 $TGT_IMG $TMPDIR/json_res1
+ read -r json_str_res1 < $TMPDIR/json_res1
+ test ${#json_str_res1} -eq $((LUKS2_JSON_SIZE*512)) || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-invalid-checksum-both-hdrs.img.sh b/tests/generators/generate-luks2-invalid-checksum-both-hdrs.img.sh
new file mode 100755
index 0000000..be98722
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-checksum-both-hdrs.img.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate header with bad checksum in both binary headerer
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ chks0=$(echo "Arbitrary chosen string: D'oh!" | calc_sha256_checksum_stdin)
+ chks1=$(echo "D'oh!: arbitrary chosen string" | calc_sha256_checksum_stdin)
+ write_checksum $chks0 $TGT_IMG
+ write_checksum $chks1 $TMPDIR/hdr1
+ write_luks2_bin_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ chks_res0=$(read_sha256_checksum $TGT_IMG)
+ chks_res1=$(read_sha256_checksum $TMPDIR/hdr1)
+ test "$chks0" = "$chks_res0" || exit 2
+ test "$chks1" = "$chks_res1" || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-invalid-checksum-hdr0.img.sh b/tests/generators/generate-luks2-invalid-checksum-hdr0.img.sh
new file mode 100755
index 0000000..ac75ccb
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-checksum-hdr0.img.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate header with bad checksum in primary binary header
+#
+
+# 1 full target dir
+# 2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+}
+
+function generate()
+{
+ chks=$(echo "Arbitrary chosen string: D'oh!" | calc_sha256_checksum_stdin)
+ write_checksum $chks $TGT_IMG
+}
+
+function check()
+{
+ chks_res=$(read_sha256_checksum $TGT_IMG)
+ test "$chks" = "$chks_res" || exit 2
+}
+
+#function cleanup()
+#{
+#}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+#cleanup
diff --git a/tests/generators/generate-luks2-invalid-checksum-hdr1.img.sh b/tests/generators/generate-luks2-invalid-checksum-hdr1.img.sh
new file mode 100755
index 0000000..f0ca01a
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-checksum-hdr1.img.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate header with bad checksum in secondary binary header
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ chks=$(echo "Arbitrary chosen string: D'oh!" | calc_sha256_checksum_stdin)
+ write_checksum $chks $TMPDIR/hdr1
+ write_luks2_bin_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ chks_res=$(read_sha256_checksum $TMPDIR/hdr1)
+ test "$chks" = "$chks_res" || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-invalid-json-size-c0.img.sh b/tests/generators/generate-luks2-invalid-json-size-c0.img.sh
new file mode 100755
index 0000000..2866b0b
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-json-size-c0.img.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with invalid json_size in config section
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ JS=$(((LUKS2_HDR_SIZE-LUKS2_BIN_HDR_SIZE)*512+4096))
+ json_str=$(jq -c --arg js $JS '.config.json_size = ($js | tostring)' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c --arg js $JS 'if .config.json_size != ($js | tostring )
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-invalid-json-size-c1.img.sh b/tests/generators/generate-luks2-invalid-json-size-c1.img.sh
new file mode 100755
index 0000000..dcab9bc
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-json-size-c1.img.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with invalid json_size in config section
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ JS=$(((LUKS2_HDR_SIZE-LUKS2_BIN_HDR_SIZE)*512-4096))
+ json_str=$(jq -c --arg js $JS '.config.json_size = ($js | tostring)' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c --arg js $JS 'if .config.json_size != ($js | tostring )
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-invalid-json-size-c2.img.sh b/tests/generators/generate-luks2-invalid-json-size-c2.img.sh
new file mode 100755
index 0000000..6de411a
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-json-size-c2.img.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with config json size mismatching
+# value in binary header
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ JS=$(((LUKS2_HDR_SIZE-LUKS2_BIN_HDR_SIZE)*512))
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_32K
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+
+ json_str=$(jq -c '.' $TMPDIR/json0)
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_offset $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr_res0
+ local str_res1=$(head -c 4 $TMPDIR/hdr_res0)
+ test "$str_res1" = "LUKS" || exit 2
+
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1 $TEST_MDA_SIZE
+ local str_res1=$(head -c 4 $TMPDIR/hdr_res1)
+ test "$str_res1" = "SKUL" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c --arg js $JS 'if .config.json_size != ( $js | tostring )
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-invalid-keyslots-size-c0.img.sh b/tests/generators/generate-luks2-invalid-keyslots-size-c0.img.sh
new file mode 100755
index 0000000..c4f002f
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-keyslots-size-c0.img.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with too large keyslots_size set in config section
+# (iow config.keyslots_size = data_offset - keyslots_offset + 512)
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # make area 7 being included in area 6
+ OFFS=$((2*LUKS2_HDR_SIZE*512))
+ json_str=$(jq -c --arg off $OFFS '.config.keyslots_size = (.segments."0".offset | tonumber - ($off | tonumber) + 4096 | tostring)' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ # [.keyslots[].area.offset | tonumber] | max | tostring ---> max offset in keyslot areas
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c --arg off $OFFS 'if .config.keyslots_size != ( .segments."0".offset | tonumber - ($off | tonumber) + 4096 | tostring )
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-invalid-keyslots-size-c1.img.sh b/tests/generators/generate-luks2-invalid-keyslots-size-c1.img.sh
new file mode 100755
index 0000000..eff2064
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-keyslots-size-c1.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with unaligned keyslots_size config section
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ json_str=$(jq -c '.config.keyslots_size = (.config.keyslots_size | tonumber - 1 | tostring)' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if (.config.keyslots_size | tonumber % 4096) == 0
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-invalid-keyslots-size-c2.img.sh b/tests/generators/generate-luks2-invalid-keyslots-size-c2.img.sh
new file mode 100755
index 0000000..f70f39f
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-keyslots-size-c2.img.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with keyslots_size less than sum of all keyslots area
+# in json
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ json_str=$(jq '.config.keyslots_size = ([.keyslots[].area.size] | map(tonumber) | add - 4096 | tostring )' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .config.keyslots_size != ([.keyslots[].area.size ] | map(tonumber) | add - 4096 | tostring)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-invalid-object-type-json0.img.sh b/tests/generators/generate-luks2-invalid-object-type-json0.img.sh
new file mode 100755
index 0000000..1063864
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-object-type-json0.img.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with well-formed json format
+# where top level value is not of type object.
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ read -r json_str < $TMPDIR/json0
+ json_str="[$json_str]" # make top level value an array
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ printf "%s" "$json_str" | _dd of=$TMPDIR/json0 bs=1 conv=notrunc
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ chks_res0=$(read_sha256_checksum $TGT_IMG)
+ test "$chks0" = "$chks_res0" || exit 2
+ read -r json_str_res0 < $TMPDIR/json_res0
+ test "$json_str" = "$json_str_res0" || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-invalid-opening-char-json0.img.sh b/tests/generators/generate-luks2-invalid-opening-char-json0.img.sh
new file mode 100755
index 0000000..996d997
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-opening-char-json0.img.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with well-formed json prefixed
+# with useless whitespace.
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ read -r json_str < $TMPDIR/json0
+ json_str=" $json_str" # add useless opening whitespace
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ printf "%s" "$json_str" | _dd of=$TMPDIR/json0 bs=1 conv=notrunc
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ chks_res0=$(read_sha256_checksum $TGT_IMG)
+ test "$chks0" = "$chks_res0" || exit 2
+ IFS= read -r json_str_res0 < $TMPDIR/json_res0
+ test "$json_str" = "$json_str_res0" || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-keyslot-missing-digest.img.sh b/tests/generators/generate-luks2-keyslot-missing-digest.img.sh
new file mode 100755
index 0000000..1914581
--- /dev/null
+++ b/tests/generators/generate-luks2-keyslot-missing-digest.img.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with luks2 keyslot not assigned
+# to any digest.
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ read -r json_str_orig < $TMPDIR/json0
+ arr_len=$(jq -c -M '.digests."0".keyslots | length' $TMPDIR/json0)
+ # remove first element from digests."0".keyslots array
+ json_str=$(jq -r -c -M 'del(.digests."0".keyslots[0])' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ chks_res0=$(read_sha256_checksum $TGT_IMG)
+ test "$chks0" = "$chks_res0" || exit 2
+ new_arr_len=$(jq -c -M '.digests."0".keyslots | length' $TMPDIR/json_res0)
+ test $((arr_len-1)) -eq $new_arr_len || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-keyslot-too-many-digests.img.sh b/tests/generators/generate-luks2-keyslot-too-many-digests.img.sh
new file mode 100755
index 0000000..5e1d6ef
--- /dev/null
+++ b/tests/generators/generate-luks2-keyslot-too-many-digests.img.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with luks2 keyslot assigned
+# to more than 1 digest.
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # add keyslot 1 to second digest
+ json_str=$(jq -r -c -M '.digests."1" = .digests."0" | .digests."1".keyslots = ["1"]' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ chks_res0=$(read_sha256_checksum $TGT_IMG)
+ test "$chks0" = "$chks_res0" || exit 2
+ new_arr_len=$(jq -c -M '.digests."1".keyslots | length' $TMPDIR/json_res0)
+ test 1 -eq $new_arr_len || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-128k-secondary.img.sh b/tests/generators/generate-luks2-metadata-size-128k-secondary.img.sh
new file mode 100755
index 0000000..ca6b0c8
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-128k-secondary.img.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate secondary header with one of allowed json area
+# size values. Test whether auto-recovery code is able
+# to validate secondary header with non-default json area
+# size.
+#
+# primary header is corrupted on purpose.
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 128 KiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_128K
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ write_bin_hdr_offset $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area0
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr_res0 $TEST_MDA_SIZE
+ local str_res0=$(head -c 6 $TMPDIR/hdr_res0)
+ test "$str_res0" = "VACUUM" || exit 2
+ read_luks2_json1 $TGT_IMG $TMPDIR/json_res1 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res1 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-128k.img.sh b/tests/generators/generate-luks2-metadata-size-128k.img.sh
new file mode 100755
index 0000000..fe76598
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-128k.img.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary with predefined json_size. There's only limited
+# set of values allowed as json size in config section of LUKS2
+# metadata
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 128KiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_128K
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area1
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1 $TEST_MDA_SIZE
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-16k-secondary.img.sh b/tests/generators/generate-luks2-metadata-size-16k-secondary.img.sh
new file mode 100755
index 0000000..14a6613
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-16k-secondary.img.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate secondary header with one of allowed json area
+# size values. Test whether auto-recovery code is able
+# to validate secondary header with non-default json area
+# size.
+#
+# primary header is corrupted on purpose.
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 16 KiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ write_bin_hdr_offset $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area0
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr_res0 $TEST_MDA_SIZE
+ local str_res0=$(head -c 6 $TMPDIR/hdr_res0)
+ test "$str_res0" = "VACUUM" || exit 2
+ read_luks2_json1 $TGT_IMG $TMPDIR/json_res1 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res1 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-1m-secondary.img.sh b/tests/generators/generate-luks2-metadata-size-1m-secondary.img.sh
new file mode 100755
index 0000000..fdcd715
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-1m-secondary.img.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate secondary header with one of allowed json area
+# size values. Test whether auto-recovery code is able
+# to validate secondary header with non-default json area
+# size.
+#
+# primary header is corrupted on purpose.
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 1 MiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_1M
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ write_bin_hdr_offset $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area0
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr_res0 $TEST_MDA_SIZE
+ local str_res0=$(head -c 6 $TMPDIR/hdr_res0)
+ test "$str_res0" = "VACUUM" || exit 2
+ read_luks2_json1 $TGT_IMG $TMPDIR/json_res1 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res1 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-1m.img.sh b/tests/generators/generate-luks2-metadata-size-1m.img.sh
new file mode 100755
index 0000000..25722dd
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-1m.img.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary with predefined json_size. There's only limited
+# set of values allowed as json size in config section of LUKS2
+# metadata
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 1 MiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_1M
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area1
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1 $TEST_MDA_SIZE
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-256k-secondary.img.sh b/tests/generators/generate-luks2-metadata-size-256k-secondary.img.sh
new file mode 100755
index 0000000..0ed66e1
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-256k-secondary.img.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate secondary header with one of allowed json area
+# size values. Test whether auto-recovery code is able
+# to validate secondary header with non-default json area
+# size.
+#
+# primary header is corrupted on purpose.
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 256 KiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_256K
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ write_bin_hdr_offset $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area0
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr_res0 $TEST_MDA_SIZE
+ local str_res0=$(head -c 6 $TMPDIR/hdr_res0)
+ test "$str_res0" = "VACUUM" || exit 2
+ read_luks2_json1 $TGT_IMG $TMPDIR/json_res1 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res1 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-256k.img.sh b/tests/generators/generate-luks2-metadata-size-256k.img.sh
new file mode 100755
index 0000000..aa5df05
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-256k.img.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary with predefined json_size. There's only limited
+# set of values allowed as json size in config section of LUKS2
+# metadata
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 256KiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_256K
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area1
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1 $TEST_MDA_SIZE
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-2m-secondary.img.sh b/tests/generators/generate-luks2-metadata-size-2m-secondary.img.sh
new file mode 100755
index 0000000..4773c94
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-2m-secondary.img.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary with predefined json_size. There's only limited
+# set of values allowed as json size in config section of LUKS2
+# metadata
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 2 MiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_2M
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ write_bin_hdr_offset $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area0
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr_res0 $TEST_MDA_SIZE
+ local str_res0=$(head -c 6 $TMPDIR/hdr_res0)
+ test "$str_res0" = "VACUUM" || exit 2
+ read_luks2_json1 $TGT_IMG $TMPDIR/json_res1 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res1 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-2m.img.sh b/tests/generators/generate-luks2-metadata-size-2m.img.sh
new file mode 100755
index 0000000..ae9bc30
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-2m.img.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary with predefined json_size. There's only limited
+# set of values allowed as json size in config section of LUKS2
+# metadata
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 2 MiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_2M
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area1
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1 $TEST_MDA_SIZE
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-32k-secondary.img.sh b/tests/generators/generate-luks2-metadata-size-32k-secondary.img.sh
new file mode 100755
index 0000000..af18f43
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-32k-secondary.img.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate secondary header with one of allowed json area
+# size values. Test whether auto-recovery code is able
+# to validate secondary header with non-default json area
+# size.
+#
+# primary header is corrupted on purpose.
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 32 KiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_32K
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ write_bin_hdr_offset $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area0
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr_res0 $TEST_MDA_SIZE
+ local str_res0=$(head -c 6 $TMPDIR/hdr_res0)
+ test "$str_res0" = "VACUUM" || exit 2
+ read_luks2_json1 $TGT_IMG $TMPDIR/json_res1 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res1 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-32k.img.sh b/tests/generators/generate-luks2-metadata-size-32k.img.sh
new file mode 100755
index 0000000..40c921e
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-32k.img.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with non-default metadata json_size.
+# There's only limited set of values allowed as json size in
+# config section of LUKS2 metadata
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 32KiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_32K
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area1
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1 $TEST_MDA_SIZE
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-4m-secondary.img.sh b/tests/generators/generate-luks2-metadata-size-4m-secondary.img.sh
new file mode 100755
index 0000000..332d67e
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-4m-secondary.img.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary with predefined json_size. There's only limited
+# set of values allowed as json size in config section of LUKS2
+# metadata
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 4 MiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_4M
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ write_bin_hdr_offset $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area0
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr_res0 $TEST_MDA_SIZE
+ local str_res0=$(head -c 6 $TMPDIR/hdr_res0)
+ test "$str_res0" = "VACUUM" || exit 2
+ read_luks2_json1 $TGT_IMG $TMPDIR/json_res1 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res1 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-4m.img.sh b/tests/generators/generate-luks2-metadata-size-4m.img.sh
new file mode 100755
index 0000000..21715fb
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-4m.img.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary with predefined json_size. There's only limited
+# set of values allowed as json size in config section of LUKS2
+# metadata
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 4 MiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_4M
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area1
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1 $TEST_MDA_SIZE
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-512k-secondary.img.sh b/tests/generators/generate-luks2-metadata-size-512k-secondary.img.sh
new file mode 100755
index 0000000..581dea0
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-512k-secondary.img.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate secondary header with one of allowed json area
+# size values. Test whether auto-recovery code is able
+# to validate secondary header with non-default json area
+# size.
+#
+# primary header is corrupted on purpose.
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 512 KiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_512K
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ write_bin_hdr_offset $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area0
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr_res0 $TEST_MDA_SIZE
+ local str_res0=$(head -c 6 $TMPDIR/hdr_res0)
+ test "$str_res0" = "VACUUM" || exit 2
+ read_luks2_json1 $TGT_IMG $TMPDIR/json_res1 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res1 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-512k.img.sh b/tests/generators/generate-luks2-metadata-size-512k.img.sh
new file mode 100755
index 0000000..8b196e6
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-512k.img.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary with predefined json_size. There's only limited
+# set of values allowed as json size in config section of LUKS2
+# metadata
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 512KiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_512K
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area1
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1 $TEST_MDA_SIZE
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-64k-inv-area-c0.img.sh b/tests/generators/generate-luks2-metadata-size-64k-inv-area-c0.img.sh
new file mode 100755
index 0000000..16e2078
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-64k-inv-area-c0.img.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with non-default metadata json_size
+# and keyslots area trespassing in json area.
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 64KiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_64K
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024-1))
+ # overlap in json area by exactly one byte
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024-1))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area1
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1 $TEST_MDA_SIZE
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-64k-inv-area-c1.img.sh b/tests/generators/generate-luks2-metadata-size-64k-inv-area-c1.img.sh
new file mode 100755
index 0000000..7ff670b
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-64k-inv-area-c1.img.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with non-default metadata json_size
+# and keyslot area overflowing out of keyslots area.
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 64KiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_64K
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ --arg mda $((2*TEST_MDA_SIZE_BYTES)) \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .keyslots."7".area.offset = ( ((.config.keyslots_size | tonumber) + ($mda | tonumber) - (.keyslots."7".area.size | tonumber) + 1) | tostring ) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area1
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1 $TEST_MDA_SIZE
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0 $TEST_JSN_SIZE
+# .keyslots.7.area.offset = ( ((.config.keyslots_size | tonumber) + ($mda | tonumber) - (.keyslots.7.area.size | tonumber) + 1) | tostring ) |
+ jq -c --arg mda $((2*TEST_MDA_SIZE_BYTES)) --arg jsize $JSON_SIZE \
+ 'if (.keyslots."7".area.offset != ( ((.config.keyslots_size | tonumber) + ($mda | tonumber) - (.keyslots."7".area.size | tonumber) + 1) | tostring )) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-64k-inv-keyslots-size-c0.img.sh b/tests/generators/generate-luks2-metadata-size-64k-inv-keyslots-size-c0.img.sh
new file mode 100755
index 0000000..8f3d8d7
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-64k-inv-keyslots-size-c0.img.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary with predefined json_size where keyslots size
+# overflows in data area (segment offset)
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 64KiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_64K
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ --arg mda $((2*TEST_MDA_SIZE_BYTES)) \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .config.keyslots_size = (((($off | tonumber) - ($mda | tonumber) + 4096)) | tostring ) |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area1
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1 $TEST_MDA_SIZE
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE --arg off $DATA_OFFSET --arg mda $((2*TEST_MDA_SIZE_BYTES)) \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize) or
+ (.config.keyslots_size != (((($off | tonumber) - ($mda | tonumber) + 4096)) | tostring ))
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-64k-secondary.img.sh b/tests/generators/generate-luks2-metadata-size-64k-secondary.img.sh
new file mode 100755
index 0000000..1b246cc
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-64k-secondary.img.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate secondary header with one of allowed json area
+# size values. Test whether auto-recovery code is able
+# to validate secondary header with non-default json area
+# size.
+#
+# primary header is corrupted on purpose.
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 64 KiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_64K
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ write_bin_hdr_offset $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area0
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr_res0 $TEST_MDA_SIZE
+ local str_res0=$(head -c 6 $TMPDIR/hdr_res0)
+ test "$str_res0" = "VACUUM" || exit 2
+ read_luks2_json1 $TGT_IMG $TMPDIR/json_res1 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res1 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-64k.img.sh b/tests/generators/generate-luks2-metadata-size-64k.img.sh
new file mode 100755
index 0000000..4e320f2
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-64k.img.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary with predefined json_size. There's only limited
+# set of values allowed as json size in config section of LUKS2
+# metadata
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # 64KiB metadata
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_64K
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
+ KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
+ JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
+ JSON_SIZE=$((TEST_JSN_SIZE*512))
+ DATA_OFFSET=16777216
+
+ json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
+ '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
+ .config.json_size = $jsize |
+ .segments."0".offset = $off' $TMPDIR/json0)
+ test -n "$json_str" || exit 2
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
+
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+
+ erase_checksum $TMPDIR/area1
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $chks0 $TMPDIR/area1
+
+ kill_bin_hdr $TMPDIR/area1
+
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1 $TEST_MDA_SIZE
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0 $TEST_JSN_SIZE
+ jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
+ 'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
+ (.config.json_size != $jsize)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-missing-keyslot-referenced-in-digest.img.sh b/tests/generators/generate-luks2-missing-keyslot-referenced-in-digest.img.sh
new file mode 100755
index 0000000..d6ebe3d
--- /dev/null
+++ b/tests/generators/generate-luks2-missing-keyslot-referenced-in-digest.img.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with missing keyslot object referenced
+# in digest object
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ read -r json_str_orig < $TMPDIR/json0
+ arr_len=$(jq -c -M '.digests."0".keyslots | length' $TMPDIR/json0)
+ # add missing keyslot reference in keyslots array of digest '0'
+ json_str=$(jq -r -c -M 'def arr: ["digests", "0", "keyslots"];
+ def missks: getpath(["keyslots"]) | keys | max | tonumber + 1 | tostring;
+ setpath(arr; getpath(arr) + [ missks ])' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ chks_res0=$(read_sha256_checksum $TGT_IMG)
+ test "$chks0" = "$chks_res0" || exit 2
+ new_arr_len=$(jq -c -M '.digests."0".keyslots | length' $TMPDIR/json_res0)
+ test $((arr_len+1)) -eq $new_arr_len || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-missing-keyslot-referenced-in-token.img.sh b/tests/generators/generate-luks2-missing-keyslot-referenced-in-token.img.sh
new file mode 100755
index 0000000..85798e5
--- /dev/null
+++ b/tests/generators/generate-luks2-missing-keyslot-referenced-in-token.img.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with missing keyslot object referenced
+# in token object
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ read -r json_str_orig < $TMPDIR/json0
+ # add missing keyslot reference in keyslots array of token '0'
+ json_str=$(jq -r -c -M 'def missks: getpath(["keyslots"]) | keys | max | tonumber + 1 | tostring;
+ .tokens += {"0":{"type":"dummy","keyslots":[ "0", missks ]}}' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ chks_res0=$(read_sha256_checksum $TGT_IMG)
+ test "$chks0" = "$chks_res0" || exit 2
+ new_arr_len=$(jq -c -M '.tokens."0".keyslots | length' $TMPDIR/json_res0)
+ test $new_arr_len -eq 2 || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-missing-segment-referenced-in-digest.img.sh b/tests/generators/generate-luks2-missing-segment-referenced-in-digest.img.sh
new file mode 100755
index 0000000..333462b
--- /dev/null
+++ b/tests/generators/generate-luks2-missing-segment-referenced-in-digest.img.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with missing segment object referenced
+# in digest object
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ read -r json_str_orig < $TMPDIR/json0
+ arr_len=$(jq -c -M '.digests."0".segments | length' $TMPDIR/json0)
+ # add missing keyslot reference in keyslots array of digest '0'
+ json_str=$(jq -c 'def arr: ["digests", "0", "segments"];
+ def missseg: getpath(["segments"]) | keys | max | tonumber + 1 | tostring;
+ setpath(arr; getpath(arr) + [ missseg ])' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ chks_res0=$(read_sha256_checksum $TGT_IMG)
+ test "$chks0" = "$chks_res0" || exit 2
+ new_arr_len=$(jq -c -M '.digests."0".segments | length' $TMPDIR/json_res0)
+ test $((arr_len+1)) -eq $new_arr_len || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-missing-trailing-null-byte-json0.img.sh b/tests/generators/generate-luks2-missing-trailing-null-byte-json0.img.sh
new file mode 100755
index 0000000..916cff7
--- /dev/null
+++ b/tests/generators/generate-luks2-missing-trailing-null-byte-json0.img.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with well-formed json but missing
+# trailing null byte.
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+PATTERN="\"config\":{"
+KEY="\"config_key\":\""
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ read -r json_str < $TMPDIR/json0
+ json_len=${#json_str}
+ pindex=$(strindex $json_str $PATTERN)
+ test $pindex -gt 0 || exit 2
+
+ offset=${#PATTERN}
+ offset=$((offset+pindex))
+ key_len=${#KEY}
+ remain=$((LUKS2_JSON_SIZE*512-key_len-json_len-1)) # -1: closing '"'
+ if [ ${json_str:offset:1} = "}" ]; then
+ format_str="%s%s%s"
+ else
+ format_str="%s%s,%s"
+ remain=$((remain-1)) # also count with separating ','
+ fi
+ test $remain -gt 0 || exit 2
+
+ fill=$(repeat_str "X" $remain)
+ fill=$(repeat_str "X" $remain)"\""
+
+ printf $format_str $KEY $fill ${json_str:$offset} | _dd of=$TMPDIR/json0 bs=1 seek=$offset conv=notrunc
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ chks_res0=$(read_sha256_checksum $TGT_IMG)
+ test "$chks0" = "$chks_res0" || exit 2
+ read -r json_str_res0 < $TMPDIR/json_res0
+ test ${#json_str_res0} -eq $((LUKS2_JSON_SIZE*512)) || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-non-null-byte-beyond-json0.img.sh b/tests/generators/generate-luks2-non-null-byte-beyond-json0.img.sh
new file mode 100755
index 0000000..fbd8cd6
--- /dev/null
+++ b/tests/generators/generate-luks2-non-null-byte-beyond-json0.img.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with json area concluded with illegal
+# byte beyond terminating '}' character.
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ read -r json_str < $TMPDIR/json0
+ json_str="$json_str"X # add illegal 'X' beyond json format
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ printf '%s' $json_str | _dd of=$TMPDIR/json0 bs=1 conv=notrunc
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ chks_res0=$(read_sha256_checksum $TGT_IMG)
+ test "$chks0" = "$chks_res0" || exit 2
+ read -r json_str_res0 < $TMPDIR/json_res0
+ local len=${#json_str_res0}
+ len=$((len-1))
+ test ${json_str_res0:len:1} = "X" || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-non-null-bytes-beyond-json0.img.sh b/tests/generators/generate-luks2-non-null-bytes-beyond-json0.img.sh
new file mode 100755
index 0000000..7d46628
--- /dev/null
+++ b/tests/generators/generate-luks2-non-null-bytes-beyond-json0.img.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with json area containing illegal bytes
+# beyond well-formed json format.
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+QUOTE="[Homer J. Simpson]: Keep looking shocked and move slowly towards the cake."
+SPACE=20
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ read -r json_str < $TMPDIR/json0
+ json_len_orig=${#json_str}
+ json_len=$((json_len_orig+${#QUOTE}+SPACE))
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ printf '%s' "$QUOTE" | _dd of=$TMPDIR/json0 seek=$((json_len_orig+SPACE)) bs=1 conv=notrunc
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ chks_res0=$(read_sha256_checksum $TGT_IMG)
+ test "$chks0" = "$chks_res0" || exit 2
+
+ _dd if=$TMPDIR/json_res0 of=$TMPDIR/quote skip=$((json_len_orig+SPACE)) count=${#QUOTE} bs=1
+ json_str_res0=$(head -c ${#QUOTE} $TMPDIR/quote)
+ test "$json_str_res0" = "$QUOTE" || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-overlapping-areas-c0-json0.img.sh b/tests/generators/generate-luks2-overlapping-areas-c0-json0.img.sh
new file mode 100755
index 0000000..c319ca3
--- /dev/null
+++ b/tests/generators/generate-luks2-overlapping-areas-c0-json0.img.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with two exactly same areas in terms of 'offset' and 'length'.
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # copy area 6 offset and length into area 7
+ json_str=$(jq -c '.keyslots."7".area.offset = .keyslots."6".area.offset |
+ .keyslots."7".area.size = .keyslots."6".area.size' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if (.keyslots."6".area.offset != .keyslots."7".area.offset) or (.keyslots."6".area.size != .keyslots."7".area.size)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-overlapping-areas-c1-json0.img.sh b/tests/generators/generate-luks2-overlapping-areas-c1-json0.img.sh
new file mode 100755
index 0000000..39f0c6a
--- /dev/null
+++ b/tests/generators/generate-luks2-overlapping-areas-c1-json0.img.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with one area included within another one (in terms of 'offset' + 'length')
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # make area 7 being included in area 6
+ json_str=$(jq -c '.keyslots."7".area.offset = (.keyslots."6".area.offset | tonumber + 1 | tostring ) |
+ .keyslots."7".area.size = ( .keyslots."6".area.size | tonumber - 1 | tostring)' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if (.keyslots."7".area.offset != (.keyslots."6".area.offset | tonumber + 1 | tostring)) or
+ (.keyslots."7".area.size != (.keyslots."6".area.size | tonumber - 1 | tostring)) or
+ (.keyslots."7".area.size | tonumber <= 0)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-overlapping-areas-c2-json0.img.sh b/tests/generators/generate-luks2-overlapping-areas-c2-json0.img.sh
new file mode 100755
index 0000000..4c02008
--- /dev/null
+++ b/tests/generators/generate-luks2-overlapping-areas-c2-json0.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with one area slightly cross the boundary of another one
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # make area 7 being included in area 6
+ json_str=$(jq -c '.keyslots."7".area.offset = ([ .keyslots."6".area.offset, .keyslots."6".area.size ] | map(tonumber) | add - 1 | tostring)' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .keyslots."7".area.offset != ([.keyslots."6".area.offset, .keyslots."6".area.size ] | map(tonumber) | add - 1 | tostring)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-pbkdf2-leftover-params-0.img.sh b/tests/generators/generate-luks2-pbkdf2-leftover-params-0.img.sh
new file mode 100755
index 0000000..1517ed6
--- /dev/null
+++ b/tests/generators/generate-luks2-pbkdf2-leftover-params-0.img.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with luks2 keyslot kdf object
+# having left over params.
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # add keyslot 1 to second digest
+ obj_len=$(jq -c -M '.keyslots."2".kdf | length' $TMPDIR/json0)
+ json_str=$(jq -r -c -M '.keyslots."2".kdf.type = "argon2i" | .keyslots."2".kdf.iterations = 1001 | .keyslots."2".kdf.hash = "sha256"' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ chks_res0=$(read_sha256_checksum $TGT_IMG)
+ test "$chks0" = "$chks_res0" || exit 2
+ new_obj_len=$(jq -c -M '.keyslots."2".kdf | length' $TMPDIR/json_res0)
+ test $((obj_len+2)) -eq $new_obj_len || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-pbkdf2-leftover-params-1.img.sh b/tests/generators/generate-luks2-pbkdf2-leftover-params-1.img.sh
new file mode 100755
index 0000000..c6aa5bf
--- /dev/null
+++ b/tests/generators/generate-luks2-pbkdf2-leftover-params-1.img.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with luks2 keyslot kdf object
+# having left over params.
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # add keyslot 1 to second digest
+ obj_len=$(jq -c -M '.keyslots."2".kdf | length' $TMPDIR/json0)
+ json_str=$(jq -r -c -M '.keyslots."2".kdf.type = "argon2id" | .keyslots."2".kdf.iterations = 1001 | .keyslots."2".kdf.hash = "sha256"' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ chks_res0=$(read_sha256_checksum $TGT_IMG)
+ test "$chks0" = "$chks_res0" || exit 2
+ new_obj_len=$(jq -c -M '.keyslots."2".kdf | length' $TMPDIR/json_res0)
+ test $((obj_len+2)) -eq $new_obj_len || exit 2
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-crypt-missing-encryption.img.sh b/tests/generators/generate-luks2-segment-crypt-missing-encryption.img.sh
new file mode 100755
index 0000000..bcd648a
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-crypt-missing-encryption.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with segment encryption field missing
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c 'del(.segments."0".encryption)' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".encryption
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-crypt-missing-ivoffset.img.sh b/tests/generators/generate-luks2-segment-crypt-missing-ivoffset.img.sh
new file mode 100755
index 0000000..e64feef
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-crypt-missing-ivoffset.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with segment iv_tweak field missing
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c 'del(.segments."0".iv_tweak)' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".iv_tweak
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-crypt-missing-sectorsize.img.sh b/tests/generators/generate-luks2-segment-crypt-missing-sectorsize.img.sh
new file mode 100755
index 0000000..de757db
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-crypt-missing-sectorsize.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with segment sector_size field missing
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c 'del(.segments."0".sector_size)' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".sector_size
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-crypt-wrong-encryption.img.sh b/tests/generators/generate-luks2-segment-crypt-wrong-encryption.img.sh
new file mode 100755
index 0000000..59c7345
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-crypt-wrong-encryption.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with segment wrong encryption field
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c '.segments."0".encryption = {}' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".encryption | type != "object"
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-crypt-wrong-ivoffset.img.sh b/tests/generators/generate-luks2-segment-crypt-wrong-ivoffset.img.sh
new file mode 100755
index 0000000..ca9461e
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-crypt-wrong-ivoffset.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with segment iv_tweak field missing
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c '.segments."0".iv_tweak = "dynamic"' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".iv_tweak != "dynamic"
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-crypt-wrong-sectorsize-0.img.sh b/tests/generators/generate-luks2-segment-crypt-wrong-sectorsize-0.img.sh
new file mode 100755
index 0000000..4ca05eb
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-crypt-wrong-sectorsize-0.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with wrong segment sector_size field
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c '.segments."0".sector_size = 1023' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".sector_size != 1023
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-crypt-wrong-sectorsize-1.img.sh b/tests/generators/generate-luks2-segment-crypt-wrong-sectorsize-1.img.sh
new file mode 100755
index 0000000..f8d251c
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-crypt-wrong-sectorsize-1.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with wrong segment sector_size field
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c '.segments."0".sector_size = "4096"' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".sector_size != "4096"
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-crypt-wrong-sectorsize-2.img.sh b/tests/generators/generate-luks2-segment-crypt-wrong-sectorsize-2.img.sh
new file mode 100755
index 0000000..87566ec
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-crypt-wrong-sectorsize-2.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with wrong segment sector_size field
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c '.segments."0".sector_size = -1024' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".sector_size != -1024
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-missing-offset.img.sh b/tests/generators/generate-luks2-segment-missing-offset.img.sh
new file mode 100755
index 0000000..6652288
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-missing-offset.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with segment offset field missing
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c 'del(.segments."0".offset)' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".offset
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-missing-size.img.sh b/tests/generators/generate-luks2-segment-missing-size.img.sh
new file mode 100755
index 0000000..616d8b3
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-missing-size.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with segment size field missing
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c 'del(.segments."0".size)' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".size
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-missing-type.img.sh b/tests/generators/generate-luks2-segment-missing-type.img.sh
new file mode 100755
index 0000000..d0014a2
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-missing-type.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with segment type field missing
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c 'del(.segments."0".type)' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".type
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-two.img.sh b/tests/generators/generate-luks2-segment-two.img.sh
new file mode 100755
index 0000000..743bbbb
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-two.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with two segments
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c '.segments."0".size = "512" | .segments."1" = {type:"some", offset: (.segments."0".offset | tonumber + 512 | tostring), size: "dynamic"}' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."1" | type != "object"
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-unknown-type.img.sh b/tests/generators/generate-luks2-segment-unknown-type.img.sh
new file mode 100755
index 0000000..a6ef8ad
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-unknown-type.img.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with generic (unknown) segment type.
+# It should pass the validation.
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c '.segments."0" = {type:"some_type", offset: .segments."0".offset, size: .segments."0".size, a_field:0}' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".type != "some_type"
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-wrong-flags-element.img.sh b/tests/generators/generate-luks2-segment-wrong-flags-element.img.sh
new file mode 100755
index 0000000..5359954
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-wrong-flags-element.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with segment flags containing invalid type
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c '.segments."0".flags = [ "hello", 1 ]' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".flags != [ "hello", 1 ]
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-wrong-flags.img.sh b/tests/generators/generate-luks2-segment-wrong-flags.img.sh
new file mode 100755
index 0000000..3ceddbf
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-wrong-flags.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with segment flags field of invalid type
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c '.segments."0".flags = "hello"' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".flags != "hello"
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-wrong-offset.img.sh b/tests/generators/generate-luks2-segment-wrong-offset.img.sh
new file mode 100755
index 0000000..9efc756
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-wrong-offset.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with wrong segment offset field
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c '.segments."0".offset = "-42"' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".offset != "-42"
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-wrong-size-0.img.sh b/tests/generators/generate-luks2-segment-wrong-size-0.img.sh
new file mode 100755
index 0000000..58b12ef
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-wrong-size-0.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with wrong segment size field
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c '.segments."0".size = 4096' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".size != 4096
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-wrong-size-1.img.sh b/tests/generators/generate-luks2-segment-wrong-size-1.img.sh
new file mode 100755
index 0000000..8171445
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-wrong-size-1.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with wrong segment size field
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c '.segments."0".size = "automatic"' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".size != "automatic"
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-wrong-size-2.img.sh b/tests/generators/generate-luks2-segment-wrong-size-2.img.sh
new file mode 100755
index 0000000..f694cf7
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-wrong-size-2.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with wrong segment size field
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c '.segments."0".size = "511"' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".size != "511"
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-segment-wrong-type.img.sh b/tests/generators/generate-luks2-segment-wrong-type.img.sh
new file mode 100755
index 0000000..4f7fd64
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-wrong-type.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with wrong segment type field
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # remove mandatory encryption field
+ json_str=$(jq -c '.segments."0".type = 42' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".type != 42
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-uint64-max-segment-size.img.sh b/tests/generators/generate-luks2-uint64-max-segment-size.img.sh
new file mode 100755
index 0000000..27d7fd2
--- /dev/null
+++ b/tests/generators/generate-luks2-uint64-max-segment-size.img.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with segment size set to UINT64_MAX - 511
+# (512 sector aligned value)
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # UINT64_MAX - 511 (so that it's sector aligned)
+ json_str=$(jq -c '.segments."0".size = "18446744073709551104"' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".size != "18446744073709551104"
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-uint64-overflow-segment-size.img.sh b/tests/generators/generate-luks2-uint64-overflow-segment-size.img.sh
new file mode 100755
index 0000000..01657d6
--- /dev/null
+++ b/tests/generators/generate-luks2-uint64-overflow-segment-size.img.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with segment size set to UINT64_MAX + 1
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ json_str=$(jq -c '.segments."0".size = "18446744073709551616"' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".size != "18446744073709551616"
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/generate-luks2-uint64-signed-segment-size.img.sh b/tests/generators/generate-luks2-uint64-signed-segment-size.img.sh
new file mode 100755
index 0000000..0a45a05
--- /dev/null
+++ b/tests/generators/generate-luks2-uint64-signed-segment-size.img.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with segment size set to -512
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function prepare()
+{
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function generate()
+{
+ # UINT64_MAX + 1 (it's 512 sector aligned)
+ json_str=$(jq -c '.segments."0".size = "-512"' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0
+ erase_checksum $TMPDIR/area0
+ chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $chks0 $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM" || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .segments."0".size != "-512"
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+function cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+test $# -eq 2 || exit 1
+
+TGT_IMG=$1/$(test_img_name $0)
+SRC_IMG=$2
+
+prepare
+generate
+check
+cleanup
diff --git a/tests/generators/lib.sh b/tests/generators/lib.sh
new file mode 100644
index 0000000..9686148
--- /dev/null
+++ b/tests/generators/lib.sh
@@ -0,0 +1,180 @@
+#!/bin/bash
+
+# all in 512 bytes blocks (including binary hdr (4KiB))
+LUKS2_HDR_SIZE=32 # 16 KiB
+LUKS2_HDR_SIZE_32K=64 # 32 KiB
+LUKS2_HDR_SIZE_64K=128 # 64 KiB
+LUKS2_HDR_SIZE_128K=256 # 128 KiB
+LUKS2_HDR_SIZE_256K=512 # 256 KiB
+LUKS2_HDR_SIZE_512K=1024 # 512 KiB
+LUKS2_HDR_SIZE_1M=2048 # 1 MiB
+LUKS2_HDR_SIZE_2M=4096 # 2 MiB
+LUKS2_HDR_SIZE_4M=8192 # 4 MiB
+
+LUKS2_BIN_HDR_SIZE=8 # 4 KiB
+LUKS2_JSON_SIZE=$((LUKS2_HDR_SIZE-LUKS2_BIN_HDR_SIZE))
+
+LUKS2_BIN_HDR_CHKS_OFFSET=0x1C0
+LUKS2_BIN_HDR_CHKS_LENGTH=64
+
+[ -z "$srcdir" ] && srcdir="."
+TMPDIR=$srcdir/tmp
+
+repeat_str() {
+ printf "$1"'%.0s' $(eval "echo {1.."$(($2))"}");
+}
+
+function strindex()
+{
+ local x="${1%%$2*}"
+ [[ $x = $1 ]] && echo -1 || echo ${#x}
+}
+
+function test_img_name()
+{
+ local str=$(basename $1)
+ str=${str#generate-}
+ str=${str%%.sh}
+ echo $str
+}
+
+# read primary bin hdr
+# 1:from 2:to
+function read_luks2_bin_hdr0()
+{
+ _dd if=$1 of=$2 bs=512 count=$LUKS2_BIN_HDR_SIZE
+}
+
+# read primary json area
+# 1:from 2:to 3:[json only size (defaults to 12KiB)]
+function read_luks2_json0()
+{
+ local _js=${4:-$LUKS2_JSON_SIZE}
+ local _js=$((_js*512/4096))
+ _dd if=$1 of=$2 bs=4096 skip=1 count=$_js
+}
+
+# read secondary bin hdr
+# 1:from 2:to 3:[metadata size (defaults to 16KiB)]
+function read_luks2_bin_hdr1()
+{
+ _dd if=$1 of=$2 skip=${3:-$LUKS2_HDR_SIZE} bs=512 count=$LUKS2_BIN_HDR_SIZE
+}
+
+# read secondary json area
+# 1:from 2:to 3:[json only size (defaults to 12KiB)]
+function read_luks2_json1()
+{
+ local _js=${3:-$LUKS2_JSON_SIZE}
+ _dd if=$1 of=$2 bs=512 skip=$((2*LUKS2_BIN_HDR_SIZE+_js)) count=$_js
+}
+
+# read primary metadata area (bin + json)
+# 1:from 2:to 3:[metadata size (defaults to 16KiB)]
+function read_luks2_hdr_area0()
+{
+ local _as=${3:-$LUKS2_HDR_SIZE}
+ local _as=$((_as*512))
+ _dd if=$1 of=$2 bs=$_as count=1
+}
+
+# read secondary metadata area (bin + json)
+# 1:from 2:to 3:[metadata size (defaults to 16KiB)]
+function read_luks2_hdr_area1()
+{
+ local _as=${3:-$LUKS2_HDR_SIZE}
+ local _as=$((_as*512))
+ _dd if=$1 of=$2 bs=$_as skip=1 count=1
+}
+
+# write secondary bin hdr
+# 1:from 2:to 3:[metadata size (defaults to 16KiB)]
+function write_luks2_bin_hdr1()
+{
+ _dd if=$1 of=$2 bs=512 seek=${3:-$LUKS2_HDR_SIZE} count=$LUKS2_BIN_HDR_SIZE conv=notrunc
+}
+
+# write primary metadata area (bin + json)
+# 1:from 2:to 3:[metadata size (defaults to 16KiB)]
+function write_luks2_hdr0()
+{
+ local _as=${3:-$LUKS2_HDR_SIZE}
+ local _as=$((_as*512))
+ _dd if=$1 of=$2 bs=$_as count=1 conv=notrunc
+}
+
+# write secondary metadata area (bin + json)
+# 1:from 2:to 3:[metadata size (defaults to 16KiB)]
+function write_luks2_hdr1()
+{
+ local _as=${3:-$LUKS2_HDR_SIZE}
+ local _as=$((_as*512))
+ _dd if=$1 of=$2 bs=$_as seek=1 count=1 conv=notrunc
+}
+
+# write json (includes padding)
+# 1:json_string 2:to 3:[json size (defaults to 12KiB)]
+function write_luks2_json()
+{
+ local _js=${3:-$LUKS2_JSON_SIZE}
+ local len=${#1}
+ echo -n -E "$1" > $2
+ truncate -s $((_js*512)) $2
+}
+
+function kill_bin_hdr()
+{
+ printf "VACUUM" | _dd of=$1 bs=1 conv=notrunc
+}
+
+function erase_checksum()
+{
+ _dd if=/dev/zero of=$1 bs=1 seek=$(printf %d $LUKS2_BIN_HDR_CHKS_OFFSET) count=$LUKS2_BIN_HDR_CHKS_LENGTH conv=notrunc
+}
+
+function read_sha256_checksum()
+{
+ _dd if=$1 bs=1 skip=$(printf %d $LUKS2_BIN_HDR_CHKS_OFFSET) count=32 | xxd -c 32 -p
+}
+
+# 1 - string with checksum
+function write_checksum()
+{
+ test $# -eq 2 || return 1
+ test $((${#1}/2)) -le $LUKS2_BIN_HDR_CHKS_LENGTH || { echo "too long"; return 1; }
+
+ echo $1 | xxd -r -p | _dd of=$2 bs=1 seek=$(printf %d $LUKS2_BIN_HDR_CHKS_OFFSET) conv=notrunc
+}
+
+function calc_sha256_checksum_file()
+{
+ sha256sum $1 | cut -d ' ' -f 1
+}
+
+function calc_sha256_checksum_stdin()
+{
+ sha256sum - | cut -d ' ' -f 1
+}
+
+# merge bin hdr with json to form metadata area
+# 1:bin_hdr 2:json 3:to 4:[json size (defaults to 12KiB)]
+function merge_bin_hdr_with_json()
+{
+ local _js=${4:-$LUKS2_JSON_SIZE}
+ local _js=$((_js*512/4096))
+ _dd if=$1 of=$3 bs=4096 count=1
+ _dd if=$2 of=$3 bs=4096 seek=1 count=$_js
+}
+
+function _dd()
+{
+ dd $@ status=none
+}
+
+function write_bin_hdr_size() {
+ printf '%016x' $2 | xxd -r -p -l 16 | _dd of=$1 bs=8 count=1 seek=1 conv=notrunc
+}
+
+function write_bin_hdr_offset() {
+ printf '%016x' $2 | xxd -r -p -l 16 | _dd of=$1 bs=8 count=1 seek=32 conv=notrunc
+}
diff --git a/tests/img_fs_ext4.img.xz b/tests/img_fs_ext4.img.xz
new file mode 100644
index 0000000..de688f0
--- /dev/null
+++ b/tests/img_fs_ext4.img.xz
Binary files differ
diff --git a/tests/img_fs_vfat.img.xz b/tests/img_fs_vfat.img.xz
new file mode 100644
index 0000000..5ecfa67
--- /dev/null
+++ b/tests/img_fs_vfat.img.xz
Binary files differ
diff --git a/tests/img_fs_xfs.img.xz b/tests/img_fs_xfs.img.xz
new file mode 100644
index 0000000..a7034a7
--- /dev/null
+++ b/tests/img_fs_xfs.img.xz
Binary files differ
diff --git a/tests/integrity-compat-test b/tests/integrity-compat-test
new file mode 100755
index 0000000..58b36e9
--- /dev/null
+++ b/tests/integrity-compat-test
@@ -0,0 +1,363 @@
+#!/bin/bash
+#
+# Test integritysetup compatibility.
+#
+INTSETUP=../integritysetup
+INTSETUP_VALGRIND=../.libs/integritysetup
+INTSETUP_LIB_VALGRIND=../.libs
+
+DEV_NAME=dmc_test
+DEV=test123.img
+DEV2=test124.img
+KEY_FILE=key.img
+
+dmremove() { # device
+ udevadm settle >/dev/null 2>&1
+ dmsetup remove $1 >/dev/null 2>&1
+}
+
+cleanup() {
+ [ -b /dev/mapper/$DEV_NAME ] && dmremove $DEV_NAME
+ rm -f $DEV $DEV2 $KEY_FILE >/dev/null 2>&1
+}
+
+fail()
+{
+ echo
+ echo "FAILED at line $(caller)"
+ cleanup
+ exit 100
+}
+
+skip()
+{
+ [ -n "$1" ] && echo "$1"
+ exit 77
+}
+
+function dm_integrity_features()
+{
+ VER_STR=$(dmsetup targets | grep integrity | cut -f2 -dv)
+ [ -z "$VER_STR" ] && skip "Cannot find dm-integrity target, test skipped."
+
+ 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_MIN -gt 1 ] && {
+ DM_INTEGRITY_META=1
+ DM_INTEGRITY_RECALC=1
+ }
+}
+
+add_device() {
+ cleanup
+ dd if=/dev/urandom of=$KEY_FILE bs=1 count=512 >/dev/null 2>&1
+ dd if=/dev/zero of=$DEV bs=1M count=32 >/dev/null 2>&1
+ dd if=/dev/zero of=$DEV2 bs=1M count=32 >/dev/null 2>&1
+ sync
+}
+
+status_check() # name value
+{
+ X=$($INTSETUP status $DEV_NAME | grep "$1" | sed 's/.*: //' | sed 's/^[[:space:]]*//')
+ if [ "$X" != "$2" ] ; then
+ echo "[status FAIL]"
+ echo " Expecting $1:$2 got \"$X\"."
+ fail
+ fi
+}
+
+dump_check() # name value
+{
+ X=$($INTSETUP dump $DEV | grep "$1" | cut -d' ' -f 2)
+ if [ "$X" != "$2" ] ; then
+ echo "[dump FAIL]"
+ echo " Expecting $1:$2 got \"$X\"."
+ fail
+ fi
+}
+
+kernel_param_check() # number value
+{
+ X=$(dmsetup table $DEV_NAME | cut -d " " -f $1)
+ if [ "$X" != $2 ] ; then
+ echo "[param_check FAIL]"
+ echo "Expecting $2 got \"$X\"."
+ fail
+ fi
+}
+
+function valgrind_setup()
+{
+ which valgrind >/dev/null 2>&1 || fail "Cannot find valgrind."
+ [ ! -f $INTSETUP_VALGRIND ] && fail "Unable to get location of cryptsetup executable."
+ export LD_LIBRARY_PATH="$INTSETUP_LIB_VALGRIND:$LD_LIBRARY_PATH"
+}
+
+function valgrind_run()
+{
+ INFOSTRING="$(basename ${BASH_SOURCE[1]})-line-${BASH_LINENO[0]}" ./valg.sh ${INTSETUP_VALGRIND} "$@"
+}
+
+int_check_sum_only() # checksum
+{
+ VSUM=$(sha256sum /dev/mapper/$DEV_NAME | cut -d' ' -f 1)
+ if [ "$VSUM" = "$1" ] ; then
+ echo -n "[CHECKSUM OK]"
+ else
+ echo "[FAIL]"
+ echo " Expecting $1 got $VSUM."
+ fail
+ fi
+}
+
+int_check_sum() # alg checksum [keyfile keysize]
+{
+ if [ -n "$4" ] ; then
+ KEY_PARAMS="--integrity-key-file $3 --integrity-key-size $4"
+ else
+ KEY_PARAMS=""
+ fi
+
+ # Fill device with zeroes and reopen it
+ dd if=/dev/zero of=/dev/mapper/$DEV_NAME bs=1M oflag=direct >/dev/null 2>&1
+ dmremove $DEV_NAME
+
+ $INTSETUP open $DEV $DEV_NAME --integrity $1 $KEY_PARAMS || fail "Cannot activate device."
+
+ int_check_sum_only $2
+}
+
+intformat() # alg alg_out tagsize sector_size csum [keyfile keysize]
+{
+ if [ -n "$7" ] ; then
+ KEY_PARAMS="--integrity-key-file $6 --integrity-key-size $7"
+ else
+ KEY_PARAMS=""
+ fi
+
+ echo -n "[INTEGRITY:$2:$3:$4]"
+ echo -n "[FORMAT]"
+ $INTSETUP format -q --integrity $1 --tag-size $3 --sector-size $4 $KEY_PARAMS $DEV || fail "Cannot format device."
+ dump_check "tag_size" $3
+ dump_check "sector_size" $4
+ echo -n "[ACTIVATE]"
+ $INTSETUP open $DEV $DEV_NAME --integrity $1 $KEY_PARAMS || fail "Cannot activate device."
+ status_check "tag size" $3
+ status_check "integrity" $2
+ status_check "sector size" "$4 bytes"
+ int_check_sum $1 $5 $6 $7
+ echo -n "[REMOVE]"
+ $INTSETUP close $DEV_NAME || fail "Cannot deactivate device."
+ echo "[OK]"
+}
+
+int_error_detection() # alg tagsize sector_size key_file key_size
+{
+ if [ -n "$5" ] ; then
+ KEY_PARAMS="--integrity-key-file $4 --integrity-key-size $5"
+ else
+ KEY_PARAMS=""
+ fi
+ dd if=/dev/zero of=$DEV bs=1M count=32 >/dev/null 2>&1
+
+ echo -n "[INTEGRITY:$1:$2:$3]"
+ echo -n "[FORMAT]"
+ $INTSETUP format -q --integrity $1 --tag-size $2 --sector-size $3 $KEY_PARAMS $DEV || fail "Cannot format device."
+ echo -n "[ACTIVATE]"
+ $INTSETUP open $DEV $DEV_NAME --integrity $1 --integrity-no-journal $KEY_PARAMS || fail "Cannot activate device."
+
+ if [ -n "$4" -a -n "$5" ]; then
+ echo -n "[KEYED HASH]"
+ KEY_HEX=$(xxd -c 256 -l $5 -p $4)
+ [ -z "$KEY_HEX" ] && fail "Cannot decode key."
+ dmsetup table --showkeys $DEV_NAME | grep -q $KEY_HEX || fail "Key mismatch."
+ fi
+
+ echo -n "[WRITE DATA]"
+ echo -n "EXAMPLE TEXT" | dd of=/dev/mapper/$DEV_NAME >/dev/null 2>&1 || fail "Cannot write to device."
+ $INTSETUP close $DEV_NAME || fail "Cannot deactivate device."
+
+ # find offset of data area
+ ARR=($(dd if=$DEV bs=512 2>/dev/null | hexdump -C | grep 'EXAMPLE TEXT'))
+ OFF_HEX=${ARR[0]}
+ OFF_DEC=$((16#$OFF_HEX))
+
+ echo -n "[CORRUPT DATA:$OFF_DEC]"
+ echo -n "Z" | dd of=$DEV bs=1 seek=$OFF_DEC conv=notrunc >/dev/null 2>&1 || fail "Cannot write to device."
+
+ echo -n "[DETECT ERROR]"
+ $INTSETUP open $DEV $DEV_NAME --integrity $1 $KEY_PARAMS || fail "Cannot activate device."
+ dd if=/dev/mapper/$DEV_NAME >/dev/null 2>&1 && fail "Error detection failed."
+
+ echo -n "[REMOVE]"
+ $INTSETUP close $DEV_NAME || fail "Cannot deactivate device."
+ echo "[OK]"
+}
+
+int_journal() # 1 alg, 2 tagsize, 3 sector_size, 4 watermark, 5 commit_time, 6 journal_integrity, 7 key-file, 8 key-size, 9 journal_integrity_out
+{
+ echo -n "[INTEGRITY JOURNAL:$6:${4}%:${5}ms]"
+ echo -n "[FORMAT]"
+ ARGS="--integrity $1 --journal-watermark $4 --journal-commit-time $5 --journal-integrity $6 --journal-integrity-key-file $7 --journal-integrity-key-size $8"
+ $INTSETUP format -q --tag-size $2 --sector-size $3 $ARGS $DEV || fail "Cannot format device."
+
+ echo -n "[ACTIVATE]"
+
+ $INTSETUP open $DEV $DEV_NAME $ARGS || fail "Cannot activate device."
+
+ echo -n "[KEYED HASH]"
+ KEY_HEX=$(xxd -c 256 -l $8 -p $7)
+ [ -z "$KEY_HEX" ] && fail "Cannot decode key."
+ dmsetup table --showkeys $DEV_NAME | grep -q $KEY_HEX || fail "Key mismatch."
+
+ status_check "journal watermark" "${4}%"
+ status_check "journal commit time" "${5} ms"
+ status_check "journal integrity MAC" $9
+
+ echo -n "[REMOVE]"
+ $INTSETUP close $DEV_NAME || fail "Cannot deactivate device."
+ echo "[OK]"
+}
+
+
+int_journal_crypt() # crypt_alg crypt_alg_kernel crypt_key crypt_key_size
+{
+ echo -n "[JOURNAL CRYPT:$1:${4}B]"
+
+ echo -n "[FORMAT]"
+ ARGS="--journal-crypt $1 --journal-crypt-key-file $3 --journal-crypt-key-size $4"
+ $INTSETUP format -q $ARGS $DEV || fail "Cannot format device."
+
+ echo -n "[ACTIVATE]"
+ $INTSETUP open $DEV $DEV_NAME $ARGS || fail "Cannot activate device."
+
+ KEY_HEX=$(xxd -c 256 -l $4 -p $3)
+ [ -z "$KEY_HEX" ] && fail "Cannot decode key."
+ dmsetup table --showkeys $DEV_NAME | grep -q "journal_crypt:$2:$KEY_HEX" || fail "Key mismatch."
+
+ $INTSETUP close $DEV_NAME
+ echo "[OK]"
+}
+
+int_mode() # alg tag_size sector_size [keyfile keysize]
+{
+ if [ -n "$5" ] ; then
+ KEY_PARAMS="--integrity-key-file $4 --integrity-key-size $5"
+ else
+ KEY_PARAMS=""
+ fi
+
+ echo -n "[MODE TESTS:$1:$2:$3]"
+ ARGS="--tag-size $2 --sector-size $3"
+
+ $INTSETUP format -q $ARGS $KEY_PARAMS $DEV --integrity $1 || fail "Cannot format device."
+
+ echo -n "[JOURNALED WRITES]"
+ $INTSETUP open $DEV $DEV_NAME --integrity $1 $KEY_PARAMS || fail "Cannot activate device with journal."
+ status_check "mode" "read/write"
+ kernel_param_check 7 "J"
+
+ $INTSETUP close $DEV_NAME fail "Cannot deactivate device."
+
+ echo -n "[DIRECT WRITES]"
+ $INTSETUP open $DEV $DEV_NAME --integrity $1 $KEY_PARAMS --integrity-no-journal || fail "Cannot activate device without journal."
+ status_check "mode" "read/write"
+ status_check "journal" "not active"
+ kernel_param_check 7 "D"
+
+ $INTSETUP close $DEV_NAME fail "Cannot deactivate device."
+
+ echo -n "[RECOVERY MODE]"
+ $INTSETUP open $DEV $DEV_NAME --integrity $1 $KEY_PARAMS --integrity-recovery-mode || fail "Cannot activate device in recovery mode."
+ status_check "mode" "read/write recovery"
+ kernel_param_check 7 "R"
+
+ $INTSETUP close $DEV_NAME fail "Cannot deactivate device."
+
+ echo "[OK]"
+}
+
+[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
+[ ! -x "$INTSETUP" ] && skip "Cannot find $INTSETUP, test skipped."
+
+[ -n "$VALG" ] && valgrind_setup && INTSETUP=valgrind_run
+which hexdump >/dev/null 2>&1 || skip "WARNING: hexdump tool required."
+modprobe dm-integrity >/dev/null 2>&1
+dm_integrity_features
+
+add_device
+intformat crc32c crc32c 4 512 08f63eb27fb9ce2ce903b0a56429c68ce5e209253ba42154841ef045a53839d7
+intformat crc32 crc32 4 512 08f63eb27fb9ce2ce903b0a56429c68ce5e209253ba42154841ef045a53839d7
+intformat sha1 sha1 20 512 6eedd6344dab8875cd185fcd6565dfc869ab36bc57e577f40c685290b1fa7fe7
+intformat sha1 sha1 16 4096 e152ec88227b539cd9cafd8bdb587a1072d720cd6bcebe1398d4136c9e7f337b
+intformat sha256 sha256 32 512 8e5fe4119558e117bfc40e3b0f13ade3abe497b52604d4c7cca0cfd6c7f4cf11
+intformat hmac-sha256 hmac\(sha256\) 32 512 8e5fe4119558e117bfc40e3b0f13ade3abe497b52604d4c7cca0cfd6c7f4cf11 $KEY_FILE 32
+intformat sha256 sha256 32 4096 33f7dfa5163ca9f740383fb8b0919574e38a7b20a94a4170fde4238196b7c4b4
+intformat hmac-sha256 hmac\(sha256\) 32 4096 33f7dfa5163ca9f740383fb8b0919574e38a7b20a94a4170fde4238196b7c4b4 $KEY_FILE 32
+
+echo "Error detection tests:"
+int_error_detection crc32c 4 512
+int_error_detection crc32c 4 4096
+int_error_detection crc32 4 512
+int_error_detection crc32 4 4096
+int_error_detection sha1 20 512
+int_error_detection sha1 16 512
+int_error_detection sha1 20 4096
+int_error_detection sha256 32 512
+int_error_detection sha256 32 4096
+
+which xxd >/dev/null 2>&1 || skip "WARNING: xxd tool required."
+int_error_detection hmac-sha256 32 512 $KEY_FILE 32
+int_error_detection hmac-sha256 32 4096 $KEY_FILE 32
+
+echo "Journal parameters tests:"
+# Watermark is calculated in kernel, so it can be rounded down/up
+int_journal crc32 4 512 66 1000 hmac-sha256 $KEY_FILE 32 hmac\(sha256\)
+int_journal sha256 32 4096 34 5000 hmac-sha1 $KEY_FILE 16 hmac\(sha1\)
+int_journal sha1 20 512 75 9999 hmac-sha256 $KEY_FILE 32 hmac\(sha256\)
+
+echo "Journal encryption tests:"
+int_journal_crypt cbc-aes cbc\(aes\) $KEY_FILE 32
+int_journal_crypt cbc-aes cbc\(aes\) $KEY_FILE 16
+int_journal_crypt salsa20 salsa20 $KEY_FILE 32
+int_journal_crypt ctr-aes ctr\(aes\) $KEY_FILE 32
+int_journal_crypt ctr-aes ctr\(aes\) $KEY_FILE 16
+
+echo "Mode tests:"
+int_mode crc32c 4 512
+int_mode crc32 4 512
+int_mode sha1 20 512
+int_mode sha256 32 512
+int_mode hmac-sha256 32 512 $KEY_FILE 32
+int_mode hmac-sha256 32 4096 $KEY_FILE 32
+
+echo -n "Recalculate tags in-kernel:"
+add_device
+if [ -n "$DM_INTEGRITY_RECALC" ] ; then
+ $INTSETUP format -q $DEV --no-wipe || fail "Cannot format device."
+ $INTSETUP open $DEV $DEV_NAME --integrity-recalculate || fail "Cannot activate device."
+ dd if=/dev/mapper/$DEV_NAME of=/dev/null bs=1M 2>/dev/null || fail "Cannot recalculate tags in-kernel"
+ int_check_sum_only 08f63eb27fb9ce2ce903b0a56429c68ce5e209253ba42154841ef045a53839d7
+ $INTSETUP close $DEV_NAME fail "Cannot deactivate device."
+ echo "[OK]"
+else
+ echo "[N/A]"
+fi
+
+echo -n "Separate metadata device:"
+if [ -n "$DM_INTEGRITY_META" ] ; then
+ add_device
+ $INTSETUP format -q $DEV --data-device $DEV2 || fail "Cannot format device."
+ $INTSETUP open $DEV --data-device $DEV2 $DEV_NAME || fail "Cannot activate device."
+ int_check_sum_only 83ee47245398adee79bd9c0a8bc57b821e92aba10f5f9ade8a5d1fae4d8c4302
+ $INTSETUP status $DEV_NAME | grep -q 'metadata device:' || fail
+ $INTSETUP close $DEV_NAME fail "Cannot deactivate device."
+ echo "[OK]"
+else
+ echo "[N/A]"
+fi
+
+cleanup
diff --git a/tests/keyring-compat-test b/tests/keyring-compat-test
new file mode 100755
index 0000000..a24c344
--- /dev/null
+++ b/tests/keyring-compat-test
@@ -0,0 +1,199 @@
+#!/bin/bash
+
+CIPHER_XTS_PLAIN="aes-xts-plain64"
+CIPHER_CBC_ESSIV="aes-cbc-essiv:sha256"
+CIPHER_CBC_TCW="serpent-cbc-tcw"
+# TODO: mode with LMK
+
+TEST_KEYRING_NAME="keyringtest_keyring"
+
+LOGON_KEY_16_OK="dmtst:lkey_16"
+LOGON_KEY_32_OK="dmtst:lkey_32"
+LOGON_KEY_64_OK="dmtst:lkey_64"
+
+HEXKEY_16="be21aa8c733229347bd4e681891e213d";
+HEXKEY_32="bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+HEXKEY_64="34f95b96abff946b64f1339ff8653cc77c38697c93b797a496f3786e86eed7781850d5112bbae17d209b8310a8f3a034f1cd297667bc0cd1438fad28d87ef6a1"
+
+DEVSIZEMB=16
+DEVSECTORS=$((DEVSIZEMB*1024*1024/512))
+NAME=testcryptdev
+CHKS_DMCRYPT=vk_in_dmcrypt.chk
+CHKS_KEYRING=vk_in_keyring.chk
+
+PWD="aaa"
+CRYPTSETUP=../cryptsetup
+
+function remove_mapping()
+{
+ [ -b /dev/mapper/$NAME ] && dmsetup remove $NAME
+
+ # unlink whole test keyring
+ [ -n "$TEST_KEYRING" ] && keyctl unlink $TEST_KEYRING "@u" >/dev/null
+
+ rmmod scsi_debug 2>/dev/null
+
+ rm -f $CHKS_DMCRYPT $CHKS_KEYRING
+}
+
+function skip()
+{
+ [ -n "$1" ] && echo "$1"
+ remove_mapping
+ exit 77
+}
+
+function fail()
+{
+ [ -n "$1" ] && echo "$1"
+ echo "FAILED at $(caller)"
+ remove_mapping
+ exit 2
+}
+
+# $1 hexbyte key
+# $2 type
+# $3 description
+# $4 keyring
+function load_key()
+{
+ local tmp="$1"
+ shift
+ echo -n "$tmp" | xxd -r -p | keyctl padd $@ >/dev/null
+}
+
+function dm_crypt_keyring_support()
+{
+ 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.)
+
+ # run the test with dm-crypt v1.15.0+ on purpose
+ # the fix is in dm-crypt v1.18.1+
+ [ $VER_MAJ -gt 1 ] && return 0
+ [ $VER_MAJ -lt 1 ] && return 1
+ [ $VER_MIN -ge 15 ]
+}
+
+function test_and_prepare_keyring() {
+ keyctl list "@s" > /dev/null || skip "Current session keyring is unreachable, test skipped"
+ TEST_KEYRING=$(keyctl newring $TEST_KEYRING_NAME "@u" 2> /dev/null)
+ test -n "$TEST_KEYRING" || skip "Failed to create keyring in user keyring"
+ keyctl search "@s" keyring "$TEST_KEYRING" > /dev/null 2>&1 || keyctl link "@u" "@s" > /dev/null 2>&1
+ load_key "$HEXKEY_16" user test_key "$TEST_KEYRING" || skip "Kernel keyring service is useless on this system, test skipped."
+}
+
+add_device() {
+ modprobe scsi_debug $@ delay=0
+ if [ $? -ne 0 ] ; then
+ echo "This kernel seems to not support proper scsi_debug module, test skipped."
+ exit 77
+ fi
+
+ sleep 2
+ DEV=$(grep -l -e scsi_debug /sys/block/*/device/model | cut -f4 -d /)
+
+ DEV="/dev/$DEV"
+ [ -b $DEV ] || fail "Cannot find $DEV."
+}
+
+[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
+which dmsetup >/dev/null 2>&1 || skip "Cannot find dmsetup, test skipped"
+which keyctl >/dev/null 2>&1 || skip "Cannot find keyctl, test skipped"
+which xxd >/dev/null 2>&1 || skip "Cannot find xxd, test skipped"
+which sha1sum > /dev/null 2>&1 || skip "Cannot find sha1sum, test skipped"
+modprobe dm-crypt || fail "dm-crypt failed to load"
+dm_crypt_keyring_support || skip "dm-crypt doesn't support kernel keyring, test skipped."
+
+test_and_prepare_keyring
+
+add_device dev_size_mb=$DEVSIZEMB
+
+dd if=/dev/urandom of=$DEV bs=1M count=$DEVSIZEMB oflag=direct > /dev/null 2>&1 || fail
+
+#test aes cipher with xts mode, plain IV
+echo -n "Testing $CIPHER_XTS_PLAIN..."
+dmsetup create $NAME --table "0 $DEVSECTORS crypt $CIPHER_XTS_PLAIN $HEXKEY_32 0 $DEV 0" || fail
+sha1sum /dev/mapper/$NAME > $CHKS_DMCRYPT || fail
+dmsetup remove $NAME || fail
+load_key "$HEXKEY_32" logon $LOGON_KEY_32_OK "$TEST_KEYRING" || fail "Cannot load 32 byte logon key type"
+dmsetup create $NAME --table "0 $DEVSECTORS crypt $CIPHER_XTS_PLAIN :32:logon:$LOGON_KEY_32_OK 0 $DEV 0" || fail
+sha1sum /dev/mapper/$NAME > $CHKS_KEYRING || fail
+dmsetup remove $NAME || fail
+diff $CHKS_DMCRYPT $CHKS_KEYRING || fail "Plaintext checksums mismatch (corruption)"
+# same test using message
+dmsetup create $NAME --table "0 $DEVSECTORS crypt $CIPHER_XTS_PLAIN $HEXKEY_32 0 $DEV 0" || fail
+sha1sum /dev/mapper/$NAME > $CHKS_DMCRYPT || fail
+dmsetup remove $NAME || fail
+dmsetup create $NAME --table "0 $DEVSECTORS crypt $CIPHER_XTS_PLAIN $HEXKEY_32 0 $DEV 0" || fail
+dmsetup suspend $NAME || fail
+dmsetup message $NAME 0 key wipe || fail
+dmsetup message $NAME 0 "key set :32:logon:$LOGON_KEY_32_OK" || fail
+dmsetup resume $NAME || fail
+sha1sum /dev/mapper/$NAME > $CHKS_KEYRING || fail
+dmsetup remove $NAME || fail
+diff $CHKS_DMCRYPT $CHKS_KEYRING || fail "Plaintext checksums mismatch (corruption)"
+echo "OK"
+
+#test aes cipher, xts mode, essiv IV
+echo -n "Testing $CIPHER_CBC_ESSIV..."
+dmsetup create $NAME --table "0 $DEVSECTORS crypt $CIPHER_CBC_ESSIV $HEXKEY_16 0 $DEV 0" || fail
+sha1sum /dev/mapper/$NAME > $CHKS_DMCRYPT || fail
+dmsetup remove $NAME || fail
+load_key "$HEXKEY_16" logon $LOGON_KEY_16_OK "$TEST_KEYRING" || fail "Cannot load 16 byte logon key type"
+dmsetup create $NAME --table "0 $DEVSECTORS crypt $CIPHER_CBC_ESSIV :16:logon:$LOGON_KEY_16_OK 0 $DEV 0" || fail
+sha1sum /dev/mapper/$NAME > $CHKS_KEYRING || fail
+dmsetup remove $NAME || fail
+diff $CHKS_DMCRYPT $CHKS_KEYRING || fail "Plaintext checksums mismatch (corruption)"
+# same test using message
+dmsetup create $NAME --table "0 $DEVSECTORS crypt $CIPHER_CBC_ESSIV $HEXKEY_16 0 $DEV 0" || fail
+sha1sum /dev/mapper/$NAME > $CHKS_DMCRYPT || fail
+dmsetup remove $NAME || fail
+dmsetup create $NAME --table "0 $DEVSECTORS crypt $CIPHER_CBC_ESSIV $HEXKEY_16 0 $DEV 0" || fail
+dmsetup suspend $NAME || fail
+dmsetup message $NAME 0 key wipe || fail
+dmsetup message $NAME 0 "key set :16:logon:$LOGON_KEY_16_OK" || fail
+dmsetup resume $NAME || fail
+sha1sum /dev/mapper/$NAME > $CHKS_KEYRING || fail
+dmsetup remove $NAME || fail
+diff $CHKS_DMCRYPT $CHKS_KEYRING || fail "Plaintext checksums mismatch (corruption)"
+echo "OK"
+
+#test serpent cipher, cbc mode, tcw IV
+echo -n "Testing $CIPHER_CBC_TCW..."
+dmsetup create $NAME --table "0 $DEVSECTORS crypt $CIPHER_CBC_TCW $HEXKEY_64 0 $DEV 0" || fail
+sha1sum /dev/mapper/$NAME > $CHKS_DMCRYPT || fail
+dmsetup remove $NAME || fail
+load_key "$HEXKEY_64" logon $LOGON_KEY_64_OK "$TEST_KEYRING" || fail "Cannot load 16 byte logon key type"
+dmsetup create $NAME --table "0 $DEVSECTORS crypt $CIPHER_CBC_TCW :64:logon:$LOGON_KEY_64_OK 0 $DEV 0" || fail
+sha1sum /dev/mapper/$NAME > $CHKS_KEYRING || fail
+dmsetup remove $NAME || fail
+diff $CHKS_DMCRYPT $CHKS_KEYRING || fail "Plaintext checksum mismatch (corruption)"
+# same test using message
+dmsetup create $NAME --table "0 $DEVSECTORS crypt $CIPHER_CBC_TCW $HEXKEY_64 0 $DEV 0" || fail
+sha1sum /dev/mapper/$NAME > $CHKS_DMCRYPT || fail
+dmsetup remove $NAME || fail
+dmsetup create $NAME --table "0 $DEVSECTORS crypt $CIPHER_CBC_TCW $HEXKEY_64 0 $DEV 0" || fail
+dmsetup suspend $NAME || fail
+dmsetup message $NAME 0 key wipe || fail
+dmsetup message $NAME 0 "key set :64:logon:$LOGON_KEY_64_OK" || fail
+dmsetup resume $NAME || fail
+sha1sum /dev/mapper/$NAME > $CHKS_KEYRING || fail
+dmsetup remove $NAME || fail
+diff $CHKS_DMCRYPT $CHKS_KEYRING || fail "Plaintext checksums mismatch (corruption)"
+echo "OK"
+
+echo -n "Test LUKS2 key refresh..."
+echo $PWD | $CRYPTSETUP luksFormat --type luks2 --luks2-metadata-size 16k --luks2-keyslots-size 4064k --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --force-password $DEV || fail
+echo $PWD | $CRYPTSETUP open $DEV $NAME || fail
+$CRYPTSETUP status $NAME | grep -q -i "location:.*keyring" || skip "LUKS2 can't use keyring. Test skipped."
+dd if=/dev/mapper/$NAME bs=1M iflag=direct status=none | sha1sum > $CHKS_KEYRING || fail
+echo $PWD | $CRYPTSETUP refresh $NAME --disable-keyring || fail
+$CRYPTSETUP status $NAME | grep -q -i "location:.*keyring" && fail "Key is still in keyring"
+dd if=/dev/mapper/$NAME bs=1M iflag=direct status=none | sha1sum > $CHKS_DMCRYPT || fail
+diff $CHKS_DMCRYPT $CHKS_KEYRING || fail "Plaintext checksum mismatch (corruption)"
+echo "OK"
+
+remove_mapping
diff --git a/tests/keyring-test b/tests/keyring-test
new file mode 100755
index 0000000..ca90d0a
--- /dev/null
+++ b/tests/keyring-test
@@ -0,0 +1,237 @@
+#!/bin/bash
+
+DEV_ZERO="dmtst-zero"
+DEV_CRYPT="dmtst-crypt"
+
+CIPHER="aes-xts-plain64"
+
+TEST_KEYRING_NAME="keyringtest_keyring"
+
+USER_KEY_32_OK="dmtst:ukey_32_ok"
+USER_KEY_32_WRONG="dmtst:ukey_32_wrong_size"
+
+LOGON_KEY_32_OK="dmtst:lkey_32_ok"
+LOGON_KEY_32_WRONG="dmtst:lkey_32_wrong_size"
+
+PAYLOAD_32="bb21158c733229347bd4e681891e213d"
+PAYLOAD_31="bb21158c733229347bd4e681891e213"
+
+HEXKEY_32="bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+HEXKEY_32_BAD="bb21158c733229347bd4e68189XXXX3d94c685be6a5b84818afe7a78a6de7a1a"
+HEXKEY_31="bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a"
+
+function remove_mapping()
+{
+ [ -b /dev/mapper/$DEV_CRYPT ] && dmsetup remove $DEV_CRYPT
+ [ -b /dev/mapper/$DEV_ZERO ] && dmsetup remove $DEV_ZERO
+
+ # unlink whole test keyring
+ [ -n "$TEST_KEYRING" ] && keyctl unlink $TEST_KEYRING "@u" >/dev/null
+}
+
+function skip()
+{
+ [ -n "$1" ] && echo "$1"
+ remove_mapping
+ exit 77
+}
+
+function fail()
+{
+ [ -n "$1" ] && echo "$1"
+ echo "FAILED at line $(caller)"
+ remove_mapping
+ exit 2
+}
+
+# $1 type
+# $2 description
+# $3 payload
+# $4 keyring
+function load_key()
+{
+ keyctl add $@ >/dev/null
+}
+
+function dm_crypt_keyring_support()
+{
+ 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_MAJ -gt 1 ] && return 0
+ [ $VER_MAJ -lt 1 ] && return 1
+ [ $VER_MIN -ge 15 ]
+}
+
+function test_and_prepare_keyring() {
+ keyctl list "@s" > /dev/null || skip "Current session keyring is unreachable, test skipped"
+ TEST_KEYRING=$(keyctl newring $TEST_KEYRING_NAME "@u" 2> /dev/null)
+ test -n "$TEST_KEYRING" || skip "Failed to create keyring in user keyring"
+ keyctl search "@s" keyring "$TEST_KEYRING" > /dev/null 2>&1 || keyctl link "@u" "@s" > /dev/null 2>&1
+ load_key user test_key test_data "$TEST_KEYRING" || skip "Kernel keyring service is useless on this system, test skipped."
+}
+
+[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
+which dmsetup >/dev/null 2>&1 || skip "Cannot find dmsetup, test skipped"
+which keyctl >/dev/null 2>&1 || skip "Cannot find keyctl, test skipped"
+modprobe dm-crypt || fail "dm-crypt failed to load"
+dm_crypt_keyring_support || skip "dm-crypt doesn't support kernel keyring, test skipped."
+
+test_and_prepare_keyring
+
+load_key logon $LOGON_KEY_32_OK $PAYLOAD_32 "$TEST_KEYRING" || fail "Cannot load 32 byte logon key type"
+load_key user $USER_KEY_32_OK $PAYLOAD_32 "$TEST_KEYRING" || fail "Cannot load 32 byte user key type"
+load_key logon $LOGON_KEY_32_WRONG $PAYLOAD_31 "$TEST_KEYRING" || fail "Cannot load 31 byte logon key type"
+load_key user $USER_KEY_32_WRONG $PAYLOAD_31 "$TEST_KEYRING" || fail "Cannot load 31 byte user key type"
+
+dmsetup create $DEV_ZERO --table "0 100 zero" || fail
+
+echo "[1] Valid keyring keys"
+
+# load logon type kernel key
+KEY=":32:logon:$LOGON_KEY_32_OK"
+dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $KEY 0 /dev/mapper/$DEV_ZERO 0" || fail
+dmsetup table --showkeys $DEV_CRYPT | grep -q "crypt $CIPHER $KEY 0" || fail
+dmsetup remove $DEV_CRYPT || fail
+
+# load user type kernel key
+KEY=":32:user:$USER_KEY_32_OK"
+dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $KEY 0 /dev/mapper/$DEV_ZERO 0" || fail
+dmsetup table --showkeys $DEV_CRYPT | grep -q "crypt $CIPHER $KEY 0" || fail
+dmsetup remove $DEV_CRYPT || fail
+
+# load logon type kernel key...
+KEY=":32:logon:$LOGON_KEY_32_OK"
+dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $KEY 0 /dev/mapper/$DEV_ZERO 0" || fail
+dmsetup suspend $DEV_CRYPT || fail
+dmsetup message $DEV_CRYPT 0 "key wipe" || fail
+# ...replace the key with hexkey...
+dmsetup message $DEV_CRYPT 0 "key set $HEXKEY_32" || fail
+dmsetup table --showkeys $DEV_CRYPT | grep -q "crypt $CIPHER $HEXKEY_32 0" || fail
+dmsetup resume $DEV_CRYPT || fail
+dmsetup suspend $DEV_CRYPT || fail
+# ...and replace it again with user type kernel key...
+dmsetup message $DEV_CRYPT 0 "key set :32:user:$USER_KEY_32_OK" || fail
+dmsetup table --showkeys $DEV_CRYPT | grep -q "crypt $CIPHER :32:user:$USER_KEY_32_OK 0" || fail
+dmsetup message $DEV_CRYPT 0 "key set $HEXKEY_32" || fail
+dmsetup table --showkeys $DEV_CRYPT | grep -q "crypt $CIPHER $HEXKEY_32 0" || fail
+dmsetup resume $DEV_CRYPT || fail
+dmsetup remove $DEV_CRYPT || fail
+
+dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $HEXKEY_32 0 /dev/mapper/$DEV_ZERO 0" || fail
+dmsetup suspend $DEV_CRYPT || fail
+dmsetup message $DEV_CRYPT 0 "key wipe" || fail
+dmsetup message $DEV_CRYPT 0 "key set :32:user:$USER_KEY_32_OK" || fail
+dmsetup resume $DEV_CRYPT || fail
+dmsetup suspend $DEV_CRYPT || fail
+dmsetup message $DEV_CRYPT 0 "key set :32:logon:$LOGON_KEY_32_OK" || fail
+dmsetup resume $DEV_CRYPT || fail
+dmsetup remove $DEV_CRYPT || fail
+
+echo "[2] message ioctl"
+dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $HEXKEY_32 0 /dev/mapper/$DEV_ZERO 0" || fail
+dmsetup suspend $DEV_CRYPT || fail
+dmsetup message $DEV_CRYPT 0 "key set :32:logon:$LOGON_KEY_32_WRONG" 2> /dev/null && fail
+# old key should be intact and valid
+dmsetup table --showkeys $DEV_CRYPT | grep -q "crypt $CIPHER $HEXKEY_32 0" || fail
+dmsetup resume $DEV_CRYPT || fail
+dmsetup suspend $DEV_CRYPT || fail
+# now the key gets destroyed by invalid input
+dmsetup message $DEV_CRYPT 0 "key set $HEXKEY_32_BAD" 2> /dev/null && fail
+dmsetup resume $DEV_CRYPT 2> /dev/null && fail
+# hmm... see the output. don't like it
+# dmsetup table --showkeys $DEV_CRYPT
+
+dmsetup message $DEV_CRYPT 0 "key set :32:user:$USER_KEY_32_OK" || fail
+dmsetup table --showkeys $DEV_CRYPT | grep -q "crypt $CIPHER :32:user:$USER_KEY_32_OK 0" || fail
+dmsetup message $DEV_CRYPT 0 "key set :31:logon:$LOGON_KEY_32_OK" 2> /dev/null && fail
+dmsetup message $DEV_CRYPT 0 "key set :" 2> /dev/null && fail
+dmsetup message $DEV_CRYPT 0 "key set ::::" 2> /dev/null && fail
+dmsetup message $DEV_CRYPT 0 "key set :0:logon:$LOGON_KEY_32_OK" 2> /dev/null && fail
+dmsetup message $DEV_CRYPT 0 "key set :32" 2> /dev/null && fail
+dmsetup message $DEV_CRYPT 0 "key set :32:" 2> /dev/null && fail
+dmsetup message $DEV_CRYPT 0 "key set :32:logon" 2> /dev/null && fail
+dmsetup message $DEV_CRYPT 0 "key set :32:logo" 2> /dev/null && fail
+dmsetup message $DEV_CRYPT 0 "key set :32:logon:" 2> /dev/null && fail
+dmsetup table --showkeys $DEV_CRYPT | grep -q "crypt $CIPHER :32:user:$USER_KEY_32_OK 0" || fail
+dmsetup message $DEV_CRYPT 0 "key set :32:user:$USER_KEY_32_OK" || fail
+dmsetup resume $DEV_CRYPT || fail
+dmsetup remove $DEV_CRYPT || fail
+
+echo "[3] bOrked keys"
+# declare the key having 32 bytes but load key which has in fact 31 bytes only
+KEY=":32:logon:$LOGON_KEY_32_WRONG"
+dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $KEY 0 /dev/mapper/$DEV_ZERO 0" 2> /dev/null && fail "dm-crypt accepted wrong key size"
+
+# declare the key having 31 bytes (incompatible with cipher) and load key with 32 bytes in real
+KEY=":31:logon:$LOGON_KEY_32_WRONG"
+dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $KEY 0 /dev/mapper/$DEV_ZERO 0" 2> /dev/null && fail "dm-crypt accepted wrong key size"
+
+# declare the key being user type but try to load logon one
+KEY=":32:user:$LOGON_KEY_32"
+dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $KEY 0 /dev/mapper/$DEV_ZERO 0" 2> /dev/null && fail "dm-crypt accepted key description for invalid key type"
+
+# now the other way
+KEY=":32:logon:$USER_KEY_32"
+dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $KEY 0 /dev/mapper/$DEV_ZERO 0" 2> /dev/null && fail "dm-crypt accepted key description for invalid key type"
+
+BORKED_KEYS=":\ 32:logon:$LOGON_KEY_32_OK
+: 32:logon:$LOGON_KEY_32_OK
+:+32:logon:$LOGON_KEY_32_OK
+:-32:logon:$LOGON_KEY_32_OK
+:32 :logon:$LOGON_KEY_32_OK
+:32\ :logon:$LOGON_KEY_32_OK
+:32_:logon:$LOGON_KEY_32_OK
+:32+:logon:$LOGON_KEY_32_OK
+:30+2:logon:$LOGON_KEY_32_OK
+:32+0:logon:$LOGON_KEY_32_OK
+:32: logon:$LOGON_KEY_32_OK
+:32:\ logon:$LOGON_KEY_32_OK
+:32:logonA:$LOGON_KEY_32_OK
+:32:logo:$LOGON_KEY_32_OK
+:32:llogon:$LOGON_KEY_32_OK
+:32xlogon:$LOGON_KEY_32_OK
+:32logon:$LOGON_KEY_32_OK
+:32:logonx$LOGON_KEY_32_OK
+:32:logon$LOGON_KEY_32_OK
+: 32:user:$USER_KEY_32_OK
+:\ 32:user:$USER_KEY_32_OK
+:+32:user:$USER_KEY_32_OK
+:-32:user:$USER_KEY_32_OK
+:32 :user:$USER_KEY_32_OK
+:32\ :user:$USER_KEY_32_OK
+:32_:user:$USER_KEY_32_OK
+:32+:user:$USER_KEY_32_OK
+:30+2:user:$USER_KEY_32_OK
+:32+0:user:$USER_KEY_32_OK
+:32: user:$USER_KEY_32_OK
+:32:\ user:$USER_KEY_32_OK
+:32:userA:$USER_KEY_32_OK
+:32:use:$USER_KEY_32_OK
+:32:uuser:$USER_KEY_32_OK
+:32xuser:$USER_KEY_32_OK
+:32user:$USER_KEY_32_OK
+:32:userx$USER_KEY_32_OK
+:32:user$USER_KEY_32_OK
+:32:userlogon:$USER_KEY_32_OK
+:32:userlogon:$LOGON_KEY_32_OK
+:32:logonuser:$USER_KEY_32_OK
+:32:logonuser:$LOGON_KEY_32_OK
+:32:logon:user:$USER_KEY_32_OK
+:32:logon:user:$LOGON_KEY_32_OK
+:32:user:logon:$USER_KEY_32_OK
+:32:user:logon:$LOGON_KEY_32_OK"
+
+# TODO: add tests with whitespace in key description (not possible with current libdevmapper)
+
+IFS="
+"
+
+for key in $BORKED_KEYS; do
+ dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $key 0 /dev/mapper/$DEV_ZERO 0" 2> /dev/null && fail "dm-crypt accepted seriously borked key string"
+done
+
+remove_mapping
diff --git a/tests/loopaes-test b/tests/loopaes-test
new file mode 100755
index 0000000..fe593aa
--- /dev/null
+++ b/tests/loopaes-test
@@ -0,0 +1,174 @@
+#!/bin/bash
+
+CRYPTSETUP=../cryptsetup
+
+# try to validate using loop-AES losetup/kernel if available
+LOSETUP_AES=/losetup-aes.old
+
+LOOP_DD_PARAM="bs=1k count=10000"
+DEV_NAME=dummy
+IMG=loopaes.img
+KEYv1=key_v1
+KEYv2=key_v2
+KEYv3=key_v3
+LOOPDEV=$(losetup -f 2>/dev/null)
+
+function dmremove() { # device
+ udevadm settle >/dev/null 2>&1
+ dmsetup remove $1 >/dev/null 2>&1
+}
+
+function remove_mapping()
+{
+ [ -b /dev/mapper/$DEV_NAME2 ] && dmremove $DEV_NAME2
+ [ -b /dev/mapper/$DEV_NAME ] && dmremove $DEV_NAME
+ losetup -d $LOOPDEV >/dev/null 2>&1
+ rm -f $IMG $KEYv1 $KEYv2 $KEYv3 >/dev/null 2>&1
+}
+
+function fail()
+{
+ echo "FAILED at line $(caller)"
+ remove_mapping
+ exit 2
+}
+
+function skip()
+{
+ [ -n "$1" ] && echo "$1"
+ exit 77
+}
+
+function prepare()
+{
+ remove_mapping
+ dd if=/dev/zero of=$IMG $LOOP_DD_PARAM >/dev/null 2>&1
+ sync
+ losetup $LOOPDEV $IMG
+
+ # Prepare raw key: v1 - one key, v2 - 64 keys, v3 - 64 + one IV
+ if [ ! -e $KEYv3 ]; then
+ head -c 3705 /dev/urandom | uuencode -m - | head -n 66 | tail -n 65 >$KEYv3
+ head -n 1 $KEYv3 > $KEYv1
+ head -n 64 $KEYv3 > $KEYv2
+ fi
+ [ -n "$1" ] && echo -n "$1 "
+}
+
+function check_exists()
+{
+ [ -b /dev/mapper/$DEV_NAME ] || fail
+}
+
+function get_offset_params() # $offset
+{
+ offset=$1
+ if [ "${offset:0:1}" = "@" ] ; then
+ echo "-o $((${offset:1} / 512)) -p 0"
+ else
+ echo "-o $((offset / 512))"
+ fi
+}
+
+function get_expsum() # $offset
+{
+ case $1 in
+ 0)
+ echo "31e00e0e4c233c89051cd748122fde2c98db0121ca09ba93a3820817ea037bc5"
+ ;;
+ @8192 | 8192)
+ echo "bfd94392d1dd8f5d477251d21b3c736e177a4945cd4937847fc7bace82996aed"
+ ;;
+ @8388608 | 8388608)
+ echo "33838fe36928a929bd7971bed7e82bd426c88193fcd692c2e6f1b9c9bfecd4d6"
+ ;;
+ *) fail
+ ;;
+ esac
+}
+
+function check_sum() # $key $keysize $offset [stdin|keyfile]
+{
+ # Fill device with zeroes and reopen it
+ dd if=/dev/zero of=/dev/mapper/$DEV_NAME bs=1k $LOOP_DD_PARAM >/dev/null 2>&1
+ sync
+ dmremove $DEV_NAME
+
+ EXPSUM=$(get_expsum $3)
+ if [ "$4" == "stdin" ] ; then
+ cat $1 | $CRYPTSETUP loopaesOpen $LOOPDEV $DEV_NAME -s $2 --key-file - $(get_offset_params $3) >/dev/null 2>&1
+ else
+ $CRYPTSETUP loopaesOpen $LOOPDEV $DEV_NAME -s $2 --key-file $1 $(get_offset_params $3) >/dev/null 2>&1
+ fi
+ ret=$?
+ VSUM=$(sha256sum /dev/mapper/$DEV_NAME | cut -d' ' -f 1)
+ if [ $ret -eq 0 -a "$VSUM" = "$EXPSUM" ] ; then
+ echo -n "[$4:OK]"
+ else
+ echo "[$4:FAIL]"
+ [ "$VSUM" != "$EXPSUM" ] && echo " Expecting $EXPSUM got $VSUM."
+ fail
+ fi
+}
+
+function check_sum_losetup() # $key $alg
+{
+ [ ! -x $LOSETUP_AES ] && echo && return
+
+ echo -n " Verification using loop-AES: "
+
+ losetup -d $LOOPDEV >/dev/null 2>&1
+ cat $1 | $LOSETUP_AES -p 0 -e $2 -o $3 $LOOPDEV $IMG
+ ret=$?
+ VSUM=$(sha256sum $LOOPDEV | cut -d' ' -f 1)
+ if [ $ret -eq 0 -a "$VSUM" = "$EXPSUM" ] ; then
+ echo "[OK]"
+ else
+ echo "[FAIL]"
+ [ "$VSUM" != "$EXPSUM" ] && echo " Expecting $EXPSUM got $VSUM (loop-AES)."
+ fail
+ fi
+ losetup -d $LOOPDEV >/dev/null 2>&1
+}
+
+function check_version()
+{
+ VER_STR=$(dmsetup version | grep Driver)
+ VER_MIN=$(echo $VER_STR | cut -f 2 -d.)
+ VER_PATCH=$(echo $VER_STR | cut -f 3 -d.)
+
+ test $VER_MIN -lt 19 && return 1
+ test $VER_MIN -eq 19 -a $VER_PATCH -ge 6 && return 1 # RHEL
+ return 0
+}
+
+[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
+[ -z "$LOOPDEV" ] && skip "Cannot find free loop device, test skipped."
+which uuencode >/dev/null 2>&1 || skip "WARNING: test require uuencode binary, test skipped."
+check_version || skip "Probably old kernel, test skipped."
+
+# loop-AES tests
+KEY_SIZES="128 256"
+KEY_FILES="$KEYv1 $KEYv2 $KEYv3"
+DEV_OFFSET="0 8192 @8192 8388608 @8388608"
+
+for key_size in $KEY_SIZES ; do
+ for key in $KEY_FILES ; do
+ for offset in $DEV_OFFSET ; do
+ prepare "Open loop-AES $key / AES-$key_size / offset $offset"
+ $CRYPTSETUP loopaesOpen $LOOPDEV $DEV_NAME \
+ -s $key_size --key-file $key $(get_offset_params $offset) \
+ 2>/dev/null
+ [ $? -ne 0 ] && echo "[SKIPPED]" && continue
+ check_exists
+ check_sum $key $key_size $offset keyfile
+ $CRYPTSETUP loopaesClose $DEV_NAME || fail
+ check_sum $key $key_size $offset stdin
+ $CRYPTSETUP loopaesClose $DEV_NAME || fail
+ check_sum_losetup $key AES$key_size $offset
+ done
+ done
+done
+
+remove_mapping
+exit 0
diff --git a/tests/luks1-compat-test b/tests/luks1-compat-test
new file mode 100755
index 0000000..2ac6c95
--- /dev/null
+++ b/tests/luks1-compat-test
@@ -0,0 +1,102 @@
+#!/bin/bash
+
+# check luks1 images parsing
+
+# NOTE: if image with whirlpool hash fails, check
+# that you are not using old gcrypt with flawed whirlpool
+# (see cryptsetup debug output)
+
+CRYPTSETUP=../cryptsetup
+TST_DIR=luks1-images
+MAP=luks1tst
+KEYFILE=keyfile1
+
+[ -z "$srcdir" ] && srcdir="."
+
+function remove_mapping()
+{
+ [ -b /dev/mapper/$MAP ] && dmsetup remove $MAP
+}
+
+function fail()
+{
+ [ -n "$1" ] && echo "$1"
+ echo " [FAILED]"
+ echo "FAILED at line $(caller)"
+ remove_mapping
+ exit 2
+}
+
+function skip()
+{
+ [ -n "$1" ] && echo "$1"
+ echo "Test skipped."
+ exit 77
+}
+
+function test_one()
+{
+ $CRYPTSETUP benchmark -c "$1" -s "$2" | grep -v "#" || skip
+}
+
+function test_required()
+{
+ which lsblk >/dev/null 2>&1 || skip "WARNING: lsblk tool required."
+
+ echo "REQUIRED KDF TEST"
+ $CRYPTSETUP benchmark -h whirlpool | grep "N/A" && skip
+
+ echo "REQUIRED CIPHERS TEST"
+ echo "# Algorithm | Key | Encryption | Decryption"
+
+ test_one aes-xts 256
+ test_one twofish-xts 256
+ test_one serpent-xts 256
+ test_one aes-cbc 256
+ test_one aes-lrw 256
+}
+
+export LANG=C
+
+test_required
+[ ! -d $TST_DIR ] && tar xJf $srcdir/luks1-images.tar.xz --no-same-owner
+
+echo "PASSPHRASE CHECK"
+for file in $(ls $TST_DIR/luks1_*) ; do
+ echo -n " $file"
+ $CRYPTSETUP luksOpen -d $TST_DIR/$KEYFILE $file --test-passphrase 2>/dev/null
+ ret=$?
+ # ignore missing whirlpool (pwd failed is exit code 2)
+ [ $ret -eq 1 ] && (echo $file | grep -q -e "whirlpool") && echo " [N/A]" && continue
+ # ignore flawed whirlpool (pwd failed is exit code 2)
+ [ $ret -eq 2 ] && (echo $file | grep -q -e "whirlpool") && \
+ ($CRYPTSETUP luksDump $file --debug | grep -q -e "flawed whirlpool") && \
+ echo " [IGNORED (flawed Whirlpool library)]" && continue
+ [ $ret -ne 0 ] && fail
+ echo " [OK]"
+done
+
+if [ $(id -u) != 0 ]; then
+ echo "WARNING: You must be root to run activation part of test, test skipped."
+ exit 0
+fi
+
+echo "ACTIVATION FS UUID CHECK"
+for file in $(ls $TST_DIR/luks1_*) ; do
+ echo -n " $file"
+ $CRYPTSETUP luksOpen -d $TST_DIR/$KEYFILE $file $MAP 2>/dev/null
+ ret=$?
+ # ignore missing whirlpool (pwd failed is exit code 2)
+ [ $ret -eq 1 ] && (echo $file | grep -q -e "whirlpool") && echo " [N/A]" && continue
+ # ignore flawed whirlpool (pwd failed is exit code 2)
+ [ $ret -eq 2 ] && (echo $file | grep -q -e "whirlpool") && \
+ ($CRYPTSETUP luksDump $file --debug | grep -q -e "flawed whirlpool") && \
+ echo " [IGNORED (flawed Whirlpool library)]" && continue
+ [ $ret -ne 0 ] && fail
+ $CRYPTSETUP status $MAP >/dev/null || fail
+ $CRYPTSETUP status /dev/mapper/$MAP >/dev/null || fail
+ UUID=$(lsblk -n -o UUID /dev/mapper/$MAP)
+ $CRYPTSETUP remove $MAP || fail
+ [ "$UUID" != "DEAD-BABE" ] && fail "UUID check failed."
+ echo " [OK]"
+done
diff --git a/tests/luks1-images.tar.xz b/tests/luks1-images.tar.xz
new file mode 100644
index 0000000..dd099c6
--- /dev/null
+++ b/tests/luks1-images.tar.xz
Binary files differ
diff --git a/tests/luks2-integrity-test b/tests/luks2-integrity-test
new file mode 100755
index 0000000..5d49fce
--- /dev/null
+++ b/tests/luks2-integrity-test
@@ -0,0 +1,168 @@
+#!/bin/bash
+#
+# Test cryptsetup/authenticated encryption compatibility.
+#
+CRYPTSETUP=../cryptsetup
+DEV_NAME=dmi_test
+DEV=mode-test.img
+PWD1=nHjJHjI23JK
+KEY_FILE=key.img
+FAST_PBKDF_OPT="--pbkdf pbkdf2 --pbkdf-force-iterations 1000"
+
+dmremove() { # device
+ udevadm settle >/dev/null 2>&1
+ dmsetup remove $1 >/dev/null 2>&1
+}
+
+cleanup() {
+ [ -b /dev/mapper/$DEV_NAME ] && dmremove $DEV_NAME
+ [ -b /dev/mapper/"$DEV_NAME"_dif ] && dmremove "$DEV_NAME"_dif
+ rm -f $DEV $KEY_FILE >/dev/null 2>&1
+}
+
+fail()
+{
+ echo
+ [ -n "$1" ] && echo "FAIL: $1"
+ echo "FAILED at line $(caller)"
+ cleanup
+ exit 100
+}
+
+skip()
+{
+ [ -n "$1" ] && echo "$1"
+ exit 77
+}
+
+add_device() {
+ cleanup
+ dd if=/dev/urandom of=$KEY_FILE bs=1 count=512 >/dev/null 2>&1
+ dd if=/dev/zero of=$DEV bs=1M count=32 >/dev/null 2>&1
+ sync
+}
+
+status_check() # name value
+{
+ #$CRYPTSETUP status $DEV_NAME
+ X=$($CRYPTSETUP status $DEV_NAME | grep -m1 "$1" | sed -e 's/.*:[ \t]\+//' | cut -d' ' -f1)
+ if [ "$X" != "$2" ] ; then
+ echo "[status FAIL]"
+ echo " Expecting $1:$2 got \"$X\"."
+ fail
+ fi
+}
+
+dump_check() # name value
+{
+ #$CRYPTSETUP luksDump $DEV
+ X=$($CRYPTSETUP luksDump $DEV | grep -m1 "$1" | sed -e 's/.*:[ \t]\+//' | cut -d' ' -f1)
+ if [ "$X" != "$2" ] ; then
+ echo "[dump FAIL]"
+ echo " Expecting $1:$2 got \"$X\"."
+ fail
+ fi
+}
+
+int_check_sum() # alg checksum
+{
+ VSUM=$(sha256sum /dev/mapper/$DEV_NAME | cut -d' ' -f 1)
+ if [ "$VSUM" = "$2" ] ; then
+ echo -n "[CHECKSUM]"
+ else
+ echo "[FAIL]"
+ echo " Expecting $2 got $VSUM."
+ fail
+ fi
+}
+
+int_error_detection() # alg int sector_size
+{
+ # FIXME: this is just a trivial failure
+ echo -n "[DETECT_CORRUPTION]"
+ echo -n "XXXXX" | dd of=$DEV bs=1M seek=28 count=1 conv=notrunc >/dev/null 2>&1 || fail "Cannot write to device."
+ $CRYPTSETUP open -d $KEY_FILE $DEV $DEV_NAME || fail "Cannot activate device."
+ dd if=/dev/mapper/$DEV_NAME of=/dev/null >/dev/null 2>&1 && fail "Error detection failed."
+ $CRYPTSETUP close $DEV_NAME || fail "Cannot deactivate device."
+}
+
+intformat() # alg integrity integrity_out key_size int_key_size sector_size csum
+{
+ echo -n "[$1:$2:$4:$6]"
+ echo -n "[FORMAT]"
+ $CRYPTSETUP luksFormat --type luks2 -q -c $1 --integrity $2 --sector-size $6 -s $4 \
+ $FAST_PBKDF_OPT -d $KEY_FILE $DEV --offset 8192 >/dev/null 2>&1
+ if [ $? -ne 0 ] ; then
+ echo "[N/A]"
+ return
+ fi
+ dump_check "cipher" $1
+ dump_check "sector" $6
+ dump_check "integrity" $3
+ dump_check "Key:" $(($4 + $5))
+ echo -n "[ACTIVATE]"
+ $CRYPTSETUP open -d $KEY_FILE $DEV $DEV_NAME || fail "Cannot activate device."
+ status_check "cipher" $1
+ status_check "sector size" $6
+ status_check "integrity:" $3
+ status_check "keysize:" $(($4 + $5))
+ [ $5 -gt 0 ] && status_check "integrity keysize:" $5
+ int_check_sum $1 $7
+ echo -n "[REMOVE]"
+ $CRYPTSETUP close $DEV_NAME || fail "Cannot deactivate device."
+ int_error_detection
+ echo "[OK]"
+}
+
+
+[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+modprobe dm-integrity >/dev/null 2>&1
+dmsetup targets | grep integrity >/dev/null 2>&1 || skip "Cannot find dm-integrity target, test skipped."
+
+add_device
+
+intformat aes-xts-plain64 hmac-sha256 hmac\(sha256\) 256 256 512 ee501705a084cd0ab6f4a28014bcf62b8bfa3434de00b82743c50b3abf06232c
+intformat aes-xts-random hmac-sha256 hmac\(sha256\) 256 256 512 492c2d1cc9e222a850c399bfef4ed5a86bf5afc59e54f0f0c7ba8e2a64548323
+intformat aes-xts-plain64 hmac-sha256 hmac\(sha256\) 512 256 512 ee501705a084cd0ab6f4a28014bcf62b8bfa3434de00b82743c50b3abf06232c
+intformat aes-xts-random hmac-sha256 hmac\(sha256\) 512 256 512 492c2d1cc9e222a850c399bfef4ed5a86bf5afc59e54f0f0c7ba8e2a64548323
+intformat aes-xts-plain64 hmac-sha256 hmac\(sha256\) 256 256 4096 358d6beceddf593aff6b22c31684e0df9c226330aff5812e060950215217d21b
+intformat aes-xts-random hmac-sha256 hmac\(sha256\) 256 256 4096 8c0463f5ac09613674bdf40b0ff6f985edbc3de04e51fdc688873cb333ef3cda
+intformat aes-xts-plain64 hmac-sha256 hmac\(sha256\) 512 256 4096 358d6beceddf593aff6b22c31684e0df9c226330aff5812e060950215217d21b
+intformat aes-xts-random hmac-sha256 hmac\(sha256\) 512 256 4096 8c0463f5ac09613674bdf40b0ff6f985edbc3de04e51fdc688873cb333ef3cda
+
+intformat aes-xts-plain64 hmac-sha512 hmac\(sha512\) 512 512 4096 9873d864fccb866521e79c9f0f75ad0c578d6bd7620399bbf4779e698c6e92fd
+intformat aes-xts-random hmac-sha512 hmac\(sha512\) 512 512 4096 621f6c03f7361c2bf8f10059ae822339223f8471c750b0cf8584fba7134bd4a2
+
+intformat aes-xts-plain64 hmac-sha1 hmac\(sha1\) 512 160 4096 7370c66a92708fb71b186931468be6aa9b26f4f88373b00b1c57360b9ee1304e
+intformat aes-xts-random hmac-sha1 hmac\(sha1\) 512 160 4096 8c0463f5ac09613674bdf40b0ff6f985edbc3de04e51fdc688873cb333ef3cda
+
+intformat aes-gcm-random aead aead 128 0 512 5f6f3f6be03c74d9aaaeaf40dd310c99a20e2786045f78a1fc6a0b189d231f57
+intformat aes-gcm-random aead aead 128 0 4096 358d6beceddf593aff6b22c31684e0df9c226330aff5812e060950215217d21b
+intformat aes-gcm-random aead aead 256 0 512 5f6f3f6be03c74d9aaaeaf40dd310c99a20e2786045f78a1fc6a0b189d231f57
+intformat aes-gcm-random aead aead 256 0 4096 358d6beceddf593aff6b22c31684e0df9c226330aff5812e060950215217d21b
+
+intformat aes-ccm-random aead aead 152 0 512 288e5e9bc5be6c0bd2a74abbb72c7944da83198b5e3041dcf159e7ae250dafa8
+intformat aes-ccm-random aead aead 152 0 4096 7370c66a92708fb71b186931468be6aa9b26f4f88373b00b1c57360b9ee1304e
+intformat aes-ccm-random aead aead 280 0 512 288e5e9bc5be6c0bd2a74abbb72c7944da83198b5e3041dcf159e7ae250dafa8
+intformat aes-ccm-random aead aead 280 0 4096 7370c66a92708fb71b186931468be6aa9b26f4f88373b00b1c57360b9ee1304e
+
+intformat chacha20-plain64 poly1305 poly1305 256 0 512 3f82eae753ff52a689ddc559c691bbdff838361bbe9a3ce8c7212e16e51b5dbe
+intformat chacha20-random poly1305 poly1305 256 0 512 5f6f3f6be03c74d9aaaeaf40dd310c99a20e2786045f78a1fc6a0b189d231f57
+intformat chacha20-plain64 poly1305 poly1305 256 0 4096 7370c66a92708fb71b186931468be6aa9b26f4f88373b00b1c57360b9ee1304e
+intformat chacha20-random poly1305 poly1305 256 0 4096 358d6beceddf593aff6b22c31684e0df9c226330aff5812e060950215217d21b
+
+intformat aegis128-random aead aead 128 0 512 ee501705a084cd0ab6f4a28014bcf62b8bfa3434de00b82743c50b3abf06232c
+intformat aegis128-random aead aead 128 0 4096 358d6beceddf593aff6b22c31684e0df9c226330aff5812e060950215217d21b
+intformat aegis128l-random aead aead 128 0 512 ee501705a084cd0ab6f4a28014bcf62b8bfa3434de00b82743c50b3abf06232c
+intformat aegis128l-random aead aead 128 0 4096 358d6beceddf593aff6b22c31684e0df9c226330aff5812e060950215217d21b
+intformat aegis256-random aead aead 256 0 512 492c2d1cc9e222a850c399bfef4ed5a86bf5afc59e54f0f0c7ba8e2a64548323
+intformat aegis256-random aead aead 256 0 4096 8c0463f5ac09613674bdf40b0ff6f985edbc3de04e51fdc688873cb333ef3cda
+intformat morus640-random aead aead 128 0 512 ee501705a084cd0ab6f4a28014bcf62b8bfa3434de00b82743c50b3abf06232c
+intformat morus640-random aead aead 128 0 4096 358d6beceddf593aff6b22c31684e0df9c226330aff5812e060950215217d21b
+intformat morus1280-random aead aead 128 0 512 ee501705a084cd0ab6f4a28014bcf62b8bfa3434de00b82743c50b3abf06232c
+intformat morus1280-random aead aead 128 0 4096 358d6beceddf593aff6b22c31684e0df9c226330aff5812e060950215217d21b
+intformat morus1280-random aead aead 256 0 512 ee501705a084cd0ab6f4a28014bcf62b8bfa3434de00b82743c50b3abf06232c
+intformat morus1280-random aead aead 256 0 4096 358d6beceddf593aff6b22c31684e0df9c226330aff5812e060950215217d21b
+
+cleanup
diff --git a/tests/luks2-validation-test b/tests/luks2-validation-test
new file mode 100755
index 0000000..06869ea
--- /dev/null
+++ b/tests/luks2-validation-test
@@ -0,0 +1,231 @@
+#!/bin/bash
+
+#turn on debug mode by following env. variable _DEBUG=1
+
+PS4='$LINENO:'
+CRYPTSETUP=../cryptsetup
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+START_DIR=$(pwd)
+
+IMG=luks2-backend.img
+ORIG_IMG=luks2_valid_hdr.img
+TST_IMGS=$START_DIR/luks2-images
+
+GEN_DIR=generators
+
+FAILS=0
+
+[ -z "$srcdir" ] && srcdir="."
+
+function remove_mapping()
+{
+ rm -rf $IMG $TST_IMGS >/dev/null 2>&1
+}
+
+function fail()
+{
+ [ -n "$1" ] && echo "$1"
+ echo "FAILED at line $(caller)"
+ cd $START_DIR
+ remove_mapping
+ exit 2
+}
+
+fail_count()
+{
+ echo "$1"
+ FAILS=$((FAILS+1))
+}
+
+function skip()
+{
+ [ -n "$1" ] && echo "$1"
+ exit 77
+}
+
+function prepare() # $1 dev1_size
+{
+ remove_mapping
+
+ test -d $TST_IMGS || mkdir $TST_IMGS
+
+ test -e $ORIG_IMG || xz -dkc $srcdir/$ORIG_IMG.xz >$ORIG_IMG
+ cp $ORIG_IMG $TST_IMGS
+ cp $ORIG_IMG $IMG
+}
+
+function test_load()
+{
+ local _debug=
+
+ test -z "$_DEBUG" || _debug="--debug"
+
+ case "$1" in
+ R)
+ if [ -n "$_debug" ]; then
+ $CRYPTSETUP luksDump $_debug $IMG
+ else
+ $CRYPTSETUP luksDump $_debug $IMG > /dev/null 2>&1
+ fi
+ test $? -eq 0 || return 1
+ ;;
+ F)
+ if [ -n "$_debug" ]; then
+ $CRYPTSETUP luksDump $_debug $IMG
+ else
+ $CRYPTSETUP luksDump $_debug $IMG > /dev/null 2>&1
+ fi
+ test $? -ne 0 || return 1
+ ;;
+ *)
+ fail "Internal test error"
+ ;;
+ esac
+}
+
+function RUN()
+{
+ echo -n "Test image: $1..."
+ cp $TST_IMGS/$1 $IMG || fail "Missing test image"
+ test_load $2 "$3"
+ if [ $? -ne 0 ]; then
+ fail_count "$3"
+ else
+ echo "OK"
+ fi
+}
+
+function valgrind_setup()
+{
+ which valgrind >/dev/null 2>&1 || fail "Cannot find valgrind."
+ [ ! -f $CRYPTSETUP_VALGRIND ] && fail "Unable to get location of cryptsetup executable."
+ export LD_LIBRARY_PATH="$CRYPTSETUP_LIB_VALGRIND:$LD_LIBRARY_PATH"
+}
+
+function valgrind_run()
+{
+ INFOSTRING="$(basename ${BASH_SOURCE[1]})-line-${BASH_LINENO[0]}" ./valg.sh ${CRYPTSETUP_VALGRIND} "$@"
+}
+
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+
+which jq >/dev/null 2>&1 || skip "Cannot find jq, test skipped."
+
+prepare
+
+echo "[0] Generating test headers"
+cd $srcdir/$GEN_DIR
+for scr in ./generate-*.sh; do
+ echo -n "$(basename $scr)..."
+ $scr $TST_IMGS $TST_IMGS/$ORIG_IMG || fail "Header generator $scr failed: '$?'"
+ echo "done"
+done
+cd $START_DIR
+
+echo "[1] Test basic auto-recovery"
+RUN luks2-invalid-checksum-hdr0.img "R" "Failed to recover from trivial header corruption at offset 0"
+# TODO: check epoch is incresed after recovery
+# TODO: check only sectors related to corrupted hdr at offset 0 are written (dmstats tool/differ.c)
+
+RUN luks2-invalid-checksum-hdr1.img "R" "Failed to recover from trivial header corruption at offset 16384"
+# TODO: check epoch is incresed after recovery
+# TODO: check only sectors related to corrupted hdr at offset 16384 are written (dmstats tool/differ.c)
+
+RUN luks2-invalid-checksum-both-hdrs.img "F" "Failed to recognise corrupted header beyond repair"
+
+echo "[2] Test ability to auto-correct mallformed json area"
+RUN luks2-corrupted-hdr0-with-correct-chks.img "R" "Failed to auto correct malformed json area at offset 512"
+# TODO: check epoch is incresed after recovery
+# TODO: check only sectors related to corrupted hdr at offset 0 are written (dmstats tool/differ.c)
+
+RUN luks2-corrupted-hdr1-with-correct-chks.img "R" "Failed to auto correct malformed json area at offset 16896"
+# TODO: check epoch is incresed after recovery
+# TODO: check only sectors related to corrupted hdr at offset 16384 are written (dmstats tool/differ.c)
+
+RUN luks2-correct-full-json0.img "R" "Failed to parse full and correct json area"
+# TODO: detect noop (norecovery, epoch untouched)
+# TODO: check epoch is NOT incresed after recovery of secondary header
+
+# these tests auto-correct json in-memory only. It'll get fixed on-disk after write operation
+RUN luks2-argon2-leftover-params.img "R" "Failed to repair keyslot with old argon2 parameters."
+RUN luks2-pbkdf2-leftover-params-0.img "R" "Failed to repair keyslot with old pbkdf2 parameters."
+RUN luks2-pbkdf2-leftover-params-1.img "R" "Failed to repair keyslot with old pbkdf2 parameters."
+
+# Secondary header is always broken in following tests
+echo "[3] Test LUKS2 json area restrictions"
+RUN luks2-non-null-byte-beyond-json0.img "F" "Failed to detect illegal data right beyond json data string"
+RUN luks2-non-null-bytes-beyond-json0.img "F" "Failed to detect illegal data in json area"
+RUN luks2-missing-trailing-null-byte-json0.img "F" "Failed to detect missing terminal null byte"
+RUN luks2-invalid-opening-char-json0.img "F" "Failed to detect invalid opening character in json area"
+RUN luks2-invalid-object-type-json0.img "F" "Failed to detect invalid json object type"
+RUN luks2-overlapping-areas-c0-json0.img "F" "Failed to detect two exactly same area specifications"
+RUN luks2-overlapping-areas-c1-json0.img "F" "Failed to detect two intersecting area specifications"
+RUN luks2-overlapping-areas-c2-json0.img "F" "Failed to detect two slightly intersecting area specifications"
+RUN luks2-area-in-json-hdr-space-json0.img "F" "Failed to detect area referencing LUKS2 header space"
+RUN luks2-missing-keyslot-referenced-in-digest.img "F" "Failed to detect missing keyslot referenced in digest"
+RUN luks2-missing-segment-referenced-in-digest.img "F" "Failed to detect missing segment referenced in digest"
+RUN luks2-missing-keyslot-referenced-in-token.img "F" "Failed to detect missing keyslots referenced in token"
+RUN luks2-keyslot-missing-digest.img "F" "Failed to detect missing keyslot digest."
+RUN luks2-keyslot-too-many-digests.img "F" "Failed to detect keyslot has too many digests."
+
+echo "[4] Test integers value limits"
+RUN luks2-uint64-max-segment-size.img "R" "Validation rejected correct value"
+RUN luks2-uint64-overflow-segment-size.img "F" "Failed to detect uint64_t overflow"
+RUN luks2-uint64-signed-segment-size.img "F" "Failed to detect negative value"
+
+echo "[5] Test segments validation"
+RUN luks2-segment-missing-type.img "F" "Failed to detect missing type field"
+RUN luks2-segment-wrong-type.img "F" "Failed to detect invalid type field"
+RUN luks2-segment-missing-offset.img "F" "Failed to detect missing offset field"
+RUN luks2-segment-wrong-offset.img "F" "Failed to detect invalid offset field"
+RUN luks2-segment-missing-size.img "F" "Failed to detect missing size field"
+RUN luks2-segment-wrong-size-0.img "F" "Failed to detect invalid size field"
+RUN luks2-segment-wrong-size-1.img "F" "Failed to detect invalid size field"
+RUN luks2-segment-wrong-size-2.img "F" "Failed to detect invalid size field"
+RUN luks2-segment-crypt-missing-encryption.img "F" "Failed to detect missing encryption field"
+RUN luks2-segment-crypt-wrong-encryption.img "F" "Failed to detect invalid encryption field"
+RUN luks2-segment-crypt-missing-ivoffset.img "F" "Failed to detect missing iv_tweak field"
+RUN luks2-segment-crypt-wrong-ivoffset.img "F" "Failed to detect invalid iv_tweak field"
+RUN luks2-segment-crypt-missing-sectorsize.img "F" "Failed to detect missing sector_size field"
+RUN luks2-segment-crypt-wrong-sectorsize-0.img "F" "Failed to detect invalid sector_size field"
+RUN luks2-segment-crypt-wrong-sectorsize-1.img "F" "Failed to detect invalid sector_size field"
+RUN luks2-segment-crypt-wrong-sectorsize-2.img "F" "Failed to detect invalid sector_size field"
+RUN luks2-segment-unknown-type.img "R" "Validation rejected segment with all mandatory fields correct"
+RUN luks2-segment-two.img "R" "Validation rejected two valid segments"
+RUN luks2-segment-wrong-flags.img "F" "Failed to detect invalid flags field"
+RUN luks2-segment-wrong-flags-element.img "F" "Failed to detect invalid flags content"
+
+echo "[6] Test metadata size and keyslots size (config section)"
+RUN luks2-invalid-keyslots-size-c0.img "F" "Failed to detect too large keyslots_size in config section"
+RUN luks2-invalid-keyslots-size-c1.img "F" "Failed to detect unaligned keyslots_size in config section"
+RUN luks2-invalid-keyslots-size-c2.img "F" "Failed to detect too small keyslots_size config section"
+RUN luks2-invalid-json-size-c0.img "F" "Failed to detect invalid json_size config section"
+RUN luks2-invalid-json-size-c1.img "F" "Failed to detect invalid json_size config section"
+RUN luks2-invalid-json-size-c2.img "F" "Failed to detect mismatching json size in config and binary hdr"
+RUN luks2-metadata-size-32k.img "R" "Valid 32KiB metadata size failed to validate"
+RUN luks2-metadata-size-64k.img "R" "Valid 64KiB metadata size failed to validate"
+RUN luks2-metadata-size-64k-inv-area-c0.img "F" "Failed to detect keyslot area trespassing in json area"
+RUN luks2-metadata-size-64k-inv-area-c1.img "F" "Failed to detect keyslot area overflowing keyslots area"
+RUN luks2-metadata-size-64k-inv-keyslots-size-c0.img "F" "Failed to detect keyslots size overflowing in data area"
+RUN luks2-metadata-size-128k.img "R" "Valid 128KiB metadata size failed to validate"
+RUN luks2-metadata-size-256k.img "R" "Valid 256KiB metadata size failed to validate"
+RUN luks2-metadata-size-512k.img "R" "Valid 512KiB metadata size failed to validate"
+RUN luks2-metadata-size-1m.img "R" "Valid 1MiB metadata size failed to validate"
+RUN luks2-metadata-size-2m.img "R" "Valid 2MiB metadata size failed to validate"
+RUN luks2-metadata-size-4m.img "R" "Valid 4MiB metadata size failed to validate"
+RUN luks2-metadata-size-16k-secondary.img "R" "Valid 16KiB metadata size in secondary hdr failed to validate"
+RUN luks2-metadata-size-32k-secondary.img "R" "Valid 32KiB metadata size in secondary hdr failed to validate"
+RUN luks2-metadata-size-64k-secondary.img "R" "Valid 64KiB metadata size in secondary hdr failed to validate"
+RUN luks2-metadata-size-128k-secondary.img "R" "Valid 128KiB metadata size in secondary hdr failed to validate"
+RUN luks2-metadata-size-256k-secondary.img "R" "Valid 256KiB metadata size in secondary hdr failed to validate"
+RUN luks2-metadata-size-512k-secondary.img "R" "Valid 512KiB metadata size in secondary hdr failed to validate"
+RUN luks2-metadata-size-1m-secondary.img "R" "Valid 1MiB metadata size in secondary hdr failed to validate"
+RUN luks2-metadata-size-2m-secondary.img "R" "Valid 2MiB metadata size in secondary hdr failed to validate"
+RUN luks2-metadata-size-4m-secondary.img "R" "Valid 4MiB metadata size in secondary hdr failed to validate"
+
+remove_mapping
+
+test $FAILS -eq 0 || fail "($FAILS wrong result(s) in total)"
diff --git a/tests/luks2_header_requirements.xz b/tests/luks2_header_requirements.xz
new file mode 100644
index 0000000..eaaa73c
--- /dev/null
+++ b/tests/luks2_header_requirements.xz
Binary files differ
diff --git a/tests/luks2_header_requirements_free.xz b/tests/luks2_header_requirements_free.xz
new file mode 100644
index 0000000..7617ee6
--- /dev/null
+++ b/tests/luks2_header_requirements_free.xz
Binary files differ
diff --git a/tests/luks2_keyslot_unassigned.img.xz b/tests/luks2_keyslot_unassigned.img.xz
new file mode 100644
index 0000000..726cdd7
--- /dev/null
+++ b/tests/luks2_keyslot_unassigned.img.xz
Binary files differ
diff --git a/tests/luks2_mda_images.tar.xz b/tests/luks2_mda_images.tar.xz
new file mode 100644
index 0000000..a27f5c7
--- /dev/null
+++ b/tests/luks2_mda_images.tar.xz
Binary files differ
diff --git a/tests/luks2_valid_hdr.img.xz b/tests/luks2_valid_hdr.img.xz
new file mode 100644
index 0000000..4e03a20
--- /dev/null
+++ b/tests/luks2_valid_hdr.img.xz
Binary files differ
diff --git a/tests/mode-test b/tests/mode-test
new file mode 100755
index 0000000..671620c
--- /dev/null
+++ b/tests/mode-test
@@ -0,0 +1,169 @@
+#!/bin/bash
+#
+# Test mode compatibility, check input + kernel and cryptsetup cipher status
+#
+CRYPTSETUP=../cryptsetup
+DEV_NAME=dmc_test
+HEADER_IMG=mode-test.img
+PASSWORD=3xrododenron
+PASSWORD1=$PASSWORD
+
+# cipher-chainmode-ivopts:ivmode
+CIPHERS="aes twofish serpent"
+MODES="cbc lrw xts"
+IVMODES="null benbi plain plain64 essiv:sha256"
+
+LOOPDEV=$(losetup -f 2>/dev/null)
+
+dmremove() { # device
+ udevadm settle >/dev/null 2>&1
+ dmsetup remove $1 >/dev/null 2>&1
+}
+
+cleanup() {
+ for dev in $(dmsetup status --target crypt | sed s/\:\ .*// | grep "^$DEV_NAME"_); do
+ dmremove $dev
+ sleep 2
+ done
+ [ -b /dev/mapper/$DEV_NAME ] && dmremove $DEV_NAME
+ losetup -d $LOOPDEV >/dev/null 2>&1
+ rm -f $HEADER_IMG >/dev/null 2>&1
+}
+
+fail()
+{
+ [ -n "$1" ] && echo "$1"
+ echo "FAILED at line $(caller)"
+ cleanup
+ exit 100
+}
+
+skip()
+{
+ [ -n "$1" ] && echo "$1"
+ exit 77
+}
+
+add_device() {
+ cleanup
+ dd if=/dev/zero of=$HEADER_IMG bs=1M count=6 >/dev/null 2>&1
+ sync
+ losetup $LOOPDEV $HEADER_IMG >/dev/null 2>&1
+ dmsetup create $DEV_NAME --table "0 10240 linear $LOOPDEV 8" >/dev/null 2>&1
+}
+
+dmcrypt_check() # device outstring
+{
+ X=$(dmsetup table $1 2>/dev/null | sed 's/.*: //' | cut -d' ' -f 4)
+ if [ "$X" = $2 ] ; then
+ echo -n "[table OK]"
+ else
+ echo "[table FAIL]"
+ echo " Expecting $2 got $X."
+ fail
+ fi
+
+ X=$($CRYPTSETUP status $1 | grep cipher: | sed s/\.\*cipher:\\s*//)
+ if [ $X = $2 ] ; then
+ echo -n "[status OK]"
+ else
+ echo "[status FAIL]"
+ echo " Expecting $2 got \"$X\"."
+ fail
+ fi
+
+ dmremove $1
+}
+
+dmcrypt_check_sum() # cipher device
+{
+ EXPSUM="c036cbb7553a909f8b8877d4461924307f27ecb66cff928eeeafd569c3887e29"
+ # Fill device with zeroes and reopen it
+ dd if=/dev/zero of=/dev/mapper/$2 bs=1M count=6 >/dev/null 2>&1
+ sync
+ dmremove $2
+
+ echo $PASSWORD | $CRYPTSETUP create -h sha256 -c $1 -s 256 $2 /dev/mapper/$DEV_NAME >/dev/null 2>&1
+ ret=$?
+ VSUM=$(sha256sum /dev/mapper/$2 | cut -d' ' -f 1)
+ if [ $ret -eq 0 -a "$VSUM" = "$EXPSUM" ] ; then
+ echo -n "[OK]"
+ else
+ echo "[FAIL]"
+ echo " Expecting $EXPSUM got $VSUM."
+ fail
+ fi
+
+ dmremove $2
+}
+
+dmcrypt()
+{
+ OUT=$2
+ [ -z "$OUT" ] && OUT=$1
+ printf "%-31s" "$1"
+
+ echo $PASSWORD | $CRYPTSETUP create -h sha256 -c $1 -s 256 "$DEV_NAME"_tstdev /dev/mapper/$DEV_NAME >/dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ echo -n -e "PLAIN:"
+ dmcrypt_check "$DEV_NAME"_tstdev $OUT
+ else
+ echo -n "[n/a]"
+ fi
+
+ echo $PASSWORD | $CRYPTSETUP luksFormat --type luks1 -i 1 -c $1 -s 256 /dev/mapper/$DEV_NAME >/dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ echo -n -e " LUKS1:"
+ echo $PASSWORD | $CRYPTSETUP luksOpen /dev/mapper/$DEV_NAME "$DEV_NAME"_tstdev >/dev/null 2>&1
+ dmcrypt_check "$DEV_NAME"_tstdev $OUT
+ fi
+
+ echo $PASSWORD | $CRYPTSETUP luksFormat --type luks2 --pbkdf pbkdf2 -i 1 -c $1 -s 256 /dev/mapper/$DEV_NAME >/dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ echo -n -e " LUKS2:"
+ echo $PASSWORD | $CRYPTSETUP luksOpen /dev/mapper/$DEV_NAME "$DEV_NAME"_tstdev >/dev/null 2>&1
+ dmcrypt_check "$DEV_NAME"_tstdev $OUT
+ fi
+
+ # repeated device creation must return the same checksum
+ echo $PASSWORD | $CRYPTSETUP create -h sha256 -c $1 -s 256 "$DEV_NAME"_tstdev /dev/mapper/$DEV_NAME >/dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ echo -n -e " CHECKSUM:"
+ dmcrypt_check_sum "$1" "$DEV_NAME"_tstdev
+ fi
+ echo
+}
+
+[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
+[ -z "$LOOPDEV" ] && skip "Cannot find free loop device, test skipped."
+
+add_device
+
+# compatibility modes
+dmcrypt aes aes-cbc-plain
+dmcrypt aes-plain aes-cbc-plain
+
+# empty cipher
+PASSWORD=""
+dmcrypt null cipher_null-ecb
+dmcrypt cipher_null cipher_null-ecb
+dmcrypt cipher_null-ecb
+
+PASSWORD=$PASSWORD1
+# codebook doesn't support IV at all
+for cipher in $CIPHERS ; do
+ dmcrypt "$cipher-ecb"
+done
+
+for cipher in $CIPHERS ; do
+ for mode in $MODES ; do
+ for ivmode in $IVMODES ; do
+ dmcrypt "$cipher-$mode-$ivmode"
+ done
+ done
+done
+
+dmcrypt xchacha12,aes-adiantum-plain64
+dmcrypt xchacha20,aes-adiantum-plain64
+
+cleanup
diff --git a/tests/password-hash-test b/tests/password-hash-test
new file mode 100755
index 0000000..fbc1f41
--- /dev/null
+++ b/tests/password-hash-test
@@ -0,0 +1,188 @@
+#!/bin/bash
+
+# check hash processing in create command
+
+CRYPTSETUP=../cryptsetup
+DEV_NAME=dmc_test
+KEY_FILE=keyfile
+
+DEV2=$DEV_NAME"_x"
+
+dmremove() { # device
+ udevadm settle >/dev/null 2>&1
+ dmsetup remove $1 >/dev/null 2>&1
+}
+
+cleanup() {
+ [ -b /dev/mapper/$DEV2 ] && dmremove $DEV2
+ [ -b /dev/mapper/$DEV_NAME ] && dmremove $DEV_NAME
+ rm -f $KEY_FILE
+ exit $1
+}
+
+function fail()
+{
+ echo " $1 [FAILED]"
+ echo "FAILED at line $(caller)"
+ cleanup 2
+}
+
+crypt_key() # hash keysize pwd/file name outkey [limit] [offset]
+{
+ DEV2=$DEV_NAME"_x"
+ LIMIT=""
+ MODE=aes-cbc-essiv:sha256
+ [ $2 -gt 256 ] && MODE=aes-xts-plain
+ [ -n "$6" ] && LIMIT="-l $6"
+ [ -n "$7" ] && LIMIT="$LIMIT --keyfile-offset $7"
+
+ echo -n "HASH: $1 KSIZE: $2 / $3"
+ case "$3" in
+ pwd)
+ echo -e -n "$4" | $CRYPTSETUP create -c $MODE -h $1 -s $2 $LIMIT $DEV2 /dev/mapper/$DEV_NAME 2>/dev/null
+ ret=$?
+ ;;
+ std-)
+ echo -e -n "$4" | $CRYPTSETUP create -c $MODE -d "-" -h $1 -s $2 $LIMIT $DEV2 /dev/mapper/$DEV_NAME 2>/dev/null
+ ret=$?
+ ;;
+ stdin)
+ echo -e -n "$4" | $CRYPTSETUP create -c $MODE -h $1 -s $2 $LIMIT $DEV2 /dev/mapper/$DEV_NAME 2>/dev/null
+ ret=$?
+ ;;
+ cat)
+ cat $4 | $CRYPTSETUP create -c $MODE -h $1 -s $2 $LIMIT $DEV2 /dev/mapper/$DEV_NAME 2>/dev/null
+ ret=$?
+ ;;
+ cat-)
+ cat $4 | $CRYPTSETUP create -c $MODE -h $1 -s $2 $LIMIT $DEV2 -d - /dev/mapper/$DEV_NAME 2>/dev/null
+ ret=$?
+ ;;
+ file)
+ $CRYPTSETUP create -q -c $MODE -d $4 -h $1 -s $2 $DEV2 /dev/mapper/$DEV_NAME 2>/dev/null
+ ret=$?
+ ;;
+ failpwd)
+ echo -e -n "$4" | $CRYPTSETUP create -c $MODE -h $1 -s $2 $LIMIT $DEV2 /dev/mapper/$DEV_NAME 2>/dev/null && fail "Expecting failure"
+ echo " [OK]"
+ return
+ ;;
+ *)
+ fail ""
+ ;;
+ esac
+
+ # ignore these cases, not all libs/kernel supports it
+ if [ "$1" != "sha1" -a "$1" != "sha256" ] || [ $2 -gt 256 ] ; then
+ if [ $ret -ne 0 ] ; then
+ echo " [N/A] ($ret, SKIPPED)"
+ return
+ fi
+ fi
+
+ VKEY=$(dmsetup table $DEV2 --showkeys 2>/dev/null | sed 's/.*: //' | cut -d' ' -f 5)
+ if [ "$VKEY" != "$5" ] ; then
+ echo " [FAILED]"
+ echo "expected: $5"
+ echo "real key: $VKEY"
+ cleanup 100
+ else
+ echo " [OK]"
+ fi
+
+ dmremove $DEV2
+}
+
+if [ $(id -u) != 0 ]; then
+ echo "WARNING: You must be root to run this test, test skipped."
+ exit 77
+fi
+
+dmsetup create $DEV_NAME --table "0 10240 zero" >/dev/null 2>&1
+
+crypt_key ripemd160 0 pwd "xxx" aeb26d1f69eb6dddfb9381eed4d7299f091e99aa5d3ff06866d4ce9f620f7aca
+crypt_key ripemd160 256 pwd "xxx" aeb26d1f69eb6dddfb9381eed4d7299f091e99aa5d3ff06866d4ce9f620f7aca
+crypt_key ripemd160 128 pwd "xxx" aeb26d1f69eb6dddfb9381eed4d7299f
+crypt_key sha1 256 pwd "xxx" b60d121b438a380c343d5ec3c2037564b82ffef30b1e0a6ad9af7a73aa91c197
+crypt_key sha1 128 pwd "xxx" b60d121b438a380c343d5ec3c2037564
+crypt_key sha256 256 pwd "xxx" cd2eb0837c9b4c962c22d2ff8b5441b7b45805887f051d39bf133b583baf6860
+crypt_key sha256 128 pwd "xxx" cd2eb0837c9b4c962c22d2ff8b5441b7
+
+crypt_key sha256 0 std- "xxx" cd2eb0837c9b4c962c22d2ff8b5441b7b45805887f051d39bf133b583baf6860
+crypt_key sha256 256 std- "xxx\n" 042aea10a0f14f2d391373599be69d53a75dde9951fc3d3cd10b6100aa7a9f24
+crypt_key sha256 128 std- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" \
+ 2a42b97084779dcedf2c66405c5d296c
+crypt_key sha256 256 stdin "xxx" cd2eb0837c9b4c962c22d2ff8b5441b7b45805887f051d39bf133b583baf6860
+crypt_key sha256 0 stdin "xxx\n" cd2eb0837c9b4c962c22d2ff8b5441b7b45805887f051d39bf133b583baf6860
+
+# with keyfile, hash is ignored
+crypt_key ripemd160 256 file /dev/zero 0000000000000000000000000000000000000000000000000000000000000000
+crypt_key sha256 256 file /dev/zero 0000000000000000000000000000000000000000000000000000000000000000
+crypt_key unknown* 256 file /dev/zero 0000000000000000000000000000000000000000000000000000000000000000
+
+# limiting key
+crypt_key sha256:20 256 pwd "xxx" cd2eb0837c9b4c962c22d2ff8b5441b7b4580588000000000000000000000000
+crypt_key sha256:32 256 pwd "xxx" cd2eb0837c9b4c962c22d2ff8b5441b7b45805887f051d39bf133b583baf6860
+
+crypt_key sha256: 256 failpwd "xxx" x
+crypt_key sha256:xx 256 failpwd "xxx" x
+
+# key file, 80 chars
+echo -n -e "0123456789abcdef\n\x01\x00\x03\xff\xff\r\xff\xff\n\r" \
+ "2352j3rkjhadcfasc823rqaw7e1 3dq sdq3d 2dkjqw3h2=====" >$KEY_FILE
+KEY_FILE_HEX="303132333435363738396162636465660a010003ffff0dffff0a0d20323335326a33726b6a686164636661736338323372716177376531203364712073647133"
+
+
+# ignore hash if keyfile is specified
+crypt_key ripemd160 256 file $KEY_FILE ${KEY_FILE_HEX:0:64}
+crypt_key sha256 256 file $KEY_FILE ${KEY_FILE_HEX:0:64}
+crypt_key sha256 128 file $KEY_FILE ${KEY_FILE_HEX:0:32}
+crypt_key sha256 512 file $KEY_FILE $KEY_FILE_HEX
+
+# stdin can be limited
+crypt_key plain 128 cat /dev/zero 00000000000000000000000000000000 16
+crypt_key plain 128 cat /dev/zero 00000000000000000000000000000000 17
+
+# read key only up to \n
+crypt_key plain 128 cat $KEY_FILE ${KEY_FILE_HEX:0:28}0000 14
+
+# read full key, ignore keyfile length
+crypt_key plain 128 cat- $KEY_FILE ${KEY_FILE_HEX:0:32}
+crypt_key plain 128 cat- $KEY_FILE ${KEY_FILE_HEX:0:32} 14
+
+# but do not ignore hash if keysgfile is "-"
+crypt_key sha256 128 cat- $KEY_FILE f3b827c8a6f159ad8c8ed5bd5ab3f8c5
+crypt_key sha256 128 cat- $KEY_FILE f3b827c8a6f159ad8c8ed5bd5ab3f8c5 0
+crypt_key sha256 128 cat- $KEY_FILE f3b827c8a6f159ad8c8ed5bd5ab3f8c5 80
+crypt_key sha256 128 cat- $KEY_FILE a82c9227cc54c7475620ce85ba1fca1e 14
+crypt_key sha256 128 cat- $KEY_FILE 7df3f4a41a33805596be85c781cac3b4 14 2
+crypt_key sha256 128 cat- $KEY_FILE ebbe65a178e886ddbb778e0a5538db72 40 40
+
+# limiting plain (no hash)
+crypt_key plain 256 pwd "xxxxxxxx" 7878787878787878000000000000000000000000000000000000000000000000
+crypt_key plain:2 256 pwd "xxxxxxxx" 7878000000000000000000000000000000000000000000000000000000000000
+crypt_key plain:9 256 failpwd "xxxxxxxx" x
+
+crypt_key sha256 128 cat $KEY_FILE a82c9227cc54c7475620ce85ba1fca1e 14
+crypt_key sha256:14 128 cat $KEY_FILE a82c9227cc54c7475620ce85ba1f0000 14
+
+crypt_key sha256 128 pwd "0123456789abcdef" 9f9f5111f7b27a781f1f1ddde5ebc2dd 16
+crypt_key sha256 128 pwd "0123456789abcdef" 1be2e452b46d7a0d9656bbb1f768e824 4
+crypt_key sha256 128 pwd "0123" 1be2e452b46d7a0d9656bbb1f768e824 4
+
+# Test list of various hash algorithms
+crypt_key sha1 256 pwd "0123456789abcdef" fe5567e8d769550852182cdf69d74bb16dff8e295e4b6077d6d3fc73e7fe33b0
+crypt_key sha224 256 pwd "0123456789abcdef" 7330215f6741fd2bacbd3658681a70f65e2e90a02887989018974ce83775a568
+crypt_key sha256 256 pwd "0123456789abcdef" 9f9f5111f7b27a781f1f1ddde5ebc2dd2b796bfc7365c9c28b548e564176929f
+crypt_key sha384 256 pwd "0123456789abcdef" fc6304023487cb6f85ac80e47817760c6b153c02da46c6429649e963b031e525
+crypt_key sha512 256 pwd "0123456789abcdef" 1c043fbe4bca7c7920dae536c680fd44c15d71ec12cd82a2a9491b0043b57f4d
+crypt_key ripemd160 256 pwd "0123456789abcdef" edf4e38018cd71dd489b9c1e54b32054eb42dfad9fdcc22d629d756391a24982
+crypt_key whirlpool 256 pwd "0123456789abcdef" b21fc274d47d79ba45f94d67077f0846f75a7acc1a4dc560eacca300179cc142
+crypt_key sha3-224 256 pwd "0123456789abcdef" bbb7d56cc80a8c80e907f7d9240edc0be264aa173266b30918bc1065d59a9388
+crypt_key sha3-256 256 pwd "0123456789abcdef" a5df4caae9fdb5dbacf667075b709a2f30a115c43168af332062b42d4b0da01f
+crypt_key sha3-384 256 pwd "0123456789abcdef" 56f351f754c418892eab4009e5f85c8d5436a591014503563e9395b895526413
+crypt_key sha3-512 256 pwd "0123456789abcdef" 59d06155d25dffdb982729de8dce9d7855ca094d8bab8124b347c40668477056
+crypt_key sm3 256 pwd "0123456789abcdef" a34fdd8e843802b31a262dce5b1f501bed68ef81520de14b39657aecffaf8a86
+crypt_key stribog512 256 pwd "0123456789abcdef" ab1284a64c2325c0ad52494e696df9aa9b92e701605a9a1258b58be08e8240ea
+
+cleanup 0
diff --git a/tests/reencryption-compat-test b/tests/reencryption-compat-test
new file mode 100755
index 0000000..6029530
--- /dev/null
+++ b/tests/reencryption-compat-test
@@ -0,0 +1,385 @@
+#!/bin/bash
+
+CRYPTSETUP=../cryptsetup
+REENC=../cryptsetup-reencrypt
+FAST_PBKDF="--pbkdf-force-iterations 1000"
+
+DEV_NAME=reenc9768
+DEV_NAME2=reenc1273
+IMG=reenc-data
+IMG_HDR=$IMG.hdr
+ORIG_IMG=reenc-data-orig
+KEY1=key1
+PWD1="93R4P4pIqAH8"
+PWD2="1cND4319812f"
+PWD3="1-9Qu5Ejfnqv"
+
+MNT_DIR=./mnt_luks
+START_DIR=$(pwd)
+[ -f /etc/system-fips ] && FIPS_MODE=$(cat /proc/sys/crypto/fips_enabled 2>/dev/null)
+
+function fips_mode()
+{
+ [ -n "$FIPS_MODE" ] && [ "$FIPS_MODE" -gt 0 ]
+}
+
+function del_scsi_device()
+{
+ rmmod scsi_debug 2>/dev/null
+ sleep 2
+}
+
+function remove_mapping()
+{
+ [ -b /dev/mapper/$DEV_NAME2 ] && dmsetup remove $DEV_NAME2
+ [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove $DEV_NAME
+ [ ! -z "$LOOPDEV1" ] && losetup -d $LOOPDEV1 >/dev/null 2>&1
+ rm -f $IMG $IMG_HDR $ORIG_IMG $KEY1 >/dev/null 2>&1
+ umount $MNT_DIR > /dev/null 2>&1
+ rmdir $MNT_DIR > /dev/null 2>&1
+ LOOPDEV1=""
+ del_scsi_device
+}
+
+function fail()
+{
+ [ -n "$1" ] && echo "$1"
+ echo "FAILED at line $(caller)"
+ cd $START_DIR
+ remove_mapping
+ exit 2
+}
+
+function skip()
+{
+ [ -n "$1" ] && echo "$1"
+ exit 77
+}
+
+function add_scsi_device() {
+ del_scsi_device
+ modprobe scsi_debug $@ delay=0
+ if [ $? -ne 0 ] ; then
+ echo "This kernel seems to not support proper scsi_debug module, test skipped."
+ exit 77
+ fi
+
+ sleep 2
+ SCSI_DEV="/dev/"$(grep -l -e scsi_debug /sys/block/*/device/model | cut -f4 -d /)
+ [ -b $SCSI_DEV ] || fail "Cannot find $SCSI_DEV."
+}
+
+function open_crypt() # $1 pwd, $2 hdr
+{
+ if [ -n "$2" ] ; then
+ echo "$1" | $CRYPTSETUP luksOpen $LOOPDEV1 $DEV_NAME --header $2 || fail
+ elif [ -n "$1" ] ; then
+ echo "$1" | $CRYPTSETUP luksOpen $LOOPDEV1 $DEV_NAME || fail
+ else
+ $CRYPTSETUP luksOpen -d $KEY1 $LOOPDEV1 $DEV_NAME || fail
+ fi
+}
+
+function wipe_dev() # $1 dev
+{
+ dd if=/dev/zero of=$1 bs=256k >/dev/null 2>&1
+}
+
+function wipe() # $1 pass
+{
+ open_crypt $1
+ wipe_dev /dev/mapper/$DEV_NAME
+ udevadm settle >/dev/null 2>&1
+ $CRYPTSETUP luksClose $DEV_NAME || fail
+}
+
+function prepare() # $1 dev1_siz
+{
+ remove_mapping
+
+ dd if=/dev/zero of=$IMG bs=1k count=$1 >/dev/null 2>&1
+ LOOPDEV1=$(losetup -f 2>/dev/null)
+ [ -z "$LOOPDEV1" ] && fail "No free loop device"
+ losetup $LOOPDEV1 $IMG
+
+ if [ ! -e $KEY1 ]; then
+ dd if=/dev/urandom of=$KEY1 count=1 bs=32 >/dev/null 2>&1
+ fi
+}
+
+function check_hash_dev() # $1 dev, $2 hash
+{
+ HASH=$(sha256sum $1 | cut -d' ' -f 1)
+ [ $HASH != "$2" ] && fail "HASH differs ($HASH)"
+}
+
+function 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
+}
+
+function backup_orig()
+{
+ sync
+ losetup -d $LOOPDEV1
+ cp $IMG $ORIG_IMG
+ losetup $LOOPDEV1 $IMG
+}
+
+function rollback()
+{
+ sync
+ losetup -d $LOOPDEV1
+ cp $ORIG_IMG $IMG
+ losetup $LOOPDEV1 $IMG
+}
+
+function check_slot() #space separated list of ENABLED key slots
+{
+ local _KS0=DISABLED
+ local _KS1=$_KS0 _KS2=$_KS0 _KS3=$_KS0 _KS4=$_KS0 _KS5=$_KS0 _KS6=$_KS0 _KS7=$_KS0
+ local _tmp
+
+ for _tmp in $*; do
+ eval _KS$_tmp=ENABLED
+ done
+
+ local _out=$($CRYPTSETUP luksDump $LOOPDEV1 | grep -e "Key Slot" | cut -d ' ' -f 4)
+
+ local _i=0
+ for _tmp in $_out; do
+ eval local _orig="\${_KS${_i}}"
+ if [ "$_tmp" != "$_orig" ]; then
+ echo "Keyslot $_i is $_tmp, expected result: $_orig"
+ return 1
+ fi
+ _i=$[_i+1]
+ done
+
+ return 0
+}
+
+function simple_scsi_reenc()
+{
+ echo -n "$1"
+ echo $PWD1 | $CRYPTSETUP luksFormat --type luks1 $FAST_PBKDF $SCSI_DEV || fail
+
+ echo $PWD1 | $CRYPTSETUP luksOpen $SCSI_DEV $DEV_NAME || fail
+ HASH=$(sha256sum /dev/mapper/$DEV_NAME | cut -d' ' -f 1)
+ $CRYPTSETUP luksClose $DEV_NAME || fail
+
+ echo $PWD1 | $REENC -q $FAST_PBKDF $SCSI_DEV || fail
+
+ echo $PWD1 | $CRYPTSETUP luksOpen $SCSI_DEV $DEV_NAME || fail
+ check_hash_dev /dev/mapper/$DEV_NAME $HASH
+ $CRYPTSETUP luksClose $DEV_NAME || fail
+}
+
+function mount_and_test() {
+ test -d $MNT_DIR || mkdir -p $MNT_DIR
+ mount $@ $MNT_DIR 2>/dev/null || {
+ echo -n "failed to mount [SKIP]"
+ return 0
+ }
+ rm $MNT_DIR/* 2>/dev/null
+ cd $MNT_DIR
+ echo $PWD2 | $START_DIR/$REENC $LOOPDEV1 -q --use-fsync --use-directio --write-log $FAST_PBKDF || return 1
+ cd $START_DIR
+ umount $MNT_DIR
+ echo -n [OK]
+}
+
+function test_logging_tmpfs() {
+ echo -n "[tmpfs]"
+ mount_and_test -t tmpfs none -o size=$[25*1024*1024] || return 1
+ echo
+}
+
+function test_logging() {
+ echo -n "$1:"
+ for img in $(ls img_fs*img.xz) ; do
+ wipefs -a $SCSI_DEV > /dev/null
+ echo -n "[${img%.img.xz}]"
+ xz -d -c $img | dd of=$SCSI_DEV bs=4k >/dev/null 2>&1
+ mount_and_test $SCSI_DEV || return 1
+ done
+ echo
+}
+
+[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
+[ ! -x "$REENC" ] && skip "Cannot find $REENC, test skipped."
+which wipefs >/dev/null 2>&1 || skip "Cannot find wipefs, test skipped."
+fips_mode && skip "This test cannot be run in FIPS mode."
+
+# REENCRYPTION tests
+
+HASH1=b69dae56a14d1a8314ed40664c4033ea0a550eea2673e04df42a66ac6b9faf2c
+HASH2=d85ef2a08aeac2812a648deb875485a6e3848fc3d43ce4aa380937f08199f86b
+HASH3=e4e5749032a5163c45125eccf3e8598ba5ed840df442c97e1d5ad4ad84359605
+HASH4=2daeb1f36095b44b318410b3f4e8b5d989dcc7bb023d1426c492dab0a3053e74
+HASH5=5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef
+
+echo "[1] Reencryption"
+prepare 8192
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 -s 128 -c aes-cbc-plain $FAST_PBKDF --align-payload 4096 $LOOPDEV1 || fail
+wipe $PWD1
+check_hash $PWD1 $HASH1
+echo $PWD1 | $REENC $LOOPDEV1 -q $FAST_PBKDF
+check_hash $PWD1 $HASH1
+echo $PWD1 | $REENC $LOOPDEV1 -q -s 256 $FAST_PBKDF
+check_hash $PWD1 $HASH1
+echo $PWD1 | $REENC $LOOPDEV1 -q -s 256 -c aes-xts-plain64 -h sha256 $FAST_PBKDF
+check_hash $PWD1 $HASH1
+echo $PWD1 | $REENC $LOOPDEV1 -q --use-directio $FAST_PBKDF
+check_hash $PWD1 $HASH1
+echo $PWD1 | $REENC $LOOPDEV1 -q --master-key-file /dev/urandom $FAST_PBKDF
+check_hash $PWD1 $HASH1
+echo $PWD1 | $REENC $LOOPDEV1 -q -s 512 --master-key-file /dev/urandom $FAST_PBKDF
+check_hash $PWD1 $HASH1
+$CRYPTSETUP --type luks1 luksDump $LOOPDEV1 > /dev/null || fail
+
+echo "[2] Reencryption with data shift"
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 -c aes-cbc-essiv:sha256 -s 128 $FAST_PBKDF --align-payload 2048 $LOOPDEV1 || fail
+wipe $PWD1
+echo $PWD1 | $REENC $LOOPDEV1 -q -s 256 --reduce-device-size 1024S $FAST_PBKDF || fail
+check_hash $PWD1 $HASH2
+echo $PWD1 | $REENC $LOOPDEV1 -q $FAST_PBKDF || fail
+check_hash $PWD1 $HASH2
+$CRYPTSETUP --type luks1 luksDump $LOOPDEV1 > /dev/null || fail
+
+echo "[3] Reencryption with keyfile"
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 -d $KEY1 -c aes-cbc-essiv:sha256 -s 128 $FAST_PBKDF --align-payload 4096 $LOOPDEV1 || fail
+wipe
+check_hash "" $HASH1
+echo $PWD1 | $CRYPTSETUP -q luksAddKey -d $KEY1 $LOOPDEV1 $FAST_PBKDF || fail
+$REENC $LOOPDEV1 -d $KEY1 $FAST_PBKDF -q 2>/dev/null && fail
+$REENC $LOOPDEV1 -d $KEY1 -S 0 $FAST_PBKDF -q || fail
+check_hash "" $HASH1
+check_slot 0 || fail "Only keyslot 0 expected to be enabled"
+$REENC $LOOPDEV1 -d $KEY1 $FAST_PBKDF -q || fail
+# FIXME echo $PWD1 | $REENC ...
+
+echo "[4] Encryption of not yet encrypted device"
+# well, movin' zeroes :-)
+OFFSET=2048
+SIZE=$(blockdev --getsz $LOOPDEV1)
+wipe_dev $LOOPDEV1
+dmsetup create $DEV_NAME2 --table "0 $(($SIZE - $OFFSET)) linear $LOOPDEV1 0" || fail
+check_hash_dev /dev/mapper/$DEV_NAME2 $HASH3
+dmsetup remove $DEV_NAME2 || fail
+echo $PWD1 | $REENC $LOOPDEV1 -c aes-cbc-essiv:sha256 -s 128 --new --type luks1 --reduce-device-size "$OFFSET"S -q $FAST_PBKDF || fail
+check_hash $PWD1 $HASH3
+$CRYPTSETUP --type luks1 luksDump $LOOPDEV1 > /dev/null || fail
+# 64MiB + 1 KiB
+prepare 65537
+OFFSET=131072
+SIZE=$(blockdev --getsz $LOOPDEV1)
+wipe_dev $LOOPDEV1
+dmsetup create $DEV_NAME2 --table "0 $(($SIZE - $OFFSET)) linear $LOOPDEV1 0" || fail
+check_hash_dev /dev/mapper/$DEV_NAME2 $HASH5
+dmsetup remove $DEV_NAME2 || fail
+echo $PWD1 | $REENC $LOOPDEV1 -c aes-cbc-essiv:sha256 -s 128 --new --type luks1 --reduce-device-size "$OFFSET"S $FAST_PBKDF || fail
+check_hash $PWD1 $HASH5
+$CRYPTSETUP --type luks1 luksDump $LOOPDEV1 > /dev/null || fail
+prepare 8192
+
+echo "[5] Reencryption using specific keyslot"
+echo $PWD2 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF $LOOPDEV1 || fail
+echo -e "$PWD2\n$PWD1" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF -S 1 $LOOPDEV1 || fail
+echo -e "$PWD2\n$PWD2" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF -S 2 $LOOPDEV1 || fail
+echo -e "$PWD2\n$PWD1" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF -S 3 $LOOPDEV1 || fail
+echo -e "$PWD2\n$PWD2" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF -S 4 $LOOPDEV1 || fail
+echo -e "$PWD2\n$PWD1" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF -S 5 $LOOPDEV1 || fail
+echo -e "$PWD2\n$PWD2" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF -S 6 $LOOPDEV1 || fail
+echo -e "$PWD2\n$PWD3" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF -S 7 $LOOPDEV1 || fail
+backup_orig
+echo $PWD2 | $REENC $FAST_PBKDF -S 0 -q $LOOPDEV1 || fail
+check_slot 0 || fail "Only keyslot 0 expected to be enabled"
+wipe $PWD2
+rollback
+echo $PWD1 | $REENC $FAST_PBKDF -S 1 -q $LOOPDEV1 || fail
+check_slot 1 || fail "Only keyslot 1 expected to be enabled"
+wipe $PWD1
+rollback
+echo $PWD2 | $REENC $FAST_PBKDF -S 6 -q $LOOPDEV1 || fail
+check_slot 6 || fail "Only keyslot 6 expected to be enabled"
+wipe $PWD2
+rollback
+echo $PWD3 | $REENC $FAST_PBKDF -S 7 -q $LOOPDEV1 || fail
+check_slot 7 || fail "Only keyslot 7 expected to be enabled"
+wipe $PWD3
+rollback
+echo $PWD3 | $REENC $FAST_PBKDF -S 8 -q $LOOPDEV1 2>/dev/null && fail
+$CRYPTSETUP luksDump $LOOPDEV1 > /dev/null || fail
+
+echo "[6] Reencryption using all active keyslots"
+echo -e "$PWD2\n$PWD1\n$PWD2\n$PWD1\n$PWD2\n$PWD1\n$PWD2\n$PWD3" | $REENC -q $LOOPDEV1 $FAST_PBKDF || fail
+check_slot 0 1 2 3 4 5 6 7 || fail "All keyslots expected to be enabled"
+
+echo "[7] Reencryption of block devices with different block size"
+add_scsi_device sector_size=512 dev_size_mb=8
+simple_scsi_reenc "[512 sector]"
+add_scsi_device sector_size=4096 dev_size_mb=8
+simple_scsi_reenc "[4096 sector]"
+add_scsi_device sector_size=512 physblk_exp=3 dev_size_mb=8
+simple_scsi_reenc "[4096/512 sector]"
+echo "[OK]"
+
+echo "[8] Header only reencryption (hash and iteration time)"
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 --hash sha1 $FAST_PBKDF $LOOPDEV1 || fail
+wipe $PWD1
+check_hash $PWD1 $HASH1
+echo $PWD1 | $REENC $LOOPDEV1 -q --keep-key || fail
+check_hash $PWD1 $HASH1
+echo $PWD1 | $REENC $LOOPDEV1 -q --keep-key --pbkdf-force-iterations 999 2>/dev/null && fail
+check_hash $PWD1 $HASH1
+echo $PWD1 | $REENC $LOOPDEV1 -q --keep-key --hash sha256 --pbkdf-force-iterations 1001
+check_hash $PWD1 $HASH1
+[ "$($CRYPTSETUP luksDump $LOOPDEV1 | grep -A1 -m1 "Key Slot 0" | grep Iterations: | sed -e 's/[[:space:]]\+Iterations:\ \+//g')" -eq 1001 ] || fail
+[ "$($CRYPTSETUP luksDump $LOOPDEV1 | grep -m1 "Hash spec:" | cut -f2)" = "sha256" ] || fail
+echo $PWD1 | $REENC $LOOPDEV1 -q --keep-key --hash sha512 $FAST_PBKDF
+check_hash $PWD1 $HASH1
+[ "$($CRYPTSETUP luksDump $LOOPDEV1 | grep -A1 -m1 "Key Slot 0" | grep Iterations: | sed -e 's/[[:space:]]\+Iterations:\ \+//g')" -eq 1000 ] || fail
+echo $PWD1 | $REENC $LOOPDEV1 -q --keep-key $FAST_PBKDF
+check_hash $PWD1 $HASH1
+$CRYPTSETUP --type luks1 luksDump $LOOPDEV1 > /dev/null || fail
+
+echo "[9] Test log I/Os on various underlaying block devices"
+prepare 8192
+echo $PWD2 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF $LOOPDEV1 || fail
+add_scsi_device sector_size=512 dev_size_mb=32
+test_logging "[512 sector]" || fail
+add_scsi_device sector_size=4096 dev_size_mb=32
+test_logging "[4096 sector]" || fail
+add_scsi_device sector_size=512 dev_size_mb=32 physblk_exp=3
+test_logging "[4096/512 sector]" || fail
+test_logging_tmpfs || fail
+
+echo "[10] Removal of encryption"
+prepare 8192
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF $LOOPDEV1 || fail
+wipe $PWD1
+check_hash $PWD1 $HASH1
+echo $PWD1 | $REENC $LOOPDEV1 -q --decrypt
+check_hash_dev $LOOPDEV1 $HASH4
+
+echo "[11] Detached header - adding encryption/reencryption/decryption"
+prepare 8192
+check_hash_dev $IMG $HASH4
+echo $PWD1 | $REENC $LOOPDEV1 -q $FAST_PBKDF --header $IMG_HDR --new --type luks1
+check_hash $PWD1 $HASH4 $IMG_HDR
+echo $PWD1 | $REENC $LOOPDEV1 -q $FAST_PBKDF --header $IMG_HDR
+check_hash $PWD1 $HASH4 $IMG_HDR
+echo $PWD1 | $REENC $LOOPDEV1 -q --header $IMG_HDR --decrypt
+check_hash_dev $IMG $HASH4
+# existing header of zero size
+cat /dev/null >$IMG_HDR
+echo $PWD1 | $REENC $LOOPDEV1 -q $FAST_PBKDF --header $IMG_HDR --new --type luks1
+check_hash $PWD1 $HASH4 $IMG_HDR
+$CRYPTSETUP isLuks $LOOPDEV1 && fail
+$CRYPTSETUP isLuks $IMG_HDR || fail
+
+remove_mapping
+exit 0
diff --git a/tests/reencryption-compat-test2 b/tests/reencryption-compat-test2
new file mode 100755
index 0000000..cb61d23
--- /dev/null
+++ b/tests/reencryption-compat-test2
@@ -0,0 +1,459 @@
+#!/bin/bash
+
+CRYPTSETUP=../cryptsetup
+REENC=../cryptsetup-reencrypt
+FAST_PBKDF_ARGON="--pbkdf-force-iterations 4 --pbkdf-memory 32 --pbkdf-parallel 1"
+FAST_PBKDF_PBKDF2="--pbkdf-force-iterations 1000 --pbkdf pbkdf2"
+DEFAULT_ARGON="argon2i"
+
+DEV_NAME=reenc9768
+DEV_NAME2=reenc1273
+IMG=reenc-data
+IMG_HDR=$IMG.hdr
+ORIG_IMG=reenc-data-orig
+KEY1=key1
+PWD1="93R4P4pIqAH8"
+PWD2="1cND4319812f"
+PWD3="1-9Qu5Ejfnqv"
+
+MNT_DIR=./mnt_luks
+START_DIR=$(pwd)
+[ -f /etc/system-fips ] && FIPS_MODE=$(cat /proc/sys/crypto/fips_enabled 2>/dev/null)
+
+function fips_mode()
+{
+ [ -n "$FIPS_MODE" ] && [ "$FIPS_MODE" -gt 0 ]
+}
+
+function dm_crypt_features()
+{
+ local VER_STR=$(dmsetup targets | grep crypt | cut -f2 -dv)
+ [ -z "$VER_STR" ] && fail "Failed to parse dm-crypt version."
+
+ local VER_MAJ=$(echo $VER_STR | cut -f 1 -d.)
+ local VER_MIN=$(echo $VER_STR | cut -f 2 -d.)
+
+ [ $VER_MAJ -lt 1 ] && return
+ [ $VER_MAJ -eq 1 -a $VER_MIN -lt 11 ] && return
+ ALLOW_DISCARDS=--allow-discards
+ [ $VER_MAJ -eq 1 -a $VER_MIN -lt 14 ] && return
+ PERF_CPU=--perf-same_cpu_crypt
+}
+
+function del_scsi_device()
+{
+ rmmod scsi_debug 2>/dev/null
+ sleep 2
+}
+
+function remove_mapping()
+{
+ [ -b /dev/mapper/$DEV_NAME2 ] && dmsetup remove $DEV_NAME2
+ [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove $DEV_NAME
+ rm -f $IMG $IMG_HDR $ORIG_IMG $KEY1 >/dev/null 2>&1
+ umount $MNT_DIR > /dev/null 2>&1
+ rmdir $MNT_DIR > /dev/null 2>&1
+ del_scsi_device
+}
+
+function fail()
+{
+ [ -n "$1" ] && echo "$1"
+ echo "FAILED at line $(caller)"
+ cd $START_DIR
+ remove_mapping
+ exit 2
+}
+
+function skip()
+{
+ [ -n "$1" ] && echo "$1"
+ exit 77
+}
+
+function add_scsi_device() {
+ del_scsi_device
+ modprobe scsi_debug $@ delay=0
+ if [ $? -ne 0 ] ; then
+ echo "This kernel seems to not support proper scsi_debug module, test skipped."
+ exit 77
+ fi
+
+ sleep 2
+ SCSI_DEV="/dev/"$(grep -l -e scsi_debug /sys/block/*/device/model | cut -f4 -d /)
+ [ -b $SCSI_DEV ] || fail "Cannot find $SCSI_DEV."
+}
+
+function open_crypt() # $1 pwd, $2 hdr
+{
+ if [ -n "$2" ] ; then
+ echo "$1" | $CRYPTSETUP luksOpen $IMG $DEV_NAME --header $2 || fail
+ elif [ -n "$1" ] ; then
+ echo "$1" | $CRYPTSETUP luksOpen $IMG $DEV_NAME || fail
+ else
+ $CRYPTSETUP luksOpen -d $KEY1 $IMG $DEV_NAME || fail
+ fi
+}
+
+function wipe_dev() # $1 dev
+{
+ dd if=/dev/zero of=$1 bs=256k conv=notrunc >/dev/null 2>&1
+}
+
+function wipe() # $1 pass
+{
+ open_crypt $1
+ wipe_dev /dev/mapper/$DEV_NAME
+ udevadm settle >/dev/null 2>&1
+ $CRYPTSETUP luksClose $DEV_NAME || fail
+}
+
+function prepare() # $1 dev1_siz
+{
+ remove_mapping
+
+ dd if=/dev/zero of=$IMG bs=1k count=$1 >/dev/null 2>&1
+
+ if [ ! -e $KEY1 ]; then
+ dd if=/dev/urandom of=$KEY1 count=1 bs=32 >/dev/null 2>&1
+ fi
+}
+
+function check_hash_dev() # $1 dev, $2 hash, $3 size
+{
+ if [ -n "$3" ]; then
+ HASH=$(head -c $3 $1 | sha256sum | cut -d' ' -f 1)
+ else
+ HASH=$(sha256sum $1 | cut -d' ' -f 1)
+ fi
+ [ $HASH != "$2" ] && fail "HASH differs ($HASH)"
+}
+
+function 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
+}
+
+function backup_orig()
+{
+ sync
+ cp $IMG $ORIG_IMG
+}
+
+function rollback()
+{
+ sync
+ cp $ORIG_IMG $IMG
+}
+
+function check_slot() #space separated list of active key slots
+{
+ local _out=$($CRYPTSETUP luksDump $IMG | grep -e ": luks2" | sed -e 's/[[:space:]]*\([0-9]\+\):.*/\1/g')
+
+ local _req
+ local _hdr
+ local _j
+
+ for _i in $*; do
+ _j=$((_i))
+ _req="$_req $_j"
+ done
+
+ for _i in $_out; do
+ _j=$((_i))
+ _hdr="$_hdr $_j"
+ done
+
+ test "$_req" = "$_hdr"
+}
+
+function simple_scsi_reenc()
+{
+ echo -n "$1"
+ echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 $FAST_PBKDF_ARGON $SCSI_DEV || fail
+
+ echo $PWD1 | $CRYPTSETUP luksOpen $SCSI_DEV $DEV_NAME || fail
+ HASH=$(sha256sum /dev/mapper/$DEV_NAME | cut -d' ' -f 1)
+ $CRYPTSETUP luksClose $DEV_NAME || fail
+
+ echo $PWD1 | $REENC -q $FAST_PBKDF_ARGON $SCSI_DEV || fail
+
+ echo $PWD1 | $CRYPTSETUP luksOpen $SCSI_DEV $DEV_NAME || fail
+ check_hash_dev /dev/mapper/$DEV_NAME $HASH
+ $CRYPTSETUP luksClose $DEV_NAME || fail
+}
+
+function mount_and_test() {
+ test -d $MNT_DIR || mkdir -p $MNT_DIR
+ mount $@ $MNT_DIR 2>/dev/null || {
+ echo -n "failed to mount [SKIP]"
+ return 0
+ }
+ rm $MNT_DIR/* 2>/dev/null
+ cd $MNT_DIR
+ echo $PWD2 | $START_DIR/$REENC $START_DIR/$IMG -q --use-fsync --use-directio --write-log $FAST_PBKDF_ARGON || return 1
+ cd $START_DIR
+ umount $MNT_DIR
+ echo -n [OK]
+}
+
+function test_logging_tmpfs() {
+ echo -n "[tmpfs]"
+ mount_and_test -t tmpfs none -o size=$[25*1024*1024] || return 1
+ echo
+}
+
+function test_logging() {
+ echo -n "$1:"
+ for img in $(ls img_fs*img.xz) ; do
+ wipefs -a $SCSI_DEV > /dev/null
+ echo -n "[${img%.img.xz}]"
+ xz -d -c $img | dd of=$SCSI_DEV bs=4k >/dev/null 2>&1
+ mount_and_test $SCSI_DEV || return 1
+ done
+ echo
+}
+
+[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
+[ ! -x "$REENC" ] && skip "Cannot find $REENC, test skipped."
+which wipefs >/dev/null || skip "Cannot find wipefs, test skipped."
+fips_mode && skip "This test cannot be run in FIPS mode."
+
+# REENCRYPTION tests
+
+HASH1=b69dae56a14d1a8314ed40664c4033ea0a550eea2673e04df42a66ac6b9faf2c
+HASH4=2daeb1f36095b44b318410b3f4e8b5d989dcc7bb023d1426c492dab0a3053e74
+HASH5=bb9f8df61474d25e71fa00722318cd387396ca1736605e1248821cc0de3d3af8
+HASH6=4d9cbaf3aa0935a8c113f139691b3daf9c94c8d6c278aedc8eec66a4b9f6c8ae
+HASH7=5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef
+
+echo "[1] Reencryption"
+prepare 8192
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c aes-cbc-plain $FAST_PBKDF_ARGON --offset 8192 $IMG || fail
+wipe $PWD1
+check_hash $PWD1 $HASH5
+echo $PWD1 | $REENC $IMG -q $FAST_PBKDF_ARGON
+check_hash $PWD1 $HASH5
+echo $PWD1 | $REENC $IMG -q -s 256 $FAST_PBKDF_ARGON
+check_hash $PWD1 $HASH5
+echo $PWD1 | $REENC $IMG -q -s 256 -c aes-xts-plain64 -h sha256 $FAST_PBKDF_ARGON
+check_hash $PWD1 $HASH5
+echo $PWD1 | $REENC $IMG -q --use-directio $FAST_PBKDF_ARGON
+check_hash $PWD1 $HASH5
+echo $PWD1 | $REENC $IMG -q --master-key-file /dev/urandom $FAST_PBKDF_ARGON
+check_hash $PWD1 $HASH5
+echo $PWD1 | $REENC $IMG -q -s 512 --master-key-file /dev/urandom $FAST_PBKDF_ARGON
+check_hash $PWD1 $HASH5
+$CRYPTSETUP luksDump $IMG | grep -q "luks2" > /dev/null || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 --luks2-metadata-size 128k -c aes-cbc-plain $FAST_PBKDF_ARGON --offset 8192 $IMG > /dev/null || fail
+wipe $PWD1
+check_hash $PWD1 $HASH5
+echo $PWD1 | $REENC $IMG -q $FAST_PBKDF_ARGON > /dev/null || fail
+check_hash $PWD1 $HASH5
+MDA_SIZE=$($CRYPTSETUP luksDump $IMG | grep "Metadata area: " | cut -f 3 -d ' ')
+test "$MDA_SIZE" -eq 131072 || fail "Unexpected Metadata area size $MDA_SIZE"
+
+echo "[2] Reencryption with data shift"
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c aes-cbc-essiv:sha256 -s 128 $FAST_PBKDF_ARGON --offset 8192 $IMG || fail
+wipe $PWD1
+echo $PWD1 | $REENC $IMG -q -s 256 --reduce-device-size 1024S $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH6
+echo $PWD1 | $REENC $IMG -q $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH6
+$CRYPTSETUP luksDump $IMG | grep -q "luks2" > /dev/null || fail
+
+echo "[3] Reencryption with keyfile"
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -d $KEY1 -c aes-cbc-essiv:sha256 -s 128 $FAST_PBKDF_ARGON --offset 8192 $IMG || fail
+wipe
+check_hash "" $HASH5
+echo $PWD1 | $CRYPTSETUP -q luksAddKey -d $KEY1 $IMG $FAST_PBKDF_ARGON || fail
+$REENC $IMG -d $KEY1 $FAST_PBKDF_ARGON -q 2>/dev/null && fail
+$REENC $IMG -d $KEY1 -S 0 $FAST_PBKDF_ARGON -q || fail
+check_hash "" $HASH5
+check_slot 0 || fail "Only keyslot 0 expected to be enabled"
+$REENC $IMG -d $KEY1 $FAST_PBKDF_ARGON -q || fail
+$CRYPTSETUP luksDump $IMG | grep -q "luks2" > /dev/null || fail
+# FIXME echo $PWD1 | $REENC ...
+
+echo "[4] Encryption of not yet encrypted device"
+# well, movin' zeroes :-)
+OFFSET=8192 # default LUKS2 header size
+prepare 8192
+check_hash_dev $IMG $HASH4
+echo $PWD1 | $REENC --type luks2 $IMG -c aes-cbc-essiv:sha256 -s 128 --new --reduce-device-size "$OFFSET"S -q $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH5
+$CRYPTSETUP luksDump $IMG | grep -q "luks2" > /dev/null || fail
+# 64MiB + 1 KiB
+prepare 65537
+OFFSET=131072
+check_hash_dev $IMG $HASH7 1024
+echo $PWD1 | $REENC --type luks2 $IMG -c aes-cbc-essiv:sha256 -s 128 --new --reduce-device-size "$OFFSET"S -q $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH7
+$CRYPTSETUP --type luks2 luksDump $IMG > /dev/null || fail
+prepare 8192
+
+echo "[5] Reencryption using specific keyslot"
+echo $PWD2 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $IMG --offset 8192 || fail
+echo -e "$PWD2\n$PWD1" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF_ARGON -S 1 $IMG || fail
+echo -e "$PWD2\n$PWD2" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF_ARGON -S 2 $IMG || fail
+echo -e "$PWD2\n$PWD1" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF_ARGON -S 3 $IMG || fail
+echo -e "$PWD2\n$PWD2" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF_ARGON -S 4 $IMG || fail
+echo -e "$PWD2\n$PWD1" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF_ARGON -S 5 $IMG || fail
+echo -e "$PWD2\n$PWD2" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF_ARGON -S 6 $IMG || fail
+echo -e "$PWD2\n$PWD3" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF_ARGON -S 22 $IMG || fail
+backup_orig
+echo $PWD2 | $REENC $FAST_PBKDF_ARGON -S 0 -q $IMG || fail
+check_slot 0 || fail "Only keyslot 0 expected to be enabled"
+wipe $PWD2
+rollback
+echo $PWD1 | $REENC $FAST_PBKDF_ARGON -S 1 -q $IMG || fail
+check_slot 1 || fail "Only keyslot 1 expected to be enabled"
+wipe $PWD1
+rollback
+echo $PWD2 | $REENC $FAST_PBKDF_ARGON -S 6 -q $IMG || fail
+check_slot 6 || fail "Only keyslot 6 expected to be enabled"
+wipe $PWD2
+rollback
+echo $PWD3 | $REENC $FAST_PBKDF_ARGON -S 22 -q $IMG || fail
+check_slot 22 || fail "Only keyslot 22 expected to be enabled"
+wipe $PWD3
+rollback
+
+echo "[6] Reencryption using all active keyslots"
+echo -e "$PWD2\n$PWD1\n$PWD2\n$PWD1\n$PWD2\n$PWD1\n$PWD2\n$PWD3" | $REENC -q $IMG $FAST_PBKDF_ARGON || fail
+check_slot 0 1 2 3 4 5 6 22 || fail "All keyslots expected to be enabled"
+
+echo "[7] Reencryption of block devices with different block size"
+add_scsi_device sector_size=512 dev_size_mb=32
+simple_scsi_reenc "[512 sector]"
+add_scsi_device sector_size=4096 dev_size_mb=32
+simple_scsi_reenc "[4096 sector]"
+add_scsi_device sector_size=512 physblk_exp=3 dev_size_mb=32
+simple_scsi_reenc "[4096/512 sector]"
+echo "[OK]"
+
+echo "[8] Header only reencryption (hash and iteration time)"
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $IMG --offset 8192 || fail
+wipe $PWD1
+check_hash $PWD1 $HASH5
+echo $PWD1 | $REENC $IMG -q --keep-key || fail
+check_hash $PWD1 $HASH5
+echo $PWD1 | $REENC $IMG -q --keep-key --pbkdf pbkdf2 --pbkdf-force-iterations 999 2>/dev/null && fail
+check_hash $PWD1 $HASH5
+echo $PWD1 | $REENC $IMG -q --keep-key --pbkdf-force-iterations 3 2>/dev/null && fail
+check_hash $PWD1 $HASH5
+echo $PWD1 | $REENC $IMG -q --keep-key --pbkdf-force-iterations 4 --pbkdf-memory 31 2>/dev/null && fail
+check_hash $PWD1 $HASH5
+echo $PWD1 | $REENC $IMG -q --keep-key --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --hash sha512
+check_hash $PWD1 $HASH5
+[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "0: luks2" | grep PBKDF: | sed -e 's/[[:space:]]\+PBKDF:\ \+//g')" = "pbkdf2" ] || fail
+[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "0: luks2" | grep Hash: | sed -e 's/[[:space:]]\+Hash:\ \+//g')" = "sha512" ] || fail
+echo $PWD1 | $REENC $IMG -q --keep-key $FAST_PBKDF_ARGON
+check_hash $PWD1 $HASH5
+[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "0: luks2" | grep PBKDF: | sed -e 's/[[:space:]]\+PBKDF:\ \+//g')" = $DEFAULT_ARGON ] || fail
+[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "0: luks2" | grep "Time cost" | sed -e 's/[[:space:]]\+Time\ cost:\ \+//g')" -eq 4 ] || fail
+[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "0: luks2" | grep Memory | sed -e 's/[[[:space:]]\+Memory:\ \+//g')" -eq 32 ] || fail
+[ "$($CRYPTSETUP luksDump $IMG | grep -A8 -m1 "0: luks2" | grep Threads | sed -e 's/[[[:space:]]\+Threads:\ \+//g')" -eq 1 ] || fail
+echo -e "$PWD1\n$PWD2" | $CRYPTSETUP -q luksAddKey -S21 $FAST_PBKDF_ARGON $IMG || fail
+echo $PWD2 | $REENC -S21 -q --keep-key --pbkdf pbkdf2 --pbkdf-force-iterations 1000 $IMG || fail
+check_hash $PWD2 $HASH5
+check_slot 21 || fail "Only keyslot 21 expected to be enabled"
+$CRYPTSETUP luksDump $IMG | grep -q "luks2" > /dev/null || fail
+
+echo "[9] Test log I/Os on various underlaying block devices"
+echo $PWD2 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $IMG --offset 8192 || fail
+add_scsi_device sector_size=512 dev_size_mb=32
+test_logging "[512 sector]" || fail
+add_scsi_device sector_size=4096 dev_size_mb=32
+test_logging "[4096 sector]" || fail
+add_scsi_device sector_size=512 dev_size_mb=32 physblk_exp=3
+test_logging "[4096/512 sector]" || fail
+test_logging_tmpfs || fail
+
+echo "[10] Removal of encryption"
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $IMG --offset 8192 || fail
+wipe $PWD1
+check_hash $PWD1 $HASH5
+echo $PWD1 | $REENC $IMG -q --decrypt
+check_hash_dev $IMG $HASH4
+
+echo "[11] Reencryption with tokens"
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $IMG --offset 8192 || fail
+wipe $PWD1
+check_hash $PWD1 $HASH5
+echo -e "$PWD1\n$PWD2" | $CRYPTSETUP -q luksAddKey -S23 $FAST_PBKDF_ARGON $IMG || fail
+echo -e "$PWD1\n$PWD3" | $CRYPTSETUP -q luksAddKey -S1 $FAST_PBKDF_ARGON $IMG || fail
+echo -e "$PWD1\n$PWD3" | $CRYPTSETUP -q luksAddKey -S3 $FAST_PBKDF_ARGON $IMG || fai
+$CRYPTSETUP token add --key-description key-name0 --key-slot 23 --token-id 0 $IMG
+$CRYPTSETUP token add --key-description key-name2 --key-slot 1 --token-id 2 $IMG
+$CRYPTSETUP token add --key-description key-name31 --token-id 31 $IMG
+echo $PWD1 | $CRYPTSETUP -q luksKillSlot $IMG 3 || fail
+echo $PWD2 | $REENC $FAST_PBKDF_ARGON -S 23 -q $IMG || fail
+$CRYPTSETUP luksDump $IMG | grep "0: luks2-keyring" >/dev/null || fail
+[ "$($CRYPTSETUP luksDump $IMG | grep -A2 -m1 "0: luks2-keyring" | grep Keyslot: | sed -e 's/[[[:space:]]\+Keyslot:\ \+//g')" -eq 23 ] || fail
+$CRYPTSETUP luksDump $IMG | grep "2: luks2-keyring" >/dev/null || fail
+$CRYPTSETUP luksDump $IMG | grep "31: luks2-keyring" >/dev/null || fail
+[ "$($CRYPTSETUP luksDump $IMG | grep -A2 -m1 "31: luks2-keyring" | grep Keyslot: | sed -e 's/[[[:space:]]\+Keyslot:\ \+//g')" -eq 23 ] || fail
+
+echo "[12] Reencryption with persistent flags"
+dm_crypt_features
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $IMG --offset 8192 || fail
+wipe $PWD1
+check_hash $PWD1 $HASH5
+echo $PWD1 | $CRYPTSETUP open $IMG $DEV_NAME $ALLOW_DISCARDS $PERF_CPU --persistent || fail
+$CRYPTSETUP close $DEV_NAME || fail
+echo $PWD1 | $REENC $FAST_PBKDF_ARGON -q $IMG || fail
+if [ -n "$PERF_CPU" ]; then
+ $CRYPTSETUP luksDump $IMG | grep -m1 Flags: | grep same-cpu-crypt > /dev/null || fail
+fi
+if [ -n "$ALLOW_DISCARDS" ]; then
+ $CRYPTSETUP luksDump $IMG | grep -m1 Flags: | grep allow-discards > /dev/null || fail
+fi
+
+echo "[13] Detached header - adding encryption/reencryption/decryption"
+prepare 8192
+check_hash_dev $IMG $HASH4
+echo $PWD1 | $REENC --type luks2 $IMG -q $FAST_PBKDF_ARGON --header $IMG_HDR --new
+check_hash $PWD1 $HASH4 $IMG_HDR
+echo $PWD1 | $REENC $IMG -q $FAST_PBKDF_ARGON --header $IMG_HDR
+check_hash $PWD1 $HASH4 $IMG_HDR
+echo $PWD1 | $REENC $IMG -q --header $IMG_HDR --decrypt
+check_hash_dev $IMG $HASH4
+# existing header of zero size
+cat /dev/null >$IMG_HDR
+echo $PWD1 | $REENC --type luks2 $IMG -q $FAST_PBKDF_ARGON --header $IMG_HDR --new
+check_hash $PWD1 $HASH4 $IMG_HDR
+$CRYPTSETUP isLuks $IMG && fail
+$CRYPTSETUP isLuks $IMG_HDR || fail
+$CRYPTSETUP luksDump $IMG_HDR | grep -q "0: luks2" || fail
+
+echo "[14] Reencryption with unbound keyslot"
+prepare 8192
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $IMG --offset 8192 || fail
+echo $PWD2 | $CRYPTSETUP -q luksAddKey -S 3 --unbound --key-size 64 $FAST_PBKDF_ARGON $IMG || fail
+wipe $PWD1
+check_hash $PWD1 $HASH5
+$CRYPTSETUP luksDump $IMG | grep -q "3: luks2 (unbound)" || fail
+echo $PWD2 | $REENC $IMG -q $FAST_PBKDF_ARGON 2>/dev/null && fail
+echo -e "$PWD1\n$PWD2" | $REENC $IMG -q $FAST_PBKDF_ARGON || fail
+$CRYPTSETUP luksDump $IMG | grep -q "3: luks2 (unbound)" || fail
+
+echo "[15] Reencryption after conversion"
+prepare 8192
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_PBKDF2 $IMG --offset 4096 || fail
+wipe $PWD1
+check_hash $PWD1 $HASH1
+$CRYPTSETUP -q convert --type luks2 $IMG || fail
+echo $PWD1 | $REENC $IMG -q $FAST_PBKDF_PBKDF2 || fail
+check_hash $PWD1 $HASH1
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_PBKDF2 $IMG --offset 8192 || fail
+wipe $PWD1
+check_hash $PWD1 $HASH5
+$CRYPTSETUP -q convert --type luks1 $IMG || fail
+echo $PWD1 | $REENC $IMG -q $FAST_PBKDF_PBKDF2 || fail
+check_hash $PWD1 $HASH5
+
+remove_mapping
+exit 0
diff --git a/tests/tcrypt-compat-test b/tests/tcrypt-compat-test
new file mode 100755
index 0000000..173b5c6
--- /dev/null
+++ b/tests/tcrypt-compat-test
@@ -0,0 +1,162 @@
+#!/bin/bash
+
+# check tcrypt images parsing
+
+CRYPTSETUP=../cryptsetup
+TST_DIR=tcrypt-images
+MAP=tctst
+PASSWORD="aaaaaaaaaaaa"
+PASSWORD_HIDDEN="bbbbbbbbbbbb"
+PIM=1234
+
+[ -z "$srcdir" ] && srcdir="."
+
+function remove_mapping()
+{
+ [ -b /dev/mapper/$MAP ] && dmsetup remove --retry $MAP
+ [ -b /dev/mapper/"$MAP"_1 ] && dmsetup remove --retry "$MAP"_1
+ [ -b /dev/mapper/"$MAP"_2 ] && dmsetup remove --retry "$MAP"_2
+}
+
+function fail()
+{
+ [ -n "$1" ] && echo "$1"
+ echo " [FAILED]"
+ echo "FAILED at line $(caller)"
+ remove_mapping
+ exit 2
+}
+
+function skip()
+{
+ [ -n "$1" ] && echo "$1"
+ echo "Test skipped."
+ exit 77
+}
+
+function test_one() # cipher mode keysize rm_pattern
+{
+ $CRYPTSETUP benchmark -c "$1-$2" -s "$3" >/dev/null 2>&1
+ if [ $? -ne 0 ] ; then
+ echo "$1-$2 [N/A]"
+ IMGS=$(ls $TST_DIR/[tv]c* | grep "$4")
+ [ -n "$IMGS" ] && rm $IMGS
+ #echo $IMGS
+ else
+ echo "$1-$2 [OK]"
+ fi
+}
+
+function test_kdf() # hash
+{
+ $CRYPTSETUP benchmark -h "$1" >/dev/null 2>&1
+ if [ $? -ne 0 ] ; then
+ echo "pbkdf2-$1 [N/A]"
+ IMGS=$(ls $TST_DIR/[tv]c* | grep "$1")
+ [ -n "$IMGS" ] && rm $IMGS
+ else
+ echo "pbkdf2-$1 [OK]"
+ fi
+}
+
+function test_required()
+{
+ which lsblk >/dev/null 2>&1 || skip "WARNING: lsblk tool required."
+
+ echo "REQUIRED KDF TEST"
+ test_kdf sha256
+ test_kdf sha512
+ test_kdf ripemd160
+ test_kdf whirlpool
+ test_kdf stribog512
+
+ echo "REQUIRED CIPHERS TEST"
+ test_one aes cbc 256 cbc-aes
+ test_one aes lrw 384 lrw-aes
+ test_one aes xts 512 xts-aes
+
+ test_one twofish ecb 256 twofish
+ test_one twofish cbc 256 cbc-twofish
+ test_one twofish lrw 384 lrw-twofish
+ test_one twofish xts 512 xts-twofish
+
+ test_one serpent ecb 256 serpent
+ test_one serpent cbc 256 cbc-serpent
+ test_one serpent lrw 384 lrw-serpent
+ test_one serpent xts 512 xts-serpent
+
+ test_one blowfish cbc 256 blowfish
+
+ test_one des3_ede cbc 192 des3_ede
+ test_one cast5 cbc 128 cast5
+
+ test_one camellia xts 512 camellia
+ test_one kuznyechik xts 512 kuznyechik
+
+ ls $TST_DIR/[tv]c* >/dev/null 2>&1 || skip "No remaining images."
+}
+
+export LANG=C
+[ ! -d $TST_DIR ] && tar xJf $srcdir/tcrypt-images.tar.xz --no-same-owner
+test_required
+
+echo "HEADER CHECK"
+for file in $(ls $TST_DIR/[tv]c_* $TST_DIR/vcpim_*) ; do
+ echo -n " $file"
+ PIM_OPT=""
+ [[ $file =~ vcpim.* ]] && PIM_OPT="--veracrypt-pim $PIM"
+ echo $PASSWORD | $CRYPTSETUP tcryptDump --veracrypt $PIM_OPT $file >/dev/null || fail
+ echo " [OK]"
+done
+
+echo "HEADER CHECK (HIDDEN)"
+for file in $(ls $TST_DIR/[tv]c_*-hidden) ; do
+ echo -n " $file (hidden)"
+ echo $PASSWORD_HIDDEN | $CRYPTSETUP tcryptDump --tcrypt-hidden --veracrypt $file >/dev/null || fail
+ echo " [OK]"
+done
+
+echo "HEADER KEYFILES CHECK"
+for file in $(ls $TST_DIR/[tv]ck_*) ; do
+ echo -n " $file"
+ echo $PASSWORD | $CRYPTSETUP tcryptDump --veracrypt -d $TST_DIR/keyfile1 -d $TST_DIR/keyfile2 $file >/dev/null || fail
+ echo " [OK]"
+done
+
+
+if [ $(id -u) != 0 ]; then
+ echo "WARNING: You must be root to run activation part of test, test skipped."
+ exit 0
+fi
+
+echo "ACTIVATION FS UUID CHECK"
+for file in $(ls $TST_DIR/[tv]c_* $TST_DIR/vcpim_*) ; do
+ echo -n " $file"
+ PIM_OPT=""
+ [[ $file =~ vcpim.* ]] && PIM_OPT="--veracrypt-pim $PIM"
+ out=$(echo $PASSWORD | $CRYPTSETUP tcryptOpen --veracrypt $PIM_OPT -r $file $MAP 2>&1)
+ ret=$?
+ [ $ret -eq 1 ] && ( echo "$out" | grep -q -e "TCRYPT legacy mode" ) && echo " [N/A]" && continue
+ [ $ret -eq 1 ] && ( echo "$out" | grep -q -e "TCRYPT compatible mapping" ) && echo " [N/A]" && continue
+ [ $ret -ne 0 ] && fail
+ $CRYPTSETUP status $MAP >/dev/null || fail
+ $CRYPTSETUP status /dev/mapper/$MAP >/dev/null || fail
+ UUID=$(lsblk -n -o UUID /dev/mapper/$MAP)
+ $CRYPTSETUP remove $MAP || fail
+ [ "$UUID" != "DEAD-BABE" ] && fail "UUID check failed."
+ echo " [OK]"
+done
+
+echo "ACTIVATION FS UUID (HIDDEN) CHECK"
+for file in $(ls $TST_DIR/[tv]c_*-hidden) ; do
+ echo -n " $file"
+ out=$(echo $PASSWORD_HIDDEN | $CRYPTSETUP tcryptOpen --veracrypt -r $file $MAP --tcrypt-hidden 2>&1)
+ ret=$?
+ [ $ret -eq 1 ] && ( echo "$out" | grep -q -e "TCRYPT legacy mode" ) && echo " [N/A]" && continue
+ [ $ret -eq 1 ] && ( echo "$out" | grep -q -e "TCRYPT compatible mapping" ) && echo " [N/A]" && continue
+ [ $ret -ne 0 ] && fail
+ UUID=$(lsblk -n -o UUID /dev/mapper/$MAP)
+ $CRYPTSETUP remove $MAP || fail
+ [ "$UUID" != "CAFE-BABE" ] && fail "UUID check failed."
+ echo " [OK]"
+done
diff --git a/tests/tcrypt-images.tar.xz b/tests/tcrypt-images.tar.xz
new file mode 100644
index 0000000..c50c6f6
--- /dev/null
+++ b/tests/tcrypt-images.tar.xz
Binary files differ
diff --git a/tests/test_utils.c b/tests/test_utils.c
new file mode 100644
index 0000000..af1dda7
--- /dev/null
+++ b/tests/test_utils.c
@@ -0,0 +1,600 @@
+/*
+ * cryptsetup library API test utilities
+ *
+ * Copyright (C) 2009-2019 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2019 Milan Broz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <libdevmapper.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef HAVE_SYS_SYSMACROS_H
+# include <sys/sysmacros.h>
+#endif
+#include <linux/loop.h>
+
+#include "api_test.h"
+#include "libcryptsetup.h"
+
+static char last_error[256];
+static char global_log[4096];
+static uint32_t t_dm_crypt_flags = 0;
+
+char *THE_LOOP_DEV = NULL;
+int _debug = 0;
+int global_lines = 0;
+int _quit = 0;
+int _verbose = 0;
+uint64_t t_dev_offset = 0;
+
+static void (*_cleanup)(void);
+
+void register_cleanup(void (*cleanup)(void))
+{
+ _cleanup = cleanup;
+}
+
+void check_ok(int status, int line, const char *func)
+{
+ if (status) {
+ printf("FAIL line %d [%s]: code %d, %s\n", line, func, status, last_error);
+ _cleanup();
+ exit(-1);
+ }
+}
+
+void check_ko(int status, int line, const char *func)
+{
+ if (status >= 0) {
+ printf("FAIL line %d [%s]: code %d, %s\n", line, func, status, last_error);
+ _cleanup();
+ exit(-1);
+ } else if (_verbose)
+ printf(" => errno %d, errmsg: %s\n", status, last_error);
+}
+
+void check_equal(int line, const char *func, int64_t x, int64_t y)
+{
+ printf("FAIL line %d [%s]: expected equal values differs: %"
+ PRIi64 " != %" PRIi64 "\n", line, func, x, y);
+ _cleanup();
+ exit(-1);
+}
+
+void check_null(int line, const char *func, const void *x)
+{
+ if (x) {
+ printf("FAIL line %d [%s]: expected NULL value: %p\n", line, func, x);
+ _cleanup();
+ exit(-1);
+ }
+}
+
+void check_notnull(int line, const char *func, const void *x)
+{
+ if (!x) {
+ printf("FAIL line %d [%s]: expected not NULL value: %p\n", line, func, x);
+ _cleanup();
+ exit(-1);
+ }
+}
+
+void xlog(const char *msg, const char *tst, const char *func, int line, const char *txt)
+{
+ if (_verbose) {
+ if (txt)
+ printf(" [%s,%s:%d] %s [%s]\n", msg, func, line, tst, txt);
+ else
+ printf(" [%s,%s:%d] %s\n", msg, func, line, tst);
+ }
+ if (_quit) {
+ if (_verbose)
+ printf("Interrupted by a signal.\n");
+ _cleanup();
+ exit(-1);
+ }
+}
+
+int t_device_size(const char *device, uint64_t *size)
+{
+ int devfd, r = 0;
+
+ devfd = open(device, O_RDONLY);
+ if(devfd == -1)
+ return -EINVAL;
+
+ if (ioctl(devfd, BLKGETSIZE64, size) < 0)
+ r = -EINVAL;
+ close(devfd);
+ return r;
+}
+
+int fips_mode(void)
+{
+ int fd;
+ char buf = 0;
+
+ fd = open("/proc/sys/crypto/fips_enabled", O_RDONLY);
+
+ if (fd < 0)
+ return 0;
+
+ if (read(fd, &buf, 1) != 1)
+ buf = '0';
+
+ close(fd);
+
+ return (buf == '1');
+}
+
+/*
+ * Creates dm-linear target over the test loop device. Offset is held in
+ * global variables so that size can be tested whether it fits into remaining
+ * size of the loop device or not
+ */
+int create_dmdevice_over_loop(const char *dm_name, const uint64_t size)
+{
+ char cmd[128];
+ int r;
+ uint64_t r_size;
+
+ if (t_device_size(THE_LOOP_DEV, &r_size) < 0 || r_size <= t_dev_offset || !size)
+ return -1;
+ if ((r_size - t_dev_offset) < size) {
+ printf("No enough space on backing loop device\n.");
+ return -2;
+ }
+ snprintf(cmd, sizeof(cmd),
+ "dmsetup create %s --table \"0 %" PRIu64 " linear %s %" PRIu64 "\"",
+ dm_name, size, THE_LOOP_DEV, t_dev_offset);
+ if (!(r = _system(cmd, 1)))
+ t_dev_offset += size;
+ return r;
+}
+
+// Get key from kernel dm mapping table using dm-ioctl
+int get_key_dm(const char *name, char *buffer, unsigned int buffer_size)
+{
+ struct dm_task *dmt;
+ struct dm_info dmi;
+ uint64_t start, length;
+ char *target_type, *key, *params;
+ void *next = NULL;
+ int r = -EINVAL;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
+ goto out;
+ if (!dm_task_set_name(dmt, name))
+ goto out;
+ if (!dm_task_run(dmt))
+ goto out;
+ if (!dm_task_get_info(dmt, &dmi))
+ goto out;
+ if (!dmi.exists)
+ goto out;
+
+ next = dm_get_next_target(dmt, next, &start, &length, &target_type, &params);
+ if (!target_type || strcmp(target_type, "crypt") != 0)
+ goto out;
+
+ (void)strsep(&params, " "); /* rcipher */
+ key = strsep(&params, " ");
+
+ if (buffer_size <= strlen(key))
+ goto out;
+
+ strncpy(buffer, key, buffer_size);
+ r = 0;
+out:
+ if (dmt)
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+int prepare_keyfile(const char *name, const char *passphrase, int size)
+{
+ int fd, r;
+
+ fd = open(name, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR|S_IWUSR);
+ if (fd != -1) {
+ r = write(fd, passphrase, size);
+ close(fd);
+ } else
+ r = 0;
+
+ return r == size ? 0 : 1;
+}
+
+// Decode key from its hex representation
+int crypt_decode_key(char *key, const char *hex, unsigned int size)
+{
+ char buffer[3];
+ char *endp;
+ unsigned int i;
+
+ buffer[2] = '\0';
+
+ for (i = 0; i < size; i++) {
+ buffer[0] = *hex++;
+ buffer[1] = *hex++;
+
+ key[i] = (unsigned char)strtoul(buffer, &endp, 16);
+
+ if (endp != &buffer[2])
+ return -1;
+ }
+
+ if (*hex != '\0')
+ return -1;
+
+ return 0;
+}
+
+void global_log_callback(int level, const char *msg, void *usrptr)
+{
+ size_t len;
+
+ if (_debug) {
+ if (level == CRYPT_LOG_DEBUG)
+ fprintf(stdout, "# %s\n", msg);
+ else
+ fprintf(stdout, "%s", msg);
+ }
+
+ if (level <= CRYPT_LOG_DEBUG)
+ return;
+
+ strncat(global_log, msg, sizeof(global_log) - strlen(global_log));
+ global_lines++;
+ if (level == CRYPT_LOG_ERROR) {
+ len = strlen(msg);
+ if (len > sizeof(last_error))
+ len = sizeof(last_error);
+ strncpy(last_error, msg, sizeof(last_error));
+ last_error[len-1] = '\0';
+ }
+}
+
+void reset_log(void)
+{
+ memset(global_log, 0, sizeof(global_log));
+ memset(last_error, 0, sizeof(last_error));
+ global_lines = 0;
+}
+
+int _system(const char *command, int warn)
+{
+ int r;
+ if (_debug)
+ printf("Running system: %s\n", command);
+ if ((r=system(command)) < 0 && warn)
+ printf("System command failed: %s", command);
+ return r;
+}
+
+static int t_dm_satisfies_version(unsigned target_maj, unsigned target_min, unsigned target_patch,
+ unsigned actual_maj, unsigned actual_min, unsigned actual_patch)
+{
+ if (actual_maj > target_maj)
+ return 1;
+ if (actual_maj == target_maj && actual_min > target_min)
+ return 1;
+ if (actual_maj == target_maj && actual_min == target_min && actual_patch >= target_patch)
+ return 1;
+ return 0;
+}
+
+static void t_dm_set_crypt_compat(const char *dm_version, unsigned crypt_maj,
+ unsigned crypt_min, unsigned crypt_patch)
+{
+ unsigned dm_maj = 0, dm_min = 0, dm_patch = 0;
+
+ if (sscanf(dm_version, "%u.%u.%u", &dm_maj, &dm_min, &dm_patch) != 3) {
+ dm_maj = 0;
+ dm_min = 0;
+ dm_patch = 0;
+ }
+
+ if (t_dm_satisfies_version(1, 2, 0, crypt_maj, crypt_min, 0))
+ t_dm_crypt_flags |= T_DM_KEY_WIPE_SUPPORTED;
+
+ if (t_dm_satisfies_version(1, 10, 0, crypt_maj, crypt_min, 0))
+ t_dm_crypt_flags |= T_DM_LMK_SUPPORTED;
+
+ if (t_dm_satisfies_version(4, 20, 0, dm_maj, dm_min, 0))
+ t_dm_crypt_flags |= T_DM_SECURE_SUPPORTED;
+
+ if (t_dm_satisfies_version(1, 8, 0, crypt_maj, crypt_min, 0))
+ t_dm_crypt_flags |= T_DM_PLAIN64_SUPPORTED;
+
+ if (t_dm_satisfies_version(1, 11, 0, crypt_maj, crypt_min, 0))
+ t_dm_crypt_flags |= T_DM_DISCARDS_SUPPORTED;
+
+ if (t_dm_satisfies_version(1, 13, 0, crypt_maj, crypt_min, 0))
+ t_dm_crypt_flags |= T_DM_TCW_SUPPORTED;
+
+ if (t_dm_satisfies_version(1, 14, 0, crypt_maj, crypt_min, 0)) {
+ t_dm_crypt_flags |= T_DM_SAME_CPU_CRYPT_SUPPORTED;
+ t_dm_crypt_flags |= T_DM_SUBMIT_FROM_CRYPT_CPUS_SUPPORTED;
+ }
+
+ if (t_dm_satisfies_version(1, 18, 1, crypt_maj, crypt_min, crypt_patch))
+ t_dm_crypt_flags |= T_DM_KERNEL_KEYRING_SUPPORTED;
+}
+
+static void t_dm_set_verity_compat(const char *dm_version, unsigned verity_maj,
+ unsigned verity_min, unsigned verity_patch)
+{
+ if (verity_maj > 0)
+ t_dm_crypt_flags |= T_DM_VERITY_SUPPORTED;
+ else
+ return;
+ /*
+ * ignore_corruption, restart_on corruption is available since 1.2 (kernel 4.1)
+ * ignore_zero_blocks since 1.3 (kernel 4.5)
+ * (but some dm-verity targets 1.2 don't support it)
+ * FEC is added in 1.3 as well.
+ */
+ if (t_dm_satisfies_version(1, 3, 0, verity_maj, verity_min, 0)) {
+ t_dm_crypt_flags |= T_DM_VERITY_ON_CORRUPTION_SUPPORTED;
+ t_dm_crypt_flags |= T_DM_VERITY_FEC_SUPPORTED;
+ }
+}
+
+static void t_dm_set_integrity_compat(const char *dm_version, unsigned integrity_maj,
+ unsigned integrity_min, unsigned integrity_patch)
+{
+ if (integrity_maj > 0)
+ t_dm_crypt_flags |= T_DM_INTEGRITY_SUPPORTED;
+}
+
+int t_dm_check_versions(void)
+{
+ struct dm_task *dmt;
+ struct dm_versions *target, *last_target;
+ char dm_version[16];
+ int r = 1;
+
+ if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS)))
+ goto out;
+
+ if (!dm_task_run(dmt))
+ goto out;
+
+ if (!dm_task_get_driver_version(dmt, dm_version, sizeof(dm_version)))
+ goto out;
+
+ target = dm_task_get_versions(dmt);
+ do {
+ last_target = target;
+ if (!strcmp("crypt", target->name)) {
+ t_dm_set_crypt_compat(dm_version,
+ (unsigned)target->version[0],
+ (unsigned)target->version[1],
+ (unsigned)target->version[2]);
+ } else if (!strcmp("verity", target->name)) {
+ t_dm_set_verity_compat(dm_version,
+ (unsigned)target->version[0],
+ (unsigned)target->version[1],
+ (unsigned)target->version[2]);
+ } else if (!strcmp("integrity", target->name)) {
+ t_dm_set_integrity_compat(dm_version,
+ (unsigned)target->version[0],
+ (unsigned)target->version[1],
+ (unsigned)target->version[2]);
+ }
+ target = (struct dm_versions *)((char *) target + target->next);
+ } while (last_target != target);
+
+ r = 0;
+out:
+ if (dmt)
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+int t_dm_crypt_keyring_support(void)
+{
+ return t_dm_crypt_flags & T_DM_KERNEL_KEYRING_SUPPORTED;
+}
+
+int t_dm_crypt_cpu_switch_support(void)
+{
+ return t_dm_crypt_flags & (T_DM_SAME_CPU_CRYPT_SUPPORTED |
+ T_DM_SUBMIT_FROM_CRYPT_CPUS_SUPPORTED);
+}
+
+int t_dm_crypt_discard_support(void)
+{
+ return t_dm_crypt_flags & T_DM_DISCARDS_SUPPORTED;
+}
+
+/* loop helpers */
+
+#define LOOP_DEV_MAJOR 7
+
+#ifndef LO_FLAGS_AUTOCLEAR
+#define LO_FLAGS_AUTOCLEAR 4
+#endif
+
+#ifndef LOOP_CTL_GET_FREE
+#define LOOP_CTL_GET_FREE 0x4C82
+#endif
+
+#ifndef LOOP_SET_CAPACITY
+#define LOOP_SET_CAPACITY 0x4C07
+#endif
+
+int loop_device(const char *loop)
+{
+ struct stat st;
+
+ if (!loop)
+ return 0;
+
+ if (stat(loop, &st) || !S_ISBLK(st.st_mode) ||
+ major(st.st_rdev) != LOOP_DEV_MAJOR)
+ return 0;
+
+ return 1;
+}
+
+static char *crypt_loop_get_device_old(void)
+{
+ char dev[20];
+ int i, loop_fd;
+ struct loop_info64 lo64 = {0};
+
+ for (i = 0; i < 256; i++) {
+ sprintf(dev, "/dev/loop%d", i);
+
+ loop_fd = open(dev, O_RDONLY);
+ if (loop_fd < 0)
+ return NULL;
+
+ if (ioctl(loop_fd, LOOP_GET_STATUS64, &lo64) &&
+ errno == ENXIO) {
+ close(loop_fd);
+ return strdup(dev);
+ }
+ close(loop_fd);
+ }
+
+ return NULL;
+}
+
+static char *crypt_loop_get_device(void)
+{
+ char dev[64];
+ int i, loop_fd;
+ struct stat st;
+
+ loop_fd = open("/dev/loop-control", O_RDONLY);
+ if (loop_fd < 0)
+ return crypt_loop_get_device_old();
+
+ i = ioctl(loop_fd, LOOP_CTL_GET_FREE);
+ if (i < 0) {
+ close(loop_fd);
+ return NULL;
+ }
+ close(loop_fd);
+
+ if (sprintf(dev, "/dev/loop%d", i) < 0)
+ return NULL;
+
+ if (stat(dev, &st) || !S_ISBLK(st.st_mode))
+ return NULL;
+
+ return strdup(dev);
+}
+
+int loop_attach(char **loop, const char *file, int offset,
+ int autoclear, int *readonly)
+{
+ struct loop_info64 lo64 = {0};
+ char *lo_file_name;
+ int loop_fd = -1, file_fd = -1, r = 1;
+
+ *loop = NULL;
+
+ file_fd = open(file, (*readonly ? O_RDONLY : O_RDWR) | O_EXCL);
+ if (file_fd < 0 && (errno == EROFS || errno == EACCES) && !*readonly) {
+ *readonly = 1;
+ file_fd = open(file, O_RDONLY | O_EXCL);
+ }
+ if (file_fd < 0)
+ goto out;
+
+ while (loop_fd < 0) {
+ *loop = crypt_loop_get_device();
+ if (!*loop)
+ goto out;
+
+ loop_fd = open(*loop, *readonly ? O_RDONLY : O_RDWR);
+ if (loop_fd < 0)
+ goto out;
+
+ if (ioctl(loop_fd, LOOP_SET_FD, file_fd) < 0) {
+ if (errno != EBUSY)
+ goto out;
+ free(*loop);
+ *loop = NULL;
+
+ close(loop_fd);
+ loop_fd = -1;
+ }
+ }
+
+ lo_file_name = (char*)lo64.lo_file_name;
+ lo_file_name[LO_NAME_SIZE-1] = '\0';
+ strncpy(lo_file_name, file, LO_NAME_SIZE-1);
+ lo64.lo_offset = offset;
+ if (autoclear)
+ lo64.lo_flags |= LO_FLAGS_AUTOCLEAR;
+
+ if (ioctl(loop_fd, LOOP_SET_STATUS64, &lo64) < 0) {
+ (void)ioctl(loop_fd, LOOP_CLR_FD, 0);
+ goto out;
+ }
+
+ /* Verify that autoclear is really set */
+ if (autoclear) {
+ memset(&lo64, 0, sizeof(lo64));
+ if (ioctl(loop_fd, LOOP_GET_STATUS64, &lo64) < 0 ||
+ !(lo64.lo_flags & LO_FLAGS_AUTOCLEAR)) {
+ (void)ioctl(loop_fd, LOOP_CLR_FD, 0);
+ goto out;
+ }
+ }
+
+ r = 0;
+out:
+ if (r && loop_fd >= 0)
+ close(loop_fd);
+ if (file_fd >= 0)
+ close(file_fd);
+ if (r && *loop) {
+ free(*loop);
+ *loop = NULL;
+ }
+ return r ? -1 : loop_fd;
+}
+
+int loop_detach(const char *loop)
+{
+ int loop_fd = -1, r = 1;
+
+ loop_fd = open(loop, O_RDONLY);
+ if (loop_fd < 0)
+ return 1;
+
+ if (!ioctl(loop_fd, LOOP_CLR_FD, 0))
+ r = 0;
+
+ close(loop_fd);
+ return r;
+}
diff --git a/tests/unit-utils-io.c b/tests/unit-utils-io.c
new file mode 100644
index 0000000..2f66d9f
--- /dev/null
+++ b/tests/unit-utils-io.c
@@ -0,0 +1,346 @@
+/*
+ * simple unit test for utils_io.c (blockwise low level functions)
+ *
+ * Copyright (C) 2018-2019 Red Hat, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "utils_io.h"
+
+enum fn_enum {
+ READ_BUFFER = 0,
+ WRITE_BUFFER,
+ READ_BLOCKWISE,
+ WRITE_BLOCKWISE,
+ READ_LSEEK_BLOCKWISE,
+ WRITE_LSEEK_BLOCKWISE
+} test_fn;
+
+char *test_file;
+size_t test_bsize;
+size_t test_alignment;
+size_t test_length;
+off_t test_offset; //FIXME: check for proper 64bit support (and test it!)
+size_t test_mem_alignment = 4096;
+
+static int test_read_buffer(void)
+{
+ void *buffer = NULL;
+ int fd = -1;
+ ssize_t ret = -EINVAL;
+
+ //printf("Entering test_read_buffer\n");
+
+ if (posix_memalign(&buffer, test_mem_alignment, test_length)) {
+ fprintf(stderr, "Failed to allocate aligned buffer.\n");
+ goto out;
+ }
+
+ fd = open(test_file, O_RDONLY | O_DIRECT);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open %s.\n", test_file);
+ goto out;
+ }
+
+ ret = read_buffer(fd, buffer, test_length);
+ if (ret < 0)
+ goto out;
+
+ ret = (size_t) ret == test_length ? 0 : -EIO;
+out:
+ if (fd >= 0)
+ close(fd);
+ free(buffer);
+ return ret;
+}
+
+static int test_write_buffer(void)
+{
+ void *buffer = NULL;
+ int fd = -1;
+ ssize_t ret = -EINVAL;
+
+ //printf("Entering test_write_buffer\n");
+
+ if (posix_memalign(&buffer, test_mem_alignment, test_length)) {
+ fprintf(stderr, "Failed to allocate aligned buffer.\n");
+ goto out;
+ }
+
+ fd = open(test_file, O_WRONLY | O_DIRECT);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open %s.\n", test_file);
+ goto out;
+ }
+
+ ret = write_buffer(fd, buffer, test_length);
+ if (ret < 0)
+ goto out;
+
+ return (size_t) ret == test_length ? 0 : -EIO;
+out:
+ if (fd >= 0)
+ close(fd);
+ free(buffer);
+ return ret;
+}
+
+static int test_read_blockwise(void)
+{
+ void *buffer = NULL;
+ int fd = -1;
+ ssize_t ret = -EINVAL;
+
+ //printf("Entering test_read_blockwise ");
+ //printf("test_bsize: %zu, test_length: %zu\n", test_bsize, test_length);
+
+ if (posix_memalign(&buffer, test_mem_alignment, test_length)) {
+ fprintf(stderr, "Failed to allocate aligned buffer.\n");
+ goto out;
+ }
+
+ fd = open(test_file, O_RDONLY | O_DIRECT);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open %s.\n", test_file);
+ goto out;
+ }
+
+
+ ret = read_blockwise(fd, test_bsize, test_mem_alignment, buffer, test_length);
+ if (ret < 0)
+ goto out;
+
+ ret = (size_t) ret == test_length ? 0 : -EIO;
+out:
+ if (fd >= 0)
+ close(fd);
+ free(buffer);
+ return ret;
+}
+
+static int test_write_blockwise(void)
+{
+ void *buffer = NULL;
+ int fd = -1;
+ ssize_t ret = -EINVAL;
+
+ //printf("Entering test_write_blockwise\n");
+
+ if (posix_memalign(&buffer, test_mem_alignment, test_length)) {
+ fprintf(stderr, "Failed to allocate aligned buffer.\n");
+ goto out;
+ }
+
+ fd = open(test_file, O_RDWR | O_DIRECT);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open %s.\n", test_file);
+ goto out;
+ }
+
+ ret = write_blockwise(fd, test_bsize, test_mem_alignment, buffer, test_length);
+ if (ret < 0)
+ goto out;
+
+ ret = (size_t) ret == test_length ? 0 : -EIO;
+out:
+ if (fd >= 0)
+ close(fd);
+ free(buffer);
+ return ret;
+}
+
+static int test_read_lseek_blockwise(void)
+{
+ void *buffer = NULL;
+ int fd = -1;
+ ssize_t ret = -EINVAL;
+
+ //printf("Entering test_read_lseek_blockwise\n");
+
+ if (posix_memalign(&buffer, test_mem_alignment, test_length)) {
+ fprintf(stderr, "Failed to allocate aligned buffer.\n");
+ goto out;
+ }
+
+ fd = open(test_file, O_RDONLY | O_DIRECT);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open %s.\n", test_file);
+ goto out;
+ }
+
+ ret = read_lseek_blockwise(fd, test_bsize, test_mem_alignment, buffer, test_length, test_offset);
+ if (ret < 0)
+ goto out;
+
+ ret = (size_t) ret == test_length ? 0 : -EIO;
+out:
+ if (fd >= 0)
+ close(fd);
+ free(buffer);
+ return ret;
+}
+
+static int test_write_lseek_blockwise(void)
+{
+ void *buffer = NULL;
+ int fd = -1;
+ ssize_t ret = -EINVAL;
+
+ //printf("Entering test_write_lseek_blockwise\n");
+
+ if (posix_memalign(&buffer, test_mem_alignment, test_length)) {
+ fprintf(stderr, "Failed to allocate aligned buffer.\n");
+ goto out;
+ }
+
+ fd = open(test_file, O_RDWR | O_DIRECT);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open %s.\n", test_file);
+ goto out;
+ }
+
+ ret = write_lseek_blockwise(fd, test_bsize, test_mem_alignment, buffer, test_length, test_offset);
+ if (ret < 0)
+ goto out;
+
+ ret = (size_t) ret == test_length ? 0 : -EIO;
+out:
+ if (fd >= 0)
+ close(fd);
+ free(buffer);
+ return ret;
+}
+
+static void usage(void)
+{
+ fprintf(stderr, "Use:\tunit-utils-io file/device blockwise_fn length [bsize] [offset].\n");
+}
+
+static int parse_input_params(int argc, char **argv)
+{
+ struct stat st;
+ unsigned long offset;
+
+ if (argc < 4) {
+ usage();
+ return 1;
+ }
+
+ if (stat(argv[1], &st)) {
+ fprintf(stderr, "File/device %s is missing?\n", argv[1]);
+ return 1;
+ }
+ test_file = argv[1];
+ if (sscanf(argv[3], "%zu", &test_length) != 1)
+ return 1;
+ if (argc >= 5 && sscanf(argv[4], "%zu", &test_bsize) != 1)
+ return 1;
+ if (argc >= 6) {
+ if (sscanf(argv[5], "%ld", &offset) != 1)
+ return 1;
+ test_offset = offset;
+ }
+
+ if (!strcmp(argv[2], "read_buffer"))
+ test_fn = READ_BUFFER;
+ else if (!strcmp(argv[2], "write_buffer"))
+ test_fn = WRITE_BUFFER;
+ else if (!strcmp(argv[2], "read_blockwise")) {
+ if (argc < 5) {
+ usage();
+ return 1;
+ }
+ test_fn = READ_BLOCKWISE;
+ } else if (!strcmp(argv[2], "write_blockwise")) {
+ if (argc < 5) {
+ usage();
+ return 1;
+ }
+ test_fn = WRITE_BLOCKWISE;
+ } else if (!strcmp(argv[2], "read_lseek_blockwise")) {
+ if (argc < 6) {
+ usage();
+ return 1;
+ }
+ test_fn = READ_LSEEK_BLOCKWISE;
+ } else if (!strcmp(argv[2], "write_lseek_blockwise")) {
+ if (argc < 6) {
+ usage();
+ return 1;
+ }
+ test_fn = WRITE_LSEEK_BLOCKWISE;
+ } else {
+ usage();
+ return 1;
+ }
+
+ /* printf("function '%s': length %zu", argv[2], test_length);
+ if (argc >= 5)
+ printf(", bsize %zu", test_bsize);
+ if (argc >= 6)
+ printf(", offset %llu", test_offset);
+ printf("\n"); */
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ long ps;
+ int r = EXIT_FAILURE;
+
+ if (parse_input_params(argc, argv))
+ return r;
+
+ ps = sysconf(_SC_PAGESIZE);
+ if (ps > 0)
+ test_mem_alignment = (size_t)ps;
+
+ switch (test_fn) {
+ case READ_BUFFER:
+ r = test_read_buffer();
+ break;
+ case WRITE_BUFFER:
+ r = test_write_buffer();
+ break;
+ case READ_BLOCKWISE:
+ r = test_read_blockwise();
+ break;
+ case WRITE_BLOCKWISE:
+ r = test_write_blockwise();
+ break;
+ case READ_LSEEK_BLOCKWISE:
+ r = test_read_lseek_blockwise();
+ break;
+ case WRITE_LSEEK_BLOCKWISE:
+ r = test_write_lseek_blockwise();
+ break;
+ default :
+ fprintf(stderr, "Internal test error.\n");
+ return r;
+ }
+
+ return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/tests/valg-api.sh b/tests/valg-api.sh
new file mode 100755
index 0000000..e27a269
--- /dev/null
+++ b/tests/valg-api.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+SUP="--suppressions=./cryptsetup-valg-supps"
+CHILD="--trace-children=no --child-silent-after-fork=yes"
+MALLOC="--malloc-fill=aa"
+FREE="--free-fill=21"
+STACK="--max-stackframe=300000"
+EXTRAS="--read-var-info=yes --show-reachable=yes"
+LOGFILE="--log-file=./valglog.$(date +%s)_${INFOSTRING}"
+LEAKCHECK="--leak-check=full --track-origins=yes"
+
+exec valgrind $SUP $GETSUP $CHILD $MALLOC $FREE $STACK $EXTRAS $LOGFILE $LEAKCHECK "$@"
diff --git a/tests/valg.sh b/tests/valg.sh
new file mode 100755
index 0000000..3dc0784
--- /dev/null
+++ b/tests/valg.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+SUP="--suppressions=./cryptsetup-valg-supps"
+CHILD="--trace-children=yes --child-silent-after-fork=yes"
+MALLOC="--malloc-fill=aa"
+FREE="--free-fill=21"
+STACK="--max-stackframe=2000000"
+EXTRAS="--read-var-info=yes --show-reachable=yes"
+LOGFILE="--log-file=./valglog.$(date +%s)_${INFOSTRING}"
+LEAKCHECK="--leak-check=full --track-origins=yes"
+
+exec valgrind $SUP $GETSUP $CHILD $MALLOC $FREE $STACK $EXTRAS $LOGFILE $LEAKCHECK "$@"
diff --git a/tests/valid_header_file.xz b/tests/valid_header_file.xz
new file mode 100644
index 0000000..4b443ae
--- /dev/null
+++ b/tests/valid_header_file.xz
Binary files differ
diff --git a/tests/verity-compat-test b/tests/verity-compat-test
new file mode 100755
index 0000000..33e5d57
--- /dev/null
+++ b/tests/verity-compat-test
@@ -0,0 +1,439 @@
+#!/bin/bash
+
+VERITYSETUP=../veritysetup
+VERITYSETUP_VALGRIND=../.libs/veritysetup
+VERITYSETUP_LIB_VALGRIND=../.libs
+
+DEV_NAME=verity3273
+DEV_OUT="$DEV_NAME.out"
+IMG=verity-data
+IMG_HASH=verity-hash
+IMG_TMP=tst-dev
+FEC_DEV=tst_fec123
+
+function remove_mapping()
+{
+ [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove $DEV_NAME >/dev/null 2>&1
+ [ ! -z "$LOOPDEV1" ] && losetup -d $LOOPDEV1 >/dev/null 2>&1
+ rm -f $IMG $IMG_HASH $DEV_OUT $FEC_DEV $IMG_TMP >/dev/null 2>&1
+ LOOPDEV1=""
+ LOOPDEV2=""
+}
+
+function fail()
+{
+ [ -n "$1" ] && echo "$1"
+ echo "FAILED at line $(caller)"
+ [ -f $DEV_OUT ] && cat $DEV_OUT
+ remove_mapping
+ exit 2
+}
+
+function skip()
+{
+ [ -n "$1" ] && echo "$1"
+ exit 77
+}
+
+function prepare() # $1 dev1_siz [$2 dev2_size]
+{
+ remove_mapping
+
+ dd if=/dev/zero of=$IMG bs=1k count=$1 >/dev/null 2>&1
+ LOOPDEV1=$(losetup -f 2>/dev/null)
+ [ -z "$LOOPDEV1" ] && fail "No free loop device"
+ losetup $LOOPDEV1 $IMG
+
+ [ -z "$2" ] && return
+ LOOPDEV2=$IMG_HASH
+}
+
+function wipe()
+{
+ dd if=/dev/zero of=$LOOPDEV1 bs=256k >/dev/null 2>&1
+ rm -f $IMG_HASH $DEV_OUT >/dev/null 2>&1
+}
+
+function check_exists()
+{
+ [ -b /dev/mapper/$DEV_NAME ] || fail
+}
+
+function check_version() # MAJ MIN
+{
+ VER_STR=$(dmsetup targets | grep verity | cut -f 3 -dv)
+ [ -z "$VER_STR" ] && fail "Failed to parse dm-verity version."
+
+ VER_MAJ=$(echo $VER_STR | cut -f 1 -d.)
+ VER_MIN=$(echo $VER_STR | cut -f 2 -d.)
+
+ test $VER_MAJ -gt $1 && return 0
+ test $VER_MIN -ge $2 && return 0
+ return 1
+}
+
+function compare_out() # $1 what, $2 expected
+{
+ OPT=$(grep -v "^#" $DEV_OUT | grep -i "$1" | sed -e s/.*\:\ // )
+ [ -z "$OPT" ] && fail
+ [ $OPT != $2 ] && fail "$1 differs ($2)"
+}
+
+function check_root_hash() # $1 size, $2 hash, $3 salt, $4 version, $5 hash, [$6 offset]
+{
+ if [ -z "$LOOPDEV2" ] ; then
+ BLOCKS=$(($6 / $1))
+ DEV_PARAMS="$LOOPDEV1 $LOOPDEV1 \
+ --hash-offset $6 \
+ --data-blocks=$BLOCKS --debug"
+ else
+ DEV_PARAMS="$LOOPDEV1 $LOOPDEV2"
+ fi
+
+ for sb in yes no; do
+ FORMAT_PARAMS="--format=$4 --data-block-size=$1 --hash-block-size=$1 --hash=$5 --salt=$3"
+ if [ $sb == yes ] ; then
+ VERIFY_PARAMS=""
+ else
+ FORMAT_PARAMS="$FORMAT_PARAMS --no-superblock"
+ VERIFY_PARAMS=$FORMAT_PARAMS
+ fi
+
+ for fail in data hash; do
+ wipe
+ echo -n "V$4(sb=$sb) $5 block size $1: "
+ $VERITYSETUP format $DEV_PARAMS $FORMAT_PARAMS >$DEV_OUT || fail
+
+ echo -n "[root hash]"
+ compare_out "root hash" $2
+ compare_out "salt" "$3"
+
+ $VERITYSETUP verify $DEV_PARAMS $VERIFY_PARAMS $2 >>$DEV_OUT 2>&1 || fail
+ echo -n "[verify]"
+
+ $VERITYSETUP create $DEV_NAME $DEV_PARAMS $VERIFY_PARAMS $2 >>$DEV_OUT 2>&1 || fail
+ check_exists
+ echo -n "[activate]"
+
+ dd if=/dev/mapper/$DEV_NAME of=/dev/null bs=$1 2>/dev/null
+ dmsetup status $DEV_NAME | grep "verity V" >/dev/null || fail
+ echo -n "[in-kernel verify]"
+
+ $VERITYSETUP close $DEV_NAME >/dev/null 2>&1 || fail
+
+ case $fail in
+ data)
+ dd if=/dev/urandom of=$LOOPDEV1 bs=1 seek=3456 count=8 conv=notrunc 2>/dev/null
+ TXT="data_dev"
+ ;;
+ hash)
+ if [ -z "$LOOPDEV2" ] ; then
+ dd if=/dev/urandom of=$LOOPDEV1 bs=1 seek=$((8193 + $4)) count=8 conv=notrunc 2>/dev/null
+ else
+ dd if=/dev/urandom of=$LOOPDEV2 bs=1 seek=8193 count=8 conv=notrunc 2>/dev/null
+ fi
+ TXT="hash_dev"
+ ;;
+ esac
+
+ $VERITYSETUP verify $DEV_PARAMS $VERIFY_PARAMS $2 >>$DEV_OUT 2>&1 && \
+ fail "userspace check for $TXT corruption"
+ $VERITYSETUP create $DEV_NAME $DEV_PARAMS $VERIFY_PARAMS $2 >>$DEV_OUT 2>&1 || \
+ fail "activation"
+ dd if=/dev/mapper/$DEV_NAME of=/dev/null bs=$1 2>/dev/null
+ dmsetup status $DEV_NAME | grep "verity V" >/dev/null && \
+ fail "in-kernel check for $TXT corruption"
+ $VERITYSETUP close $DEV_NAME >/dev/null 2>&1 || fail "deactivation"
+ echo "[$TXT corruption]"
+ done
+ done
+}
+
+function corrupt_device() # $1 device, $2 device_size(in bytes), $3 #{corrupted_bytes}
+{
+ # Repeatable magic corruption :-)
+ CORRUPT=$3
+ RANDOM=43
+ while [ "$CORRUPT" -gt 0 ]; do
+ SEEK=$RANDOM
+ while [ $SEEK -ge $2 ] ; do SEEK=$RANDOM; done
+ echo -n -e "\x55" | dd of=$1 bs=1 count=1 seek=$SEEK conv=notrunc > /dev/null 2>&1
+ CORRUPT=$(($CORRUPT - 1))
+ done
+}
+
+# $1 data_device, $2 hash_device, $3 fec_device, $4 data/hash_block_size(in bytes),
+# $5 data_size(in blocks), $6 device_size(in blocks), $7 hash_offset(in bytes),
+# $8 fec_offset(in bytes), $9 fec_roots, ${10} corrupted_bytes, [${11} superblock(y/n), ${12} salt]
+function check_fec()
+{
+ INDEX=25
+ dd if=/dev/zero of=$1 bs=$4 count=$6 > /dev/null 2>&1
+
+ echo -n "Block_size: $4, Data_size: $(($4 * $5))B, FEC_roots: $9, Corrupted_bytes: ${10} "
+
+ PARAMS=" --data-block-size=$4 --hash-block-size=$4 "
+ if [ "$5" -ne "$6" ]; then
+ PARAMS="$PARAMS --data-blocks=$5"
+ fi
+
+ if [ "$7" -ne 0 ]; then
+ PARAMS="$PARAMS --hash-offset=$7"
+ fi
+
+ if [ "$8" -ne 0 ]; then
+ PARAMS="$PARAMS --fec-offset=$8"
+ fi
+
+ if [ "${11}" == "n" ]; then
+ INDEX=24
+ echo -n "[no-superblock]"
+ PARAMS="$PARAMS --no-superblock -s=${12}"
+ elif [ -n "${12}" ]; then
+ PARAMS="$PARAMS -s=${12}"
+ fi
+
+ if [[ "$1" == "$2" && "$1" == "$3" ]]; then
+ echo -n "[one_device_test]"
+ dd if=/dev/zero of=$IMG_TMP bs=$4 count=$5 > /dev/null 2>&1
+ ARR=(`sha256sum $IMG_TMP`)
+ HASH_ORIG=${ARR[0]}
+ else
+ ARR=(`sha256sum $1`)
+ HASH_ORIG=${ARR[0]}
+ fi
+
+ ARR=(`$VERITYSETUP format $1 $2 --fec-device=$3 $PARAMS`)
+ SALT=${ARR[$INDEX]}
+ ROOT_HASH=${ARR[$(($INDEX+3))]}
+
+ corrupt_device $1 $(($5 * $4)) ${10}
+
+ $VERITYSETUP create $DEV_NAME $1 $2 $ROOT_HASH --fec-device=$3 $PARAMS > /dev/null 2>&1
+ if [ "$?" -ne "0" ] ; then
+ echo "[N/A, test skipped]"
+ return 3
+ fi
+
+ udevadm settle
+
+ dd if=/dev/mapper/$DEV_NAME of=$IMG_TMP > /dev/null 2>&1
+ ARR=(`sha256sum $IMG_TMP`)
+
+ HASH_REPAIRED=${ARR[0]}
+
+ $VERITYSETUP close $DEV_NAME
+ rm $1 $2 $3 $IMG_TMP > /dev/null 2>&1
+
+ if [ "$HASH_ORIG" != "$HASH_REPAIRED" ]; then
+ echo -n "[correction failed]"
+ return 1
+ fi
+
+ echo "[file was repaired][OK]"
+}
+
+function check_option() # $1 size, $2 hash, $3 salt, $4 version, $5 hash, $6 CLI option, $7 status option
+{
+ DEV_PARAMS="$LOOPDEV1 $LOOPDEV2"
+ FORMAT_PARAMS="--format=$4 --data-block-size=$1 --hash-block-size=$1 --hash=$5 --salt=$3"
+
+ echo -n "Option $6 "
+ $VERITYSETUP format $DEV_PARAMS $FORMAT_PARAMS >/dev/null 2>&1 || fail
+ $VERITYSETUP create $DEV_NAME $DEV_PARAMS $2 $6 >/dev/null 2>&1 || fail
+ check_exists
+ $VERITYSETUP status $DEV_NAME 2>/dev/null | grep flags | grep -q $7 || fail
+ dmsetup table $DEV_NAME 2>/dev/null | grep -q $7 || fail
+ $VERITYSETUP close $DEV_NAME >/dev/null 2>&1 || fail
+ echo "[OK]"
+}
+
+function valgrind_setup()
+{
+ which valgrind >/dev/null 2>&1 || fail "Cannot find valgrind."
+ [ ! -f $VERITYSETUP_VALGRIND ] && fail "Unable to get location of veritysetup executable."
+ export LD_LIBRARY_PATH="$VERITYSETUP_LIB_VALGRIND:$LD_LIBRARY_PATH"
+}
+
+function valgrind_run()
+{
+ INFOSTRING="$(basename ${BASH_SOURCE[1]})-line-${BASH_LINENO[0]}" ./valg.sh ${VERITYSETUP_VALGRIND} "$@"
+}
+
+function checkOffsetBug() # $1 size, $2 hash-offset, $3 data-blocks
+{
+ echo -n "Size :: $1 B | Hash-offset :: $2 blocks | Data-blocks :: $3 "
+ dd if=/dev/zero of=$IMG bs=1 count=0 seek=$1 >/dev/null 2>&1
+ $VERITYSETUP --data-blocks=$3 --hash-offset=$2 format $IMG $IMG >/dev/null 2>&1 || fail "Test [hash-offset greater than 2G] failed"
+ echo "[OK]"
+ remove_mapping
+}
+
+function checkOverlapBug() # $1 size, $2 hash-offset, $3 data-blocks, $4 block_size, $5 fec_offset
+{
+ echo -n "Device-size :: $1 B | "
+ [ $# -ge 3 ] && echo -n "Data-blocks :: $3 blocks| "
+ [ $# -lt 3 ] && echo -n "Data-blocks :: whole device | "
+ [ $# -ge 4 ] && echo -n "Block-size :: $4 B | "
+ [ $# -lt 4 ] && echo -n "Block-size :: 4096 B | "
+ echo -n "Hash-offset :: $2 B | "
+
+ dd if=/dev/zero of=$IMG bs=1 count=0 seek=$1 >/dev/null 2>&1
+ if [ -z $3 ] ; then
+ # veritysetup must fail
+ $VERITYSETUP --hash-offset=$2 format $IMG $IMG >/dev/null 2>&1 && fail "Test [overlap with option \"--data-blocks\" not entered] failed"
+ else
+ $VERITYSETUP --data-block-size=$4 --hash-block-size=$4 --data-blocks=$3 --hash-offset=$2 format $IMG $IMG >/dev/null 2>&1
+ RET=$?
+ [ "$3" -gt "$(($2 / $4))" ] && [ "$RET" -eq "0" ] && fail "Test [overlap - hash-offset in data area] failed"
+ fi
+
+ if [ $# -eq 5 ] ; then
+ echo -n "FEC-offset :: $5 B | "
+ PARAMS="--data-block-size=$4 --hash-block-size=$4 --data-blocks=$3 --fec-device=$IMG --fec-offset=$5"
+
+ # test data-fec area overlap
+ $VERITYSETUP format $IMG $IMG_HASH $PARAMS >/dev/null 2>&1
+ RET=$?
+ [ "$(($3*$4))" -gt "$5" ] && [ "$RET" -eq "0" ] && fail "Test [data/fec area overlap] failed"
+
+ HASH_SIZE=$(stat --printf="%s" $IMG_HASH)
+
+ # test hash-fec area overlap
+ $VERITYSETUP format $IMG $IMG $PARAMS --hash-offset=$2 >/dev/null 2>&1
+ RET=$?
+ [ "$(($2 + $HASH_SIZE))" -gt "$5" ] && [ "$RET" -eq "0" ] && fail "Test [hash/fec area overlap] failed"
+ fi
+
+ echo "[OK]"
+ remove_mapping
+}
+
+# $1 size, $2 block size, $3 roots, $4 hash offset, $5 fec offset,
+# $6 one dev(1 - one device, 2 - one device for data and hash, one device for fec data, 3 - three separate devices),
+# $7 #{corrupted bytes}
+function checkUserSpaceRepair()
+{
+ BS=512
+ COUNT=50000
+ dd if=/dev/zero of=$IMG bs=$BS count=$COUNT >/dev/null 2>&1
+ PARAMS="--data-block-size=$2 --hash-block-size=$2 --fec-roots=$3"
+ [ "$1" -gt 0 ] && PARAMS="$PARAMS --data-blocks=$1" && BS=$2 && COUNT=$1
+
+ # different parameters for different number of devices
+ [ "$6" -eq 1 ] && HASH_DEV=$IMG && FEC=$IMG && PARAMS="$PARAMS --hash-offset=$4 --fec-offset=$5" && echo -n "[One device]"
+ [ "$6" -eq 2 ] && HASH_DEV=$IMG && FEC=$FEC_DEV && PARAMS="$PARAMS --hash-offset=$4" && echo -n "[Two separate data/hash and fec devices]"
+ [ "$6" -eq 3 ] && HASH_DEV=$IMG_HASH && FEC=$FEC_DEV && echo -n "[Three separate devices]"
+
+ echo -n "[nroots::$3]"
+
+ ARR=(`$VERITYSETUP format $IMG $HASH_DEV --fec-device $FEC $PARAMS`)
+ ROOT_HASH=${ARR[28]}
+
+ echo -n "[Errors can be corrected]"
+ corrupt_device $IMG $(($BS*$COUNT)) $7
+ $VERITYSETUP verify $IMG $HASH_DEV $ROOT_HASH --fec-device=$FEC $PARAMS >/dev/null 2>&1
+ RET=$?
+ [ "$RET" -ne 0 ] && fail "Device can be corrected, but it wasn't."
+ echo -n "[OK]"
+
+ echo -n "[Errors cannot be corrected]"
+ dd if=/dev/urandom of=$IMG bs=$BS count=$COUNT conv=notrunc >/dev/null 2>&1
+ $VERITYSETUP verify $IMG $HASH_DEV $ROOT_HASH --fec-device=$FEC $PARAMS >/dev/null 2>&1
+ RET=$?
+ [ "$RET" -eq 0 ] && fail "Device cannot be correct, but it didn't fail."
+ echo "[OK]"
+}
+
+[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
+[ ! -x "$VERITYSETUP" ] && skip "Cannot find $VERITYSETUP, test skipped."
+
+[ -n "$VALG" ] && valgrind_setup && VERITYSETUP=valgrind_run
+modprobe dm-verity >/dev/null 2>&1
+dmsetup targets | grep verity >/dev/null 2>&1 || skip "Cannot find dm-verity target, test skipped."
+
+# VERITYSETUP tests
+
+SALT=e48da609055204e89ae53b655ca2216dd983cf3cb829f34f63a297d106d53e2d
+
+echo "Verity tests [separate devices]"
+prepare 8192 1024
+check_root_hash 512 9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174 $SALT 1 sha256
+check_root_hash 1024 54d92778750495d1f80832b486ebd007617d746271511bbf0e295e143da2b3df $SALT 1 sha256
+check_root_hash 4096 e522df0f97da4febb882ac40f30b37dc0b444bf6df418929463fa25280f09d5c $SALT 1 sha256
+# version 0
+check_root_hash 4096 cbbf4ebd004ef65e29b935bb635a39cf754d677f3fa10b0126da725bbdf10f7d $SALT 0 sha256
+# no salt
+check_root_hash 4096 ef29c902d87350f1da4bfa536e16cebc162a909bf89abe448b81ec500d4fb9bf - 1 sha256
+# sha1
+check_root_hash 1024 d0e9163ca8844aaa2e88fe5265a8c5d9ee494a99 $SALT 1 sha1
+check_root_hash 1024 73509e8e868be6b8ac939817a98a3d35121413b2 dadada 1 sha1
+
+echo "Verity tests [one device offset]"
+prepare $((8192 + 1024))
+check_root_hash 512 9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174 $SALT 1 sha256 8388608
+check_root_hash 1024 54d92778750495d1f80832b486ebd007617d746271511bbf0e295e143da2b3df $SALT 1 sha256 8388608
+check_root_hash 4096 e522df0f97da4febb882ac40f30b37dc0b444bf6df418929463fa25280f09d5c $SALT 1 sha256 8388608
+# version 0
+check_root_hash 4096 cbbf4ebd004ef65e29b935bb635a39cf754d677f3fa10b0126da725bbdf10f7d $SALT 0 sha256 8388608
+# no salt
+check_root_hash 4096 ef29c902d87350f1da4bfa536e16cebc162a909bf89abe448b81ec500d4fb9bf - 1 sha256 8388608
+# sha1
+check_root_hash 1024 d0e9163ca8844aaa2e88fe5265a8c5d9ee494a99 $SALT 1 sha1 8388608
+check_root_hash 1024 73509e8e868be6b8ac939817a98a3d35121413b2 dadada 1 sha1 8388608
+
+if check_version 1 3; then
+ echo "Verity data corruption options test."
+ SALT=e48da609055204e89ae53b655ca2216dd983cf3cb829f34f63a297d106d53e2d
+ HASH=9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174
+ prepare 8192 1024
+ check_option 512 $HASH $SALT 1 sha256 "--ignore-corruption" "ignore_corruption"
+ check_option 512 $HASH $SALT 1 sha256 "--restart-on-corruption" "restart_on_corruption"
+ check_option 512 $HASH $SALT 1 sha256 "--ignore-zero-blocks" "ignore_zero_blocks"
+ check_option 512 $HASH $SALT 1 sha256 "--ignore-corruption --ignore-zero-blocks" "ignore_corruption"
+ if check_version 1 4; then
+ check_option 512 $HASH $SALT 1 sha256 "--check-at-most-once" "check_at_most_once"
+ fi
+fi
+
+echo "Veritysetup [hash-offset bigger than 2G works] "
+checkOffsetBug 3000000000 2499997696 256
+checkOffsetBug 10000000000 8000000000 128
+
+echo "Veritysetup [overlap-detection] "
+checkOverlapBug 2097152 1433600
+checkOverlapBug 2097152 1433600 350 4096
+checkOverlapBug 2097152 1228800 350 4096 # data-hash overlap
+checkOverlapBug 2097152 0 350 4096 1228800 # data-fec overlap
+checkOverlapBug 10240000 256000 400 512 256512 # hash-fec overlap
+
+if check_version 1 3; then
+ echo "Veritysetup [FEC tests]"
+ for INDEX in {1..4}; do
+ # in the first iteration check if we can use FEC (it can be compiled-out)
+ (check_fec $IMG $IMG $IMG 4096 30 150 163840 409600 $(($RANDOM % 23 + 2)) $(($INDEX * 4)) )
+ RET=$?
+ [ "$RET" -eq "3" ] && break
+ [ "$RET" -eq "0" ] || fail "FEC repair failed"
+
+ (check_fec $IMG $IMG_HASH $FEC_DEV 4096 30 30 0 0 $(($RANDOM % 23 + 2)) $(($INDEX * 4)) 'n' $SALT) || fail "FEC repair failed"
+ (check_fec $IMG $IMG_HASH $FEC_DEV 4096 35 35 0 0 $(($RANDOM % 23 + 2)) $(($INDEX * 4))) || fail "FEC repair failed"
+ (check_fec $IMG $IMG_HASH $FEC_DEV 512 2000 2000 0 0 $(($RANDOM % 23 + 2)) $(($INDEX * 4))) || fail "FEC repair failed"
+ (check_fec $IMG $IMG_HASH $FEC_DEV 1024 2000 2000 0 0 $(($RANDOM % 23 + 2)) $(($INDEX * 4))) || fail "FEC repair failed"
+ # this test should fail
+ (check_fec $IMG $IMG_HASH $FEC_DEV 4096 30 30 0 0 $(($RANDOM % 23 + 2)) $(($RANDOM % 200 + 200))) && fail "FEC repair must fail"
+ echo "[OK]"
+ done
+fi
+
+echo "Correction in userspace: "
+# checkUserSpaceRepair <#blocks> <block_size> <roots> <hash_offset> <fec_offset> <#devices> <#corrupted bytes>
+checkUserSpaceRepair -1 512 2 0 0 3 100
+checkUserSpaceRepair 400 512 2 256000 0 2 50
+checkUserSpaceRepair 500 512 2 2457600 4915200 1 1
+checkUserSpaceRepair -1 4096 2 0 0 3 10
+checkUserSpaceRepair 400 4096 2 2048000 0 2 1
+#checkUserSpaceRepair 500 4096 2 2457600 4915200 1 2 # FIXME
+
+remove_mapping
+exit 0