diff options
Diffstat (limited to 'tests')
133 files changed, 24391 insertions, 0 deletions
diff --git a/tests/00modules-test b/tests/00modules-test new file mode 100755 index 0000000..64e054a --- /dev/null +++ b/tests/00modules-test @@ -0,0 +1,45 @@ +#!/bin/bash + +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." + +function pversion() { + if [ ! -x $CRYPTSETUP_PATH/$1 ] ; then + return + fi + + echo -n "$CRYPTSETUP_PATH/" + $CRYPTSETUP_PATH/$1 --version +} + +echo "Cryptsetup test environment ($(date))" +uname -a +if [ "$(cat /proc/sys/crypto/fips_enabled 2>/dev/null)" = "1" ] ; then + echo "Kernel running in FIPS mode." +fi + +if [ -f /etc/os-release ] ; then + source /etc/os-release + echo "$PRETTY_NAME ($NAME) $VERSION" +fi + +echo "Memory" +free -h + +pversion cryptsetup +pversion veritysetup +pversion integritysetup +pversion cryptsetup-reencrypt + +[ $(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..4688bff --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,130 @@ +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 \ + bitlk-compat-test + +if VERITYSETUP +TESTS += verity-compat-test +endif + +if REENCRYPT +TESTS += reencryption-compat-test reencryption-compat-test2 luks2-reencryption-test luks2-reencryption-mangle-test +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 \ + luks2-reencryption-test \ + luks2-reencryption-mangle-test \ + 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 \ + Makefile.localtest \ + bitlk-compat-test \ + bitlk-images.tar.xz + +CLEANFILES = cryptsetup-tst* valglog* *-fail-*.log +clean-local: + -rm -rf tcrypt-images luks1-images luks2-images bitlk-images conversion_imgs luks2_valid_hdr.img blkid-luks2-pv-img blkid-luks2-pv-img.bcp + +LDADD = $(LTLIBINTL) + +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 = $(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 = $(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 + @VALG=1 ./luks2-reencryption-test + @VALG=1 ./luks2-reencryption-mangle-test + @VALG=1 ./bitlk-compat-test + @grep -l "ERROR SUMMARY: [^0] errors" valglog* || echo "No leaks detected." + +.PHONY: valgrind-check diff --git a/tests/Makefile.localtest b/tests/Makefile.localtest new file mode 100644 index 0000000..29a62f3 --- /dev/null +++ b/tests/Makefile.localtest @@ -0,0 +1,30 @@ +# +# Makefile to run tests with system binaries +# USE: make -f Makefile.localtest tests CRYPTSETUP_PATH=/sbin +# +CPPFLAGS=-I../lib/ -I../lib/luks1 -DHAVE_DECL_DM_TASK_RETRY_REMOVE -DKERNEL_KEYRING -DHAVE_SYS_SYSMACROS_H -DNO_CRYPTSETUP_PATH +CFLAGS=-O2 -g -Wall +LDLIBS=-lcryptsetup -ldevmapper +TESTS=$(wildcard *-test *-test2) api-test api-test-2 + +differ: differ.o + $(CC) -o $@ $^ + +api-test: api-test.o test_utils.o + $(CC) -o $@ $^ $(LDLIBS) + +api-test-2: api-test-2.o test_utils.o + $(CC) -o $@ $^ $(LDLIBS) + +tests: differ $(TESTS) + @for test in $(sort $(TESTS)); do \ + echo [$$test]; \ + ./$$test; \ + [ $$? -ne 77 -a $$? -ne 0 ] && exit 1; \ + true; \ + done; + +clean: + rm -f *.o differ api-test api-test-2 + +.PHONY: clean diff --git a/tests/align-test b/tests/align-test new file mode 100755 index 0000000..ac3af88 --- /dev/null +++ b/tests/align-test @@ -0,0 +1,320 @@ +#!/bin/bash + +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/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 backtrace:" + while caller $frame; do ((frame++)); done + 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..75d9cf4 --- /dev/null +++ b/tests/align-test2 @@ -0,0 +1,367 @@ +#!/bin/bash + +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/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 backtrace:" + while caller $frame; do ((frame++)); done + 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 2>&1 || 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 drive with misaligned opt-io to page-size (some bad USB enclosures)" +echo "# (logical_block_size=512, physical_block_size=512, alignment_offset=0, opt-io=33553920)" +add_device dev_size_mb=32 sector_size=512 num_tgts=1 opt_blks=65535 +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..c0bfc9a --- /dev/null +++ b/tests/api-test-2.c @@ -0,0 +1,4531 @@ +/* + * cryptsetup library LUKS2 API check functions + * + * Copyright (C) 2009-2021 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2021 Milan Broz + * Copyright (C) 2016-2021 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 <stdbool.h> +#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_EMPTY_SMALL "empty_small.img" +#define IMAGE_EMPTY_SMALL_2 "empty_small2.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" + +/* Allow to run without config.h */ +#ifndef DEFAULT_LUKS1_HASH + #define DEFAULT_LUKS1_HASH "sha256" + #define DEFAULT_LUKS1_ITER_TIME 2000 + #define DEFAULT_LUKS2_ITER_TIME 2000 + #define DEFAULT_LUKS2_MEMORY_KB 1048576 + #define DEFAULT_LUKS2_PARALLEL_THREADS 4 + #define DEFAULT_LUKS2_PBKDF "argon2i" +#endif + +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; + +unsigned int test_progress_steps; + +struct crypt_device *cd = NULL, *cd2 = 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 = alignpayload_sec * sector_size; + else + *r_payload_offset = DIV_ROUND_UP_MODULO(default_header_size * 512, (alignpayload_sec ?: 1) * 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 + +#define DM_NOSTDERR " 2>/dev/null" + +static void _cleanup_dmdevices(void) +{ + struct stat st; + + if (!stat(DMDIR H_DEVICE, &st)) + _system("dmsetup remove " DM_RETRY H_DEVICE DM_NOSTDERR, 0); + + if (!stat(DMDIR H_DEVICE_WRONG, &st)) + _system("dmsetup remove " DM_RETRY H_DEVICE_WRONG DM_NOSTDERR, 0); + + if (!stat(DMDIR L_DEVICE_0S, &st)) + _system("dmsetup remove " DM_RETRY L_DEVICE_0S DM_NOSTDERR, 0); + + if (!stat(DMDIR L_DEVICE_1S, &st)) + _system("dmsetup remove " DM_RETRY L_DEVICE_1S DM_NOSTDERR, 0); + + if (!stat(DMDIR L_DEVICE_WRONG, &st)) + _system("dmsetup remove " DM_RETRY L_DEVICE_WRONG DM_NOSTDERR, 0); + + if (!stat(DMDIR L_DEVICE_OK, &st)) + _system("dmsetup remove " DM_RETRY L_DEVICE_OK DM_NOSTDERR, 0); + + t_dev_offset = 0; +} + +static void _cleanup(void) +{ + struct stat st; + + CRYPT_FREE(cd); + CRYPT_FREE(cd2); + + //_system("udevadm settle", 0); + + if (!stat(DMDIR CDEVICE_1, &st)) + _system("dmsetup remove " DM_RETRY CDEVICE_1 DM_NOSTDERR, 0); + + if (!stat(DMDIR CDEVICE_2, &st)) + _system("dmsetup remove " DM_RETRY CDEVICE_2 DM_NOSTDERR, 0); + + if (!stat(DEVICE_EMPTY, &st)) + _system("dmsetup remove " DM_RETRY DEVICE_EMPTY_name DM_NOSTDERR, 0); + + if (!stat(DEVICE_ERROR, &st)) + _system("dmsetup remove " DM_RETRY DEVICE_ERROR_name DM_NOSTDERR, 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(IMAGE_EMPTY_SMALL); + remove(IMAGE_EMPTY_SMALL_2); + + _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("dd if=/dev/zero of=" IMAGE_EMPTY_SMALL " bs=1M count=7 2>/dev/null", 1); + + _system("dd if=/dev/zero of=" IMAGE_EMPTY_SMALL_2 " bs=512 count=2050 2>/dev/null", 1); + + _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) +{ + 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"); + GE_(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)); + + 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)); + GE_(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) +{ + struct crypt_active_device cad; + char key[128]; + size_t key_size; + int suspend_status; + uint64_t r_payload_offset; + const struct crypt_pbkdf_type fast_pbkdf = { + .type = "pbkdf2", + .hash = "sha256", + .iterations = 1000, + .flags = CRYPT_PBKDF_NO_BENCHMARK + }; + + 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); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(CRYPT_ACTIVATE_SUSPENDED, cad.flags & CRYPT_ACTIVATE_SUSPENDED); +#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_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(0, cad.flags & CRYPT_ACTIVATE_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))); + + /* Resume by volume key */ + OK_(crypt_suspend(cd, CDEVICE_1)); + key_size = sizeof(key); + memset(key, 0, key_size); + FAIL_(crypt_resume_by_volume_key(cd, CDEVICE_1, key, key_size), "wrong key"); + OK_(crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key, &key_size, KEY1, strlen(KEY1))); + OK_(crypt_resume_by_volume_key(cd, CDEVICE_1, key, key_size)); + OK_(crypt_deactivate(cd, CDEVICE_1)); + CRYPT_FREE(cd); + + OK_(get_luks2_offsets(0, 0, 0, NULL, &r_payload_offset)); + OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 1)); + + /* Resume device with cipher_null */ + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_set_pbkdf_type(cd, &fast_pbkdf)); + OK_(crypt_format(cd, CRYPT_LUKS2, "cipher_null", "ecb", NULL, key, key_size, NULL)); + EQ_(0, crypt_keyslot_add_by_volume_key(cd, 0, key, key_size, PASSPHRASE, strlen(PASSPHRASE))); + OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0)); + OK_(crypt_suspend(cd, CDEVICE_1)); + OK_(crypt_resume_by_volume_key(cd, CDEVICE_1, key, key_size)); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(0, cad.flags & CRYPT_ACTIVATE_SUSPENDED); + OK_(crypt_suspend(cd, CDEVICE_1)); + OK_(crypt_resume_by_passphrase(cd, CDEVICE_1, 0, PASSPHRASE, strlen(PASSPHRASE))); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(0, cad.flags & CRYPT_ACTIVATE_SUSPENDED); + OK_(crypt_deactivate(cd, CDEVICE_1)); + CRYPT_FREE(cd); + + _remove_keyfiles(); + _cleanup_dmdevices(); +} + +static void AddDeviceLuks2(void) +{ + enum { OFFSET_1M = 2048 , OFFSET_2M = 4096, OFFSET_4M = 8192, OFFSET_8M = 16384 }; + 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; + + /* 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(0, 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_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms), "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, ¶ms)); + 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, ¶ms)); + 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, ¶ms)); + 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, ¶ms), "Alignment not compatible"); + OK_(crypt_set_data_offset(cd, OFFSET_4M)); + OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms)); + 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(0, 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, ¶ms), "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, ¶ms)); + 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, ¶ms)); + EQ_(crypt_get_data_offset(cd), r_payload_offset); + OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0)); + GE_(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, ¶ms), "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, ¶ms)); + 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)); + if (!_fips_mode) { + NULL_(pbkdf_tmp.hash); + OK_(!(pbkdf_tmp.max_memory_kb >= 32)); + OK_(!(pbkdf_tmp.parallel_threads >= 1)); + } else + OK_(strcmp(pbkdf_tmp.hash, pbkdf.hash)); + OK_(!(pbkdf_tmp.iterations >= 4)); + EQ_(0, pbkdf_tmp.time_ms); /* not usable in per-keyslot call */ + + 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, ¶ms), "Context is already formatted"); + GE_(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)); + GE_(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, ¶ms)); + 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, ¶ms)); + CRYPT_FREE(cd); + // there we've got uuid mismatch + OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, DMDIR H_DEVICE)); + GE_(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, ¶ms)); + + // 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)); + GE_(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)); + GE_(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))); + + 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); + + /* + * Check regression in getting keyslot encryption parameters when + * volume key size is unknown (no active keyslots). + */ + 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, 0, NULL, key_size, PASSPHRASE, strlen(PASSPHRASE)), 0); + /* drop context copy of volume key */ + CRYPT_FREE(cd); + OK_(crypt_init(&cd, DMDIR L_DEVICE_1S)); + OK_(crypt_load(cd, CRYPT_LUKS, NULL)); + EQ_(crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key, &key_size, PASSPHRASE, strlen(PASSPHRASE)), 0); + OK_(crypt_keyslot_destroy(cd, 0)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 0, key, key_size, PASSPHRASE, strlen(PASSPHRASE)), 0); + CRYPT_FREE(cd); + + _cleanup_dmdevices(); +} + +static void Luks2MetadataSize(void) +{ + struct crypt_pbkdf_type pbkdf = { + .type = CRYPT_KDF_ARGON2I, + .hash = "sha256", + .parallel_threads = 1, + .max_memory_kb = 128, + .iterations = 4, + .flags = CRYPT_PBKDF_NO_BENCHMARK + }; + struct crypt_params_luks2 params = { + .pbkdf = &pbkdf, + .data_device = DEVICE_2, + .sector_size = 512 + }; + char key[128], tmp[128]; + + const char *passphrase = "blabla"; + 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_header_size, default_mdata_size, default_keyslots_size, mdata_size, + keyslots_size, r_header_wrong_size = 14336; + + /* 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; + } + + crypt_decode_key(key, mk_hex, key_size); + + // init test devices + OK_(get_luks2_offsets(0, 0, 0, &r_header_size, NULL)); + OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size)); + OK_(create_dmdevice_over_loop(H_DEVICE_WRONG, r_header_wrong_size)); /* 7 MiBs only */ + //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, ¶ms)); + 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, ¶ms)); + OK_(crypt_get_metadata_size(cd, &default_mdata_size, &default_keyslots_size)); + EQ_(default_mdata_size, 0x04000); + EQ_(default_keyslots_size, (r_header_size * 512) - 2 * 0x04000); + CRYPT_FREE(cd); + // check keyslots size calculation is correct + OK_(crypt_init(&cd, DMDIR H_DEVICE)); + OK_(crypt_set_metadata_size(cd, 0x80000, 0)); + OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms)); + OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size)); + EQ_(mdata_size, 0x80000); + EQ_(keyslots_size, (r_header_size * 512) - 2 * 0x80000); + CRYPT_FREE(cd); + + // various metadata size checks combined with data offset + OK_(crypt_init(&cd, DMDIR H_DEVICE)); + OK_(crypt_set_metadata_size(cd, 0, default_keyslots_size + 4096)); + FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms), "Device is too small."); + OK_(crypt_set_metadata_size(cd, 0x20000, (r_header_size * 512) - 2 * 0x20000 + 4096)); + FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms), "Device is too small."); + CRYPT_FREE(cd); + + OK_(crypt_init(&cd, DMDIR H_DEVICE)); + OK_(crypt_set_metadata_size(cd, 0x80000, 0)); + OK_(crypt_set_data_offset(cd, 0x80000 / 512 - 8)); + FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms), "Data offset is too small."); + CRYPT_FREE(cd); + + // H_DEVICE_WRONG size is 7MiB + OK_(crypt_init(&cd, DMDIR H_DEVICE_WRONG)); + OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms)); + OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size)); + EQ_(mdata_size, default_mdata_size); + EQ_(keyslots_size, (r_header_wrong_size * 512) - 2 * default_mdata_size); + CRYPT_FREE(cd); + + OK_(crypt_init(&cd, DMDIR H_DEVICE_WRONG)); + OK_(crypt_set_metadata_size(cd, 0x400000, 0)); + FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms), "Device is too small."); + CRYPT_FREE(cd); + + // IMAGE_EMPTY_SMALL size is 7MiB but now it's regulare file + OK_(crypt_init(&cd, IMAGE_EMPTY_SMALL)); + OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms)); + OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size)); + EQ_(mdata_size, default_mdata_size); + EQ_(keyslots_size, default_keyslots_size); + EQ_(crypt_get_data_offset(cd), 0); + CRYPT_FREE(cd); + + sprintf(tmp, "truncate -s %" PRIu64 " " IMAGE_EMPTY_SMALL, r_header_wrong_size * 512); + _system(tmp, 1); + + // check explicit keyslots size and data offset are respected even with regular file mdevice + OK_(crypt_init(&cd, IMAGE_EMPTY_SMALL)); + OK_(crypt_set_metadata_size(cd, 0, default_keyslots_size)); + OK_(crypt_set_data_offset(cd, r_header_size + 8)); + OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms)); + OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size)); + EQ_(mdata_size, default_mdata_size); + EQ_(keyslots_size, default_keyslots_size); + EQ_(crypt_get_data_offset(cd), r_header_size + 8); + CRYPT_FREE(cd); + + _cleanup_dmdevices(); +} + +static void UseTempVolumes(void) +{ + 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)); + GE_(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 manipulate 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_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(0, 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"); + GE_(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, ¶ms)); + // 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, ¶ms)); + 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, ¶ms)); + // 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_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(0, 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, ¶ms)); + 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)); + GE_(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, ¶ms)); + 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, ¶ms)); + 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_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(1, 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, ¶ms)); + 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)); + GE_(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); + GE_(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); + GE_(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); + NOTFAIL_(fd, "Bad loop device."); + close(fd); + 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); + GE_(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); + GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); + OK_(crypt_deactivate(cd, CDEVICE_1)); + CRYPT_FREE(cd); + + _cleanup_dmdevices(); +} + +static void ResizeDeviceLuks2(void) +{ + 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(0, params.data_alignment, 0, NULL, &r_payload_offset)); + OK_(get_luks2_offsets(0, 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, ¶ms)); + 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); + GE_(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, ¶ms)); + 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); + GE_(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, ¶ms)); + 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_active_device cad; + + 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; + + if (!t_dm_crypt_keyring_support()) { + printf("WARNING: Kernel keyring not supported, skipping test.\n"); + return; + } + + kid = add_key("user", KEY_DESC_TEST0, PASSPHRASE, strlen(PASSPHRASE), KEY_SPEC_THREAD_KEYRING); + NOTFAIL_(kid, "Test or kernel keyring are broken."); + + OK_(get_luks2_offsets(0, 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, ¶ms), 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); + + NOTFAIL_(keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING), "Test or kernel keyring are broken."); + + kid = add_key("user", KEY_DESC_TEST0, PASSPHRASE, strlen(PASSPHRASE), KEY_SPEC_PROCESS_KEYRING); + NOTFAIL_(kid, "Test or kernel keyring are broken."); + + // 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, ¶ms), 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); + + NOTFAIL_(keyctl_unlink(kid, KEY_SPEC_PROCESS_KEYRING), "Test or kernel keyring are broken."); + + // 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); + NOTFAIL_(kid, "Test or kernel keyring are broken."); + + kid1 = add_key("user", KEY_DESC_TEST1, PASSPHRASE1, strlen(PASSPHRASE1), KEY_SPEC_THREAD_KEYRING); + NOTFAIL_(kid1, "Test or kernel keyring are broken."); + + OK_(crypt_init(&cd, DMDIR L_DEVICE_1S)); + OK_(crypt_load(cd, CRYPT_LUKS2, NULL)); + EQ_(crypt_token_luks2_keyring_set(cd, 0, ¶ms), 0); + EQ_(crypt_token_assign_keyslot(cd, 0, 0), 0); + EQ_(crypt_token_luks2_keyring_set(cd, 1, ¶ms2), 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); + if (t_dm_crypt_keyring_support()) { + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(cad.flags & CRYPT_ACTIVATE_KEYRING_KEY, CRYPT_ACTIVATE_KEYRING_KEY); + } + 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); + + NOTFAIL_(keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING), "Test or kernel keyring are broken."); + + // 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); + if (t_dm_crypt_keyring_support()) { + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(cad.flags & CRYPT_ACTIVATE_KEYRING_KEY, CRYPT_ACTIVATE_KEYRING_KEY); + } + OK_(crypt_deactivate(cd, CDEVICE_1)); + CRYPT_FREE(cd); + + kid = add_key("user", KEY_DESC_TEST0, PASSPHRASE, strlen(PASSPHRASE), KEY_SPEC_THREAD_KEYRING); + NOTFAIL_(kid, "Test or kernel keyring are broken."); + + // 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, ¶ms), 0); + EQ_(crypt_token_assign_keyslot(cd, 0, 0), 0); + EQ_(crypt_token_luks2_keyring_set(cd, 2, ¶ms2), 2); + EQ_(crypt_token_assign_keyslot(cd, 2, 1), 2); + CRYPT_FREE(cd); + + NOTFAIL_(keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING), "Test or kernel keyring are broken."); + + kid1 = add_key("user", KEY_DESC_TEST1, PASSPHRASE1, strlen(PASSPHRASE1), KEY_SPEC_THREAD_KEYRING); + NOTFAIL_(kid1, "Test or kernel keyring are broken."); + + 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\"}" + + + int ks; + 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(0, 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, ¶ms), 5); + EQ_(crypt_token_status(cd, 5, &dummy), CRYPT_TOKEN_INTERNAL); + OK_(strcmp(dummy, "luks2-keyring")); + + FAIL_(crypt_token_luks2_keyring_get(cd, 2, ¶ms), "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, ¶ms), 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); + + // test crypt_keyslot_change_by_passphrase does not erase token references + EQ_(crypt_keyslot_change_by_passphrase(cd, 1, 5, PASSPHRASE1, strlen(PASSPHRASE1), PASSPHRASE1, strlen(PASSPHRASE1)), 5); + OK_(crypt_token_is_assigned(cd, 10, 5)); + ks = crypt_keyslot_change_by_passphrase(cd, 5, CRYPT_ANY_SLOT, PASSPHRASE1, strlen(PASSPHRASE1), PASSPHRASE1, strlen(PASSPHRASE1)); + NOTFAIL_(ks, "Failed to change keyslot passphrase."); + OK_(crypt_token_is_assigned(cd, 10, ks)); + + CRYPT_FREE(cd); + + _cleanup_dmdevices(); +} + +static void LuksConvert(void) +{ + 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_luks1 luks1 = { + .hash = "sha256", + .data_device = DMDIR L_DEVICE_1S + }; + + 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(0, 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); + + // detached LUKS1 header upconversion + OK_(create_dmdevice_over_loop(H_DEVICE, 2050)); // default LUKS1 header should fit there + OK_(crypt_init(&cd, DMDIR H_DEVICE)); + crypt_set_iteration_time(cd, 1); + //OK_(crypt_set_pbkdf_type(cd, &pbkdf2)); + OK_(crypt_format(cd, CRYPT_LUKS1, "aes", "xts-plain64", NULL, NULL, 32, &luks1)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 7, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 7); + FAIL_(crypt_convert(cd, CRYPT_LUKS2, NULL), "Unable to move keyslots. Not enough space."); + CRYPT_FREE(cd); + + // 2050 sectors, empty file + OK_(crypt_init(&cd, IMAGE_EMPTY_SMALL_2)); + //OK_(crypt_set_pbkdf_type(cd, &pbkdf2)); + crypt_set_iteration_time(cd, 1); + OK_(crypt_format(cd, CRYPT_LUKS1, "aes", "xts-plain64", NULL, NULL, 32, &luks1)); + EQ_(crypt_get_data_offset(cd), 0); + EQ_(crypt_keyslot_add_by_volume_key(cd, 7, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 7); + OK_(crypt_convert(cd, CRYPT_LUKS2, NULL)); + CRYPT_FREE(cd); + + _cleanup_dmdevices(); +} + +static void Pbkdf(void) +{ + 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 = "sha512", // 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(0, 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, ¶ms)); + 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]; + 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; + struct crypt_pbkdf_type pbkdf = { + .type = "argon2i", + .hash = "sha256", + .iterations = 4, + .max_memory_kb = 32, + .parallel_threads = 1, + .flags = CRYPT_PBKDF_NO_BENCHMARK, + }; + struct crypt_params_luks2 params2 = { + .pbkdf = &pbkdf, + .sector_size = SECTOR_SIZE + }; + + crypt_decode_key(key, mk_hex, key_size); + crypt_decode_key(key2, mk_hex2, key_size); + + /* 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; + } + + OK_(get_luks2_offsets(0, 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)); + OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms2)); + 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"); + + 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); + + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms2)); + /* keyslot 0, volume key, digest 0 */ + EQ_(crypt_keyslot_add_by_key(cd, 0, key, key_size, PASSPHRASE, strlen(PASSPHRASE), 0), 0); + /* keyslot 1, unbound key, digest 1 */ + EQ_(crypt_keyslot_add_by_key(cd, 1, key2, key_size, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT), 1); + /* keyslot 2, unbound key, digest 1 */ + EQ_(crypt_keyslot_add_by_key(cd, 2, key2, key_size, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT | CRYPT_VOLUME_KEY_DIGEST_REUSE), 2); + /* keyslot 3, unbound key, digest 2 */ + EQ_(crypt_keyslot_add_by_key(cd, 3, key2, key_size - 1, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT | CRYPT_VOLUME_KEY_DIGEST_REUSE), 3); + /* keyslot 4, unbound key, digest 1 */ + EQ_(crypt_keyslot_add_by_key(cd, CRYPT_ANY_SLOT, key2, key_size, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT | CRYPT_VOLUME_KEY_DIGEST_REUSE), 4); + FAIL_(crypt_keyslot_add_by_key(cd, CRYPT_ANY_SLOT, key, key_size, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT | CRYPT_VOLUME_KEY_SET), "Illegal"); + FAIL_(crypt_keyslot_add_by_key(cd, CRYPT_ANY_SLOT, key, key_size, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT | CRYPT_VOLUME_KEY_SET | CRYPT_VOLUME_KEY_DIGEST_REUSE), "Illegal"); + /* Such key doesn't exist, nothing to reuse */ + FAIL_(crypt_keyslot_add_by_key(cd, CRYPT_ANY_SLOT, key2, key_size - 2, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_DIGEST_REUSE), "Key digest doesn't match any existing."); + /* Keyslot 5, volume key, digest 0 */ + EQ_(crypt_keyslot_add_by_key(cd, 5, key, key_size, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_DIGEST_REUSE), 5); + + OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0)); + EQ_(crypt_keyslot_add_by_key(cd, 1, NULL, key_size, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_SET), 1); + OK_(crypt_activate_by_volume_key(cd, NULL, key2, key_size, 0)); + FAIL_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0), "Not a volume key"); + EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 1, PASSPHRASE1, strlen(PASSPHRASE1), 0), 1); + OK_(crypt_deactivate(cd, CDEVICE_1)); + EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 2, PASSPHRASE1, strlen(PASSPHRASE1), 0), 2); + OK_(crypt_deactivate(cd, CDEVICE_1)); + FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, PASSPHRASE, strlen(PASSPHRASE), 0), "No volume key keyslot"); + + /* TODO: key is unusable with aes-xts */ + // FAIL_(crypt_keyslot_add_by_key(cd, 3, NULL, 0, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_SET), "Unusable key with segment cipher"); + + EQ_(crypt_keyslot_add_by_key(cd, 5, NULL, 0, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_SET), 5); + FAIL_(crypt_activate_by_volume_key(cd, NULL, key2, key_size, 0), "Not a volume key"); + EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 5, PASSPHRASE1, strlen(PASSPHRASE1), 0), 5); + OK_(crypt_deactivate(cd, CDEVICE_1)); + + CRYPT_FREE(cd); + + _cleanup_dmdevices(); +} + +static void Luks2KeyslotParams(void) +{ + char key[128], key2[128]; + 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; + const struct crypt_pbkdf_type fast_pbkdf = { + .type = "pbkdf2", + .hash = "sha256", + .iterations = 1000, + .flags = CRYPT_PBKDF_NO_BENCHMARK + }; + + 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(0, 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)); + OK_(crypt_set_pbkdf_type(cd, &fast_pbkdf)); + 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); + + OK_(crypt_set_pbkdf_type(cd, &fast_pbkdf)); + 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)); + OK_(crypt_set_pbkdf_type(cd, &fast_pbkdf)); + 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); + + /* LUKS2 cipher null checks */ + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_set_pbkdf_type(cd, &fast_pbkdf)); + OK_(crypt_format(cd, CRYPT_LUKS2, "cipher_null", "ecb", NULL, key, key_size, NULL)); + FAIL_(crypt_keyslot_set_encryption(cd, "null", 32), "cipher null is not allowed"); + FAIL_(crypt_keyslot_set_encryption(cd, "cipher_null", 32), "cipher null is not allowed"); + FAIL_(crypt_keyslot_set_encryption(cd, "cipher_null-ecb", 32), "cipher null is not allowed"); + EQ_(0, crypt_keyslot_add_by_volume_key(cd, 0, key, key_size, PASSPHRASE, strlen(PASSPHRASE))); + NOTNULL_(crypt_keyslot_get_encryption(cd, 0, &key_size_ret)); + NULL_(strstr(crypt_keyslot_get_encryption(cd, 0, &key_size_ret), "null")); + CRYPT_FREE(cd); + + _cleanup_dmdevices(); + _remove_keyfiles(); +} + +static void Luks2ActivateByKeyring(void) +{ +#ifdef KERNEL_KEYRING + + key_serial_t kid, kid1; + uint64_t r_payload_offset; + + const char *cipher = "aes"; + const char *cipher_mode = "xts-plain64"; + + if (!t_dm_crypt_keyring_support()) { + printf("WARNING: Kernel keyring not supported, skipping test.\n"); + return; + } + + kid = add_key("user", KEY_DESC_TEST0, PASSPHRASE, strlen(PASSPHRASE), KEY_SPEC_THREAD_KEYRING); + NOTFAIL_(kid, "Test or kernel keyring are broken."); + kid1 = add_key("user", KEY_DESC_TEST1, PASSPHRASE1, strlen(PASSPHRASE1), KEY_SPEC_THREAD_KEYRING); + NOTFAIL_(kid1, "Test or kernel keyring are broken."); + + OK_(get_luks2_offsets(0, 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); + GE_(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); + GE_(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); + + NOTFAIL_(keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING), "Test or kernel keyring are broken."); + NOTFAIL_(keyctl_unlink(kid1, KEY_SPEC_THREAD_KEYRING), "Test or kernel keyring are broken."); + + 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; + 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) */ + 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 + if (t_dm_crypt_keyring_support()) { + kid = add_key("user", KEY_DESC_TEST0, "aaa", 3, KEY_SPEC_THREAD_KEYRING); + NOTFAIL_(kid, "Test or kernel keyring are broken."); + + /* 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, t_dm_crypt_keyring_support() ? -ETXTBSY : -EINVAL); + OK_(crypt_activate_by_keyring(cd, NULL, KEY_DESC_TEST0, 0, 0)); + OK_(crypt_activate_by_keyring(cd, NULL, KEY_DESC_TEST0, 0, CRYPT_ACTIVATE_KEYRING_KEY)); + } +#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, ¶ms)), "Unmet requirements detected"); + EQ_(r, -ETXTBSY); + + /* crypt_token_luks2_keyring_get (unrestricted) */ + EQ_(crypt_token_luks2_keyring_get(cd, 1, ¶ms_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 + if (t_dm_crypt_keyring_support()) { + 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, CRYPT_ACTIVATE_KEYRING_KEY)); + } +#endif + OK_(get_luks2_offsets(0, 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); + GE_(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)); + + CRYPT_FREE(cd); + _cleanup_dmdevices(); +} + +static void Luks2Integrity(void) +{ + 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, ¶ms); + 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); + GE_(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, ¶ms), "Wrong key size."); + FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, "xts-plainx", NULL, NULL, key_size, ¶ms), "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; + 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(0, 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(&cd, DMDIR L_DEVICE_OK)); + OK_(set_fast_pbkdf(cd)); + OK_(crypt_format(cd, CRYPT_LUKS2, cipher, mode, NULL, key, 32, NULL)); + OK_(crypt_keyslot_add_by_volume_key(cd, CRYPT_ANY_SLOT, key, 32, "aaa", 3)); + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, "aaa", 3, 0)); + + /* check we can refresh significant flags */ + if (t_dm_crypt_discard_support()) { + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH | CRYPT_ACTIVATE_ALLOW_DISCARDS)); + OK_(crypt_get_active_device(cd, 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(cd, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH | CRYPT_ACTIVATE_SAME_CPU_CRYPT)); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + OK_(check_flag(cad.flags, CRYPT_ACTIVATE_SAME_CPU_CRYPT)); + cad.flags = 0; + + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH | CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS)); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + OK_(check_flag(cad.flags, CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS)); + cad.flags = 0; + + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH | CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS)); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + OK_(check_flag(cad.flags, CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS)); + cad.flags = 0; + } + + OK_(crypt_volume_key_keyring(cd, 0)); + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH)); + OK_(crypt_get_active_device(cd, 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(cd, 1)); + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH)); + OK_(crypt_get_active_device(cd, 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(cd, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH | CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS | CRYPT_ACTIVATE_ALLOW_DISCARDS)); + OK_(crypt_get_active_device(cd, 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(cd, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH | CRYPT_ACTIVATE_READONLY)); + OK_(crypt_get_active_device(cd, 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(cd, CDEVICE_1)); + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH)); + + /* check read-only flag is not lost after reload */ + OK_(crypt_deactivate(cd, CDEVICE_1)); + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_READONLY)); + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, "aaa", 3, CRYPT_ACTIVATE_REFRESH)); + OK_(crypt_get_active_device(cd, 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(cd), key, 32, ¶ms)) { + 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(cd), 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(cd), 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(cd, CDEVICE_1)); + + CRYPT_FREE(cd); + CRYPT_FREE(cd2); + + _cleanup_dmdevices(); +} + +static void Luks2Flags(void) +{ + 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); +} + +#if KERNEL_KEYRING && USE_LUKS2_REENCRYPTION +static int test_progress(uint64_t size, uint64_t offset, void *usrptr) +{ + while (--test_progress_steps) + return 0; + return 1; +} + +static void Luks2Reencryption(void) +{ +/* reencryption currently depends on kernel keyring support */ + /* NOTES: + * - reencryption requires luks2 parameters. can we avoid it? + */ + uint32_t getflags; + uint64_t r_header_size, r_size_1; + struct crypt_active_device cad; + struct crypt_pbkdf_type pbkdf = { + .type = CRYPT_KDF_ARGON2I, + .hash = "sha256", + .parallel_threads = 1, + .max_memory_kb = 128, + .iterations = 4, + .flags = CRYPT_PBKDF_NO_BENCHMARK + }; + struct crypt_params_luks2 params2 = { + .pbkdf = &pbkdf, + .sector_size = 4096 + }; + struct crypt_params_reencrypt retparams = {}, rparams = { + .direction = CRYPT_REENCRYPT_FORWARD, + .resilience = "checksum", + .hash = "sha1", + .luks2 = ¶ms2, + }; + + const char *mk_hex = "bb21babe733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a"; + size_t key_size = strlen(mk_hex) / 2; + char key[128]; + + crypt_decode_key(key, mk_hex, key_size); + + /* reencryption currently depends on kernel keyring support in dm-crypt */ + if (!t_dm_crypt_keyring_support()) + return; + + /* 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; + } + + 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_header_size + 16)); + + /* create device */ + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 32, ¶ms2)); + OK_(crypt_set_pbkdf_type(cd, &pbkdf)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 21, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 21); + + /* add several unbound keys */ + EQ_(crypt_keyslot_add_by_key(cd, 9, NULL, 64, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 9); + EQ_(crypt_keyslot_add_by_key(cd, 10, NULL, 32, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 10); + EQ_(crypt_keyslot_add_by_key(cd, 11, NULL, 42, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 11); + EQ_(crypt_keyslot_status(cd, 21), CRYPT_SLOT_ACTIVE_LAST); + + /* test cipher parameters validation */ + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 11, "aes", "xts-plain64", &rparams), "Cipher not compatible with new volume key size."); + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 10, "tHeHamstErciphErr", "xts-plain64", &rparams), "Wrong cipher."); + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 10, "aes", "HamSterMoOode-plain64", &rparams), "Wrong mode."); + + /* test reencryption flags */ + rparams.flags = CRYPT_REENCRYPT_RESUME_ONLY; + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 9, "aes", "xts-plain64", &rparams), "Reencryption not initialized."); + rparams.flags |= CRYPT_REENCRYPT_INITIALIZE_ONLY; + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 9, "aes", "xts-plain64", &rparams), "Invalid flags combination."); + + OK_(crypt_persistent_flags_get(cd, CRYPT_FLAGS_REQUIREMENTS, &getflags)); + EQ_(getflags & CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 0); + FAIL_(crypt_reencrypt(cd, NULL), "Reencryption context not initialized."); + + rparams.flags &= ~CRYPT_REENCRYPT_RESUME_ONLY; + OK_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 9, "aes", "xts-plain64", &rparams)); + OK_(crypt_persistent_flags_get(cd, CRYPT_FLAGS_REQUIREMENTS, &getflags)); + EQ_(getflags & CRYPT_REQUIREMENT_ONLINE_REENCRYPT, CRYPT_REQUIREMENT_ONLINE_REENCRYPT); + + /* check reencrypt status is correct */ + EQ_(crypt_reencrypt_status(cd, &retparams), CRYPT_REENCRYPT_CLEAN); + EQ_(retparams.mode, CRYPT_REENCRYPT_REENCRYPT); + EQ_(retparams.direction, CRYPT_REENCRYPT_FORWARD); + EQ_(retparams.data_shift, 0); + EQ_(retparams.device_size, 0); + + /* check reencryption flag in metadata */ + OK_(crypt_persistent_flags_get(cd, CRYPT_FLAGS_REQUIREMENTS, &getflags)); + EQ_(getflags & CRYPT_REQUIREMENT_ONLINE_REENCRYPT, CRYPT_REQUIREMENT_ONLINE_REENCRYPT); + + /* some parameters are expected to change immediately after reencryption initialization */ + EQ_(crypt_get_volume_key_size(cd), 64); + OK_(strcmp(crypt_get_cipher_mode(cd), "xts-plain64")); + EQ_(crypt_get_sector_size(cd), 4096); + /* reencrypt keyslot must be unbound */ + EQ_(crypt_keyslot_status(cd, 0), CRYPT_SLOT_UNBOUND); + /* keyslot assigned to new segment is switched to last active */ + EQ_(crypt_keyslot_status(cd, 9), CRYPT_SLOT_ACTIVE_LAST); + /* keyslot assigned to old segment remains active */ + EQ_(crypt_keyslot_status(cd, 21), CRYPT_SLOT_ACTIVE); + + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 10, "aes", "xts-plain", &rparams), "Reencryption already initialized."); + + rparams.flags = 0; + OK_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 9, "aes", "xts-plain64", &rparams)); + OK_(crypt_reencrypt(cd, NULL)); + + /* check keyslots are reassigned to segment after reencryption */ + EQ_(crypt_keyslot_status(cd, 0), CRYPT_SLOT_INACTIVE); + EQ_(crypt_keyslot_status(cd, 9), CRYPT_SLOT_ACTIVE_LAST); + EQ_(crypt_keyslot_status(cd, 10), CRYPT_SLOT_UNBOUND); + EQ_(crypt_keyslot_status(cd, 11), CRYPT_SLOT_UNBOUND); + EQ_(crypt_keyslot_status(cd, 21), CRYPT_SLOT_INACTIVE); + + EQ_(crypt_keyslot_add_by_key(cd, 21, NULL, 32, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 21); + rparams.flags = CRYPT_REENCRYPT_INITIALIZE_ONLY; + params2.sector_size = 512; + OK_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 9, 21, "aes", "xts-plain64", &rparams)); + + /* fixed device size parameter impact */ + rparams.flags = CRYPT_REENCRYPT_RESUME_ONLY; + rparams.device_size = 24; + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 9, 21, "aes", "xts-plain64", &rparams), "Invalid device size."); + OK_(crypt_persistent_flags_get(cd, CRYPT_FLAGS_REQUIREMENTS, &getflags)); + EQ_(getflags & CRYPT_REQUIREMENT_ONLINE_REENCRYPT, CRYPT_REQUIREMENT_ONLINE_REENCRYPT); + rparams.device_size = 15; + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 9, 21, "aes", "xts-plain64", &rparams), "Invalid device size alignment."); + OK_(crypt_persistent_flags_get(cd, CRYPT_FLAGS_REQUIREMENTS, &getflags)); + EQ_(getflags & CRYPT_REQUIREMENT_ONLINE_REENCRYPT, CRYPT_REQUIREMENT_ONLINE_REENCRYPT); + FAIL_(crypt_reencrypt(cd, NULL), "Reencryption context not initialized."); + rparams.device_size = 16; + OK_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 9, 21, "aes", "xts-plain64", &rparams)); + OK_(crypt_reencrypt(cd, NULL)); + OK_(crypt_persistent_flags_get(cd, CRYPT_FLAGS_REQUIREMENTS, &getflags)); + EQ_(getflags & CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 0); + + /* limited hotzone size parameter impact */ + EQ_(crypt_keyslot_add_by_key(cd, 9, NULL, 64, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 9); + rparams.flags = CRYPT_REENCRYPT_INITIALIZE_ONLY; + rparams.device_size = 0; + params2.sector_size = 4096; + OK_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 9, "aes", "xts-plain64", &rparams)); + + /* max hotzone size parameter impact */ + rparams.flags = CRYPT_REENCRYPT_RESUME_ONLY; + rparams.max_hotzone_size = 1; + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 9, "aes", "xts-plain64", &rparams), "Invalid hotzone size alignment."); + rparams.max_hotzone_size = 24; /* should be ok. Device size is 16 sectors and the parameter defines upper limit, not lower */ + OK_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 9, "aes", "xts-plain64", &rparams)); + rparams.max_hotzone_size = 8; + OK_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 9, "aes", "xts-plain64", &rparams)); + CRYPT_FREE(cd); + + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd, CRYPT_LUKS2, NULL)); + OK_(crypt_set_pbkdf_type(cd, &pbkdf)); + + rparams.max_hotzone_size = 0; + rparams.resilience = "haMster"; + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 9, "aes", "xts-plain64", &rparams), "Invalid resilience mode."); + rparams.resilience = "checksum"; + rparams.hash = "hamSter"; + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 9, "aes", "xts-plain64", &rparams), "Invalid resilience hash."); + + rparams.hash = "sha1"; + OK_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 9, "aes", "xts-plain64", &rparams)); + OK_(crypt_reencrypt(cd, NULL)); + + /* FIXME: this is a bug, but not critical (data shift parameter is ignored after initialization) */ + //rparams.data_shift = 8; + //FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 9, "aes", "xts-plain64", &rparams), "Invalid reencryption parameters."); + + EQ_(crypt_keyslot_add_by_key(cd, 21, NULL, 32, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 21); + rparams.flags = 0; + rparams.resilience = "none"; + rparams.max_hotzone_size = 2048; + /* online reencryption on inactive device */ + FAIL_(crypt_reencrypt_init_by_passphrase(cd, CDEVICE_1, PASSPHRASE, strlen(PASSPHRASE), 9, 21, "aes", "xts-plain64", &rparams), "Device is not active."); + /* FIXME: this is minor bug. In fact we need only key from keyslot 9 */ + //EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 9, PASSPHRASE, strlen(PASSPHRASE), 0), 9); + NOTFAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0), "Failed to activate device."); + /* offline reencryption on active device */ + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 9, 21, "aes", "xts-plain64", &rparams), "Device mounted or active."); + OK_(crypt_deactivate(cd, CDEVICE_1)); + /* Wrong context checks */ + OK_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 9, 21, "aes", "xts-plain64", &rparams)); + /* cd is ready for reencryption */ + OK_(crypt_init(&cd2, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd2, CRYPT_LUKS2, NULL)); + rparams.flags = CRYPT_REENCRYPT_RESUME_ONLY; + FAIL_(crypt_reencrypt_init_by_passphrase(cd2, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 9, "aes", "xts-plain64", &rparams), "Reencryption already running."); + rparams.flags = 0; + FAIL_(crypt_reencrypt_init_by_passphrase(cd2, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 9, "aes", "xts-plain64", &rparams), "Reencryption already running."); + FAIL_(crypt_reencrypt(cd2, NULL), "Invalid reencryption context."); + OK_(crypt_persistent_flags_get(cd, CRYPT_FLAGS_REQUIREMENTS, &getflags)); + EQ_(getflags & CRYPT_REQUIREMENT_ONLINE_REENCRYPT, CRYPT_REQUIREMENT_ONLINE_REENCRYPT); + OK_(crypt_persistent_flags_get(cd2, CRYPT_FLAGS_REQUIREMENTS, &getflags)); + EQ_(getflags & CRYPT_REQUIREMENT_ONLINE_REENCRYPT, CRYPT_REQUIREMENT_ONLINE_REENCRYPT); + EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_CLEAN); + EQ_(crypt_reencrypt_status(cd2, NULL), CRYPT_REENCRYPT_CLEAN); + FAIL_(crypt_activate_by_passphrase(cd2, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0), "Reencryption already in progress."); + FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0), "Reencryption already in progress."); + OK_(crypt_reencrypt(cd, NULL)); + CRYPT_FREE(cd); + CRYPT_FREE(cd2); + + /* Partial device reencryption parameter */ + params2.sector_size = 512; + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 32, ¶ms2)); + OK_(crypt_set_pbkdf_type(cd, &pbkdf)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 0); + EQ_(crypt_keyslot_add_by_key(cd, 1, NULL, 64, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 1); + + rparams.device_size = 2; + rparams.max_hotzone_size = 1; + rparams.resilience = "none"; + EQ_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, 1, "aes", "xts-plain64", &rparams), 2); + + /* interrupt reencryption after 'test_progress_steps' */ + test_progress_steps = 1; + OK_(crypt_reencrypt(cd, &test_progress)); + EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_CLEAN); + + NOTFAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0), "Could not activate device in reencryption."); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(cad.size, 2); + EQ_(cad.offset, r_header_size); + /* TODO: this should work in future releases unless reencryption process is running */ + FAIL_(crypt_resize(cd, CDEVICE_1, 1), "Device in reencryption."); + FAIL_(crypt_resize(cd, CDEVICE_1, 0), "Device in reencryption."); + + rparams.max_hotzone_size = 0; + rparams.device_size = 3; + FAIL_(crypt_reencrypt_init_by_passphrase(cd, CDEVICE_1, PASSPHRASE, strlen(PASSPHRASE), 0, 1, "aes", "xts-plain64", &rparams), "Invalid device size."); + crypt_deactivate(cd, CDEVICE_1); + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, 1, "aes", "xts-plain64", &rparams), "Invalid device size."); + rparams.device_size = 2; + rparams.flags = CRYPT_REENCRYPT_RESUME_ONLY; + NOTFAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, 1, "aes", "xts-plain64", &rparams), "Failed to initialize reencryption."); + OK_(crypt_reencrypt(cd, NULL)); + EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_NONE); + EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 1, PASSPHRASE, strlen(PASSPHRASE), 0), 1); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + /* after reencryption use whole device again */ + EQ_(cad.size, 16); + OK_(crypt_deactivate(cd, CDEVICE_1)); + + /* Reencrypt device with wrong size */ + EQ_(crypt_keyslot_add_by_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 0); + EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 1, PASSPHRASE, strlen(PASSPHRASE), 0), 1); + OK_(crypt_resize(cd, CDEVICE_1, 7)); + rparams.device_size = 0; + rparams.flags = 0; + params2.sector_size = 4096; + FAIL_(crypt_reencrypt_init_by_passphrase(cd, CDEVICE_1, PASSPHRASE, strlen(PASSPHRASE), 1, 0, "aes", "xts-plain64", &rparams), "Active device size is not aligned to new sector size."); + rparams.device_size = 8; + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 1, 0, "aes", "xts-plain64", &rparams), "Reduced reencryption size does not match active device."); + /* FIXME: allow after resize in reencryption is supported */ + //NOTFAIL_(crypt_activate_by_passphrase(cd, NULL, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY | CRYPT_ACTIVATE_KEYRING_KEY), "Failed to load keys."); + // OK_(crypt_resize(cd, CDEVICE_1, 8)); + OK_(crypt_deactivate(cd, CDEVICE_1)); + CRYPT_FREE(cd); + + params2.sector_size = 512; + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_init(&cd2, DMDIR H_DEVICE)); + OK_(crypt_set_data_offset(cd2, r_header_size - 8)); + OK_(crypt_set_pbkdf_type(cd, &pbkdf)); + OK_(crypt_set_pbkdf_type(cd2, &pbkdf)); + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 32, ¶ms2)); + OK_(crypt_format(cd2, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 32, ¶ms2)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 0); + EQ_(crypt_keyslot_add_by_volume_key(cd2, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 0); + EQ_(crypt_keyslot_add_by_key(cd, 1, NULL, 64, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 1); + EQ_(crypt_keyslot_add_by_key(cd2, 1, NULL, 64, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 1); + EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, PASSPHRASE, strlen(PASSPHRASE), 0), 0); + EQ_(crypt_activate_by_passphrase(cd2, CDEVICE_2, 0, PASSPHRASE, strlen(PASSPHRASE), 0), 0); + rparams.flags = CRYPT_REENCRYPT_INITIALIZE_ONLY; + EQ_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, 1, "aes", "xts-plain64", &rparams), 2); + EQ_(crypt_reencrypt_init_by_passphrase(cd2, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, 1, "aes", "xts-plain64", &rparams), 2); + rparams.flags = CRYPT_REENCRYPT_RESUME_ONLY; + /* reference wrong device in active device name */ + FAIL_(crypt_reencrypt_init_by_passphrase(cd, CDEVICE_2, PASSPHRASE, strlen(PASSPHRASE), 0, 1, "aes", "xts-plain64", &rparams), "Wrong device."); + FAIL_(crypt_reencrypt_init_by_passphrase(cd2, CDEVICE_1, PASSPHRASE, strlen(PASSPHRASE), 0, 1, "aes", "xts-plain64", &rparams), "Wrong device."); + EQ_(crypt_reencrypt_init_by_passphrase(cd2, CDEVICE_2, PASSPHRASE, strlen(PASSPHRASE), 0, 1, "aes", "xts-plain64", &rparams), 2); + FAIL_(crypt_set_data_device(cd2, DMDIR L_DEVICE_OK), "Device in reencryption."); + OK_(crypt_deactivate(cd, CDEVICE_1)); + OK_(crypt_deactivate(cd2, CDEVICE_2)); + CRYPT_FREE(cd); + CRYPT_FREE(cd2); + + /* data shift related tests */ + params2.sector_size = 512; + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 32, ¶ms2)); + OK_(crypt_set_pbkdf_type(cd, &pbkdf)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 0); + EQ_(crypt_keyslot_add_by_key(cd, 1, NULL, 64, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 1); + memset(&rparams, 0, sizeof(rparams)); + rparams.direction = CRYPT_REENCRYPT_BACKWARD; + rparams.resilience = "datashift"; + rparams.data_shift = 8; + rparams.flags = CRYPT_REENCRYPT_INITIALIZE_ONLY; + rparams.luks2 = ¶ms2; + EQ_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, 1, "aes", "xts-plain64", &rparams), 2); + EQ_(crypt_reencrypt_status(cd, &retparams), CRYPT_REENCRYPT_CLEAN); + EQ_(retparams.data_shift, 8); + EQ_(retparams.mode, CRYPT_REENCRYPT_REENCRYPT); + OK_(strcmp(retparams.resilience, "datashift")); + EQ_(crypt_get_data_offset(cd), 32776); + rparams.flags = CRYPT_REENCRYPT_RESUME_ONLY; + EQ_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, 1, "aes", "xts-plain64", &rparams), 2); + OK_(crypt_reencrypt(cd, NULL)); + CRYPT_FREE(cd); + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_set_pbkdf_type(cd, &pbkdf)); + OK_(crypt_load(cd, CRYPT_LUKS2, NULL)); + EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 1, PASSPHRASE, strlen(PASSPHRASE), 0), 1); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(cad.size, 8); + EQ_(crypt_get_data_offset(cd), 32776); + OK_(crypt_deactivate(cd, CDEVICE_1)); + rparams.flags = 0; + EQ_(crypt_keyslot_add_by_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 0); + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 1, 0, "aes", "xts-plain64", &rparams), "Device is too small."); + CRYPT_FREE(cd); + // BUG: We need reencrypt abort flag + /* it fails, but it's already initialized and we have no way to abort yet */ + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 32, ¶ms2)); + OK_(crypt_set_pbkdf_type(cd, &pbkdf)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 1, NULL, 64, PASSPHRASE, strlen(PASSPHRASE)), 1); + EQ_(crypt_keyslot_add_by_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 0); + rparams.direction = CRYPT_REENCRYPT_FORWARD; + rparams.resilience = "datashift"; + rparams.data_shift = 8; + rparams.flags = CRYPT_REENCRYPT_INITIALIZE_ONLY; + EQ_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 1, 0, "aes", "xts-plain64", &rparams), 2); + EQ_(crypt_reencrypt_status(cd, &retparams), CRYPT_REENCRYPT_CLEAN); + EQ_(retparams.data_shift, 8); + EQ_(retparams.mode, CRYPT_REENCRYPT_REENCRYPT); + OK_(strcmp(retparams.resilience, "datashift")); + EQ_(crypt_get_data_offset(cd), 32760); + rparams.flags = CRYPT_REENCRYPT_RESUME_ONLY; + EQ_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 1, 0, "aes", "xts-plain64", &rparams), 2); + OK_(crypt_reencrypt(cd, NULL)); + CRYPT_FREE(cd); + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd, CRYPT_LUKS2, NULL)); + EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, PASSPHRASE, strlen(PASSPHRASE), 0), 0); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(cad.size, 24); + EQ_(crypt_get_data_offset(cd), 32760); + OK_(crypt_deactivate(cd, CDEVICE_1)); + CRYPT_FREE(cd); + + /* data shift with online device */ + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 32, ¶ms2)); + OK_(crypt_set_pbkdf_type(cd, &pbkdf)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 0); + EQ_(crypt_keyslot_add_by_key(cd, 1, NULL, 64, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 1); + rparams.direction = CRYPT_REENCRYPT_BACKWARD; + rparams.resilience = "datashift"; + rparams.data_shift = 8; + rparams.flags = 0; + EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, PASSPHRASE, strlen(PASSPHRASE), 0), 0); + FAIL_(crypt_reencrypt_init_by_passphrase(cd, CDEVICE_1, PASSPHRASE, strlen(PASSPHRASE), 0, 1, "aes", "xts-plain64", &rparams), "Active device too large."); + OK_(crypt_deactivate(cd, CDEVICE_1)); + NOTFAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0), "Failed to activate device in reencryption."); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(cad.size, 8); + rparams.flags = CRYPT_REENCRYPT_RESUME_ONLY; + EQ_(crypt_reencrypt_init_by_passphrase(cd, CDEVICE_1, PASSPHRASE, strlen(PASSPHRASE), 0, 1, "aes", "xts-plain64", &rparams), 2); + OK_(crypt_reencrypt(cd, NULL)); + OK_(crypt_deactivate(cd, CDEVICE_1)); + CRYPT_FREE(cd); + + _cleanup_dmdevices(); + + /* encryption with datashift and moved segment (limit values for data shift) */ + OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size)); + OK_(create_dmdevice_over_loop(L_DEVICE_OK, 12*1024*2)); + + OK_(crypt_init(&cd, DMDIR H_DEVICE)); + + memset(&rparams, 0, sizeof(rparams)); + params2.sector_size = 512; + params2.data_device = DMDIR L_DEVICE_OK; + rparams.mode = CRYPT_REENCRYPT_ENCRYPT; + rparams.direction = CRYPT_REENCRYPT_BACKWARD; + rparams.resilience = "datashift"; + rparams.data_shift = 8192; + rparams.luks2 = ¶ms2; + rparams.flags = CRYPT_REENCRYPT_INITIALIZE_ONLY | CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT; + OK_(crypt_set_data_offset(cd, 8192)); + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "xts-plain64", NULL, NULL, 64, ¶ms2)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 30, NULL, 64, PASSPHRASE, strlen(PASSPHRASE)), 30); + EQ_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), CRYPT_ANY_SLOT, 30, "aes", "xts-plain64", &rparams), 0); + CRYPT_FREE(cd); + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_header_restore(cd, CRYPT_LUKS2, DMDIR H_DEVICE)); + CRYPT_FREE(cd); + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd, CRYPT_LUKS2, NULL)); + EQ_(crypt_reencrypt_status(cd, &retparams), CRYPT_REENCRYPT_CLEAN); + EQ_(retparams.mode, CRYPT_REENCRYPT_ENCRYPT); + OK_(strcmp(retparams.resilience, "datashift")); + EQ_(retparams.data_shift, 8192); + EQ_(retparams.flags & CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT, CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT); + EQ_(crypt_get_data_offset(cd), 8192); + rparams.flags = CRYPT_REENCRYPT_RESUME_ONLY; + EQ_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), CRYPT_ANY_SLOT, 30, NULL, NULL, &rparams), 0); + OK_(crypt_reencrypt(cd, NULL)); + CRYPT_FREE(cd); + + _cleanup_dmdevices(); + OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size)); + OK_(create_dmdevice_over_loop(L_DEVICE_OK, 12*1024*2+1)); + + /* encryption with datashift and moved segment (data shift + 1 sector) */ + OK_(crypt_init(&cd, DMDIR H_DEVICE)); + rparams.flags = CRYPT_REENCRYPT_INITIALIZE_ONLY | CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT; + OK_(crypt_set_data_offset(cd, 8192)); + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "xts-plain64", NULL, NULL, 64, ¶ms2)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 30, NULL, 64, PASSPHRASE, strlen(PASSPHRASE)), 30); + EQ_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), CRYPT_ANY_SLOT, 30, "aes", "xts-plain64", &rparams), 0); + CRYPT_FREE(cd); + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_header_restore(cd, CRYPT_LUKS2, DMDIR H_DEVICE)); + EQ_(crypt_get_data_offset(cd), 8192); + rparams.flags = CRYPT_REENCRYPT_RESUME_ONLY; + EQ_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), CRYPT_ANY_SLOT, 30, NULL, NULL, &rparams), 0); + OK_(crypt_reencrypt(cd, NULL)); + CRYPT_FREE(cd); + + _cleanup_dmdevices(); + OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size)); + OK_(create_dmdevice_over_loop(L_DEVICE_OK, 12*1024*2)); + + OK_(crypt_init(&cd, DMDIR H_DEVICE)); + + /* encryption with datashift and moved segment (data shift + data offset > device size) */ + memset(&rparams, 0, sizeof(rparams)); + params2.sector_size = 512; + params2.data_device = DMDIR L_DEVICE_OK; + rparams.mode = CRYPT_REENCRYPT_ENCRYPT; + rparams.direction = CRYPT_REENCRYPT_BACKWARD; + rparams.resilience = "datashift"; + rparams.data_shift = 8200; + rparams.luks2 = ¶ms2; + rparams.flags = CRYPT_REENCRYPT_INITIALIZE_ONLY | CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT; + OK_(crypt_set_data_offset(cd, 8200)); + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "xts-plain64", NULL, NULL, 64, ¶ms2)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 30, NULL, 64, PASSPHRASE, strlen(PASSPHRASE)), 30); + FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), CRYPT_ANY_SLOT, 30, "aes", "xts-plain64", &rparams), "Data device is too small"); + EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_NONE); + CRYPT_FREE(cd); + + _cleanup_dmdevices(); + OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size)); + OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_header_size + 1)); + + /* decryption backward */ + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + params2.data_device = NULL; + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 32, ¶ms2)); + OK_(crypt_set_pbkdf_type(cd, &pbkdf)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 6, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 6); + memset(&rparams, 0, sizeof(rparams)); + rparams.mode = CRYPT_REENCRYPT_DECRYPT; + rparams.direction = CRYPT_REENCRYPT_BACKWARD; + rparams.resilience = "none"; + rparams.max_hotzone_size = 2048; + OK_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 6, CRYPT_ANY_SLOT, NULL, NULL, &rparams)); + OK_(crypt_reencrypt(cd, NULL)); + CRYPT_FREE(cd); + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd, CRYPT_LUKS2, NULL)); + EQ_(crypt_get_data_offset(cd), r_header_size); + EQ_(crypt_get_volume_key_size(cd), 0); + OK_(strcmp(crypt_get_cipher(cd), "cipher_null")); + CRYPT_FREE(cd); + + /* decryption forward */ + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + params2.data_device = NULL; + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 32, ¶ms2)); + OK_(crypt_set_pbkdf_type(cd, &pbkdf)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 6, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 6); + memset(&rparams, 0, sizeof(rparams)); + rparams.mode = CRYPT_REENCRYPT_DECRYPT; + rparams.direction = CRYPT_REENCRYPT_FORWARD; + rparams.resilience = "none"; + rparams.max_hotzone_size = 2048; + OK_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 6, CRYPT_ANY_SLOT, NULL, NULL, &rparams)); + OK_(crypt_reencrypt(cd, NULL)); + CRYPT_FREE(cd); + + /* decryption with data shift */ + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + params2.data_device = NULL; + OK_(crypt_set_pbkdf_type(cd, &pbkdf)); + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 32, ¶ms2)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 6, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 6); + remove(BACKUP_FILE); + OK_(crypt_header_backup(cd, CRYPT_LUKS2, BACKUP_FILE)); + CRYPT_FREE(cd); + // FIXME: we need write flock + OK_(chmod(BACKUP_FILE, S_IRUSR|S_IWUSR)); + OK_(crypt_init_data_device(&cd, BACKUP_FILE, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd, CRYPT_LUKS2, NULL)); + EQ_(crypt_get_data_offset(cd), r_header_size); + memset(&rparams, 0, sizeof(rparams)); + rparams.mode = CRYPT_REENCRYPT_DECRYPT; + rparams.direction = CRYPT_REENCRYPT_FORWARD; + rparams.resilience = "datashift"; + rparams.data_shift = r_header_size; + OK_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 6, CRYPT_ANY_SLOT, NULL, NULL, &rparams)); + EQ_(crypt_get_data_offset(cd), 0); + OK_(crypt_reencrypt(cd, NULL)); + remove(BACKUP_FILE); + CRYPT_FREE(cd); + + /* online decryption with data shift (future feature) */ + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + params2.data_device = NULL; + OK_(crypt_set_pbkdf_type(cd, &pbkdf)); + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 32, ¶ms2)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 6, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 6); + EQ_(crypt_activate_by_passphrase(cd, CDEVICE_2, 6, PASSPHRASE, strlen(PASSPHRASE), 0), 6); + OK_(t_device_size(DMDIR CDEVICE_2, &r_size_1)); + EQ_(r_size_1, 512); + // create placeholder device to block automatic deactivation after decryption + OK_(_system("dmsetup create " CDEVICE_1 " --table \"0 1 linear " DMDIR CDEVICE_2 " 0\"", 1)); + remove(BACKUP_FILE); + OK_(crypt_header_backup(cd, CRYPT_LUKS2, BACKUP_FILE)); + CRYPT_FREE(cd); + // FIXME: we need write flock + OK_(chmod(BACKUP_FILE, S_IRUSR|S_IWUSR)); + OK_(crypt_init_data_device(&cd, BACKUP_FILE, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd, CRYPT_LUKS2, NULL)); + EQ_(crypt_get_data_offset(cd), r_header_size); + memset(&rparams, 0, sizeof(rparams)); + rparams.mode = CRYPT_REENCRYPT_DECRYPT; + rparams.direction = CRYPT_REENCRYPT_FORWARD; + rparams.resilience = "datashift"; + rparams.data_shift = r_header_size; + OK_(crypt_reencrypt_init_by_passphrase(cd, CDEVICE_2, PASSPHRASE, strlen(PASSPHRASE), 6, CRYPT_ANY_SLOT, NULL, NULL, &rparams)); + EQ_(crypt_get_data_offset(cd), 0); + OK_(crypt_reencrypt(cd, NULL)); + remove(BACKUP_FILE); + OK_(t_device_size(DMDIR CDEVICE_2, &r_size_1)); + EQ_(r_size_1, 512); + OK_(_system("dmsetup remove " DM_RETRY CDEVICE_1 DM_NOSTDERR, 0)); + CRYPT_FREE(cd); + + _cleanup_dmdevices(); + OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size)); + OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_header_size)); + OK_(create_dmdevice_over_loop(L_DEVICE_WRONG, r_header_size)); + + /* check detached header misuse (mismatching keys in table and mda) */ + OK_(crypt_init(&cd, IMAGE_EMPTY_SMALL)); + OK_(crypt_set_pbkdf_type(cd, &pbkdf)); + params2.data_device = DMDIR L_DEVICE_WRONG; + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 32, ¶ms2)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 6, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 6); + EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 6, PASSPHRASE, strlen(PASSPHRASE), 0), 6); + /* activate second device using same header */ + OK_(crypt_init_data_device(&cd2, IMAGE_EMPTY_SMALL, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd2, CRYPT_LUKS2, NULL)); + OK_(crypt_set_pbkdf_type(cd2, &pbkdf)); + EQ_(crypt_activate_by_passphrase(cd2, CDEVICE_2, 6, PASSPHRASE, strlen(PASSPHRASE), 0), 6); + CRYPT_FREE(cd2); + EQ_(crypt_keyslot_add_by_key(cd, 1, NULL, 32, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 1); + + memset(&rparams, 0, sizeof(rparams)); + rparams.resilience = "none"; + rparams.max_hotzone_size = 16*2048; + rparams.luks2 = ¶ms2; + + OK_(crypt_reencrypt_init_by_passphrase(cd, CDEVICE_1, PASSPHRASE, strlen(PASSPHRASE), 6, 1, "aes", "cbc-essiv:sha256", &rparams)); + OK_(crypt_reencrypt(cd, NULL)); + + OK_(crypt_init_data_device(&cd2, IMAGE_EMPTY_SMALL, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd2, CRYPT_LUKS2, NULL)); + OK_(crypt_set_pbkdf_type(cd2, &pbkdf)); + EQ_(crypt_keyslot_add_by_key(cd2, 2, NULL, 32, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 2); + rparams.flags = CRYPT_REENCRYPT_INITIALIZE_ONLY; + FAIL_(crypt_reencrypt_init_by_passphrase(cd2, CDEVICE_2, PASSPHRASE, strlen(PASSPHRASE), 1, 2, "aes", "cbc-essiv:sha256", &rparams), "Mismatching parameters in device table."); + OK_(crypt_reencrypt_init_by_passphrase(cd2, NULL, PASSPHRASE, strlen(PASSPHRASE), 1, 2, "aes", "cbc-essiv:sha256", &rparams)); + rparams.flags = CRYPT_REENCRYPT_RESUME_ONLY; + FAIL_(crypt_reencrypt_init_by_passphrase(cd2, CDEVICE_2, PASSPHRASE, strlen(PASSPHRASE), 1, 2, "aes", "cbc-essiv:sha256", &rparams), "Mismatching parameters in device table."); + OK_(crypt_deactivate(cd, CDEVICE_1)); + OK_(crypt_deactivate(cd2, CDEVICE_2)); + CRYPT_FREE(cd); + CRYPT_FREE(cd2); + + /* check detached header misuse (mismatching progress data in active device and mda) */ + OK_(crypt_init(&cd, IMAGE_EMPTY_SMALL)); + OK_(crypt_set_pbkdf_type(cd, &pbkdf)); + params2.data_device = DMDIR L_DEVICE_WRONG; + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 32, ¶ms2)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 6, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 6); + EQ_(crypt_keyslot_add_by_key(cd, 1, NULL, 32, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 1); + rparams.flags = 0; + rparams.max_hotzone_size = 8; + OK_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 6, 1, "aes", "cbc-essiv:sha256", &rparams)); + /* reencrypt 8 srectors of device */ + test_progress_steps = 1; + OK_(crypt_reencrypt(cd, &test_progress)); + + /* activate another data device with same LUKS2 header (this is wrong, but we can't detect such mistake) */ + OK_(crypt_init_data_device(&cd2, IMAGE_EMPTY_SMALL, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd2, CRYPT_LUKS2, NULL)); + NOTFAIL_(crypt_activate_by_passphrase(cd2, CDEVICE_2, CRYPT_ANY_SLOT, PASSPHRASE, strlen(PASSPHRASE), 0), "Failed to activate device in reencryption."); + CRYPT_FREE(cd2); + + /* reencrypt yet another 8 sectors of first device */ + rparams.flags = CRYPT_REENCRYPT_RESUME_ONLY; + OK_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 6, 1, "aes", "cbc-essiv:sha256", &rparams)); + test_progress_steps = 1; + OK_(crypt_reencrypt(cd, &test_progress)); + + /* Now active mapping for second data device does not match its metadata */ + OK_(crypt_init_data_device(&cd2, IMAGE_EMPTY_SMALL, DMDIR L_DEVICE_OK)); + OK_(crypt_load(cd2, CRYPT_LUKS2, NULL)); + rparams.flags = CRYPT_REENCRYPT_RESUME_ONLY; + FAIL_(crypt_reencrypt_init_by_passphrase(cd2, CDEVICE_2, PASSPHRASE, strlen(PASSPHRASE), 6, 1, "aes", "cbc-essiv:sha256", &rparams), "Mismatching device table."); + OK_(crypt_deactivate(cd2, CDEVICE_2)); + CRYPT_FREE(cd2); + CRYPT_FREE(cd); + + _cleanup_dmdevices(); + OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_header_size + 16)); + + /* Test LUKS2 reencryption honors flags device was activate with */ + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_set_pbkdf_type(cd, &pbkdf)); + params2.sector_size = 512; + params2.data_device = NULL; + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 32, ¶ms2)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 6, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 6); + OK_(crypt_volume_key_keyring(cd, 0)); /* disable keyring */ + EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 6, PASSPHRASE, strlen(PASSPHRASE), CRYPT_ACTIVATE_ALLOW_DISCARDS), 6); + OK_(crypt_volume_key_keyring(cd, 1)); + rparams.mode = CRYPT_REENCRYPT_REENCRYPT; + rparams.direction = CRYPT_REENCRYPT_FORWARD, + rparams.resilience = "none", + rparams.max_hotzone_size = 8; + rparams.luks2 = ¶ms2; + rparams.flags = 0; + EQ_(crypt_keyslot_add_by_key(cd, 1, NULL, 64, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 1); + OK_(crypt_reencrypt_init_by_passphrase(cd, CDEVICE_1, PASSPHRASE, strlen(PASSPHRASE), 6, 1, "aes", "xts-plain64", &rparams)); + test_progress_steps = 1; + OK_(crypt_reencrypt(cd, &test_progress)); + EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_CLEAN); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(cad.flags & CRYPT_ACTIVATE_ALLOW_DISCARDS, CRYPT_ACTIVATE_ALLOW_DISCARDS); + EQ_(cad.flags & CRYPT_ACTIVATE_KEYRING_KEY, 0); + CRYPT_FREE(cd); + OK_(crypt_init_by_name(&cd, CDEVICE_1)); + rparams.flags = CRYPT_REENCRYPT_RESUME_ONLY; + OK_(crypt_reencrypt_init_by_passphrase(cd, CDEVICE_1, PASSPHRASE, strlen(PASSPHRASE), 6, 1, "aes", "xts-plain64", &rparams)); + OK_(crypt_reencrypt(cd, NULL)); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(cad.flags & CRYPT_ACTIVATE_ALLOW_DISCARDS, CRYPT_ACTIVATE_ALLOW_DISCARDS); + EQ_(cad.flags & CRYPT_ACTIVATE_KEYRING_KEY, 0); + OK_(crypt_deactivate(cd, CDEVICE_1)); + CRYPT_FREE(cd); + + _cleanup_dmdevices(); + OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_header_size + 16)); + + rparams.mode = CRYPT_REENCRYPT_REENCRYPT; + rparams.direction = CRYPT_REENCRYPT_FORWARD; + rparams.resilience = "none"; + rparams.hash = NULL; + rparams.data_shift = 0; + rparams.max_hotzone_size = 0; + rparams.device_size = 0; + rparams.luks2 = ¶ms2; + rparams.flags = 0; + + /* Test support for specific key reencryption */ + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_set_pbkdf_type(cd, &pbkdf)); + OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 32, ¶ms2)); + EQ_(crypt_keyslot_add_by_volume_key(cd, 3, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 3); + EQ_(crypt_keyslot_add_by_key(cd, 9, key, key_size, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 9); + EQ_(crypt_keyslot_add_by_key(cd, 10, key, key_size, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT | CRYPT_VOLUME_KEY_DIGEST_REUSE ), 10); + OK_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 3, 9, "aes", "xts-plain64", &rparams)); + OK_(crypt_reencrypt(cd, NULL)); + OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0)); + OK_(crypt_keyslot_destroy(cd, 9)); + OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0)); + crypt_free(cd); + + _cleanup_dmdevices(); +} +#endif + +static void Luks2Repair(void) +{ + 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); + } +#ifndef NO_CRYPTSETUP_PATH + if (getenv("CRYPTSETUP_PATH")) { + printf("Cannot run this test with CRYPTSETUP_PATH set.\n"); + exit(77); + } +#endif + 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()) { + printf("Cannot set test devices.\n"); + _cleanup(); + exit(77); + } + + crypt_set_debug_level(_debug ? CRYPT_DEBUG_JSON : CRYPT_DEBUG_NONE); + + RUN_(AddDeviceLuks2, "Format and use LUKS2 device"); + RUN_(Luks2MetadataSize, "LUKS2 metadata settings"); + 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"); +#if KERNEL_KEYRING && USE_LUKS2_REENCRYPTION + RUN_(Luks2Reencryption, "LUKS2 reencryption"); +#endif + RUN_(Luks2Repair, "LUKS2 repair"); // test disables metadata locking. Run always last! + + _cleanup(); + return 0; +} diff --git a/tests/api-test.c b/tests/api-test.c new file mode 100644 index 0000000..85f7a93 --- /dev/null +++ b/tests/api-test.c @@ -0,0 +1,1974 @@ +/* + * cryptsetup library API check functions + * + * Copyright (C) 2009-2021 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2021 Milan Broz + * Copyright (C) 2016-2021 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; + +struct crypt_device *cd = NULL, *cd2 = 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 + +#define DM_NOSTDERR " 2>/dev/null" + +static void _cleanup_dmdevices(void) +{ + struct stat st; + + if (!stat(DMDIR H_DEVICE, &st)) + _system("dmsetup remove " DM_RETRY H_DEVICE DM_NOSTDERR, 0); + + if (!stat(DMDIR H_DEVICE_WRONG, &st)) + _system("dmsetup remove " DM_RETRY H_DEVICE_WRONG DM_NOSTDERR, 0); + + if (!stat(DMDIR L_DEVICE_0S, &st)) + _system("dmsetup remove " DM_RETRY L_DEVICE_0S DM_NOSTDERR, 0); + + if (!stat(DMDIR L_DEVICE_1S, &st)) + _system("dmsetup remove " DM_RETRY L_DEVICE_1S DM_NOSTDERR, 0); + + if (!stat(DMDIR L_DEVICE_WRONG, &st)) + _system("dmsetup remove " DM_RETRY L_DEVICE_WRONG DM_NOSTDERR, 0); + + if (!stat(DMDIR L_DEVICE_OK, &st)) + _system("dmsetup remove " DM_RETRY L_DEVICE_OK DM_NOSTDERR, 0); + + t_dev_offset = 0; +} + +static void _cleanup(void) +{ + struct stat st; + + CRYPT_FREE(cd); + CRYPT_FREE(cd2); + + //_system("udevadm settle", 0); + + if (!stat(DMDIR CDEVICE_1, &st)) + _system("dmsetup remove " DM_RETRY CDEVICE_1 DM_NOSTDERR, 0); + + if (!stat(DMDIR CDEVICE_2, &st)) + _system("dmsetup remove " DM_RETRY CDEVICE_2 DM_NOSTDERR, 0); + + if (!stat(DEVICE_EMPTY, &st)) + _system("dmsetup remove " DM_RETRY DEVICE_EMPTY_name DM_NOSTDERR, 0); + + if (!stat(DEVICE_ERROR, &st)) + _system("dmsetup remove " DM_RETRY DEVICE_ERROR_name DM_NOSTDERR, 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_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,¶ms),"cipher param is null"); + FAIL_(crypt_format(cd,CRYPT_PLAIN,cipher,NULL,NULL,NULL,key_size,¶ms),"cipher_mode param is null"); + OK_(crypt_format(cd,CRYPT_PLAIN,cipher,cipher_mode,NULL,NULL,key_size,¶ms)); + 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)); + GE_(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, ¶ms)); + 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, ¶ms)); + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0)); + GE_(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, ¶ms)); + 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, ¶ms)); + 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, ¶ms)); + 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, ¶ms)); + 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, ¶ms)); + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0)); + GE_(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, ¶ms)); + 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 + GE_(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, ¶ms)); + 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, ¶ms), + "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,¶ms)); + 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)); + GE_(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,¶ms)); + 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); + GE_(crypt_status(cd,CDEVICE_1),CRYPT_ACTIVE); + fd = open(path, O_RDONLY); + NOTFAIL_(fd, "Bad loop device."); + close(fd); + + // 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,¶ms)); + 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"); + GE_(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"); + GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); + + // retrieve volume key check + 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)); + GE_(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,¶ms)); + + // 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_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, ¶ms)); + OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0)); + GE_(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) +{ + 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"); + GE_(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)); + + 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)); + GE_(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) +{ + struct crypt_active_device cad; + char key[128]; + size_t key_size; + int suspend_status; + uint64_t r_payload_offset; + const struct crypt_pbkdf_type fast_pbkdf = { + .type = "pbkdf2", + .hash = "sha256", + .iterations = 1000, + .flags = CRYPT_PBKDF_NO_BENCHMARK + }; + + 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); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(CRYPT_ACTIVATE_SUSPENDED, cad.flags & CRYPT_ACTIVATE_SUSPENDED); + + 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_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(0, cad.flags & CRYPT_ACTIVATE_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))); + + /* Resume by volume key */ + OK_(crypt_suspend(cd, CDEVICE_1)); + key_size = sizeof(key); + memset(key, 0, key_size); + FAIL_(crypt_resume_by_volume_key(cd, CDEVICE_1, key, key_size), "wrong key"); + OK_(crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key, &key_size, KEY1, strlen(KEY1))); + OK_(crypt_resume_by_volume_key(cd, CDEVICE_1, key, key_size)); + OK_(crypt_deactivate(cd, CDEVICE_1)); + CRYPT_FREE(cd); + + OK_(get_luks_offsets(0, key_size, 1024*2, 0, NULL, &r_payload_offset)); + OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 1)); + + /* Resume device with cipher_null */ + OK_(crypt_init(&cd, DMDIR L_DEVICE_OK)); + OK_(crypt_set_pbkdf_type(cd, &fast_pbkdf)); + OK_(crypt_format(cd, CRYPT_LUKS1, "cipher_null", "ecb", NULL, key, key_size, NULL)); + EQ_(0, crypt_keyslot_add_by_volume_key(cd, 0, key, key_size, "", 0)); + OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0)); + OK_(crypt_suspend(cd, CDEVICE_1)); + OK_(crypt_resume_by_volume_key(cd, CDEVICE_1, key, key_size)); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(0, cad.flags & CRYPT_ACTIVATE_SUSPENDED); + OK_(crypt_suspend(cd, CDEVICE_1)); + OK_(crypt_resume_by_passphrase(cd, CDEVICE_1, 0, "", 0)); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(0, cad.flags & CRYPT_ACTIVATE_SUSPENDED); + OK_(crypt_deactivate(cd, CDEVICE_1)); + CRYPT_FREE(cd); + + _remove_keyfiles(); + _cleanup_dmdevices(); +} + +static void AddDeviceLuks(void) +{ + enum { OFFSET_1M = 2048 , OFFSET_2M = 4096, OFFSET_4M = 8192, OFFSET_8M = 16384 }; + 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, ¶ms), "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, ¶ms)); + 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, ¶ms)); + 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, ¶ms)); + 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, ¶ms), "Alignment not compatible"); + OK_(crypt_set_data_offset(cd, OFFSET_4M)); + OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms)); + 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, ¶ms), "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, ¶ms)); + 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, ¶ms)); + EQ_(crypt_get_data_offset(cd), params.data_alignment); + OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0)); + GE_(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, ¶ms), "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, ¶ms)); + 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, ¶ms), "Context is already formatted"); + GE_(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)); + GE_(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, ¶ms)); + 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, ¶ms)); + CRYPT_FREE(cd); + // there we've got uuid mismatch + OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, DMDIR H_DEVICE)); + GE_(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, ¶ms)); + + // 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)); + GE_(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)); + GE_(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 the 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)); + OK_(pbkdf.iterations < 1000); /* set by minimum iterations above */ + EQ_(0, pbkdf.max_memory_kb); + EQ_(0, pbkdf.parallel_threads); + FAIL_(crypt_keyslot_get_pbkdf(cd, 2, &pbkdf), "Keyslot 2 is inactive."); + + 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))); + + 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); + + // No benchmark PBKDF2 + pbkdf.flags = CRYPT_PBKDF_NO_BENCHMARK; + pbkdf.hash = "sha256"; + pbkdf.iterations = 1000; + pbkdf.time_ms = 0; + + OK_(crypt_init(&cd, DEVICE_2)); + OK_(crypt_set_pbkdf_type(cd, &pbkdf)); + OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms)); + CRYPT_FREE(cd); + + _cleanup_dmdevices(); +} + +static void UseTempVolumes(void) +{ + 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)); + GE_(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 manipulate 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)); + GE_(crypt_status(cd, CDEVICE_2), CRYPT_ACTIVE); + OK_(crypt_deactivate(cd, CDEVICE_2)); + CRYPT_FREE(cd); +} + +static void LuksHeaderRestore(void) +{ + 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"); + GE_(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, ¶ms)); + 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, ¶ms)); + 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, ¶ms)); + 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_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, ¶ms)); + 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)); + GE_(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, ¶ms)); + 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, ¶ms)); + 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_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, ¶ms)); + 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)); + GE_(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); + GE_(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); + GE_(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); + NOTFAIL_(fd, "Bad loop device."); + close(fd); + 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); + GE_(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); + GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); + OK_(crypt_deactivate(cd, CDEVICE_1)); + CRYPT_FREE(cd); + + _cleanup_dmdevices(); +} + +static void ResizeDeviceLuks(void) +{ + 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, ¶ms)); + 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); + GE_(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, ¶ms)); + 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); + GE_(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, ¶ms)); + 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_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, ¶ms)); + + // 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, ¶ms)); + 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, ¶ms)); + + // 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) +{ + const char *salt_hex = "20c28ffc129c12360ba6ceea2b6cf04e89c2b41cfe6b8439eb53c1897f50df7b"; + const char *root_hex = "ab018b003a967fc782effb293b6dccb60b4f40c06bf80d16391acf686d28b5d6"; + char salt[256], root_hash[256], root_hash_out[256]; + size_t root_hash_out_size = 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, ¶ms), + "Unsupported block size."); + params.data_block_size = 4096; + params.hash_block_size = 333; + FAIL_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, ¶ms), + "Unsupported block size."); + params.hash_block_size = 4096; + + /* salt size */ + params.salt_size = 257; + FAIL_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, ¶ms), + "Too large salt."); + params.salt_size = 32; + + /* hash_type */ + params.hash_type = 3; + FAIL_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, ¶ms), + "Unsupported hash type."); + params.hash_type = 1; + params.hash_name = "blah"; + FAIL_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, ¶ms), + "Unsupported hash name."); + params.hash_name = "sha256"; + + OK_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, ¶ms)); + 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, ¶ms)); + EQ_(strcmp(DEVICE_2, crypt_get_metadata_device_name(cd)), 0); + CRYPT_FREE(cd); + + /* Verify */ + OK_(crypt_init(&cd, DEVICE_2)); + memset(¶ms, 0, sizeof(params)); + params.data_device = DEVICE_EMPTY; + params.flags = CRYPT_VERITY_CHECK_HASH; + OK_(crypt_load(cd, CRYPT_VERITY, ¶ms)); + + /* check verity params */ + EQ_(crypt_get_volume_key_size(cd), 32); + OK_(strcmp(CRYPT_VERITY, crypt_get_type(cd))); + memset(¶ms, 0, sizeof(params)); + OK_(crypt_get_verity_info(cd, ¶ms)); + 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)); + memset(root_hash_out, 0, root_hash_out_size); + OK_(crypt_volume_key_get(cd, CRYPT_ANY_SLOT, root_hash_out, &root_hash_out_size, NULL, 0)); + EQ_(32, root_hash_out_size); + OK_(memcmp(root_hash, root_hash_out, root_hash_out_size)); + 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_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, ¶ms), "Wrong passphrase"); + params.passphrase_size++; + OK_(crypt_load(cd, CRYPT_TCRYPT, ¶ms)); + + // 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); + + 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)); + GE_(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, ¶ms), "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, ¶ms); + 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_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,¶ms); + 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, ¶ms), "tag size mismatch"); + params.tag_size = 4; + OK_(crypt_load(cd, CRYPT_INTEGRITY, ¶ms)); + OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, NULL, 0, 0)); + GE_(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_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, ¶ms)); + FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms), + "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, ¶ms); + 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, ¶ms), + "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); + } +#ifndef NO_CRYPTSETUP_PATH + if (getenv("CRYPTSETUP_PATH")) { + printf("Cannot run this test with CRYPTSETUP_PATH set.\n"); + exit(77); + } +#endif + 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()) { + printf("Cannot set test devices.\n"); + _cleanup(); + exit(77); + } + + 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"); + + _cleanup(); + return 0; +} diff --git a/tests/api_test.h b/tests/api_test.h new file mode 100644 index 0000000..91b47b7 --- /dev/null +++ b/tests/api_test.h @@ -0,0 +1,127 @@ +/* + * cryptsetup library API check functions + * + * Copyright (C) 2009-2021 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2021 Milan Broz + * Copyright (C) 2016-2021 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_ok_return(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_ge_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 NOTFAIL_(x, y) do { xlog("(notfail)", #x, __FUNCTION__, __LINE__, y); \ + check_ok_return((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 GE_(x, y) do { int64_t _x = (x), _y = (y); \ + xlog("(g_equal)", #x " == " #y, __FUNCTION__, __LINE__, NULL); \ + if (_x < _y) check_ge_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 CRYPT_FREE(x) do { crypt_free(x); x = NULL; } 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/bitlk-compat-test b/tests/bitlk-compat-test new file mode 100755 index 0000000..c8210b5 --- /dev/null +++ b/tests/bitlk-compat-test @@ -0,0 +1,162 @@ +#!/bin/bash + +# check bitlk images parsing + +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup +TST_DIR=bitlk-images +MAP=bitlktst + +CRYPTSETUP_VALGRIND=../.libs/cryptsetup +CRYPTSETUP_LIB_VALGRIND=../.libs + +[ -z "$srcdir" ] && srcdir="." + +function remove_mapping() +{ + [ -b /dev/mapper/$MAP ] && dmsetup remove --retry $MAP +} + +function fail() +{ + [ -n "$1" ] && echo "$1" + echo " [FAILED]" + echo "FAILED backtrace:" + while caller $frame; do ((frame++)); done + remove_mapping + exit 2 +} + +function skip() +{ + [ -n "$1" ] && echo "$1" + echo "Test skipped." + exit 77 +} + +function load_vars() +{ + local file=$(echo $1 | sed -e s/^$TST_DIR\\/// | sed -e s/\.img$//) + source <(grep = <(grep -A8 "\[$file\]" $TST_DIR/images.conf)) +} + +function check_dump() +{ + dump=$1 + file=$2 + + # load variables for this image from config file + load_vars $file + + # GUID + dump_guid=$(echo "$dump" | grep Version -A 1 | tail -1 | cut -d: -f2 | tr -d "\t\n ") + [ ! -z "$GUID" -a "$dump_guid" = "$GUID" ] || fail " GUID check from dump failed." + + # cipher + dump_cipher=$(echo "$dump" | grep "Cipher name" | cut -d: -f2 | tr -d "\t\n ") + dump_mode=$(echo "$dump" | grep "Cipher mode" | cut -d: -f2 | tr -d "\t\n ") + cipher=$(echo "$dump_cipher-$dump_mode") + [ ! -z "$CIPHER" -a "$cipher" = "$CIPHER" ] || fail " cipher check from dump failed." + + if echo "$file" | grep -q -e "smart-card"; then + # smart card protected VMK GUID + dump_sc_vmk=$(echo "$dump" | grep "VMK protected with smart card" -B 1 | head -1 | cut -d: -f2 | tr -d "\t ") + [ ! -z "$SC_VMK_GUID" -a "$dump_sc_vmk" = "$SC_VMK_GUID" ] || fail " smart card protected VMK GUID check from dump failed." + elif echo "$file" | grep -q -e "startup-key"; then + # startup key protected VMK GUID + dump_sk_vmk=$(echo "$dump" | grep "VMK protected with startup key" -B 1 | head -1 | cut -d: -f2 | tr -d "\t ") + [ ! -z "$SK_VMK_GUID" -a "$dump_sk_vmk" = "$SK_VMK_GUID" ] || fail " startup key protected VMK GUID check from dump failed." + else + # password protected VMK GUID + dump_pw_vmk=$(echo "$dump" | grep "VMK protected with passphrase" -B 1 | head -1 | cut -d: -f2 | tr -d "\t ") + [ ! -z "$PW_VMK_GUID" -a "$dump_pw_vmk" = "$PW_VMK_GUID" ] || fail " password protected VMK GUID check from dump failed." + fi + + # recovery password protected VMK GUID + dump_rp_vmk=$(echo "$dump" | grep "VMK protected with recovery passphrase" -B 1 | head -1 | cut -d: -f2 | tr -d "\t ") + [ ! -z "$RP_VMK_GUID" -a "$dump_rp_vmk" = "$RP_VMK_GUID" ] || fail " recovery password protected VMK GUID check from dump failed." + +} + +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 +[ ! -d $TST_DIR ] && tar xJSf $srcdir/bitlk-images.tar.xz --no-same-owner + +[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run + +echo "HEADER CHECK" +for file in $(ls $TST_DIR/bitlk-*) ; do + echo -n " $file" + out=$($CRYPTSETUP bitlkDump $file) + check_dump "$out" "$file" + 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 + +remove_mapping + +echo "ACTIVATION FS UUID CHECK" +for file in $(ls $TST_DIR/bitlk-*) ; do + # load variables for this image from config file + load_vars $file + + # test with both passphrase and recovery passphrase + for PASSPHRASE in $PW $RP ; do + echo -n " $file" + echo $PASSPHRASE | $CRYPTSETUP bitlkOpen -r $file --test-passphrase >/dev/null 2>&1 + ret=$? + [ $ret -eq 1 ] && echo " [N/A]" && continue + echo $PASSPHRASE | $CRYPTSETUP bitlkOpen -r $file $MAP >/dev/null 2>&1 + ret=$? + [ $ret -eq 1 ] && ( echo "$file" | grep -q -e "aes-cbc" ) && echo " [N/A]" && continue + [ $ret -eq 1 ] && ( echo "$file" | grep -q -e "aes-cbc-elephant" ) && echo " [N/A]" && continue + [ $ret -eq 1 ] && ( echo "$file" | grep -q -e "clearkey" ) && echo " [N/A]" && continue + [ $ret -eq 1 ] && ( echo "$file" | grep -q -e "eow" ) && echo " [N/A]" && continue + [ $ret -eq 1 ] && ( echo "$file" | grep -q -e "-4k.img" ) && echo " [N/A]" && continue + [ $ret -eq 0 ] || fail " failed to open $file ($ret)" + $CRYPTSETUP status $MAP >/dev/null || fail + $CRYPTSETUP status /dev/mapper/$MAP >/dev/null || fail + uuid=$(lsblk -n -o UUID /dev/mapper/$MAP) + sha256sum=$(sha256sum /dev/mapper/$MAP | cut -d" " -f1) + $CRYPTSETUP remove $MAP || fail + [ "$uuid" = "$UUID" ] || fail " UUID check failed." + [ "$sha256sum" = "$SHA256SUM" ] || fail " SHA256 sum check failed." + echo " [OK]" + done + + # startup key test -- we need to use BEK file from the archive + if echo "$file" | grep -q -e "startup-key"; then + echo -n " $file" + bek_file=$(echo $SK_VMK_GUID.BEK | tr /a-z/ /A-Z/) + $CRYPTSETUP bitlkOpen -r $file --test-passphrase --key-file $TST_DIR/$bek_file + ret=$? + [ $ret -eq 1 ] && echo " [N/A]" && continue + $CRYPTSETUP bitlkOpen -r $file $MAP --key-file $TST_DIR/$bek_file >/dev/null 2>&1 + ret=$? + [ $ret -eq 0 ] || fail " failed to open $file ($ret)" + $CRYPTSETUP status $MAP >/dev/null || fail + $CRYPTSETUP status /dev/mapper/$MAP >/dev/null || fail + uuid=$(lsblk -n -o UUID /dev/mapper/$MAP) + sha256sum=$(sha256sum /dev/mapper/$MAP | cut -d" " -f1) + $CRYPTSETUP remove $MAP || fail + [ "$uuid" = "$UUID" ] || fail " UUID check failed." + [ "$sha256sum" = "$SHA256SUM" ] || fail " SHA256 sum check failed." + echo " [OK]" + + fi +done diff --git a/tests/bitlk-images.tar.xz b/tests/bitlk-images.tar.xz Binary files differnew file mode 100644 index 0000000..eea33ed --- /dev/null +++ b/tests/bitlk-images.tar.xz diff --git a/tests/blkid-luks2-pv.img.xz b/tests/blkid-luks2-pv.img.xz Binary files differnew file mode 100644 index 0000000..c9d0e57 --- /dev/null +++ b/tests/blkid-luks2-pv.img.xz diff --git a/tests/blockwise-compat b/tests/blockwise-compat new file mode 100755 index 0000000..a764020 --- /dev/null +++ b/tests/blockwise-compat @@ -0,0 +1,377 @@ +#!/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)) +} + +[ -n "$CRYPTSETUP_PATH" ] && skip "Cannot run this test with CRYPTSETUP_PATH set." + +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..6059880 --- /dev/null +++ b/tests/compat-test @@ -0,0 +1,1065 @@ +#!/bin/bash + +PS4='$LINENO:' +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup +CRYPTSETUP_RAW=$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 --retry $DEV_NAME3 >/dev/null 2>&1 + [ -b /dev/mapper/$DEV_NAME2 ] && dmsetup remove --retry $DEV_NAME2 >/dev/null 2>&1 + [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove --retry $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 + rmmod scsi_debug 2> /dev/null + scsi_debug_teardown $DEV +} + +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 backtrace:" + while caller $frame; do ((frame++)); done + 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 --retry $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 +} + +# $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 2> /dev/null +} + +function add_scsi_device() { + scsi_debug_teardown $DEV + 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 1 + DEV="/dev/"$(grep -l -e scsi_debug /sys/block/*/device/model | cut -f4 -d /) + [ -b $DEV ] || fail "Cannot find $DEV." +} + +function valgrind_setup() +{ + [ -n "$VALG" ] || return + 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" + CRYPTSETUP=valgrind_run + CRYPTSETUP_RAW="./valg.sh ${CRYPTSETUP_VALGRIND}" +} + +function valgrind_run() +{ + export INFOSTRING="$(basename ${BASH_SOURCE[1]})-line-${BASH_LINENO[0]}" + $CRYPTSETUP_RAW "$@" +} + +function expect_run() +{ + export INFOSTRING="$(basename ${BASH_SOURCE[1]})-line-${BASH_LINENO[0]}" + expect "$@" +} + +export LANG=C +valgrind_setup + +# 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 +# test detached header --test-passphrase +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT --header $HEADER_IMG $IMG || fail +echo $PWD1 | $CRYPTSETUP open --test-passphrase $HEADER_IMG || fail +rm -f $HEADER_IMG +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 1 ] && fail "luksOpen should return ENOENT exit code" +echo $PWD2 | $CRYPTSETUP luksOpen $IMG --test-passphrase 2>/dev/null && fail +[ $? -ne 1 ] && fail "luksOpen should return ENOENT 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:" || fail +$CRYPTSETUP luksDump -q $IMG --dump-master-key -d $KEY1 | grep -q "MK dump:" || fail +echo $PWD1 | $CRYPTSETUP luksDump -q $IMG --dump-master-key --master-key-file $VK_FILE >/dev/null || fail +echo $PWD1 | $CRYPTSETUP luksDump -q $IMG --dump-master-key --master-key-file $VK_FILE 2>/dev/null && fail +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 +$CRYPTSETUP -q resize $DEV_NAME --device-size 1M || fail +$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "2048 sectors" || fail +$CRYPTSETUP -q resize $DEV_NAME --device-size 512k --size 1023 >/dev/null 2>&1 && fail +$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "2048 sectors" || fail +$CRYPTSETUP -q resize $DEV_NAME --device-size 513 >/dev/null 2>&1 && fail +$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "2048 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" || fail + $CRYPTSETUP -q resize $DEV_NAME --size 16 || fail + $CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "16 sectors" || fail + $CRYPTSETUP -q resize $DEV_NAME --size 9 2>/dev/null && fail + $CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "16 sectors" || fail + $CRYPTSETUP -q resize $DEV_NAME --device-size 4608 2>/dev/null && fail + $CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "16 sectors" || fail + $CRYPTSETUP -q remove $DEV_NAME || fail +fi +# Resize not aligned to logical block size +add_scsi_device dev_size_mb=32 sector_size=4096 +echo $PWD1 | $CRYPTSETUP create $DEV_NAME --hash sha1 $DEV || fail +OLD_SIZE=$($CRYPTSETUP status $DEV_NAME | grep "^ \+size:" | sed 's/.* \([0-9]\+\) .*/\1/') +$CRYPTSETUP resize $DEV_NAME -b 7 2> /dev/null && fail +dmsetup info $DEV_NAME | grep -q SUSPENDED && fail +NEW_SIZE=$($CRYPTSETUP status $DEV_NAME | grep "^ \+size:" | sed 's/.* \([0-9]\+\) .*/\1/') +test $OLD_SIZE -eq $NEW_SIZE || fail +$CRYPTSETUP close $DEV_NAME || fail +# Add check for unaligned plain crypt activation +echo $PWD1 | $CRYPTSETUP create $DEV_NAME --hash sha1 $DEV -b 7 2>/dev/null && fail +$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 && 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 + +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:" || fail +$CRYPTSETUP luksDump -q $LOOPDEV --dump-master-key -d $KEY1 | grep -q "MK dump:" || fail +echo $PWD1 | $CRYPTSETUP luksDump -q $LOOPDEV --dump-master-key --master-key-file $VK_FILE > /dev/null || fail +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 --retry $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 --retry $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 status $DEV_NAME | grep -q "(suspended)" || 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 +echo | $CRYPTSETUP -q luksFormat -c null $FAST_PBKDF_OPT --type luks1 $LOOPDEV || fail +echo | $CRYPTSETUP -q luksOpen $LOOPDEV $DEV_NAME || fail +$CRYPTSETUP luksSuspend $DEV_NAME || fail +$CRYPTSETUP -q status $DEV_NAME | grep -q "(suspended)" || fail +echo | $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 2>/dev/null && 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-missing --header $HEADER_IMG $DEV_NAME 2>/dev/null && 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 2>/dev/null && 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 +echo $PWD1 | $CRYPTSETUP open --test-passphrase $HEADER_IMG || 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=$(losetup $LOOPDEV | sed -e "s/.*(\(.*\))/\1/") +EXPECT_TIMEOUT=10 +[ -n "$VALG" ] && EXPECT_TIMEOUT=60 + +expect_run - >/dev/null <<EOF +proc abort {} { send_error "Timeout. "; exit 2 } +set timeout $EXPECT_TIMEOUT +eval spawn $CRYPTSETUP_RAW luksOpen -v -T 2 $LOOPDEV $DEV_NAME +expect timeout abort "Enter passphrase for $EXPECT_DEV:" +sleep 0.1 +send "$PWD0 x\n" +expect timeout abort "No key available with this passphrase." +expect timeout abort "Enter passphrase for $EXPECT_DEV:" +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_run - >/dev/null <<EOF +proc abort {} { send_error "Timeout. "; exit 2 } +set timeout $EXPECT_TIMEOUT +eval spawn $CRYPTSETUP_RAW luksOpen -v -T 2 $LOOPDEV $DEV_NAME +expect timeout abort "Enter passphrase for $EXPECT_DEV:" +sleep 0.1 +send "$PWD0 x\n" +expect timeout abort "No key available with this passphrase." +expect timeout abort "Enter passphrase for $EXPECT_DEV:" +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_run - >/dev/null <<EOF +proc abort {} { send_error "Timeout. "; exit 2 } +set timeout $EXPECT_TIMEOUT +eval spawn $CRYPTSETUP_RAW luksKillSlot -v $LOOPDEV 0 +expect timeout abort "Are you sure? (Type 'yes' in capital letters):" +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_RAW 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_run - >/dev/null <<EOF +proc abort {} { send_error "Timeout. "; exit 2 } +set timeout $EXPECT_TIMEOUT +eval spawn $CRYPTSETUP_RAW luksFormat --type luks1 $FAST_PBKDF_OPT -v $LOOPDEV +expect timeout abort "Are you sure? (Type 'yes' in capital letters):" +send "YES\n" +expect timeout abort "Enter passphrase for $EXPECT_DEV:" +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_RAW luksOpen -v $LOOPDEV --test-passphrase +expect timeout abort "Enter passphrase for $EXPECT_DEV:" +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_run - >/dev/null <<EOF +proc abort {} { send_error "Timeout. "; exit 2 } +set timeout $EXPECT_TIMEOUT +eval spawn $CRYPTSETUP_RAW erase -v $LOOPDEV +expect timeout abort "Are you sure? (Type 'yes' in capital letters):" +send "YES\n" +expect timeout abort "Command successful." +expect timeout abort eof +eval spawn $CRYPTSETUP_RAW luksFormat --type luks1 $FAST_PBKDF_OPT -v $LOOPDEV +expect timeout abort "Are you sure? (Type 'yes' in capital letters):" +send "YES\n" +expect timeout abort "Enter passphrase for $EXPECT_DEV:" +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_RAW luksOpen -v $LOOPDEV -T 1 --test-passphrase +expect timeout abort "Enter passphrase for $EXPECT_DEV:" +sleep 0.1 +send "$PWD0\n" +expect timeout abort "No usable keyslot is available." +expect timeout abort eof +exit +EOF +[ $? -eq 0 ] || fail "Expect script failed." + +prepare "[37] Interactive add key." new +expect_run - >/dev/null <<EOF +proc abort {} { send_error "Timeout. "; exit 2 } +set timeout $EXPECT_TIMEOUT +eval spawn $CRYPTSETUP_RAW 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_RAW 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_RAW luksKillSlot -v $LOOPDEV 1 +expect timeout abort "Keyslot 1 is not active." +expect timeout abort eof +eval spawn $CRYPTSETUP_RAW 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_run - >/dev/null <<EOF +proc abort {} { send_error "Timeout. "; exit 2 } +set timeout $EXPECT_TIMEOUT +eval spawn $CRYPTSETUP_RAW 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_RAW luksOpen -v $LOOPDEV --test-passphrase +expect timeout abort "Enter passphrase for $EXPECT_DEV:" +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_run - >/dev/null <<EOF +proc abort {} { send_error "Timeout. "; exit 2 } +set timeout $EXPECT_TIMEOUT +eval spawn $CRYPTSETUP_RAW luksSuspend -v $DEV_NAME +expect timeout abort "Command successful." +expect timeout abort eof +eval spawn $CRYPTSETUP_RAW luksResume -v -T 3 $DEV_NAME +expect timeout abort "Enter passphrase for $EXPECT_DEV:" +sleep 0.1 +send "$PWD0 x\n" +expect timeout abort "No key available with this passphrase." +expect timeout abort "Enter passphrase for $EXPECT_DEV:" +sleep 0.1 +send "$PWD1\n" +expect timeout abort "No key available with this passphrase." +expect timeout abort "Enter passphrase for $EXPECT_DEV:" +sleep 0.1 +send "$PWD0 y\n" +expect timeout abort "No key available with this passphrase." +expect timeout abort eof +eval spawn $CRYPTSETUP_RAW luksResume -v $DEV_NAME +expect timeout abort "Enter passphrase for $EXPECT_DEV:" +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 + +prepare "[40] Long passphrase from TTY." wipe +EXPECT_DEV=$(losetup $LOOPDEV | sed -e "s/.*(\(.*\))/\1/") + +# Password of maximal length 512 characters +LONG_PWD=\ +"0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF"\ +"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "\ +"eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut e"\ +"nim ad minim veniam, quis nostrud exercitation ullamco laboris n"\ +"isi ut aliquip ex ea commodo consequat. Duis aute irure dolor in"\ +" reprehenderit in voluptate velit esse cillum dolore eu fugiat n"\ +"ulla pariatur. Excepteur sint occaecat cupidatat non proident, s"\ +"unt in culpa qui officia deserunt mollit anim id est laborum.DEF" + +echo -n "$LONG_PWD" >$KEYE + +expect_run - >/dev/null <<EOF +proc abort {} { send_error "Timeout. "; exit 2 } +set timeout $EXPECT_TIMEOUT +eval spawn $CRYPTSETUP_RAW luksFormat --type luks1 $FAST_PBKDF_OPT -v $LOOPDEV +expect timeout abort "Are you sure? (Type 'yes' in capital letters):" +send "YES\n" +expect timeout abort "Enter passphrase for $EXPECT_DEV:" +sleep 0.1 +send "$LONG_PWD\n" +expect timeout abort "Verify passphrase:" +sleep 0.1 +send "$LONG_PWD\n" +expect timeout abort "Command successful." +expect timeout abort eof +eval spawn $CRYPTSETUP_RAW luksOpen -v $LOOPDEV --test-passphrase --key-file $KEYE +expect timeout abort "Command successful." +expect timeout abort eof +EOF +[ $? -eq 0 ] || fail "Expect script failed." + +remove_mapping +exit 0 diff --git a/tests/compat-test2 b/tests/compat-test2 new file mode 100755 index 0000000..1612569 --- /dev/null +++ b/tests/compat-test2 @@ -0,0 +1,1055 @@ +#!/bin/bash + +PS4='$LINENO:' +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/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" +TEST_TOKEN2="compattest2_desc2" +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 +KEY_FILE0=test-key-file0 +KEY_FILE1=test-key-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) + +function remove_mapping() +{ + [ -b /dev/mapper/$DEV_NAME3 ] && dmsetup remove --retry $DEV_NAME3 + [ -b /dev/mapper/$DEV_NAME2 ] && dmsetup remove --retry $DEV_NAME2 + [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove --retry $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_* $KEY_FILE0 $KEY_FILE1 >/dev/null 2>&1 + + # unlink whole test keyring + [ -n "$TEST_KEYRING" ] && keyctl unlink $TEST_KEYRING "@u" >/dev/null + unset TEST_KEYRING + + rmmod scsi_debug 2> /dev/null + scsi_debug_teardown $DEV +} + +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 backtrace:" + while caller $frame; do ((frame++)); done + 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 --retry $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.) + + test -d /proc/sys/kernel/keys || return 1 + + [ $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 dm_crypt_sector_size_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.) + + if [ $VER_MIN -ge 17 -o \( $VER_MIN -eq 14 -a $VER_PTC -ge 5 \) ]; then + return 0 + fi + + 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 +} + +function setup_luks2_env() { + echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 $FAST_PBKDF_OPT $LOOPDEV || fail + $CRYPTSETUP luksDump $LOOPDEV >/dev/null || fail + echo $PWD1 | $CRYPTSETUP open $LOOPDEV $DEV_NAME || fail + HAVE_KEYRING=$($CRYPTSETUP status $DEV_NAME | grep "keyring") + if [ -n "$HAVE_KEYRING" ]; then + HAVE_KEYRING=1 + else + HAVE_KEYRING=0 + fi + $CRYPTSETUP close $DEV_NAME || fail +} + +# $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 2> /dev/null +} + +function add_scsi_device() { + scsi_debug_teardown $DEV + 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 1 + DEV="/dev/"$(grep -l -e scsi_debug /sys/block/*/device/model | cut -f4 -d /) + [ -b $DEV ] || fail "Cannot find $DEV." +} + +export LANG=C + +[ $(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." + +prepare "[0] Detect LUKS2 environment" wipe +setup_luks2_env + +[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run + +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 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 + if [ $HAVE_KEYRING -gt 0 -a -d /proc/sys/kernel/keys ]; then + test_and_prepare_keyring + load_key user $TEST_TOKEN2 $PWD1 "$TEST_KEYRING" || skip "Kernel keyring service is useless on this system, test skipped." + $CRYPTSETUP token add $LOOPDEV --key-description $TEST_TOKEN2 --token-id 1 || fail + $CRYPTSETUP -q resize --size 99 $DEV_NAME <&- || fail + $CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "99 sectors" || fail + #replace kernel key with wrong pass + load_key user $TEST_TOKEN2 $PWD2 "$TEST_KEYRING" || skip "Kernel keyring service is useless on this system, test skipped." + # must fail due to --token-only + echo $PWD1 | $CRYPTSETUP -q resize --token-only --size 100 $DEV_NAME && fail + $CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "100 sectors" && fail + fi +fi +echo $PWD1 | $CRYPTSETUP -q resize --size 100 $DEV_NAME || fail +$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "100 sectors" || fail +echo $PWD1 | $CRYPTSETUP -q resize --device-size 51200 $DEV_NAME || fail +$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "100 sectors" || fail +echo $PWD1 | $CRYPTSETUP -q resize --device-size 1M $DEV_NAME || fail +$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "2048 sectors" || fail +echo $PWD1 | $CRYPTSETUP -q resize --device-size 512k --size 1024 $DEV_NAME > /dev/null 2>&1 && fail +echo $PWD1 | $CRYPTSETUP -q resize --device-size 4097 $DEV_NAME > /dev/null 2>&1 && fail +$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "2048 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 +if dm_crypt_sector_size_support; then + $CRYPTSETUP close $DEV_NAME || fail + echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 --sector-size 4096 $LOOPDEV > /dev/null || fail + echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail + echo $PWD1 | $CRYPTSETUP -q resize --device-size 1M $DEV_NAME || fail + $CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "2048 sectors" || fail + echo $PWD1 | $CRYPTSETUP -q resize --device-size 2049s $DEV_NAME > /dev/null 2>&1 && fail + echo $PWD1 | $CRYPTSETUP -q resize --size 2049 $DEV_NAME > /dev/null 2>&1 && fail + $CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "2048 sectors" || fail +fi +$CRYPTSETUP close $DEV_NAME || fail +# Resize not aligned to logical block size +add_scsi_device dev_size_mb=32 sector_size=4096 +echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 $FAST_PBKDF_OPT $DEV || fail +echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail +OLD_SIZE=$($CRYPTSETUP status $DEV_NAME | grep "^ \+size:" | sed 's/.* \([0-9]\+\) .*/\1/') +echo $PWD1 | $CRYPTSETUP resize $DEV_NAME -b 7 2> /dev/null && fail +dmsetup info $DEV_NAME | grep -q SUSPENDED && fail +NEW_SIZE=$($CRYPTSETUP status $DEV_NAME | grep "^ \+size:" | sed 's/.* \([0-9]\+\) .*/\1/') +test $OLD_SIZE -eq $NEW_SIZE || fail +$CRYPTSETUP close $DEV_NAME || fail + +prepare "[20] Disallow open/create if already mapped." wipe +$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 || 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 --key-size 256 $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:" || fail +$CRYPTSETUP luksDump -q $LOOPDEV --dump-master-key -d $KEY1 | grep -q "MK dump:" || fail +echo $PWD1 | $CRYPTSETUP luksDump -q $LOOPDEV --dump-master-key --master-key-file $VK_FILE >/dev/null || fail +echo $PWD1 | $CRYPTSETUP luksDump -q $LOOPDEV --dump-master-key --master-key-file $VK_FILE 2>/dev/null && fail +echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT --master-key-file $VK_FILE $LOOPDEV || fail +# Use volume key file without keyslots +$CRYPTSETUP luksErase -q $LOOPDEV || fail +$CRYPTSETUP luksOpen --master-key-file $VK_FILE --key-size 256 --test-passphrase $LOOPDEV || fail +echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT --master-key-file $VK_FILE --key-size 256 $LOOPDEV || fail +echo $PWD1 | $CRYPTSETUP luksOpen --test-passphrase $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 --retry $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 "[26] Suspend/Resume" wipe +# 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 status $DEV_NAME | grep -q "(suspended)" || 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 +echo $PWD1 | $CRYPTSETUP -q luksFormat -c null $FAST_PBKDF_OPT --type luks2 $LOOPDEV || fail +echo $PWD1 | $CRYPTSETUP -q luksOpen $LOOPDEV $DEV_NAME || fail +$CRYPTSETUP luksSuspend $DEV_NAME || fail +$CRYPTSETUP -q status $DEV_NAME | grep -q "(suspended)" || fail +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 2>/dev/null && 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 2>/dev/null && fail +[ -b /dev/mapper/$DEV_NAME ] && fail +echo $PWD1 | $CRYPTSETUP open $HEADER_KEYU $DEV_NAME 2>/dev/null && 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 2>/dev/null && 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-missing --header $HEADER_IMG $DEV_NAME 2>/dev/null && 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 2>/dev/null && 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 +echo $PWD1 | $CRYPTSETUP open --test-passphrase $HEADER_IMG || fail +rm $HEADER_IMG || fail +# create exactly 16 MiBs LUKS2 header +echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --header $HEADER_IMG --luks2-keyslots-size 16352k --luks2-metadata-size 16k --offset 131072 >/dev/null || fail +SIZE=$(stat --printf=%s $HEADER_IMG) +test $SIZE -eq 16777216 || fail +$CRYPTSETUP -q luksDump $HEADER_IMG | grep -q "offset: $((512 * 131072)) \[bytes\]" || 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 --sector-size 512 $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 +# sector size test +$CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 --sector-size 1024 $LOOPDEV $KEY5 || fail +$CRYPTSETUP -q convert --type luks1 $LOOPDEV >/dev/null 2>&1 && fail + +# create LUKS1 with data offset not aligned to 4KiB +$CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks1 $LOOPDEV $KEY5 --align-payload 4097 || fail +$CRYPTSETUP -q convert --type luks2 $LOOPDEV || fail +$CRYPTSETUP isLuks --type luks2 $LOOPDEV || 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 -a -d /proc/sys/kernel/keys ]; 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 +dd if=/dev/urandom of=$KEY_FILE0 bs=64 count=1 > /dev/null 2>&1 || fail +echo $PWD3 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT --unbound -s 512 -S 3 --master-key-file $KEY_FILE0 $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 +echo $PWD3 | $CRYPTSETUP luksDump --unbound --master-key-file $KEY_FILE1 $LOOPDEV 2> /dev/null && fail +echo $PWD3 | $CRYPTSETUP luksDump --unbound 2> /dev/null $LOOPDEV 2> /dev/null && fail +echo $PWD3 | $CRYPTSETUP luksDump --unbound --master-key-file $KEY_FILE1 -S3 $LOOPDEV > /dev/null || fail +diff $KEY_FILE0 $KEY_FILE1 || fail +echo $PWD3 | $CRYPTSETUP luksDump --unbound --master-key-file $KEY_FILE1 -S3 $LOOPDEV 2> /dev/null && fail +diff $KEY_FILE0 $KEY_FILE1 || fail +rm $KEY_FILE1 || fail +echo $PWD3 | $CRYPTSETUP luksDump --unbound --master-key-file $KEY_FILE1 -S3 $LOOPDEV | grep -q "Unbound Key:" && fail +echo $PWD3 | $CRYPTSETUP luksDump --unbound -S3 $LOOPDEV | grep -q "Unbound Key:" || fail +$CRYPTSETUP luksKillSlot -q $LOOPDEV 3 || fail +$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 + +prepare "[42] Some encryption compatibility mode tests" wipe +CIPHERS="aes-ecb aes-cbc-null aes-cbc-plain64 aes-cbc-essiv:sha256 aes-xts-plain64" +key_size=256 +for cipher in $CIPHERS ; do + echo -n "[$cipher/$key_size]" + $CRYPTSETUP -q luksFormat --type luks2 $LOOPDEV $KEY1 $FAST_PBKDF_OPT --cipher $cipher --key-size $key_size || fail +done +echo + +remove_mapping +exit 0 diff --git a/tests/compatimage.img.xz b/tests/compatimage.img.xz Binary files differnew file mode 100644 index 0000000..37fe163 --- /dev/null +++ b/tests/compatimage.img.xz diff --git a/tests/compatimage2.img.xz b/tests/compatimage2.img.xz Binary files differnew file mode 100644 index 0000000..ceaeafc --- /dev/null +++ b/tests/compatimage2.img.xz diff --git a/tests/compatv10image.img.xz b/tests/compatv10image.img.xz Binary files differnew file mode 100644 index 0000000..2203626 --- /dev/null +++ b/tests/compatv10image.img.xz diff --git a/tests/conversion_imgs.tar.xz b/tests/conversion_imgs.tar.xz Binary files differnew file mode 100644 index 0000000..cdeb961 --- /dev/null +++ b/tests/conversion_imgs.tar.xz diff --git a/tests/crypto-vectors.c b/tests/crypto-vectors.c new file mode 100644 index 0000000..004e426 --- /dev/null +++ b/tests/crypto-vectors.c @@ -0,0 +1,1272 @@ +/* + * cryptsetup crypto backend test vectors + * + * Copyright (C) 2018-2021 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" + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + +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; +}; + +static 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_test_vector { + const char *data; + unsigned int data_length; + struct { + const char *name; + unsigned int length; + const char *out; + } out[8]; +}; + +static struct hash_test_vector hash_test_vectors[] = { +{ + "", 0, { + { "crc32", 4, "\x00\x00\x00\x00" }, + { "sha1", 20, "\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18\x90\xaf\xd8\x07\x09" }, + { "sha256", 32, "\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" }, + { "sha512", 64, "\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" }, + { "ripemd160", 20, "\x9c\x11\x85\xa5\xc5\xe9\xfc\x54\x61\x28\x08\x97\x7e\xe8\xf5\x48\xb2\x25\x8d\x31" }, + { "whirlpool", 64, "\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" }, + { "blake2b-512",64,"\x78\x6a\x02\xf7\x42\x01\x59\x03\xc6\xc6\xfd\x85\x25\x52\xd2\x72" + "\x91\x2f\x47\x40\xe1\x58\x47\x61\x8a\x86\xe2\x17\xf7\x1f\x54\x19" + "\xd2\x5e\x10\x31\xaf\xee\x58\x53\x13\x89\x64\x44\x93\x4e\xb0\x4b" + "\x90\x3a\x68\x5b\x14\x48\xb7\x55\xd5\x6f\x70\x1a\xfe\x9b\xe2\xce" }, + { "blake2s-256",32,"\x69\x21\x7a\x30\x79\x90\x80\x94\xe1\x11\x21\xd0\x42\x35\x4a\x7c" + "\x1f\x55\xb6\x48\x2c\xa1\xa5\x1e\x1b\x25\x0d\xfd\x1e\xd0\xee\xf9" }, +}},{ + "a", 1, { + { "crc32", 4, "\xe8\xb7\xbe\x43" }, + { "sha1", 20, "\x86\xf7\xe4\x37\xfa\xa5\xa7\xfc\xe1\x5d\x1d\xdc\xb9\xea\xea\xea\x37\x76\x67\xb8" }, + { "sha256", 32, "\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" }, + { "sha512", 64, "\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" }, + { "ripemd160", 20, "\x0b\xdc\x9d\x2d\x25\x6b\x3e\xe9\xda\xae\x34\x7b\xe6\xf4\xdc\x83\x5a\x46\x7f\xfe" }, + { "whirlpool", 64, "\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" }, + { "blake2b-512",64,"\x33\x3f\xcb\x4e\xe1\xaa\x7c\x11\x53\x55\xec\x66\xce\xac\x91\x7c" + "\x8b\xfd\x81\x5b\xf7\x58\x7d\x32\x5a\xec\x18\x64\xed\xd2\x4e\x34" + "\xd5\xab\xe2\xc6\xb1\xb5\xee\x3f\xac\xe6\x2f\xed\x78\xdb\xef\x80" + "\x2f\x2a\x85\xcb\x91\xd4\x55\xa8\xf5\x24\x9d\x33\x08\x53\xcb\x3c" }, + { "blake2s-256",32,"\x4a\x0d\x12\x98\x73\x40\x30\x37\xc2\xcd\x9b\x90\x48\x20\x36\x87" + "\xf6\x23\x3f\xb6\x73\x89\x56\xe0\x34\x9b\xd4\x32\x0f\xec\x3e\x90" }, +}},{ + "abc", 3, { + { "crc32", 4, "\x35\x24\x41\xc2" }, + { "sha1", 20, "\xa9\x99\x3e\x36\x47\x06\x81\x6a\xba\x3e\x25\x71\x78\x50\xc2\x6c\x9c\xd0\xd8\x9d" }, + { "sha256", 32, "\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" }, + { "sha512", 64, "\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" }, + { "ripemd160", 20, "\x8e\xb2\x08\xf7\xe0\x5d\x98\x7a\x9b\x04\x4a\x8e\x98\xc6\xb0\x87\xf1\x5a\x0b\xfc" }, + { "whirlpool", 64, "\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" }, + { "blake2b-512",64,"\xba\x80\xa5\x3f\x98\x1c\x4d\x0d\x6a\x27\x97\xb6\x9f\x12\xf6\xe9" + "\x4c\x21\x2f\x14\x68\x5a\xc4\xb7\x4b\x12\xbb\x6f\xdb\xff\xa2\xd1" + "\x7d\x87\xc5\x39\x2a\xab\x79\x2d\xc2\x52\xd5\xde\x45\x33\xcc\x95" + "\x18\xd3\x8a\xa8\xdb\xf1\x92\x5a\xb9\x23\x86\xed\xd4\x00\x99\x23" }, + { "blake2s-256",32,"\x50\x8c\x5e\x8c\x32\x7c\x14\xe2\xe1\xa7\x2b\xa3\x4e\xeb\x45\x2f" + "\x37\x45\x8b\x20\x9e\xd6\x3a\x29\x4d\x99\x9b\x4c\x86\x67\x59\x82" }, +}},{ + "abcdefghijklmnopqrstuvwxyz", 26, { + { "crc32", 4, "\x4c\x27\x50\xbd" }, + { "sha1", 20, "\x32\xd1\x0c\x7b\x8c\xf9\x65\x70\xca\x04\xce\x37\xf2\xa1\x9d\x84\x24\x0d\x3a\x89" }, + { "sha256", 32, "\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" }, + { "sha512", 64, "\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" }, + { "ripemd160", 20, "\xf7\x1c\x27\x10\x9c\x69\x2c\x1b\x56\xbb\xdc\xeb\x5b\x9d\x28\x65\xb3\x70\x8d\xbc" }, + { "whirlpool", 64, "\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" }, + { "blake2b-512",64,"\xc6\x8e\xde\x14\x3e\x41\x6e\xb7\xb4\xaa\xae\x0d\x8e\x48\xe5\x5d" + "\xd5\x29\xea\xfe\xd1\x0b\x1d\xf1\xa6\x14\x16\x95\x3a\x2b\x0a\x56" + "\x66\xc7\x61\xe7\xd4\x12\xe6\x70\x9e\x31\xff\xe2\x21\xb7\xa7\xa7" + "\x39\x08\xcb\x95\xa4\xd1\x20\xb8\xb0\x90\xa8\x7d\x1f\xbe\xdb\x4c" }, + { "blake2s-256",32,"\xbd\xf8\x8e\xb1\xf8\x6a\x0c\xdf\x0e\x84\x0b\xa8\x8f\xa1\x18\x50" + "\x83\x69\xdf\x18\x6c\x73\x55\xb4\xb1\x6c\xf7\x9f\xa2\x71\x0a\x12" }, +}},{ + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62, { + { "crc32", 4, "\x1f\xc2\xe6\xd2" }, + { "sha1", 20, "\x76\x1c\x45\x7b\xf7\x3b\x14\xd2\x7e\x9e\x92\x65\xc4\x6f\x4b\x4d\xda\x11\xf9\x40" }, + { "sha256", 32, "\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" }, + { "sha512", 64, "\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" }, + { "ripemd160", 20, "\xb0\xe2\x0b\x6e\x31\x16\x64\x02\x86\xed\x3a\x87\xa5\x71\x30\x79\xb2\x1f\x51\x89" }, + { "whirlpool", 64, "\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" }, + { "blake2b-512",64,"\x99\x96\x48\x02\xe5\xc2\x5e\x70\x37\x22\x90\x5d\x3f\xb8\x00\x46" + "\xb6\xbc\xa6\x98\xca\x9e\x2c\xc7\xe4\x9b\x4f\xe1\xfa\x08\x7c\x2e" + "\xdf\x03\x12\xdf\xbb\x27\x5c\xf2\x50\xa1\xe5\x42\xfd\x5d\xc2\xed" + "\xd3\x13\xf9\xc4\x91\x12\x7c\x2e\x8c\x0c\x9b\x24\x16\x8e\x2d\x50" }, + { "blake2s-256",32,"\xc7\x54\x39\xea\x17\xe1\xde\x6f\xa4\x51\x0c\x33\x5d\xc3\xd3\xf3" + "\x43\xe6\xf9\xe1\xce\x27\x73\xe2\x5b\x41\x74\xf1\xdf\x8b\x11\x9b" }, +}},{ + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, { + { "crc32", 4, "\x17\x1a\x3f\x5f" }, + { "sha1", 20, "\x84\x98\x3e\x44\x1c\x3b\xd2\x6e\xba\xae\x4a\xa1\xf9\x51\x29\xe5\xe5\x46\x70\xf1" }, + { "sha256", 32, "\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" }, + { "sha512", 64, "\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" }, + { "ripemd160", 20, "\x12\xa0\x53\x38\x4a\x9c\x0c\x88\xe4\x05\xa0\x6c\x27\xdc\xf4\x9a\xda\x62\xeb\x2b" }, + { "whirlpool", 64, "\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" }, + { "blake2b-512",64,"\x72\x85\xff\x3e\x8b\xd7\x68\xd6\x9b\xe6\x2b\x3b\xf1\x87\x65\xa3" + "\x25\x91\x7f\xa9\x74\x4a\xc2\xf5\x82\xa2\x08\x50\xbc\x2b\x11\x41" + "\xed\x1b\x3e\x45\x28\x59\x5a\xcc\x90\x77\x2b\xdf\x2d\x37\xdc\x8a" + "\x47\x13\x0b\x44\xf3\x3a\x02\xe8\x73\x0e\x5a\xd8\xe1\x66\xe8\x88" }, + { "blake2s-256",32,"\x6f\x4d\xf5\x11\x6a\x6f\x33\x2e\xda\xb1\xd9\xe1\x0e\xe8\x7d\xf6" + "\x55\x7b\xea\xb6\x25\x9d\x76\x63\xf3\xbc\xd5\x72\x2c\x13\xf1\x89" }, +}},{ + "message digest", 14, { + { "crc32", 4, "\x20\x15\x9d\x7f" }, + { "sha1", 20, "\xc1\x22\x52\xce\xda\x8b\xe8\x99\x4d\x5f\xa0\x29\x0a\x47\x23\x1c\x1d\x16\xaa\xe3" }, + { "sha256", 32, "\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" }, + { "sha512", 64, "\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" }, + { "ripemd160", 20, "\x5d\x06\x89\xef\x49\xd2\xfa\xe5\x72\xb8\x81\xb1\x23\xa8\x5f\xfa\x21\x59\x5f\x36" }, + { "whirlpool", 64, "\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" }, + { "blake2b-512",64,"\x3c\x26\xce\x48\x7b\x1c\x0f\x06\x23\x63\xaf\xa3\xc6\x75\xeb\xdb" + "\xf5\xf4\xef\x9b\xdc\x02\x2c\xfb\xef\x91\xe3\x11\x1c\xdc\x28\x38" + "\x40\xd8\x33\x1f\xc3\x0a\x8a\x09\x06\xcf\xf4\xbc\xdb\xcd\x23\x0c" + "\x61\xaa\xec\x60\xfd\xfa\xd4\x57\xed\x96\xb7\x09\xa3\x82\x35\x9a" }, + { "blake2s-256",32,"\xfa\x10\xab\x77\x5a\xcf\x89\xb7\xd3\xc8\xa6\xe8\x23\xd5\x86\xf6" + "\xb6\x7b\xdb\xac\x4c\xe2\x07\xfe\x14\x5b\x7d\x3a\xc2\x5c\xd2\x8c" }, +}}}; + +/* + * 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; + struct { + const char *name; + unsigned int length; + const char *out; + } out[3]; +}; + +static 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" */ { + { "sha1", 20, "\xb6\x17\x31\x86\x55\x05\x72\x64\xe2\x8b\xc0\xb6\xfb\x37\x8c\x8e\xf1\x46\xbe\x00" }, + { "sha256", 32, "\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" }, + { "sha512", 64, "\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?" */ { + { "sha1", 20, "\xef\xfc\xdf\x6a\xe5\xeb\x2f\xa2\xd2\x74\x16\xd5\xf1\x84\xdf\x9c\x25\x9a\x7c\x79" }, + { "sha256", 32, "\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" }, + { "sha512", 64, "\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, { + { "sha1", 20, "\x12\x5d\x73\x42\xb9\xac\x11\xcd\x91\xa3\x9a\xf4\x8a\xa1\x7b\x4f\x63\xf1\x75\xd3" }, + { "sha256", 32, "\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" }, + { "sha512", 64, "\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, { + { "sha1", 20, "\x4c\x90\x07\xf4\x02\x62\x50\xc6\xbc\x84\x14\xf9\xbf\x50\xc8\x6c\x2d\x72\x35\xda" }, + { "sha256", 32, "\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" }, + { "sha512", 64, "\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" */ { + { "sha1", 20, "\x90\xd0\xda\xce\x1c\x1b\xdc\x95\x73\x39\x30\x78\x03\x16\x03\x35\xbd\xe6\xdf\x2b" }, + { "sha256", 32, "\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" }, + { "sha512", 64, "\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, { + { "sha1", 20, "\x21\x7e\x44\xbb\x08\xb6\xe0\x6a\x2d\x6c\x30\xf3\xcb\x9f\x53\x7f\x97\xc6\x33\x56" }, + { "sha256", 32, "\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" }, + { "sha512", 64, "\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" }, +}}}; + +/* + * Block cipher tests + */ +struct cipher_test_vector { + const char *key; + unsigned int key_length; + const char *iv; + unsigned int iv_length; + const char *plaintext; + unsigned int data_length; + struct { + const char *name; + const char *mode; + const char *ciphertext; + } out[2]; +}; + +static struct cipher_test_vector cipher_test_vectors[] = { +{ // NIST SP 800-38A + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", 16, + NULL, 0, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" + "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" + "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10", 64, { + { + "aes", "ecb", + "\x3a\xd7\x7b\xb4\x0d\x7a\x36\x60\xa8\x9e\xca\xf3\x24\x66\xef\x97" + "\xf5\xd3\xd5\x85\x03\xb9\x69\x9d\xe7\x85\x89\x5a\x96\xfd\xba\xaf" + "\x43\xb1\xcd\x7f\x59\x8e\xce\x23\x88\x1b\x00\xe3\xed\x03\x06\x88" + "\x7b\x0c\x78\x5e\x27\xe8\xad\x3f\x82\x23\x20\x71\x04\x72\x5d\xd4" + },{ + "serpent", "ecb", + "\xf7\xa7\x21\xe6\xc7\x56\xb6\x55\xcb\xdf\x53\x3f\xc3\xb3\x1a\xc4" + "\x4b\xc6\x04\x29\x3a\x81\xa6\xa6\xe4\xcb\xa7\x8d\x1a\x32\xa2\x9e" + "\xcf\xc2\x8e\x50\x97\xdd\x6b\x49\xa9\x38\xb1\x51\x5e\xbc\x5a\xac" + "\xfe\xd2\xc4\x95\x92\xf9\x1c\x0c\x9f\x17\xcd\x86\x38\x65\x29\xeb" + }, +}},{ // NIST SP 800-38A + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", 16, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" + "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" + "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10", 64, { + { + "aes", "cbc", + "\x76\x49\xab\xac\x81\x19\xb2\x46\xce\xe9\x8e\x9b\x12\xe9\x19\x7d" + "\x50\x86\xcb\x9b\x50\x72\x19\xee\x95\xdb\x11\x3a\x91\x76\x78\xb2" + "\x73\xbe\xd6\xb8\xe3\xc1\x74\x3b\x71\x16\xe6\x9e\x22\x22\x95\x16" + "\x3f\xf1\xca\xa1\x68\x1f\xac\x09\x12\x0e\xca\x30\x75\x86\xe1\xa7" + },{ + "serpent", "cbc", + "\xdd\x73\x69\x1a\xb5\x66\xb6\x38\xe3\xb9\x62\x36\xc8\xc8\xa1\xdd" + "\xa9\xb5\xd9\xdb\x20\xfb\x8b\x82\x51\x40\xbf\xe6\x4d\xf2\x1c\xa8" + "\x5f\x48\xbc\x29\xff\x62\x27\xda\x09\x7c\xaa\x22\x75\x6f\x43\xff" + "\x31\xd8\x3e\x83\x4d\x92\x48\xeb\x49\x1c\xf8\x26\x80\x4e\xb9\x02" + }, +}},{ // NIST SP 800-38A + "\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81" + "\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4", 32, + NULL, 0, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" + "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" + "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10", 64, { + { + "aes", "ecb", + "\xf3\xee\xd1\xbd\xb5\xd2\xa0\x3c\x06\x4b\x5a\x7e\x3d\xb1\x81\xf8" + "\x59\x1c\xcb\x10\xd4\x10\xed\x26\xdc\x5b\xa7\x4a\x31\x36\x28\x70" + "\xb6\xed\x21\xb9\x9c\xa6\xf4\xf9\xf1\x53\xe7\xb1\xbe\xaf\xed\x1d" + "\x23\x30\x4b\x7a\x39\xf9\xf3\xff\x06\x7d\x8d\x8f\x9e\x24\xec\xc7" + },{ + "serpent", "ecb", + "\x78\xe5\x84\x8e\xd9\xd5\xde\x2d\x4d\xb0\x2f\x53\x61\x6a\xfd\xf2" + "\x50\x5d\xf1\x68\x92\x40\x8e\xf6\x9c\x3b\x9e\xa6\x67\xd9\xdd\xb8" + "\xb9\x5f\xc8\x20\x76\x52\x1d\xce\x60\xe4\xfc\xac\xe3\xd3\x91\x51" + "\x09\x22\x62\xde\x62\x6d\xc5\x7b\x4c\x87\x0c\x65\xe7\x1f\xc7\x13" + }, +}},{ // NIST SP 800-38A + "\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81" + "\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4", 32, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51" + "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef" + "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10", 64, { + { + "aes", "cbc", + "\xf5\x8c\x4c\x04\xd6\xe5\xf1\xba\x77\x9e\xab\xfb\x5f\x7b\xfb\xd6" + "\x9c\xfc\x4e\x96\x7e\xdb\x80\x8d\x67\x9f\x77\x7b\xc6\x70\x2c\x7d" + "\x39\xf2\x33\x69\xa9\xd9\xba\xcf\xa5\x30\xe2\x63\x04\x23\x14\x61" + "\xb2\xeb\x05\xe2\xc3\x9b\xe9\xfc\xda\x6c\x19\x07\x8c\x6a\x9d\x1b" + },{ + "serpent", "cbc", + "\xb8\x93\xc8\xde\xc5\xc8\x5f\x03\x01\xac\x32\x74\xdf\xc6\x71\x9d" + "\x37\x61\xc5\xf8\x34\x4d\xe9\x10\x91\xd3\x87\x80\x42\xcc\x70\x95" + "\x40\x95\xa3\x2c\xdb\x38\xe2\x6f\x03\x91\xf5\xd3\x51\x7e\x52\xb0" + "\x8a\x1c\x2d\x7f\x04\x59\x13\x93\x31\xa9\x82\xc9\x4e\xd9\x11\x0c" + }, +}},{ // CAVS XTSGenAES128,101 + "\xb7\xb9\x3f\x51\x6a\xef\x29\x5e\xff\x3a\x29\xd8\x37\xcf\x1f\x13" + "\x53\x47\xe8\xa2\x1d\xae\x61\x6f\xf5\x06\x2b\x2e\x8d\x78\xce\x5e", 32, + "\x87\x3e\xde\xa6\x53\xb6\x43\xbd\x8b\xcf\x51\x40\x31\x97\xed\x14", 16, + "\x23\x6f\x8a\x5b\x58\xdd\x55\xf6\x19\x4e\xd7\x0c\x4a\xc1\xa1\x7f" + "\x1f\xe6\x0e\xc9\xa6\xc4\x54\xd0\x87\xcc\xb7\x7d\x6b\x63\x8c\x47", 32, { + { + "aes", "xts", + "\x22\xe6\xa3\xc6\x37\x9d\xcf\x75\x99\xb0\x52\xb5\xa7\x49\xc7\xf7" + "\x8a\xd8\xa1\x1b\x9f\x1a\xa9\x43\x0c\xf3\xae\xf4\x45\x68\x2e\x19" + },{ + "serpent", "xts", + "\x6d\xa2\xa4\x2b\x18\x71\x57\xdc\x03\xaf\x8b\x82\x28\x66\x3d\xf1" + "\x70\x8b\x75\x98\xd2\xdd\xbf\x72\x9e\xb3\xb4\xc2\x3f\x18\xdf\xa1" + }, +}},{ // CAVS XTSGenAES256,101 + "\x26\x6c\x33\x6b\x3b\x01\x48\x9f\x32\x67\xf5\x28\x35\xfd\x92\xf6" + "\x74\x37\x4b\x88\xb4\xe1\xeb\xd2\xd3\x6a\x5f\x45\x75\x81\xd9\xd0" + "\x42\xc3\xee\xf7\xb0\xb7\xe5\x13\x7b\x08\x64\x96\xb4\xd9\xe6\xac" + "\x65\x8d\x71\x96\xa2\x3f\x23\xf0\x36\x17\x2f\xdb\x8f\xae\xe5\x27", 64, + "\x06\xb2\x09\xa7\xa2\x2f\x48\x6e\xcb\xfa\xdb\x0f\x31\x37\xba\x42", 16, + "\xca\x7d\x65\xef\x8d\x3d\xfa\xd3\x45\xb6\x1c\xcd\xdc\xa1\xad\x81" + "\xde\x83\x0b\x9e\x86\xc7\xb4\x26\xd7\x6c\xb7\xdb\x76\x68\x52\xd9" + "\x81\xc6\xb2\x14\x09\x39\x9d\x78\xf4\x2c\xc0\xb3\x3a\x7b\xbb\x06", 48, { + { + "aes", "xts", + "\xc7\x32\x56\x87\x0c\xc2\xf4\xdd\x57\xac\xc7\x4b\x54\x56\xdb\xd7" + "\x76\x91\x2a\x12\x8b\xc1\xf7\x7d\x72\xcd\xeb\xbf\x27\x00\x44\xb7" + "\xa4\x3c\xee\xd2\x90\x25\xe1\xe8\xbe\x21\x1f\xa3\xc3\xed\x00\x2d" + },{ + "serpent", "xts", + "\x37\xe4\xc0\xa9\xf1\x49\xe5\x3e\x73\xb9\x1f\xec\xdc\xe0\xbd\xc5" + "\x31\xd7\xef\x08\x65\x20\xe3\xad\xd9\x84\x60\xdc\x61\x6f\x26\x86" + "\xb8\xd5\x29\x4b\x04\x41\x52\x59\x05\x00\xb0\xc2\x9b\x30\xda\x48" + }, +}},{ + "\xa5\x28\x24\x34\x1a\x3c\xd8\xf7\x05\x91\x8f\xee\x85\x1f\x35\x7f" + "\x80\x3d\xfc\x9b\x94\xf6\xfc\x9e\x19\x09\x00\xa9\x04\x31\x4f\x11", 32, + "\xa1\xba\x49\x95\xff\x34\x6d\xb8\xcd\x87\x5d\x5e\xfd\xea\x85\xdb" + "\x8a\x7b\x5e\xb2\x5d\x57\xdd\x62\xac\xa9\x8c\x41\x42\x94\x75\xb7", 32, + "\x69\xb4\xe8\x8c\x37\xe8\x67\x82\xf1\xec\x5d\x04\xe5\x14\x91\x13" + "\xdf\xf2\x87\x1b\x69\x81\x1d\x71\x70\x9e\x9c\x3b\xde\x49\x70\x11" + "\xa0\xa3\xdb\x0d\x54\x4f\x66\x69\xd7\xdb\x80\xa7\x70\x92\x68\xce" + "\x81\x04\x2c\xc6\xab\xae\xe5\x60\x15\xe9\x6f\xef\xaa\x8f\xa7\xa7" + "\x63\x8f\xf2\xf0\x77\xf1\xa8\xea\xe1\xb7\x1f\x9e\xab\x9e\x4b\x3f" + "\x07\x87\x5b\x6f\xcd\xa8\xaf\xb9\xfa\x70\x0b\x52\xb8\xa8\xa7\x9e" + "\x07\x5f\xa6\x0e\xb3\x9b\x79\x13\x79\xc3\x3e\x8d\x1c\x2c\x68\xc8" + "\x51\x1d\x3c\x7b\x7d\x79\x77\x2a\x56\x65\xc5\x54\x23\x28\xb0\x03", 128, { + { + "xchacha12,aes", "adiantum", + "\x9e\x16\xab\xed\x4b\xa7\x42\x5a\xc6\xfb\x4e\x76\xff\xbe\x03\xa0" + "\x0f\xe3\xad\xba\xe4\x98\x2b\x0e\x21\x48\xa0\xb8\x65\x48\x27\x48" + "\x84\x54\x54\xb2\x9a\x94\x7b\xe6\x4b\x29\xe9\xcf\x05\x91\x80\x1a" + "\x3a\xf3\x41\x96\x85\x1d\x9f\x74\x51\x56\x63\xfa\x7c\x28\x85\x49" + "\xf7\x2f\xf9\xf2\x18\x46\xf5\x33\x80\xa3\x3c\xce\xb2\x57\x93\xf5" + "\xae\xbd\xa9\xf5\x7b\x30\xc4\x93\x66\xe0\x30\x77\x16\xe4\xa0\x31" + "\xba\x70\xbc\x68\x13\xf5\xb0\x9a\xc1\xfc\x7e\xfe\x55\x80\x5c\x48" + "\x74\xa6\xaa\xa3\xac\xdc\xc2\xf5\x8d\xde\x34\x86\x78\x60\x75\x8d", + },{ + "xchacha20,aes", "adiantum", + "\xb1\x8b\xa0\x05\x77\xa8\x4d\x59\x1b\x8e\x21\xfc\x3a\x49\xfa\xd4" + "\xeb\x36\xf3\xc4\xdf\xdc\xae\x67\x07\x3f\x70\x0e\xe9\x66\xf5\x0c" + "\x30\x4d\x66\xc9\xa4\x2f\x73\x9c\x13\xc8\x49\x44\xcc\x0a\x90\x9d" + "\x7c\xdd\x19\x3f\xea\x72\x8d\x58\xab\xe7\x09\x2c\xec\xb5\x44\xd2" + "\xca\xa6\x2d\x7a\x5c\x9c\x2b\x15\xec\x2a\xa6\x69\x91\xf9\xf3\x13" + "\xf7\x72\xc1\xc1\x40\xd5\xe1\x94\xf4\x29\xa1\x3e\x25\x02\xa8\x3e" + "\x94\xc1\x91\x14\xa1\x14\xcb\xbe\x67\x4c\xb9\x38\xfe\xa7\xaa\x32" + "\x29\x62\x0d\xb2\xf6\x3c\x58\x57\xc1\xd5\x5a\xbb\xd6\xa6\x2a\xe5" + }, +}}}; + +/* + * Cipher IV tests + */ +struct cipher_iv_test_vector { + const char *cipher_name; + const char *cipher_mode; + const char *key; + unsigned int key_length; + const char *iv_name; + uint64_t iv_offset; + unsigned int data_length; + const char in_sha256[32]; + struct { + size_t sector_size; + bool large_iv; + const char out_sha256[32]; + } out[7]; +}; + +static struct cipher_iv_test_vector cipher_iv_test_vectors[] = { +{ + "aes", "cbc", + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", 16, + "null", UINT32_MAX-7, 8192, + "\x9f\x1d\xcb\xc3\x5c\x35\x0d\x60\x27\xf9\x8b\xe0\xf5\xc8\xb4\x3b" + "\x42\xca\x52\xb7\x60\x44\x59\xc0\xc4\x2b\xe3\xaa\x88\x91\x3d\x47", { + { 512, false, + "\xfd\x05\xd0\x4d\x51\xb9\xd4\x87\xa4\x57\x9a\x62\x07\x39\xc9\x4a" + "\x00\x90\x3e\xaf\xe8\xb2\xac\x12\xca\xeb\x58\xf9\x48\xf6\xef\x08" + },{ 1024, false, + "\x55\x87\x5c\xde\x86\x6a\x8b\xab\x08\xbe\x5b\x38\x17\x53\xdf\xe5" + "\x7e\xb9\x5f\x59\xaf\x07\xa4\xca\x6a\x24\xd1\x12\xa9\x15\x25\xf4" + },{ 1024, true, + "\x55\x87\x5c\xde\x86\x6a\x8b\xab\x08\xbe\x5b\x38\x17\x53\xdf\xe5" + "\x7e\xb9\x5f\x59\xaf\x07\xa4\xca\x6a\x24\xd1\x12\xa9\x15\x25\xf4" + },{ 2048, false, + "\x55\x5b\x8e\x74\x90\x9d\x0d\x4b\x74\x8c\x16\x7e\x29\xcf\xa9\xa3" + "\xf3\x42\x8b\x62\xda\x2d\x8c\xda\xc9\x32\xc8\x78\xe2\x7e\xd2\x70" + },{ 2048, true, + "\x55\x5b\x8e\x74\x90\x9d\x0d\x4b\x74\x8c\x16\x7e\x29\xcf\xa9\xa3" + "\xf3\x42\x8b\x62\xda\x2d\x8c\xda\xc9\x32\xc8\x78\xe2\x7e\xd2\x70" + },{ 4096, false, + "\xc6\x45\xba\xe0\x40\x3a\x96\x09\x5e\x46\x0d\x19\x9d\x58\x4b\x93" + "\x78\xc5\x3f\xa4\x2e\x9e\xb0\x19\x04\x4b\x73\x26\xf4\xa6\xb5\xc3" + },{ 4096, true, + "\xc6\x45\xba\xe0\x40\x3a\x96\x09\x5e\x46\x0d\x19\x9d\x58\x4b\x93" + "\x78\xc5\x3f\xa4\x2e\x9e\xb0\x19\x04\x4b\x73\x26\xf4\xa6\xb5\xc3" + }, +}}, +{ + "aes", "cbc", + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", 16, + "plain", UINT32_MAX-7, 8192, + "\x9f\x1d\xcb\xc3\x5c\x35\x0d\x60\x27\xf9\x8b\xe0\xf5\xc8\xb4\x3b" + "\x42\xca\x52\xb7\x60\x44\x59\xc0\xc4\x2b\xe3\xaa\x88\x91\x3d\x47", { + { 512, false, + "\x43\xfd\x6e\x25\x80\xb2\x13\xf5\xca\x71\x79\x18\xe4\x12\x91\xe0" + "\x6e\x37\x24\x32\xfd\x40\x4b\x42\xcb\xc1\x72\x1a\xc7\x5a\x19\xc8" + },{ 1024, false, + "\x18\x79\x8d\xad\xf2\x7b\x38\x03\x27\xa5\x76\x19\x07\xcd\x12\x62" + "\x03\x36\x57\x85\x88\x50\xd0\x6c\xf6\xdf\xf1\xcf\xb8\xcf\x01\x77" + },{ 1024, true, + "\xd0\x21\xcf\xb2\x7a\x01\xa8\x94\xb2\x87\x49\xc4\x9f\x9c\xb2\x3a" + "\x7c\xc4\x0d\x50\x08\xea\x4d\xfb\x87\xe4\x49\x8c\x1a\xd6\xec\x16" + },{ 2048, false, + "\xa4\x89\x72\xb9\xcf\x78\x0c\x2a\xc8\x20\x4f\xd5\x13\xcb\x75\x30" + "\x90\xd2\x4a\xfd\xd3\xb2\xe8\xf0\xd2\xb7\x9d\x07\xbd\xa9\x70\x97" + },{ 2048, true, + "\x2a\xcf\x07\x57\xc8\xea\x64\xc7\xd0\xd5\x28\xe6\xd1\x9a\xb5\x7d" + "\xe4\xb9\x63\xa2\x66\x5a\x3d\x14\xbd\x27\xc7\x09\xc0\x3c\xd9\x00" + },{ 4096, false, + "\x12\x1b\x00\x54\x6e\x2d\x08\xc1\x15\x8b\x15\x57\xc5\x11\x30\x8b" + "\x63\x33\x64\xa0\xd1\x45\xd6\xcb\xdd\x49\x91\x04\x29\xe6\x93\x08" + },{ 4096, true, + "\x44\xaa\xf1\x23\x0c\x34\x32\x2a\xfa\xe3\xf7\x95\x7a\x7c\xa8\x8b" + "\x34\x78\xbd\x12\x5c\xae\x4a\x65\x23\x8a\x6f\x3a\x96\x05\xfa\xae" + }, +}}, +{ + "aes", "cbc", + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", 16, + "plain64", UINT32_MAX-7, 8192, + "\x9f\x1d\xcb\xc3\x5c\x35\x0d\x60\x27\xf9\x8b\xe0\xf5\xc8\xb4\x3b" + "\x42\xca\x52\xb7\x60\x44\x59\xc0\xc4\x2b\xe3\xaa\x88\x91\x3d\x47", { + { 512, false, + "\xb3\x65\x7e\x6c\xba\xe0\x39\xcd\x1e\x1d\xaf\x65\xae\xb7\xda\x20" + "\x25\x17\x6a\x38\x75\x79\x68\x4c\x9a\x75\xc7\xfb\x2b\xa2\x17\xd2" + },{ 1024, false, + "\x0a\xa3\x23\x72\x80\xd3\x76\x33\x8b\x2b\xae\x01\x03\x99\xa5\xca" + "\xcd\x95\x27\x40\x27\xec\x14\x90\xfd\x58\xb0\x08\x9b\x99\x27\xe2" + },{ 1024, true, + "\xd0\x21\xcf\xb2\x7a\x01\xa8\x94\xb2\x87\x49\xc4\x9f\x9c\xb2\x3a" + "\x7c\xc4\x0d\x50\x08\xea\x4d\xfb\x87\xe4\x49\x8c\x1a\xd6\xec\x16" + },{ 2048, false, + "\x67\x87\xeb\xed\xe1\x16\x85\x0a\x3f\xb2\x5c\xbc\x27\x61\x99\x52" + "\xfe\x64\xb9\xab\x24\xdd\x2c\x1a\x2c\xff\xcd\x7e\x2e\x74\xb5\xd4" + },{ 2048, true, + "\x2a\xcf\x07\x57\xc8\xea\x64\xc7\xd0\xd5\x28\xe6\xd1\x9a\xb5\x7d" + "\xe4\xb9\x63\xa2\x66\x5a\x3d\x14\xbd\x27\xc7\x09\xc0\x3c\xd9\x00" + },{ 4096, false, + "\xb2\xf1\x0e\x66\xd4\x58\x4e\x93\xe7\x98\xae\x9c\x3e\xa7\xad\xf2" + "\x93\x1a\xaa\x3c\xc4\x90\x12\x05\x00\x58\x25\x8f\x1f\x5d\xc6\x67" + },{ 4096, true, + "\x44\xaa\xf1\x23\x0c\x34\x32\x2a\xfa\xe3\xf7\x95\x7a\x7c\xa8\x8b" + "\x34\x78\xbd\x12\x5c\xae\x4a\x65\x23\x8a\x6f\x3a\x96\x05\xfa\xae" + }, +}}, +{ + "aes", "cbc", + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", 16, + "plain64be", UINT32_MAX-7, 8192, + "\x9f\x1d\xcb\xc3\x5c\x35\x0d\x60\x27\xf9\x8b\xe0\xf5\xc8\xb4\x3b" + "\x42\xca\x52\xb7\x60\x44\x59\xc0\xc4\x2b\xe3\xaa\x88\x91\x3d\x47", { + { 512, false, + "\x28\xbf\x09\xe1\x68\xcc\x05\x1b\x20\xaf\x8d\x01\x36\x21\x8a\x8d" + "\x7a\x94\x98\xa8\x99\xe9\xf4\x66\xd8\xb7\x99\xca\x04\x58\x83\x90" + },{ 1024, false, + "\x9b\x74\xf7\xd5\x5a\x6b\xb2\x3a\xd2\x09\xdd\x80\x59\x28\x70\x8f" + "\x3a\x61\xf2\x14\xc3\x0d\xa8\xd7\xd9\xcb\x57\x26\x73\x88\x93\xd2" + },{ 1024, true, + "\x36\xb5\x68\x08\x29\x55\xb9\xe9\x01\xc1\xa8\xcf\x3e\x5b\x00\x28" + "\xb6\xd1\x35\xc5\xf7\x0c\xf6\x59\xb5\x8f\xb9\xa2\x00\x43\x29\x48" + },{ 2048, false, + "\x94\x4f\xc8\xb4\xfe\xad\xdc\x56\xf0\x62\x00\x8d\x52\x0b\x2d\x58" + "\xc0\x05\xd6\x1d\x47\x35\xc6\x6a\x42\xec\x98\xee\x21\x74\x7b\xe5" + },{ 2048, true, + "\x14\x6b\xaa\x2f\xf4\xa8\x24\x3f\x4e\x92\x97\x1a\xca\x1c\xbb\x46" + "\xa7\x08\xbb\xc5\x95\xac\x73\x81\x25\x34\x33\x41\x95\x71\xd9\xe7" + },{ 4096, false, + "\xa8\x17\x5d\x84\xc8\x16\x06\x7f\xa2\x68\xdd\x1e\x7d\x63\x34\x93" + "\x7b\x45\x2d\xf4\x10\x0b\x90\xfa\x14\x8b\x73\x86\xbc\x09\x4a\xe3" + },{ 4096, true, + "\xe2\xc3\x30\xd8\xa1\xb3\xa8\xeb\xde\xdc\xfe\x9b\xe0\x0b\x62\x4e" + "\x38\x2f\xa1\x45\x0e\x8f\x6c\xf0\x4e\x88\x58\x17\x13\xb5\x10\x98" + }, +}}, +{ + "aes", "cbc", + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", 16, + "essiv:sha256", 0, 8192, + "\x9f\x1d\xcb\xc3\x5c\x35\x0d\x60\x27\xf9\x8b\xe0\xf5\xc8\xb4\x3b" + "\x42\xca\x52\xb7\x60\x44\x59\xc0\xc4\x2b\xe3\xaa\x88\x91\x3d\x47", { + { 512, false, + "\xa5\x3e\x74\xc4\x1a\x5c\xf3\x6b\x63\x49\xd5\xd9\xbb\x7a\x89\x5a" + "\xd5\x3e\x76\x6f\x4c\x2d\x0b\xd3\x8b\x5e\x0e\x91\xa3\x8c\x2a\xde" + },{ 1024, false, + "\x41\x6b\xc6\x75\x2e\x99\x76\xa1\x83\xea\xd5\x97\x64\x0e\x24\x8c" + "\x91\x17\x03\x38\xe7\xd8\x66\x64\xaa\xd7\x27\x50\x2a\xd3\x0b\xe6" + },{ 1024, true, + "\x02\x3c\xbe\xe6\x1e\x9a\xf3\x14\xab\x16\xff\x6f\xb6\xa2\x3e\x03" + "\xa1\xbd\xe9\xe4\xfa\x44\x5b\x22\xc6\x53\xe8\x60\x58\x15\x99\xea" + },{ 2048, false, + "\x84\xdc\x45\xd3\x61\x03\xa8\x51\x85\x5b\xef\xf8\x92\x6b\x12\x06" + "\x2c\xfe\x75\x3e\xcf\x28\xd1\x8b\x4d\xcb\x88\x9e\x31\xb0\x0b\x92" + },{ 2048, true, + "\x4b\x9d\xe4\x3c\xe2\x4e\x7a\x13\x72\x02\x48\xf8\x7a\x7e\x15\xe8" + "\x3a\xc3\x92\x0b\xe8\x30\xac\xb7\x9a\xe0\xcf\xf9\xb1\xf5\x61\x5b" + },{ 4096, false, + "\xbb\x1b\xa3\xa9\x41\xbf\x17\xd8\x76\x19\x08\x8e\x3f\x50\xed\xfd" + "\x57\x1d\xd2\xc2\x8a\x32\x01\xb9\xd9\x8a\xcc\x0d\xa0\x65\x8b\x6d" + },{ 4096, true, + "\xa6\xdc\x7d\xc8\xc4\x9b\x78\x81\x72\xe9\xdd\x35\x6c\x07\xeb\x7b" + "\xd6\x56\x9e\xe4\xdf\xf5\xdd\x2e\x2c\x19\x8f\x63\x58\xdb\xa7\xd0" + }, +}}, +{ + "aes", "cbc", + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", 16, + "benbi", 0, 8192, + "\x9f\x1d\xcb\xc3\x5c\x35\x0d\x60\x27\xf9\x8b\xe0\xf5\xc8\xb4\x3b" + "\x42\xca\x52\xb7\x60\x44\x59\xc0\xc4\x2b\xe3\xaa\x88\x91\x3d\x47", { + { 512, false, + "\x3c\xe3\x94\xe3\x6d\x68\x5b\xdb\x5a\x8d\x71\xbf\xd3\xa6\x68\xb9" + "\x1f\x33\x0f\x97\xe2\xd6\xe8\xe2\xe1\xfc\x7e\x80\x28\xf1\x73\xbd" + },{ 1024, false, + "\x0f\x27\xa7\xae\x31\x9e\x71\x02\x12\x16\x44\x5f\xbb\xc6\xcb\x78" + "\xd4\x84\x49\xe0\x88\x85\x04\xbf\x6d\xea\x60\x76\x98\x34\x0a\x7e" + },{ 1024, true, + "\x3e\xf3\x08\x8d\x3b\x20\x4b\x51\x54\xde\x7f\x77\x5b\xcf\x02\x8b" + "\x0e\xb0\x74\x2e\x8e\x29\xfa\x5e\x86\xb4\xab\x65\x18\x59\x48\xb1" + },{ 2048, false, + "\xb0\x9a\xe5\x31\x5f\x2e\x9d\x13\x04\x08\x2a\x02\x71\x3d\xdb\x5d" + "\xb2\xc9\x68\x5b\xdc\xd1\x38\xc2\x96\xb3\x3b\x72\xda\x9d\xcb\xe6" + },{ 2048, true, + "\x6f\x34\xf0\xc1\xea\x72\xe4\xdc\x91\x91\x78\xb3\x7c\xb0\x9d\x41" + "\x94\xf6\xb8\xad\x05\xc4\x0e\x49\x05\x31\x90\xf0\x56\xfe\x21\x3f" + },{ 4096, false, + "\xaa\x74\x7d\xd6\x73\xa7\x77\xe1\x7f\xb9\x76\xf7\x5c\xcf\xc0\xb7" + "\xfa\x7b\xed\x15\xc2\x32\x7c\x27\xbb\x35\xfc\xfe\x12\xee\x14\x2d" + },{ 4096, true, + "\x71\x1b\x3d\x26\xf4\x44\x82\x72\x1b\x7a\x65\x0b\x37\x8c\x94\x5b" + "\x1c\xd3\x30\x2f\xf6\xce\xa4\x24\x25\xeb\x9b\xb9\x83\xe5\x71\xbb" + }, +}}, +{ + "aes", "cbc", + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", 16, + "eboiv", 0, 8192, + "\x9f\x1d\xcb\xc3\x5c\x35\x0d\x60\x27\xf9\x8b\xe0\xf5\xc8\xb4\x3b" + "\x42\xca\x52\xb7\x60\x44\x59\xc0\xc4\x2b\xe3\xaa\x88\x91\x3d\x47", { + { 512, false, + "\x04\x4e\x92\x9f\x79\x66\xfe\x93\x1b\xa5\xb8\x02\xfe\x7e\xf9\x26" + "\x7b\x64\x39\xe7\xb3\xca\xc4\x6e\xca\x27\xa0\x2f\xe2\xea\x91\x16" + },{ 1024, false, + "\xb0\x4a\xa4\xb5\xd6\x45\x7a\x86\xe9\x43\x3d\xd6\x01\xf7\x68\x8e" + "\xe6\x81\x8d\x50\x55\x18\x8e\x4b\xb6\xa7\x89\xdf\xe2\x4b\x94\xe2" + },{ 1024, true, + "\x95\x08\x4d\x4e\x89\xab\x91\x4e\xae\x56\x5d\xec\xf2\x78\x13\xb1" + "\x82\xf7\xc8\xb5\x03\xd6\xfa\xb0\xe3\xf9\xc1\x01\xc0\x0c\x35\xa4" + },{ 2048, false, + "\xd4\x00\x1f\x26\x18\xd1\x6d\xd5\xc4\xbf\x4a\x13\x30\xae\xd7\x4b" + "\x33\x1e\xd5\xe8\x43\x2d\x95\x84\x67\x39\x04\x51\x5f\x1f\x49\xe4" + },{ 2048, true, + "\x89\x8d\xa2\xec\x45\x7f\xf0\xac\xfc\x70\xb6\x36\xf0\x89\xca\x86" + "\x6b\xbf\x09\xd2\x54\xa0\x7c\xbc\x17\xd3\x4e\xb8\x10\x8a\x3f\x5d" + },{ 4096, false, + "\xd1\xd7\x4f\x70\x9a\xa0\x22\x27\x60\xdb\x40\x5a\x84\xce\x89\x2c" + "\x4f\x98\x55\xd2\x2d\xd1\xea\x9e\x47\xae\x8a\x83\xb5\x90\xbb\x49" + },{ 4096, true, + "\xdb\xe7\xd2\x25\xb0\x4f\x5d\x36\x20\xc4\xc2\xb4\xe8\x7e\xae\xe9" + "\x95\x10\x45\x5d\xdd\xc4\xcd\x33\xad\xbd\x39\x49\xf2\x85\x82\x4c" + }, +}}}; + +static int pbkdf_test_vectors(void) +{ + char result[256]; + unsigned int i; + const struct kdf_test_vector *vec; + + for (i = 0; i < (sizeof(kdf_test_vectors) / sizeof(*kdf_test_vectors)); i++) { + crypt_backend_memzero(result, sizeof(result)); + vec = &kdf_test_vectors[i]; + printf("PBKDF vector %02d %s ", i, vec->type); + if (vec->hash && crypt_hmac_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) < 0) { + printf("[%s-%s N/A]\n", vec->type, vec->hash); + continue; + } + if (memcmp(result, vec->output, vec->output_length)) { + printf("[FAILED]\n"); + printhex(" got", result, vec->output_length); + printhex("want", vec->output, vec->output_length); + return EXIT_FAILURE; + } + printf("[OK]\n"); + } + return EXIT_SUCCESS; +} + +static int crc32_test(const struct hash_test_vector *vector, unsigned int i) +{ + uint32_t crc32; + + if (vector->out[i].length != sizeof(uint32_t)) + return EXIT_FAILURE; + + crc32 = crypt_crc32(~0, (const unsigned char*)vector->data, vector->data_length) ^ ~0; + + if ((unsigned char)vector->out[i].out[0] != ((crc32 >> 24) & 0xFF) || + (unsigned char)vector->out[i].out[1] != ((crc32 >> 16) & 0xFF) || + (unsigned char)vector->out[i].out[2] != ((crc32 >> 8) & 0xFF) || + (unsigned char)vector->out[i].out[3] != ((crc32 >> 0) & 0xFF)) { + printf("[FAILED]\n"); + printhex(" got", (const char *)&crc32, sizeof(crc32)); + printhex("want", vector->out[i].out, vector->out[i].length); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +static int hash_test(void) +{ + const struct hash_test_vector *vector; + unsigned int i, j; + int r; + struct crypt_hash *h; + char result[64]; + + for (i = 0; i < ARRAY_SIZE(hash_test_vectors); i++) { + vector = &hash_test_vectors[i]; + printf("Hash vector %02d: ", i); + + for (j = 0; j < ARRAY_SIZE(vector->out); j++) { + + // CRC32 vector test is special + if (!strcmp("crc32", vector->out[j].name)) { + if (crc32_test(vector, j) < 0) + return EXIT_FAILURE; + printf("[%s]", vector->out[j].name); + continue; + } + + if (crypt_hash_size(vector->out[j].name) < 0) { + printf("[%s N/A]", vector->out[j].name); + continue; + } + + if (crypt_hash_size(vector->out[j].name) != (int)vector->out[j].length) + return EXIT_FAILURE; + + if (sizeof(result) < vector->out[j].length) + return EXIT_FAILURE; + + crypt_backend_memzero(result, sizeof(result)); + printf("[%s]", vector->out[j].name); + + if (crypt_hash_init(&h, vector->out[j].name)) { + printf("[%s N/A (init)]", vector->out[j].name); + continue; + } + + r = crypt_hash_write(h, vector->data, vector->data_length); + if (!r) + r = crypt_hash_final(h, result, vector->out[j].length); + + crypt_hash_destroy(h); + + if (r) + return EXIT_FAILURE; + + if (memcmp(result, vector->out[j].out, vector->out[j].length)) { + printf("[FAILED]\n"); + printhex(" got", result, vector->out[j].length); + printhex("want", vector->out[j].out, vector->out[j].length); + return EXIT_FAILURE; + } + } + printf("\n"); + } + + return EXIT_SUCCESS; +} + +static int hmac_test(void) +{ + const struct hmac_test_vector *vector; + struct crypt_hmac *hmac; + unsigned int i, j; + int r; + char result[64]; + + for (i = 0; i < ARRAY_SIZE(hmac_test_vectors); i++) { + vector = &hmac_test_vectors[i]; + printf("HMAC vector %02d: ", i); + + for(j = 0; j < ARRAY_SIZE(vector->out); j++) { + + if (crypt_hmac_size(vector->out[j].name) < 0) { + printf("[%s N/A]", vector->out[j].name); + continue; + } + + if (crypt_hmac_size(vector->out[j].name) != (int)vector->out[j].length) + return EXIT_FAILURE; + + if (sizeof(result) < vector->out[j].length) + return EXIT_FAILURE; + + crypt_backend_memzero(result, sizeof(result)); + printf("[%s]", vector->out[j].name); + + if (crypt_hmac_init(&hmac, vector->out[j].name, vector->key, vector->key_length)) + return EXIT_FAILURE; + + r = crypt_hmac_write(hmac, vector->data, vector->data_length); + if (!r) + r = crypt_hmac_final(hmac, result, vector->out[j].length); + + crypt_hmac_destroy(hmac); + + if (r) + return EXIT_FAILURE; + + if (memcmp(result, vector->out[j].out, vector->out[j].length)) { + printf("[FAILED]\n"); + printhex(" got", result, vector->out[j].length); + printhex("want", vector->out[j].out, vector->out[j].length); + return EXIT_FAILURE; + } + } + printf("\n"); + } + + return EXIT_SUCCESS; +} + +static int cipher_test(void) +{ + const struct cipher_test_vector *vector; + struct crypt_cipher *cipher; + unsigned int i, j; + char result[256]; + int r; + + for (i = 0; i < ARRAY_SIZE(cipher_test_vectors); i++) { + vector = &cipher_test_vectors[i]; + printf("CIPHER vector %02d: ", i); + + for (j = 0; j < ARRAY_SIZE(vector->out); j++) { + if (vector->iv_length && + crypt_cipher_ivsize(vector->out[j].name, vector->out[j].mode) != (int)vector->iv_length) + return EXIT_FAILURE; + if (vector->data_length > sizeof(result)) + return EXIT_FAILURE; + + r = crypt_cipher_init(&cipher, vector->out[j].name, vector->out[j].mode, + vector->key, vector->key_length); + if (r == -ENOENT || r == -ENOTSUP) { + printf("[%s-%s N/A]", vector->out[j].name, vector->out[j].mode); + continue; + } else { + printf("[%s-%s,%dbits]", vector->out[j].name, vector->out[j].mode, vector->key_length * 8); + if (r) + return EXIT_FAILURE; + } + + crypt_backend_memzero(result, sizeof(result)); + if (crypt_cipher_encrypt(cipher, vector->plaintext, result, vector->data_length, + vector->iv, vector->iv_length)) { + crypt_cipher_destroy(cipher); + return EXIT_FAILURE; + } + + if (memcmp(vector->out[j].ciphertext, result, vector->data_length)) { + printf("[ENCRYPTION FAILED]\n"); + printhex(" got", result, vector->data_length); + printhex("want", vector->out[j].ciphertext, vector->data_length); + crypt_cipher_destroy(cipher); + return EXIT_FAILURE; + } + + crypt_backend_memzero(result, sizeof(result)); + if (crypt_cipher_decrypt(cipher, vector->out[j].ciphertext, result, vector->data_length, + vector->iv, vector->iv_length)) { + crypt_cipher_destroy(cipher); + return EXIT_FAILURE; + } + + if (memcmp(vector->plaintext, result, vector->data_length)) { + printf("[DECRYPTION FAILED]\n"); + printhex(" got", result, vector->data_length); + printhex("want", vector->plaintext, vector->data_length); + crypt_cipher_destroy(cipher); + return EXIT_FAILURE; + } + + crypt_cipher_destroy(cipher); + } + printf("\n"); + } + + return EXIT_SUCCESS; +} + +static void get_sha256(const char *in, size_t length, char out[32]) +{ + struct crypt_hash *h; + + crypt_backend_memzero(out, 32); + if (crypt_hash_init(&h, "sha256")) + return; + + if (!crypt_hash_write(h, in, length)) + crypt_hash_final(h, out, 32); + + crypt_hash_destroy(h); +} + +static int cipher_iv_test(void) +{ + const struct cipher_iv_test_vector *vector; + struct crypt_storage *storage; + unsigned int i, j; + char mode_iv[256]; + char result[8192], hash[32]; + int r; + + for (i = 0; i < ARRAY_SIZE(cipher_iv_test_vectors); i++) { + vector = &cipher_iv_test_vectors[i]; + printf("IV vector %02d: [%s-%s-%s]", i, vector->cipher_name, vector->cipher_mode, vector->iv_name); + + for (j = 0; j < ARRAY_SIZE(vector->out); j++) { + if (vector->data_length > sizeof(result)) + return EXIT_FAILURE; + + snprintf(mode_iv, sizeof(mode_iv)-2, "%s-%s", vector->cipher_mode, vector->iv_name); + r = crypt_storage_init(&storage, vector->out[j].sector_size, vector->cipher_name, mode_iv, + vector->key, vector->key_length, vector->out[j].large_iv); + if (r == -ENOENT || r == -ENOTSUP) { + printf("[N/A]"); + continue; + } else { + printf("[%i%s]", (int)vector->out[j].sector_size, vector->out[j].large_iv ? "L" : ""); + if (r) + return EXIT_FAILURE; + } + + crypt_backend_memzero(result, sizeof(result)); + if (crypt_storage_encrypt(storage, vector->iv_offset, vector->data_length, result)) { + crypt_storage_destroy(storage); + return EXIT_FAILURE; + } + + get_sha256(result, vector->data_length, hash); + if (memcmp(vector->out[j].out_sha256, hash, sizeof(hash))) { + printf("[ENCRYPTION FAILED]\n"); + printhex(" got", hash, sizeof(hash)); + printhex("want", vector->out[j].out_sha256, sizeof(vector->out[j].out_sha256)); + crypt_storage_destroy(storage); + return EXIT_FAILURE; + } + + if (crypt_storage_decrypt(storage, vector->iv_offset, vector->data_length, result)) { + crypt_storage_destroy(storage); + return EXIT_FAILURE; + } + + get_sha256(result, vector->data_length, hash); + if (memcmp(vector->in_sha256, hash, sizeof(hash))) { + printf("[DECRYPTION FAILED]\n"); + printhex(" got", hash, sizeof(hash)); + printhex("want", vector->in_sha256, sizeof(vector->in_sha256)); + crypt_storage_destroy(storage); + return EXIT_FAILURE; + } + + crypt_storage_destroy(storage); + } + printf("\n"); + } + + return EXIT_SUCCESS; +} + +static void __attribute__((noreturn)) exit_test(const char *msg, int r) +{ + if (msg) + printf("%s\n", msg); + crypt_backend_destroy(); + exit(r); +} + +int main(__attribute__ ((unused)) int argc, __attribute__ ((unused))char *argv[]) +{ + setvbuf(stdout, NULL, _IONBF, 0); + + if (getenv("CRYPTSETUP_PATH")) { + printf("Cannot run this test with CRYPTSETUP_PATH set.\n"); + exit(77); + } + + if (crypt_backend_init()) + exit_test("Crypto backend init error.", EXIT_FAILURE); + + printf("Test vectors using %s crypto backend.\n", crypt_backend_version()); + + if (pbkdf_test_vectors()) + exit_test("PBKDF test failed.", EXIT_FAILURE); + + if (hash_test()) + exit_test("HASH test failed.", EXIT_FAILURE); + + if (hmac_test()) + exit_test("HMAC test failed.", EXIT_FAILURE); + + if (cipher_test()) + exit_test("CIPHER test failed.", EXIT_FAILURE); + + if (cipher_iv_test()) + exit_test("IV test failed.", EXIT_FAILURE); + + exit_test(NULL, EXIT_SUCCESS); +} diff --git a/tests/cryptsetup-valg-supps b/tests/cryptsetup-valg-supps new file mode 100644 index 0000000..493e125 --- /dev/null +++ b/tests/cryptsetup-valg-supps @@ -0,0 +1,14 @@ +# 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 + ... +} diff --git a/tests/device-test b/tests/device-test new file mode 100755 index 0000000..617f16a --- /dev/null +++ b/tests/device-test @@ -0,0 +1,328 @@ +#!/bin/bash + +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/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 --retry $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 + rmmod scsi_debug 2>/dev/null +} + +fail() +{ + [ -n "$1" ] && echo "FAIL $1" + echo "FAILED backtrace:" + while caller $frame; do ((frame++)); done + cleanup + exit 100 +} + +skip() +{ + echo "TEST SKIPPED: $1" + cleanup + exit 77 +} + +add_device() { + modprobe scsi_debug $@ delay=0 + [ $? -ne 0 ] && skip "This kernel seems to not support proper scsi_debug module." + + sleep 1 + SCSI_DEV=$(grep -l -e scsi_debug /sys/block/*/device/model | cut -f4 -d /) + + [ -b "/dev/$SCSI_DEV" ] || fail "Cannot find $SCSI_DEV." +} + +function dm_crypt_features() +{ + modprobe dm-crypt || fail "dm-crypt failed to load" + 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 + test -d /proc/sys/kernel/keys && 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 + test -d /proc/sys/kernel/keys && DM_KEYRING=1 + fi + + [ $VER_MIN -lt 22 ] && return + DM_PERF_NO_WORKQUEUE=1 +} + +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 + echo -n "PLAIN: same_cpu_crypt submit_from_cpus " + 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 -n "allow_discards " + 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 + if [ -n "$DM_PERF_NO_WORKQUEUE" ]; then + echo -n "no_read_workqueue no_write_workqueue" + echo -e "$PWD1" | $CRYPTSETUP refresh --hash sha256 -q $DEV_NAME --perf-no_read_workqueue --perf-no_write_workqueue || fail + $CRYPTSETUP status $DEV_NAME | grep -q no_read_workqueue || fail + $CRYPTSETUP status $DEV_NAME | grep -q no_write_workqueue || fail + fi + $CRYPTSETUP close $DEV_NAME || fail + echo + + echo -n "LUKS: same_cpu_crypt submit_from_cpus " + 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 -n "allow_discards " + 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 + if [ -n "$DM_PERF_NO_WORKQUEUE" ]; then + echo -n "no_read_workqueue no_write_workqueue" + echo -e "$PWD1" | $CRYPTSETUP refresh $DEV_NAME --perf-no_read_workqueue --perf-no_write_workqueue || fail + $CRYPTSETUP status $DEV_NAME | grep -q no_read_workqueue || fail + $CRYPTSETUP status $DEV_NAME | grep -q no_write_workqueue || fail + fi + $CRYPTSETUP close $DEV_NAME || fail + echo + + format luks2 + echo -n "LUKS2: same_cpu_crypt submit_from_cpus " + 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 -n "allow_discards [persistent flags] " + 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 -n "keyring " + echo -e "$PWD1" | $CRYPTSETUP refresh $DEV $DEV_NAME || fail + $CRYPTSETUP status $DEV_NAME | grep -q keyring || fail + fi + if [ -n "$DM_PERF_NO_WORKQUEUE" ]; then + echo -n "no_read_workqueue no_write_workqueue" + echo -e "$PWD1" | $CRYPTSETUP refresh $DEV $DEV_NAME --perf-no_read_workqueue --perf-no_write_workqueue --persistent || fail + $CRYPTSETUP status $DEV_NAME | grep -q no_read_workqueue || fail + $CRYPTSETUP status $DEV_NAME | grep -q no_write_workqueue || fail + $CRYPTSETUP close $DEV_NAME || fail + echo -e "$PWD1" | $CRYPTSETUP open $DEV $DEV_NAME || fail + $CRYPTSETUP status $DEV_NAME | grep -q no_read_workqueue || fail + $CRYPTSETUP status $DEV_NAME | grep -q no_write_workqueue || fail + fi + echo -e "$PWD1" | $CRYPTSETUP refresh $DEV $DEV_NAME2 2>/dev/null && fail + $CRYPTSETUP close $DEV_NAME || fail + echo +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 -e "$PWD1" | $CRYPTSETUP open --type plain --hash sha256 $DEV $DEV_NAME --iv-large-sectors >/dev/null 2>&1 && fail + for S in 1024 2048 4096; do + echo -n "[$S/IV]" + echo -e "$PWD1" | $CRYPTSETUP open -q --type plain --hash sha256 $DEV $DEV_NAME --sector-size $S --iv-large-sectors || fail + check_sector_size $S + dmsetup table $DEV_NAME | grep -q "iv_large_sectors" || fail + $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 + +echo "[4] Disappeared device test:" +KEY="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001" +for F in LUKS1 LUKS2 BITLK TCRYPT; do + echo -n "$F" + add_device dev_size_mb=1 sector_size=512 num_tgts=1 lbpu=1 + # Fake CRYPT UUID to force code to parse type-specific path + dmsetup create $DEV_NAME --uuid CRYPT-$F-$DEV_NAME --table "0 1024 crypt aes-xts-plain64 $KEY 16 /dev/$SCSI_DEV 16" + $CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 || fail + echo 1 > /sys/block/$SCSI_DEV/device/delete + udevadm settle >/dev/null 2>&1 + $CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 || fail + dmsetup remove $DEV_NAME --retry || fail + rmmod scsi_debug + echo -n "[OK] " +done +echo + +cleanup +exit 0 diff --git a/tests/differ.c b/tests/differ.c new file mode 100644 index 0000000..ec811a4 --- /dev/null +++ b/tests/differ.c @@ -0,0 +1,166 @@ +/* + * cryptsetup file differ check (rewritten Clemens' fileDiffer in Python) + * + * Copyright (C) 2010-2021 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..8a838e6 --- /dev/null +++ b/tests/discards-test @@ -0,0 +1,86 @@ +#!/bin/bash + +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup +DEV_NAME="discard-t3st" +DEV="" +PWD1="93R4P4pIqAH8" + +cleanup() { + [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove --retry $DEV_NAME + udevadm settle >/dev/null 2>&1 + rmmod scsi_debug 2>/dev/null + sleep 2 +} + +fail() +{ + echo "FAILED backtrace:" + while caller $frame; do ((frame++)); done + 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 Binary files differnew file mode 100644 index 0000000..6d56368 --- /dev/null +++ b/tests/evil_hdr-keyslot_overlap.xz diff --git a/tests/evil_hdr-luks_hdr_damage.xz b/tests/evil_hdr-luks_hdr_damage.xz Binary files differnew file mode 100644 index 0000000..b0b5254 --- /dev/null +++ b/tests/evil_hdr-luks_hdr_damage.xz diff --git a/tests/evil_hdr-payload_overwrite.xz b/tests/evil_hdr-payload_overwrite.xz Binary files differnew file mode 100644 index 0000000..b9e0e68 --- /dev/null +++ b/tests/evil_hdr-payload_overwrite.xz diff --git a/tests/evil_hdr-small_luks_device.xz b/tests/evil_hdr-small_luks_device.xz Binary files differnew file mode 100644 index 0000000..700392d --- /dev/null +++ b/tests/evil_hdr-small_luks_device.xz diff --git a/tests/evil_hdr-stripes_payload_dmg.xz b/tests/evil_hdr-stripes_payload_dmg.xz Binary files differnew file mode 100644 index 0000000..dc91556 --- /dev/null +++ b/tests/evil_hdr-stripes_payload_dmg.xz 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-backup-key-0.img.sh b/tests/generators/generate-luks2-segment-wrong-backup-key-0.img.sh new file mode 100755 index 0000000..2499a5e --- /dev/null +++ b/tests/generators/generate-luks2-segment-wrong-backup-key-0.img.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +. lib.sh + +# +# *** Description *** +# +# generate primary header with wrong backup segment id +# +# 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() +{ + # create illegal backup segment key (used to be bug in 32bit implementations) + json_str=$(jq -c '.segments[(.segments | length + 1 | tostring)] = { "type" : "linear", "offset" : "512", "size" : "512", "flags":["backup-x"]}' $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 | length < 2 + then error("Unexpected segments count") 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-backup-key-1.img.sh b/tests/generators/generate-luks2-segment-wrong-backup-key-1.img.sh new file mode 100755 index 0000000..702fe71 --- /dev/null +++ b/tests/generators/generate-luks2-segment-wrong-backup-key-1.img.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +. lib.sh + +# +# *** Description *** +# +# generate primary header with wrong backup segment id +# +# 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() +{ + # create illegal backup segment key (used to be bug in 32bit implementations) + json_str=$(jq -c '(.segments."0".offset | tonumber) as $i | .segments[range(1;65) | tostring] = { "type" : "linear", "offset" : ($i + 512 | tostring), "size" : "512" } | .segments."268435472" = { "type":"linear","offset":"512","size":"512","flags":["backup-x"]}' $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 | length < 64 + then error("Unexpected segments count") 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 Binary files differnew file mode 100644 index 0000000..de688f0 --- /dev/null +++ b/tests/img_fs_ext4.img.xz diff --git a/tests/img_fs_vfat.img.xz b/tests/img_fs_vfat.img.xz Binary files differnew file mode 100644 index 0000000..5ecfa67 --- /dev/null +++ b/tests/img_fs_vfat.img.xz diff --git a/tests/img_fs_xfs.img.xz b/tests/img_fs_xfs.img.xz Binary files differnew file mode 100644 index 0000000..a7034a7 --- /dev/null +++ b/tests/img_fs_xfs.img.xz diff --git a/tests/integrity-compat-test b/tests/integrity-compat-test new file mode 100755 index 0000000..a3d3b8c --- /dev/null +++ b/tests/integrity-compat-test @@ -0,0 +1,484 @@ +#!/bin/bash +# +# Test integritysetup compatibility. +# +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +INTSETUP=$CRYPTSETUP_PATH/integritysetup + +INTSETUP_VALGRIND=../.libs/integritysetup +INTSETUP_LIB_VALGRIND=../.libs + +DEV_NAME=dmc_test +DEV_NAME_BIG=dmc_fake +DEV_LOOP="" +DEV=test123.img +DEV2=test124.img +KEY_FILE=key.img +KEY_FILE2=key2.img + +dmremove() { # device + udevadm settle >/dev/null 2>&1 + dmsetup remove --retry $1 >/dev/null 2>&1 +} + +cleanup() { + [ -b /dev/mapper/$DEV_NAME ] && dmremove $DEV_NAME + [ -b /dev/mapper/$DEV_NAME_BIG ] && dmremove $DEV_NAME_BIG + [ -n "$DEV_LOOP" ] && losetup -d "$DEV_LOOP" + DEV_LOOP="" + rm -f $DEV $DEV2 $KEY_FILE $KEY_FILE2 >/dev/null 2>&1 +} + +fail() +{ + [ -n "$1" ] && echo "$1" + echo "FAILED backtrace:" + while caller $frame; do ((frame++)); done + 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 + } + [ $VER_MIN -gt 2 ] && { + DM_INTEGRITY_BITMAP=1 + } + [ $VER_MIN -gt 6 ] && { + DM_INTEGRITY_HMAC_FIX=1 + } +} + +add_device() { + cleanup + dd if=/dev/urandom of=$KEY_FILE bs=4096 count=1 >/dev/null 2>&1 + dd if=/dev/urandom of=$KEY_FILE2 bs=1 count=32 >/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 outtagsize sector_size csum [keyfile keysize] +{ + if [ -n "$8" ] ; then + KEY_PARAMS="--integrity-key-file $7 --integrity-key-size $8" + else + KEY_PARAMS="" + fi + + if [ $3 -ne 0 ] ; then + TAG_PARAMS="--tag-size $3" + else + TAG_PARAMS="" + fi + + echo -n "[INTEGRITY:$2:$4:$5]" + [ -n "$8" ] && echo -n "[KEYFILE:$8]" + echo -n "[FORMAT]" + $INTSETUP format --integrity-legacy-padding -q --integrity $1 $TAG_PARAMS --sector-size $5 $KEY_PARAMS $DEV >/dev/null 2>&1 + if [ $? -ne 0 ] ; then + ALG=$(echo $1 | sed -e 's/hmac-//') + if ! grep -q $ALG /proc/crypto ; then + echo "[N/A]" + return + fi + fail "Cannot format device." + fi + + dump_check "tag_size" $4 + dump_check "sector_size" $5 + echo -n "[ACTIVATE]" + $INTSETUP open $DEV $DEV_NAME --integrity $1 $KEY_PARAMS || fail "Cannot activate device." + if [ -n "$8" ]; then + KEY_HEX=$(xxd -c 4096 -l $8 -p $7) + [ -z "$KEY_HEX" ] && fail "Cannot decode key." + dmsetup table --showkeys $DEV_NAME | grep -q $KEY_HEX || fail "Key mismatch." + fi + status_check "tag size" $4 + status_check "integrity" $2 + status_check "sector size" "$5 bytes" + int_check_sum $1 $6 $7 $8 + echo -n "[REMOVE]" + $INTSETUP close $DEV_NAME || fail "Cannot deactivate device." + echo "[OK]" +} + +int_error_detection() # mode alg tagsize outtagsize sector_size key_file key_size +{ + if [ "$1" == "B" ] ; then + INT_MODE="-B" + else + INT_MODE="" + fi + if [ -n "$7" ] ; then + KEY_PARAMS="--integrity-key-file $6 --integrity-key-size $7" + else + KEY_PARAMS="" + fi + if [ $3 -ne 0 ] ; then + TAG_PARAMS="--tag-size $3" + else + TAG_PARAMS="" + fi + dd if=/dev/zero of=$DEV bs=1M count=32 >/dev/null 2>&1 + + echo -n "[INTEGRITY:$1:$2:$4:$5]" + echo -n "[FORMAT]" + $INTSETUP format -q --integrity $2 $TAG_PARAMS --sector-size $5 $KEY_PARAMS $DEV $INT_MODE >/dev/null || fail "Cannot format device." + echo -n "[ACTIVATE]" + $INTSETUP open $DEV $DEV_NAME --integrity $2 --integrity-no-journal $KEY_PARAMS $INT_MODE || fail "Cannot activate device." + + if [ -n "$6" -a -n "$7" ]; then + echo -n "[KEYED HASH]" + KEY_HEX=$(xxd -c 256 -l $7 -p $6) + [ -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 $2 $KEY_PARAMS $INT_MODE || 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:$8]" + 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 4096 -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." +which blockdev >/dev/null || skip "Cannot find blockdev utility, 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 blake2s-256 blake2s-256 32 32 512 8e5fe4119558e117bfc40e3b0f13ade3abe497b52604d4c7cca0cfd6c7f4cf11 +intformat blake2b-256 blake2b-256 32 32 512 8e5fe4119558e117bfc40e3b0f13ade3abe497b52604d4c7cca0cfd6c7f4cf11 +intformat crc32c crc32c 0 4 512 08f63eb27fb9ce2ce903b0a56429c68ce5e209253ba42154841ef045a53839d7 +intformat crc32 crc32 0 4 512 08f63eb27fb9ce2ce903b0a56429c68ce5e209253ba42154841ef045a53839d7 +intformat sha1 sha1 0 20 512 6eedd6344dab8875cd185fcd6565dfc869ab36bc57e577f40c685290b1fa7fe7 +intformat sha1 sha1 16 16 4096 e152ec88227b539cd9cafd8bdb587a1072d720cd6bcebe1398d4136c9e7f337b +intformat sha256 sha256 0 32 512 8e5fe4119558e117bfc40e3b0f13ade3abe497b52604d4c7cca0cfd6c7f4cf11 +intformat hmac-sha256 hmac\(sha256\) 0 32 512 8e5fe4119558e117bfc40e3b0f13ade3abe497b52604d4c7cca0cfd6c7f4cf11 $KEY_FILE 32 +intformat sha256 sha256 0 32 4096 33f7dfa5163ca9f740383fb8b0919574e38a7b20a94a4170fde4238196b7c4b4 +intformat hmac-sha256 hmac\(sha256\) 0 32 4096 33f7dfa5163ca9f740383fb8b0919574e38a7b20a94a4170fde4238196b7c4b4 $KEY_FILE 32 +intformat hmac-sha256 hmac\(sha256\) 0 32 4096 33f7dfa5163ca9f740383fb8b0919574e38a7b20a94a4170fde4238196b7c4b4 $KEY_FILE 4096 + +echo "Error detection tests:" +int_error_detection J crc32c 0 4 512 +int_error_detection J crc32c 0 4 4096 +int_error_detection J crc32 0 4 512 +int_error_detection J crc32 0 4 4096 +int_error_detection J sha1 0 20 512 +int_error_detection J sha1 16 16 512 +int_error_detection J sha1 0 20 4096 +int_error_detection J sha256 0 32 512 +int_error_detection J sha256 0 32 4096 + +which xxd >/dev/null 2>&1 || skip "WARNING: xxd tool required." +int_error_detection J hmac-sha256 0 32 512 $KEY_FILE 32 +int_error_detection J hmac-sha256 0 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\) +int_journal sha1 20 512 75 9999 hmac-sha256 $KEY_FILE 4096 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 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 + +echo -n "Bitmap mode parameters:" +if [ -n "$DM_INTEGRITY_BITMAP" ] ; then + add_device + $INTSETUP format -q $DEV --integrity-bitmap-mode $DEV2 || fail "Cannot format device." + $INTSETUP open $DEV --integrity-bitmap-mode --bitmap-sectors-per-bit 65536 --bitmap-flush-time 5000 $DEV_NAME || fail "Cannot activate device." + $INTSETUP status $DEV_NAME | grep -q 'bitmap 512-byte sectors per bit: 65536' || fail + $INTSETUP status $DEV_NAME | grep -q 'bitmap flush interval: 5000 ms' || fail + $INTSETUP close $DEV_NAME || fail "Cannot deactivate device." + echo "[OK]" + echo "Bitmap error detection tests:" + int_error_detection B crc32c 0 4 512 + int_error_detection B crc32c 0 4 4096 + int_error_detection B sha256 0 32 512 + int_error_detection B sha256 0 32 4096 + int_error_detection B hmac-sha256 0 32 512 $KEY_FILE 32 + int_error_detection B hmac-sha256 0 32 4096 $KEY_FILE 32 +else + echo "[N/A]" +fi + +echo -n "Big device:" +add_device +DEV_LOOP=$(losetup -f $DEV --show) +if [ -n "$DEV_LOOP" ] ; then +dmsetup create $DEV_NAME_BIG <<EOF +0 16284 linear $DEV_LOOP 0 +16284 80000000000 zero +EOF + [ ! -b /dev/mapper/$DEV_NAME_BIG ] && fail + $INTSETUP format -q -s 512 --no-wipe /dev/mapper/$DEV_NAME_BIG + $INTSETUP open /dev/mapper/$DEV_NAME_BIG $DEV_NAME || fail + D_SIZE=$($INTSETUP dump /dev/mapper/$DEV_NAME_BIG | grep provided_data_sectors | sed -e 's/.*provided_data_sectors\ \+//g') + A_SIZE=$(blockdev --getsz /dev/mapper/$DEV_NAME) + # Compare strings (to avoid 64bit integers), not integers + [ -n "$A_SIZE" -a "$D_SIZE" != "$A_SIZE" ] && fail + echo "[OK]" +else + echo "[N/A]" +fi + +echo -n "Fixed HMAC and legacy flags:" +if [ -n "$DM_INTEGRITY_HMAC_FIX" ] ; then + add_device + # only data HMAC + ARGS="--integrity hmac-sha256 --integrity-key-file $KEY_FILE --integrity-key-size 32" + $INTSETUP format -q $DEV --integrity-legacy-hmac --no-wipe --tag-size 32 $ARGS || fail "Cannot format device." + $INTSETUP open $DEV $DEV_NAME --integrity-recalculate $ARGS >/dev/null 2>&1 && fail "Cannot activate device." + $INTSETUP open $DEV $DEV_NAME --integrity-legacy-recalculate $ARGS || fail "Cannot activate device." + $INTSETUP close $DEV_NAME || fail "Cannot deactivate device." + # New version - must fail (no journal HMAC) + $INTSETUP format -q $DEV --no-wipe --tag-size 32 $ARGS || fail "Cannot format device." + $INTSETUP open $DEV $DEV_NAME --integrity-recalculate $ARGS >/dev/null 2>&1 && fail "Cannot activate device." + $INTSETUP open $DEV $DEV_NAME --integrity-legacy-recalculate $ARGS || fail "Cannot activate device." + $INTSETUP close $DEV_NAME || fail "Cannot deactivate device." + + # data and journal HMAC + ARGS="$ARGS --journal-integrity hmac-sha256 --journal-integrity-key-file $KEY_FILE2 --journal-integrity-key-size 32" + $INTSETUP format -q $DEV --integrity-legacy-hmac --no-wipe --tag-size 32 $ARGS || fail "Cannot format device." + $INTSETUP open $DEV $DEV_NAME --integrity-recalculate $ARGS >/dev/null 2>&1 && fail "Cannot activate device." + $INTSETUP open $DEV $DEV_NAME --integrity-legacy-recalculate $ARGS || fail "Cannot activate device." + $INTSETUP close $DEV_NAME || fail "Cannot deactivate device." + # New fixed version + $INTSETUP format -q $DEV --no-wipe --tag-size 32 $ARGS || fail "Cannot format device." + $INTSETUP dump $DEV | grep "flags" | grep -q "fix_hmac" || fail "Flag for HMAC not set." + $INTSETUP open $DEV $DEV_NAME --integrity-recalculate $ARGS || fail "Cannot activate device." + $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..7a49936 --- /dev/null +++ b/tests/keyring-compat-test @@ -0,0 +1,211 @@ +#!/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" + +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup + +[ -f /etc/system-fips ] && FIPS_MODE=$(cat /proc/sys/crypto/fips_enabled 2>/dev/null) + +function remove_mapping() +{ + [ -b /dev/mapper/$NAME ] && dmsetup remove --retry $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 backtrace:" + while caller $frame; do ((frame++)); done + 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." +} + +function fips_mode() +{ + [ -n "$FIPS_MODE" ] && [ "$FIPS_MODE" -gt 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 + + 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 --retry $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 --retry $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 --retry $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 --retry $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 --retry $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 --retry $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 --retry $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 --retry $NAME || fail +diff $CHKS_DMCRYPT $CHKS_KEYRING || fail "Plaintext checksums mismatch (corruption)" +echo "OK" + +#test serpent cipher, cbc mode, tcw IV +fips_mode || { +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 --retry $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 --retry $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 --retry $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 --retry $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..3ed3aff --- /dev/null +++ b/tests/keyring-test @@ -0,0 +1,238 @@ +#!/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 --retry $DEV_CRYPT + [ -b /dev/mapper/$DEV_ZERO ] && dmsetup remove --retry $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 backtrace:" + while caller $frame; do ((frame++)); done + 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 --retry $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 --retry $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 --retry $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 --retry $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 --retry $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..5c28be3 --- /dev/null +++ b/tests/loopaes-test @@ -0,0 +1,174 @@ +#!/bin/bash + +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/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 --retry $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 backtrace:" + while caller $frame; do ((frame++)); done + 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] +{ + $CRYPTSETUP close $DEV_NAME || fail + + 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 + # Fill device with zeroes and reopen it + dd if=/dev/zero of=/dev/mapper/$DEV_NAME $LOOP_DD_PARAM >/dev/null 2>&1 + check_sum $key $key_size $offset keyfile + 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..311a559 --- /dev/null +++ b/tests/luks1-compat-test @@ -0,0 +1,104 @@ +#!/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) + +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup +TST_DIR=luks1-images +MAP=luks1tst +KEYFILE=keyfile1 + +[ -z "$srcdir" ] && srcdir="." + +function remove_mapping() +{ + [ -b /dev/mapper/$MAP ] && dmsetup remove --retry $MAP +} + +function fail() +{ + [ -n "$1" ] && echo "$1" + echo " [FAILED]" + echo "FAILED backtrace:" + while caller $frame; do ((frame++)); done + 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 Binary files differnew file mode 100644 index 0000000..dd099c6 --- /dev/null +++ b/tests/luks1-images.tar.xz diff --git a/tests/luks2-integrity-test b/tests/luks2-integrity-test new file mode 100755 index 0000000..0ba4b67 --- /dev/null +++ b/tests/luks2-integrity-test @@ -0,0 +1,166 @@ +#!/bin/bash +# +# Test cryptsetup/authenticated encryption compatibility. +# +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/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 backtrace:" + while caller $frame; do ((frame++)); done + 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 --integrity-legacy-padding >/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-cbc-essiv:sha256 hmac-sha256 hmac\(sha256\) 128 256 512 ee501705a084cd0ab6f4a28014bcf62b8bfa3434de00b82743c50b3abf06232c +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-cbc-essiv:sha256 hmac-sha256 hmac\(sha256\) 256 256 512 ee501705a084cd0ab6f4a28014bcf62b8bfa3434de00b82743c50b3abf06232c +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-cbc-essiv:sha256 hmac-sha256 hmac\(sha256\) 128 256 4096 358d6beceddf593aff6b22c31684e0df9c226330aff5812e060950215217d21b +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-cbc-essiv:sha256 hmac-sha256 hmac\(sha256\) 256 256 4096 358d6beceddf593aff6b22c31684e0df9c226330aff5812e060950215217d21b +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-cbc-essiv:sha256 hmac-sha512 hmac\(sha512\) 256 512 4096 9873d864fccb866521e79c9f0f75ad0c578d6bd7620399bbf4779e698c6e92fd +intformat aes-xts-essiv:sha256 hmac-sha512 hmac\(sha512\) 512 512 4096 9873d864fccb866521e79c9f0f75ad0c578d6bd7620399bbf4779e698c6e92fd +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 + +cleanup diff --git a/tests/luks2-reencryption-mangle-test b/tests/luks2-reencryption-mangle-test new file mode 100755 index 0000000..8f308f5 --- /dev/null +++ b/tests/luks2-reencryption-mangle-test @@ -0,0 +1,506 @@ +#!/bin/bash + +PS4='$LINENO:' +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup +CRYPTSETUP_RAW=$CRYPTSETUP + +CRYPTSETUP_VALGRIND=../.libs/cryptsetup +CRYPTSETUP_LIB_VALGRIND=../.libs +IMG=reenc-mangle-data +IMG_HDR=$IMG.hdr +IMG_JSON=$IMG.json +KEY1=key1 +DEV_NAME=reenc3492834 + +FAST_PBKDF2="--pbkdf pbkdf2 --pbkdf-force-iterations 1000" +CS_PWPARAMS="--disable-keyring --key-file $KEY1" +CS_PARAMS="-q --disable-locks $CS_PWPARAMS" +JSON_MSIZE=16384 + +function remove_mapping() +{ + [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove --retry $DEV_NAME + rm -f $IMG $IMG_HDR $IMG_JSON $KEY1 >/dev/null 2>&1 +} + +function fail() +{ + local frame=0 + [ -n "$1" ] && echo "$1" + echo "FAILED backtrace:" + while caller $frame; do ((frame++)); done + remove_mapping + exit 2 +} + +function skip() +{ + [ -n "$1" ] && echo "$1" + remove_mapping + exit 77 +} + +function bin_check() +{ + which $1 >/dev/null 2>&1 || skip "WARNING: test require $1 binary, test skipped." +} + +function img_json_save() +{ + # FIXME: why --json-file cannot be used? + #$CRYPTSETUP luksDump --dump-json-metadata $IMG | jq -c -M | tr -d '\n' >$IMG_JSON + local LUKS2_JSON_SIZE=$(($JSON_MSIZE - 4096)) + _dd if=$IMG count=$LUKS2_JSON_SIZE skip=4096 | jq -c -M . | tr -d '\n' >$IMG_JSON +} + +function img_json_dump() +{ + img_json_save + jq . $IMG_JSON +} + +function img_hash_save() +{ + IMG_HASH=$(sha256sum $IMG | cut -d' ' -f 1) +} + +function img_hash_unchanged() +{ + local IMG_HASH2=$(sha256sum $IMG | cut -d' ' -f 1) + [ "$IMG_HASH" != "$IMG_HASH2" ] && fail "Image changed!" +} + +function img_prepare_raw() # $1 options +{ + remove_mapping + + if [ ! -e $KEY1 ]; then + dd if=/dev/urandom of=$KEY1 count=1 bs=32 >/dev/null 2>&1 + fi + + truncate -s 32M $IMG || fail + $CRYPTSETUP luksFormat $FAST_PBKDF2 $CS_PARAMS --luks2-metadata-size $JSON_MSIZE $IMG $1 || fail +} + +function img_prepare() # $1 options +{ + img_prepare_raw + # FIXME: resilience is not saved here (always none)? + $CRYPTSETUP reencrypt $IMG $CS_PARAMS -q --init-only --resilience none $1 >/dev/null 2>&1 + [ $? -ne 0 ] && skip "Reencryption unsupported, test skipped." + img_json_save + img_hash_save +} + +function _dd() +{ + dd $@ status=none conv=notrunc bs=1 +} + +# header mangle functions +function img_update_json() +{ + local LUKS2_BIN1_OFFSET=448 + local LUKS2_BIN2_OFFSET=$((LUKS2_BIN1_OFFSET + $JSON_MSIZE)) + local LUKS2_JSON_SIZE=$(($JSON_MSIZE - 4096)) + + # if present jq script, mangle JSON + if [ -n "$1" ]; then + local JSON=$(cat $IMG_JSON) + echo $JSON | jq -M -c "$1" >$IMG_JSON || fail + local JSON=$(cat $IMG_JSON) + echo $JSON | tr -d '\n' >$IMG_JSON || fail + fi + + # wipe JSON areas + _dd if=/dev/zero of=$IMG count=$LUKS2_JSON_SIZE seek=4096 + _dd if=/dev/zero of=$IMG count=$LUKS2_JSON_SIZE seek=$(($JSON_MSIZE + 4096)) + + # write JSON data + _dd if=$IMG_JSON of=$IMG count=$LUKS2_JSON_SIZE seek=4096 + _dd if=$IMG_JSON of=$IMG count=$LUKS2_JSON_SIZE seek=$(($JSON_MSIZE + 4096)) + + # erase sha256 checksums + _dd if=/dev/zero of=$IMG count=64 seek=$LUKS2_BIN1_OFFSET + _dd if=/dev/zero of=$IMG count=64 seek=$LUKS2_BIN2_OFFSET + + # calculate sha256 and write chexksums + local SUM1_HEX=$(_dd if=$IMG count=$JSON_MSIZE | sha256sum | cut -d ' ' -f 1) + echo $SUM1_HEX | xxd -r -p | _dd of=$IMG seek=$LUKS2_BIN1_OFFSET count=64 || fail + + local SUM2_HEX=$(_dd if=$IMG skip=$JSON_MSIZE count=$JSON_MSIZE | sha256sum | cut -d ' ' -f 1) + echo $SUM2_HEX | xxd -r -p | _dd of=$IMG seek=$LUKS2_BIN2_OFFSET count=64 || fail + + img_hash_save +} + +function img_check_ok() +{ + if [ $(id -u) == 0 ]; then + $CRYPTSETUP open $CS_PWPARAMS $IMG $DEV_NAME || fail + $CRYPTSETUP close $DEV_NAME || fail + fi + + $CRYPTSETUP repair $IMG $CS_PARAMS || fail +} + +function img_check_fail() +{ + if [ $(id -u) == 0 ]; then + $CRYPTSETUP open $CS_PWPARAMS $IMG $DEV_NAME 2>/dev/null && fail + fi + + $CRYPTSETUP repair $IMG $CS_PARAMS 2>/dev/null && fail + img_hash_unchanged +} + +function img_run_reenc_ok() +{ +local EXPECT_TIMEOUT=5 +[ -n "$VALG" ] && EXPECT_TIMEOUT=60 +# For now, we cannot run reencryption in batch mode for non-block device. Just fake the terminal here. +expect_run - >/dev/null <<EOF +proc abort {} { send_error "Timeout. "; exit 2 } +set timeout $EXPECT_TIMEOUT +eval spawn $CRYPTSETUP_RAW reencrypt $IMG $CS_PWPARAMS --disable-locks --resilience none +expect timeout abort "Are you sure? (Type 'yes' in capital letters):" +send "YES\n" +expect timeout abort eof +exit +EOF +[ $? -eq 0 ] || fail "Expect script failed." +} + +function img_run_reenc_fail() +{ +local EXPECT_TIMEOUT=5 +[ -n "$VALG" ] && EXPECT_TIMEOUT=60 +# For now, we cannot run reencryption in batch mode for non-block device. Just fake the terminal here. +expect_run - >/dev/null <<EOF +proc abort {} { send_error "Timeout. "; exit 42 } +set timeout $EXPECT_TIMEOUT +eval spawn $CRYPTSETUP_RAW reencrypt $IMG $CS_PWPARAMS --disable-locks +expect timeout abort "Are you sure? (Type 'yes' in capital letters):" +send "YES\n" +expect timeout abort eof +catch wait result +exit [lindex \$result 3] +EOF +local ret=$? +[ $ret -eq 0 ] && fail "Reencryption passed (should have failed)." +[ $ret -eq 42 ] && fail "Expect script failed." +img_hash_unchanged +} + +function img_check_fail_repair_ok() +{ + if [ $(id -u) == 0 ]; then + $CRYPTSETUP open $CS_PWPARAMS $IMG $DEV_NAME 2>/dev/null && fail + fi + + img_run_reenc_fail + + # repair metadata + $CRYPTSETUP repair $IMG $CS_PARAMS || fail + + img_check_ok + img_run_reenc_ok +} + +function valgrind_setup() +{ + bin_check valgrind + [ ! -f $CRYPTSETUP_VALGRIND ] && fail "Unable to get location of cryptsetup executable." + export LD_LIBRARY_PATH="$CRYPTSETUP_LIB_VALGRIND:$LD_LIBRARY_PATH" + CRYPTSETUP=valgrind_run + CRYPTSETUP_RAW="./valg.sh ${CRYPTSETUP_VALGRIND}" +} + +function valgrind_run() +{ + INFOSTRING="$(basename ${BASH_SOURCE[1]})-line-${BASH_LINENO[0]}" ./valg.sh ${CRYPTSETUP_VALGRIND} "$@" +} + +function expect_run() +{ + export INFOSTRING="$(basename ${BASH_SOURCE[1]})-line-${BASH_LINENO[0]}" + expect "$@" +} + +bin_check jq +bin_check sha256sum +bin_check xxd +bin_check expect + +export LANG=C + +[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run + +#while false; do + +echo "[1] Reencryption with old flag is rejected" +img_prepare +img_update_json '.config.requirements.mandatory = ["online-reencryptx"]' +img_check_fail +img_update_json '.config.requirements.mandatory = ["online-reencrypt-v2"]' +img_check_ok +img_run_reenc_ok +img_check_ok + +# Simulate old reencryption with no digest (repairable) +img_prepare +img_update_json 'del(.digests."2") | .config.requirements.mandatory = ["online-reencrypt"]' +img_check_fail_repair_ok + +# This must fail for new releases +echo "[2] Old reencryption in-progress (journal)" +img_prepare +img_update_json ' + del(.digests."2") | + .keyslots."2".area.type = "journal" | + .segments = { + "0" : (.segments."0" + + {"size" : .keyslots."2".area.size} + + {"flags" : ["in-reencryption"]}), + "1" : (.segments."0" + + {"offset" : ((.segments."0".offset|tonumber) + + (.keyslots."2".area.size|tonumber))|tostring}), + "2" : .segments."1", + "3" : .segments."2" + } | + .digests."0".segments = ["1","2"] | + .digests."1".segments = ["0","3"] | + .config.requirements.mandatory = ["online-reencrypt"]' +img_check_fail_repair_ok + +echo "[3] Old reencryption in-progress (checksum)" +img_prepare +img_update_json ' + del(.digests."2") | + .keyslots."2".area.type = "checksum" | + .keyslots."2".area.hash = "sha256" | + .keyslots."2".area.sector_size = 4096 | + .segments = { + "0" : (.segments."0" + + {"size" : .keyslots."2".area.size} + + {"flags" : ["in-reencryption"]}), + "1" : (.segments."0" + + {"offset": ((.segments."0".offset|tonumber) + + (.keyslots."2".area.size|tonumber))|tostring}), + "2" : .segments."1", + "3" : .segments."2" + } | + .digests."0".segments = ["1","2"] | + .digests."1".segments = ["0","3"] | + .config.requirements.mandatory = ["online-reencrypt"]' +img_check_fail_repair_ok + +# Note: older tools cannot create this from commandline +echo "[4] Old decryption in-progress (journal)" +img_prepare +img_update_json ' + del(.digests."1") | + del(.digests."2") | + del(.keyslots."1") | + .keyslots."2".mode = "decrypt" | + .keyslots."2".area.type = "journal" | + .segments = { + "0" : { + "type" : "linear", + "offset" : .segments."0".offset, + "size" : .keyslots."2".area.size, + "flags" : ["in-reencryption"] + }, + "1" : (.segments."0" + + {"offset" : ((.segments."0".offset|tonumber) + + (.keyslots."2".area.size|tonumber))|tostring}), + "2" : .segments."1", + "3" : { + "type" : "linear", + "offset" : .segments."0".offset, + "size" : "dynamic", + "flags" : ["backup-final"] + } + } | + .digests."0".segments = ["1","2"] | + .config.requirements.mandatory = ["online-reencrypt"]' +img_check_fail_repair_ok + +echo "[5] Old decryption in-progress (checksum)" +img_prepare +img_update_json ' + del(.digests."1") | + del(.digests."2") | + del(.keyslots."1") | + .keyslots."2".mode = "decrypt" | + .keyslots."2".area.type = "checksum" | + .keyslots."2".area.hash = "sha256" | + .keyslots."2".area.sector_size = 4096 | + .segments = { + "0" : { + "type" : "linear", + "offset" : .segments."0".offset, + "size" : .keyslots."2".area.size, + "flags" : ["in-reencryption"] + }, + "1" : (.segments."0" + + {"offset" : ((.segments."0".offset|tonumber) + + (.keyslots."2".area.size|tonumber))|tostring}), + "2" : .segments."1", + "3" : { + "type" : "linear", + "offset" : .segments."0".offset, + "size" : "dynamic", + "flags" : ["backup-final"] + } + } | + .digests."0".segments = ["1","2"] | + .config.requirements.mandatory = ["online-reencrypt"]' +img_check_fail_repair_ok + +# Note - offset is set to work with the old version (with a datashift bug) +echo "[6] Old reencryption in-progress (datashift)" +img_prepare +img_update_json ' + del(.digests."2") | + .keyslots."2".direction = "backward" | + .keyslots."2".area.type = "datashift" | + .keyslots."2".area.size = "4096" | + .keyslots."2".area.shift_size = ((1 * 1024 * 1024)|tostring) | + .segments = { + "0" : (.segments."0" + + {"size" : ((13 * 1024 * 1024)|tostring)}), + "1" : (.segments."0" + + {"offset" : ((30 * 1024 * 1024)|tostring)}), + "2" : .segments."1", + "3" : (.segments."2" + + {"offset" : ((17 * 1024 * 1024)|tostring)}), + } | + .digests."0".segments = ["0","2"] | + .digests."1".segments = ["1","3"] | + .config.requirements.mandatory = ["online-reencrypt"]' +img_check_fail_repair_ok + +# +# NEW metadata (with reenc digest) +# +echo "[7] Reencryption with various mangled metadata" + +# Normal situation +img_prepare +img_run_reenc_ok +img_check_ok + +# The same in various steps. +# Repair must validate not only metadata, but also reencryption digest. +img_prepare +img_update_json 'del(.digests."2")' +img_check_fail_repair_ok + +img_prepare '--reduce-device-size 2M' +img_update_json '.keyslots."2".area.shift_size = ((.keyslots."2".area.shift_size|tonumber / 2)|tostring)' +img_check_fail + +#FIXME: cannot check with correct digest for now (--init-only does not store area type) +img_prepare +img_update_json ' + .keyslots."2".area.type = "checksum" | + .keyslots."2".area.hash = "sha256" | + .keyslots."2".area.sector_size = 4096' +img_check_fail + +img_prepare +img_update_json '.keyslots."2".area.type = "journal"' +img_check_fail + +img_prepare +img_update_json '.keyslots."2".mode = "decrypt"' +img_check_fail + +img_prepare +img_update_json '.keyslots."2".direction = "backward"' +img_check_fail + +# key_size must be 1 +img_prepare +img_update_json '.keyslots."2".key_size = 16' +img_check_fail + +# Mangling segments +img_prepare +img_update_json 'del(.segments."1")' +img_check_fail + +img_prepare +img_update_json '.segments."0".encryption = "aes-cbc-null"' +img_check_fail + +img_prepare +img_update_json '.segments."1".encryption = "aes-cbc-null"' +img_check_fail + +img_prepare +img_update_json '.segments."2".encryption = "aes-cbc-null"' +img_check_fail + +# Mangling digests +img_prepare +img_update_json ' + .digests."2" = .digests."0" | + .digests."2".keyslots = ["2"] | + .digests."2".segments = []' +img_check_fail + +img_prepare +img_update_json '.digests."2".iterations = 1111' +img_check_fail + +# Simulate correct progress +img_prepare +img_update_json ' + .segments = { + "0" : (.segments."0" + + {"size" : ((1 * 1024 * 1024)|tostring)}), + "1" : (.segments."0" + + {"offset" : ((17 * 1024 * 1024)|tostring)}), + "2" : .segments."1", + "3" : .segments."2" + } | + .digests."0".segments = ["1","2"] | + .digests."1".segments = ["0","3"]' +img_check_ok + +# Mangling keyslots + +# Set reencrypt slot to non-ignore priority +# This should be benign, just avoid noisy messages +img_prepare +img_update_json 'del(.keyslots."2".priority)' +img_check_ok + +# Flags + +# Remove mandatory reenc flag, but keep reenc metadata +img_prepare +img_update_json '.config.requirements.mandatory = []' +img_check_fail + +# Unknown segment flag, should be ignored +img_prepare +img_update_json '.segments."0".flags = ["dead-parrot"]' +img_check_ok + +echo "[8] Reencryption with AEAD is not supported" +img_prepare_raw +img_json_save +img_update_json ' + .segments."0".integrity = { + "type" : "hmac(sha256)", + "journal_encryption": "none", + "journal_integrity": "none" + }' +$CRYPTSETUP reencrypt $IMG $CS_PARAMS >/dev/null 2>&1 && fail + +remove_mapping +exit 0 diff --git a/tests/luks2-reencryption-test b/tests/luks2-reencryption-test new file mode 100755 index 0000000..92f223d --- /dev/null +++ b/tests/luks2-reencryption-test @@ -0,0 +1,1550 @@ +#!/bin/bash + +PS4='$LINENO:' +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup + +CRYPTSETUP_VALGRIND=../.libs/cryptsetup +CRYPTSETUP_LIB_VALGRIND=../.libs + +FAST_PBKDF2="--pbkdf pbkdf2 --pbkdf-force-iterations 1000" +FAST_PBKDF_ARGON="--pbkdf-force-iterations 4 --pbkdf-memory 32 --pbkdf-parallel 1" +DEFAULT_ARGON="argon2i" + +DEV="" +OVRDEV="123reenc321" +DEVBIG="reenc2134" +DEV_NAME=reenc9768 +DEV_NAME2=reenc97682 +IMG=reenc-data +IMG_HDR=/tmp/$IMG.hdr +KEY1=key1 +VKEY1=vkey1 +PWD1="93R4P4pIqAH8" +PWD2="1cND4319812f" +PWD3="1-9Qu5Ejfnqv" + +[ -f /etc/system-fips ] && FIPS_MODE=$(cat /proc/sys/crypto/fips_enabled 2>/dev/null) + +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 +} + +function dm_delay_features() +{ + local _ver_str=$(dmsetup targets | grep delay | cut -f2 -dv) + [ -z "$_ver_str" ] && return 1 + return 0 +} + +# $1 path to scsi debug bdev +scsi_debug_teardown() { + local _tries=15; + + while [ -b "$1" -a $_tries -gt 0 ]; do + rmmod scsi_debug 2> /dev/null + if [ -b "$1" ]; then + sleep .1 + _tries=$((_tries-1)) + fi + done + + test ! -b "$1" || rmmod scsi_debug 2> /dev/null +} + +function remove_mapping() +{ + [ -b /dev/mapper/$DEV_NAME ] && { + dmsetup resume $DEV_NAME + dmsetup remove --retry $DEV_NAME + } + [ -b /dev/mapper/$DEV_NAME2 ] && { + dmsetup resume $DEV_NAME2 + dmsetup remove --retry $DEV_NAME2 + } + [ -b /dev/mapper/$DEV_NAME-overlay ] && { + dmsetup resume $DEV_NAME-overlay + dmsetup remove --retry $DEV_NAME-overlay + } + [ -b /dev/mapper/$DEV_NAME-hotzone-forward ] && { + dmsetup resume $DEV_NAME-hotzone-forward + dmsetup remove --retry $DEV_NAME-hotzone-forward + } + [ -b /dev/mapper/$DEV_NAME-hotzone-backward ] && { + dmsetup resume $DEV_NAME-hotzone-backward + dmsetup remove --retry $DEV_NAME-hotzone-backward + } + [ -b /dev/mapper/$OVRDEV ] && dmsetup remove --retry $OVRDEV 2>/dev/null + [ -b /dev/mapper/$OVRDEV-err ] && dmsetup remove --retry $OVRDEV-err 2>/dev/null + [ -n "$LOOPDEV" ] && losetup -d $LOOPDEV + unset LOOPDEV + rm -f $IMG $IMG_HDR $KEY1 $VKEY1 $DEVBIG >/dev/null 2>&1 + rmmod scsi_debug 2> /dev/null + scsi_debug_teardown $DEV +} + +function fail() +{ + local frame=0 + [ -n "$1" ] && echo "$1" + echo "FAILED backtrace:" + while caller $frame; do ((frame++)); done + remove_mapping + exit 2 +} + +function skip() +{ + [ -n "$1" ] && echo "$1" + remove_mapping + exit 77 +} + +function fips_mode() +{ + [ -n "$FIPS_MODE" ] && [ "$FIPS_MODE" -gt 0 ] +} + +function add_scsi_device() { + scsi_debug_teardown $DEV + 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 1 + DEV="/dev/"$(grep -l -e scsi_debug /sys/block/*/device/model | cut -f4 -d /) + [ -b $DEV ] || fail "Cannot find $DEV." +} + +function open_crypt() # $1 pwd, $2 hdr +{ + if [ -n "$2" ] ; then + echo "$1" | $CRYPTSETUP luksOpen $DEV $DEV_NAME --header $2 || fail + elif [ -n "$1" ] ; then + echo "$1" | $CRYPTSETUP luksOpen $DEV $DEV_NAME || fail + else + $CRYPTSETUP luksOpen -d $KEY1 $DEV $DEV_NAME || fail + fi +} + +function wipe_dev() # $1 dev +{ + if [ -b $1 ] ; then + blkdiscard --zeroout $1 2>/dev/null || dd if=/dev/zero of=$1 bs=1M conv=notrunc >/dev/null 2>&1 + else + local size=$(stat --printf="%s" $1) + truncate -s 0 $1 + truncate -s $size $1 + fi +} + +function wipe() # $1 pass, $2 hdr +{ + open_crypt $1 $2 + wipe_dev /dev/mapper/$DEV_NAME + udevadm settle >/dev/null 2>&1 + $CRYPTSETUP luksClose $DEV_NAME || fail +} + +function prepare() # $1 dev1_siz +{ + remove_mapping + + if [ ! -e $KEY1 ]; then + dd if=/dev/urandom of=$KEY1 count=1 bs=32 >/dev/null 2>&1 + fi + + if [ ! -e $VKEY1 ]; then + echo -n $'\x44\xc6\x74\x4f\x41\x4e\x50\xc0\x79\xc2\x2d\x5b\x5f\x68\x84\x17' >$VKEY1 + echo -n $'\x9c\x03\xba\xbe\x4d\x0f\x9a\x75\xb3\x90\x70\x32\x0a\xf8\xae\xc4'>>$VKEY1 + fi + + add_scsi_device $@ +} + +function preparebig() # $1 dev1_siz +{ + remove_mapping + + if [ ! -e $KEY1 ]; then + dd if=/dev/urandom of=$KEY1 count=1 bs=32 >/dev/null 2>&1 + fi + + truncate -s "$1"M $DEVBIG + LOOPDEV=$(losetup -f) + losetup -f $DEVBIG || fail + DEV=$LOOPDEV +} + +function check_hash_dev() # $1 dev, $2 hash +{ + HASH=$(sha256sum $1 | cut -d' ' -f 1) + [ $HASH != "$2" ] && fail "HASH differs (expected: $2) (result $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 check_hash_head() # $1 pwd, $2 len, $3 hash, $4 hdr +{ + open_crypt $1 $4 + if [ -n "$4" ]; then + echo $1 | $CRYPTSETUP resize $DEV_NAME --size $2 --header $4 || fail + else + echo $1 | $CRYPTSETUP resize $DEV_NAME --size $2 || fail + fi + check_hash_dev /dev/mapper/$DEV_NAME $3 + $CRYPTSETUP remove $DEV_NAME || fail +} + +function resize_file() # $1 dev, $2 shrink bytes +{ + local size=$(stat --printf="%s" $1) + truncate -s $(($size + $2)) $1 + losetup -c $LOOPDEV +} + +function error_writes() { # $1 dmdev, $2 data dev, $3 offset, $4 size + local _dev_size=$(blockdev --getsz /dev/mapper/$1) + local _offset=$(($3+$4)) + local _size=$((_dev_size-_offset)) + local _err=$1-err + local _table= + dmsetup create $_err --table "0 $_dev_size error" || fail + + if [ $3 -ne 0 ]; then + _table="0 $3 linear $2 0\n" + fi + + _table=$_table"$3 $4 delay $2 $3 0 /dev/mapper/$_err $3 0" + + if [ $_size -ne 0 ]; then + _table="$_table\n$_offset $_size linear $2 $_offset" + fi + + echo -e "$_table" | dmsetup load $1 || fail + dmsetup resume $1 || fail + blockdev --setra 0 /dev/mapper/$1 + blockdev --setra 0 /dev/mapper/$_err +} + +function fix_writes() { # $1 dmdev, $2 data dev + local _dev_size=$(blockdev --getsz /dev/mapper/$1) + dmsetup load $1 --table "0 $_dev_size linear $2 0" || fail + dmsetup resume $1 || fail + dmsetup remove --retry $1-err 2>/dev/null || fail +} + +function prepare_linear_dev() { + local _sizemb=$1 + shift + + if [ "$_sizemb" -gt 32 ]; then + preparebig $_sizemb + else + prepare dev_size_mb=$_sizemb $@ + fi + + dmsetup create $OVRDEV --table "0 $((_sizemb*1024*2)) linear $DEV 0" || fail + + OLD_DEV=$DEV + DEV=/dev/mapper/$OVRDEV +} + +function get_error_offsets() # $1 devsize, $2 minimal offset, $3 sector_size [512 if omitted], $4 max offset +{ + local _devsize=$(($1*1024*2)) + local _sector_size=${3:-512} + local _max_offset=${4:-$_devsize} + _sector_size=$((_sector_size/512)) + + # 8 sectors minimal size (4096) + ERRLENGTH=$((($RANDOM%56)+8)) + ERRLENGTH=$(($ERRLENGTH-($ERRLENGTH%$_sector_size))) + + ERROFFSET=$(($2+((2*$RANDOM)%($_max_offset-$2-$ERRLENGTH)))) + ERROFFSET=$(($ERROFFSET-($ERROFFSET%$_sector_size))) +} + +function reencrypt_recover() { # $1 sector size, $2 resilience, $3 digest, [$4 header] + echo -n "resilience mode: $2 ..." + local _hdr="" + test -z "$4" || _hdr="--header $4" + + error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH + echo $PWD1 | $CRYPTSETUP reencrypt $DEV $_hdr --hotzone-size 1M --resilience $2 --sector-size $1 -q $FAST_PBKDF_ARGON >/dev/null 2>&1 && fail + fix_writes $OVRDEV $OLD_DEV + + echo $PWD1 | $CRYPTSETUP -q repair $DEV $_hdr || fail + + check_hash $PWD1 $3 $4 + + echo $PWD1 | $CRYPTSETUP reencrypt $DEV $_hdr --resilience $2 --sector-size $1 -q $FAST_PBKDF_ARGON || fail + check_hash $PWD1 $3 $4 + + echo "[OK]" +} + +function reencrypt_recover_online() { # $1 sector size, $2 resilience, $3 digest, [$4 header] + echo -n "resilience mode: $2 ..." + local _hdr="" + test -z "$4" || _hdr="--header $4" + + echo $PWD1 | $CRYPTSETUP open $DEV $_hdr $DEV_NAME || fail + + error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH + echo $PWD1 | $CRYPTSETUP reencrypt --active-name $DEV_NAME $_hdr --hotzone-size 1M --resilience $2 --sector-size $1 -q $FAST_PBKDF_ARGON >/dev/null 2>&1 && fail + $CRYPTSETUP status $DEV_NAME $_hdr | grep -q "reencryption: in-progress" || fail + $CRYPTSETUP close $DEV_NAME || fail + fix_writes $OVRDEV $OLD_DEV + + # recovery during activation + echo $PWD1 | $CRYPTSETUP open $DEV $_hdr $DEV_NAME || fail + check_hash_dev /dev/mapper/$DEV_NAME $3 + + $CRYPTSETUP luksDump ${4:-$DEV} | grep -q "online-reencrypt" + if [ $? -eq 0 ]; then + $CRYPTSETUP status $DEV_NAME $_hdr | grep -q "reencryption: in-progress" || fail + echo $PWD1 | $CRYPTSETUP reencrypt --active-name $DEV_NAME $_hdr --resilience $2 --resume-only -q || fail + check_hash_dev /dev/mapper/$DEV_NAME $3 + fi + + $CRYPTSETUP close $DEV_NAME || fail + echo "[OK]" +} + +function encrypt_recover() { # $1 sector size, $2 reduce size, $3 digest, $4 device size in sectors, $5 origin digest + wipe_dev $DEV + check_hash_dev $DEV $5 + + echo -n "resilience mode: datashift ..." + + echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --reduce-device-size $2 --sector-size $1 -q $FAST_PBKDF_ARGON --init-only >/dev/null 2>&1 || fail + + error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH + echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q >/dev/null 2>&1 && fail + fix_writes $OVRDEV $OLD_DEV + + echo $PWD1 | $CRYPTSETUP -q repair $DEV || fail + + $CRYPTSETUP luksDump $DEV | grep -q "online-reencrypt" + if [ $? -eq 0 ]; then + check_hash $PWD1 $3 + echo $PWD1 | $CRYPTSETUP reencrypt $DEV --sector-size $1 -q $FAST_PBKDF_ARGON || fail + fi + + check_hash_head $PWD1 $4 $3 + + echo "[OK]" +} + +function encrypt_recover_online() { # $1 sector size, $2 reduce size, $3 digest, $4 device size in sectors, $5 origin digest + wipe_dev $DEV + check_hash_dev $DEV $5 + + echo -n "resilience mode: datashift ..." + + echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --reduce-device-size $2 --sector-size $1 -q $FAST_PBKDF_ARGON --init-only > /dev/null || fail + echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail + + error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH + echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q >/dev/null 2>&1 && fail + $CRYPTSETUP status $DEV_NAME | grep -q "reencryption: in-progress" || fail + $CRYPTSETUP close $DEV_NAME || fail + fix_writes $OVRDEV $OLD_DEV + + # recovery in activation + echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail + + $CRYPTSETUP luksDump $DEV | grep -q "online-reencrypt" + if [ $? -eq 0 ]; then + $CRYPTSETUP status $DEV_NAME | grep -q "reencryption: in-progress" || fail + check_hash_dev /dev/mapper/$DEV_NAME $3 + echo $PWD1 | $CRYPTSETUP reencrypt --resume-only --active-name $DEV_NAME -q || fail + fi + + $CRYPTSETUP close $DEV_NAME || fail + check_hash_head $PWD1 $4 $3 + + echo "[OK]" +} + +function encrypt_recover_detached() { # $1 sector size, $2 resilience, $3 digest, $4 hdr + wipe_dev $DEV + check_hash_dev $DEV $3 + + echo -n "resilience mode: $2 ..." + + error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH + echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --hotzone-size 1M --header $4 --resilience $2 --sector-size $1 -q $FAST_PBKDF_ARGON 2>/dev/null && fail + fix_writes $OVRDEV $OLD_DEV + + echo $PWD1 | $CRYPTSETUP repair $DEV --header $4 || fail + + check_hash $PWD1 $3 $4 + + echo $PWD1 | $CRYPTSETUP reencrypt $DEV --header $4 --resilience $2 --sector-size $1 -q $FAST_PBKDF_ARGON || fail + check_hash $PWD1 $3 $4 + + echo "[OK]" +} + +function encrypt_recover_detached_online() { # $1 sector size, $2 resilience, $3 digest, $4 hdr + wipe_dev $DEV + check_hash_dev $DEV $3 + + echo -n "resilience mode: $2 ..." + + echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --hotzone-size 1M --header $4 --resilience $2 --sector-size $1 -q $FAST_PBKDF_ARGON --init-only || fail + echo $PWD1 | $CRYPTSETUP open $DEV --header $4 $DEV_NAME || fail + + error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH + echo $PWD1 | $CRYPTSETUP reencrypt -q $DEV --header $4 --hotzone-size 1M 2>/dev/null && fail + $CRYPTSETUP status $DEV_NAME --header $4 | grep -q "reencryption: in-progress" || fail + $CRYPTSETUP close $DEV_NAME || fail + fix_writes $OVRDEV $OLD_DEV + + echo $PWD1 | $CRYPTSETUP open $DEV --header $4 $DEV_NAME || fail + check_hash_dev /dev/mapper/$DEV_NAME $3 + + $CRYPTSETUP luksDump $4 | grep -q "online-reencrypt" + if [ $? -eq 0 ]; then + $CRYPTSETUP status $DEV_NAME --header $4 | grep -q "reencryption: in-progress" || fail + echo $PWD1 | $CRYPTSETUP reencrypt --active-name $DEV_NAME --resume-only --header $4 --resilience $2 -q || fail + check_hash_dev /dev/mapper/$DEV_NAME $3 + fi + + $CRYPTSETUP close $DEV_NAME || fail + + echo "[OK]" +} + +function decrypt_recover_detached() { # $1 sector size, $2 resilience, $3 digest, $4 hdr + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size $1 --header $4 $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 $4 + check_hash $PWD1 $3 $4 + + echo -n "resilience mode: $2 ..." + + error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH + echo $PWD1 | $CRYPTSETUP reencrypt $DEV --decrypt --hotzone-size 1M --header $4 --resilience $2 -q 2>/dev/null && fail + fix_writes $OVRDEV $OLD_DEV + + echo $PWD1 | $CRYPTSETUP repair $DEV --header $4 || fail + + $CRYPTSETUP luksDump $4 | grep -q "online-reencrypt" + if [ $? -eq 0 ]; then + check_hash $PWD1 $3 $4 + echo $PWD1 | $CRYPTSETUP reencrypt $DEV --resume-only --header $4 --resilience $2 -q || fail + fi + + check_hash_dev $DEV $3 + + echo "[OK]" +} + +function decrypt_recover_detached_online() { # $1 sector size, $2 resilience, $3 digest, $4 hdr + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size $1 --header $4 $FAST_PBKDF_ARGON $DEV || fail + echo $PWD1 | $CRYPTSETUP open $DEV --header $4 $DEV_NAME || fail + wipe_dev /dev/mapper/$DEV_NAME + check_hash_dev /dev/mapper/$DEV_NAME $3 + + echo -n "resilience mode: $2 ..." + + error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH + echo $PWD1 | $CRYPTSETUP reencrypt $DEV --decrypt --hotzone-size 1M --header $4 --resilience $2 -q 2>/dev/null && fail + $CRYPTSETUP status $DEV_NAME --header $4 | grep -q "reencryption: in-progress" || fail + $CRYPTSETUP close $DEV_NAME || fail + fix_writes $OVRDEV $OLD_DEV + + # recovery during activation + echo $PWD1 | $CRYPTSETUP open $DEV --header $4 $DEV_NAME || fail + + $CRYPTSETUP luksDump $4 | grep -q "online-reencrypt" + if [ $? -eq 0 ]; then + $CRYPTSETUP status $DEV_NAME --header $4 | grep -q "reencryption: in-progress" || fail + check_hash_dev /dev/mapper/$DEV_NAME $3 + echo $PWD1 | $CRYPTSETUP reencrypt $DEV --header $4 --resilience $2 -q || fail + fi + + $CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 && fail + check_hash_dev $DEV $3 + + echo "[OK]" +} + +# sector size (bytes) +# reenc dev size (sectors) +# reenc dev digest +# resilience +# orig size +# orig size digest +# hdr (optional) +function reencrypt_offline_fixed_size() { + local _esz=$(($1>>9)) + local _hdr="" + test -z "$7" || _hdr="--header $7" + + # reencrypt with fixed device size + echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --sector-size $1 --device-size $2s --resilience $4 || fail + check_hash_head $PWD1 $2 $3 $7 + wipe $PWD1 $7 + + # try to reencrypt device size + 1 encryption sector size + echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --sector-size $1 --init-only || fail + echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --device-size $(($5+_esz))s --resilience $4 2>/dev/null && fail + check_hash $PWD1 $6 $7 + + # misaligned reencryption size + if [ $_esz -gt 1 ]; then + echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --device-size $(($2+_esz-1))s --resilience $4 2>/dev/null && fail + $CRYPTSETUP luksDump ${7:-$DEV} | grep -q "2: crypt" || fail + $CRYPTSETUP luksDump ${7:-$DEV} | grep -q "3: crypt" && fail + check_hash $PWD1 $6 $7 + fi +} + +# sector size (bytes) +# reenc dev size (sectors) +# reenc dev digest +# resilience +# orig size +# orig size digest +# hdr +function encrypt_offline_fixed_size() { + local _esz=$(($1>>9)) + + # reencrypt with fixed device size + wipe_dev $DEV + echo $PWD1 | $CRYPTSETUP reencrypt --encrypt -q $FAST_PBKDF_ARGON $DEV --header $7 --sector-size $1 --device-size $2s --resilience $4 || fail + check_hash_head $PWD1 $2 $3 $7 + + # try to reencrypt device size + 1 encryption sector size + wipe_dev $DEV + echo $PWD1 | $CRYPTSETUP reencrypt --encrypt -q $FAST_PBKDF_ARGON $DEV --header $7 --sector-size $1 --init-only || fail + echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV --header $7 --device-size $(($5+_esz))s --resilience $4 2>/dev/null && fail + check_hash $PWD1 $6 $7 + + # misaligned reencryption size + if [ $_esz -gt 1 ]; then + echo $PWD1 | $CRYPTSETUP reencrypt --encrypt -q $FAST_PBKDF_ARGON $DEV --header $7 --sector-size $1 --init-only || fail + echo $PWD1 | $CRYPTSETUP reencrypt -q $DEV --header $7 --device-size $(($2+_esz-1))s --resilience $4 2>/dev/null && fail + $CRYPTSETUP luksDump $7 | grep -q "2: crypt" || fail + $CRYPTSETUP luksDump $7 | grep -q "3: crypt" && fail + check_hash $PWD1 $6 $7 + fi +} + +# sector size (bytes) +# reenc dev size (sectors) +# reenc dev digest +# resilience +# orig size +# orig size digest +# hdr +function decrypt_offline_fixed_size() { + local _esz=$(($1>>9)) + + # decrypt with fixed device size + echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -q $FAST_PBKDF_ARGON $DEV --header $7 --sector-size $1 || fail + wipe $PWD1 $7 + echo $PWD1 | $CRYPTSETUP reencrypt --decrypt -q $DEV --header $7 --device-size $2s --resilience $4 || fail + + dmsetup load $OVRDEV --table "0 $2 linear $OLD_DEV 0" || fail + dmsetup resume $OVRDEV || fail + check_hash_dev $DEV $3 + dmsetup load $OVRDEV --table "0 $5 linear $OLD_DEV 0" || fail + dmsetup resume $OVRDEV || fail + + # try to decrypt device size + 1 encryption sector size + echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -q $FAST_PBKDF_ARGON $DEV --header $7 --sector-size $1 || fail + wipe $PWD1 $7 + echo $PWD1 | $CRYPTSETUP reencrypt --decrypt -q $FAST_PBKDF_ARGON $DEV --header $7 --init-only || fail + echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV --header $7 --device-size $(($5+_esz))s --resilience $4 2>/dev/null && fail + check_hash $PWD1 $6 $7 + + # misaligned reencryption size + if [ $_esz -gt 1 ]; then + echo $PWD1 | $CRYPTSETUP reencrypt -q $DEV --header $7 --device-size $(($2+_esz-1))s --resilience $4 2>/dev/null && fail + $CRYPTSETUP luksDump $7 | grep -q "2: linear\|2: crypt" || fail + $CRYPTSETUP luksDump $7 | grep -q "3: crypt\|3: linear" && fail + check_hash $PWD1 $6 $7 + fi +} + +# sector size (bytes) +# reenc dev size (sectors) +# reenc dev digest +# resilience +# orig size +# orig size digest +# hdr (optional) +function reencrypt_online_fixed_size() { + local _esz=$(($1>>9)) + local _hdr="" + test -z "$7" || _hdr="--header $7" + + if [ -z "$_hdr" ]; then + echo $PWD1 | $CRYPTSETUP -q luksFormat --sector-size 512 --type luks2 --offset 16384 $FAST_PBKDF_ARGON $DEV || fail + else + echo $PWD1 | $CRYPTSETUP -q luksFormat --sector-size 512 --type luks2 $_hdr $FAST_PBKDF_ARGON $DEV || fail + fi + wipe $PWD1 $7 + + # reencrypt with fixed device size + echo $PWD1 | $CRYPTSETUP open $DEV $_hdr $DEV_NAME || fail + echo $PWD1 | $CRYPTSETUP resize $DEV_NAME $_hdr --size $2 || fail + echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --sector-size $1 --resilience $4 || fail + $CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "$2 sectors" || fail + $CRYPTSETUP close $DEV_NAME || fail + check_hash_head $PWD1 $2 $3 $7 + wipe $PWD1 $7 + + # active device != requested reencryption size + echo $PWD1 | $CRYPTSETUP open $DEV $_hdr $DEV_NAME || fail + echo $PWD1 | $CRYPTSETUP resize $DEV_NAME $_hdr --size $2 || fail + echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --sector-size $1 --init-only || fail + echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --device-size $(($2-_esz))s --resilience $4 2>/dev/null && fail + echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --device-size $2s --resilience $4 || fail + $CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "$2 sectors" || fail + $CRYPTSETUP close $DEV_NAME || fail + check_hash_head $PWD1 $2 $3 $7 + + # misaligned reencryption size + if [ $_esz -gt 1 ]; then + if [ -z "$_hdr" ]; then + echo $PWD1 | $CRYPTSETUP -q luksFormat --sector-size 512 --type luks2 --offset 16384 $FAST_PBKDF_ARGON $DEV || fail + else + echo $PWD1 | $CRYPTSETUP -q luksFormat --sector-size 512 --type luks2 $_hdr $FAST_PBKDF_ARGON $DEV || fail + fi + wipe $PWD1 $7 + check_hash $PWD1 $6 $7 + + echo $PWD1 | $CRYPTSETUP open $DEV $_hdr $DEV_NAME || fail + echo $PWD1 | $CRYPTSETUP resize $DEV_NAME $_hdr --size $(($2+_esz-1)) || fail + echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --sector-size $1 --init-only || fail + echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --resilience $4 2>/dev/null && fail + $CRYPTSETUP close $DEV_NAME || fail + check_hash $PWD1 $6 $7 + fi +} + +function setup_luks2_env() { + echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c aes-xts-plain64 $FAST_PBKDF_ARGON $DEV || fail + echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail + HAVE_KEYRING=$($CRYPTSETUP status $DEV_NAME | grep "key location: keyring") + if [ -n "$HAVE_KEYRING" ]; then + HAVE_KEYRING=1 + else + HAVE_KEYRING=0 + fi + DEF_XTS_KEY=$($CRYPTSETUP status $DEV_NAME | grep "keysize:" | sed 's/\( keysize: \)\([0-9]\+\)\(.*\)/\2/') + [ -n "$DEF_XTS_KEY" ] || fail "Failed to parse xts mode key size." + $CRYPTSETUP close $DEV_NAME || fail +} + +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} "$@" +} + +[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped." +[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped." +fips_mode && skip "This test cannot be run in FIPS mode." +modprobe --dry-run scsi_debug || exit 77 +modprobe dm-crypt || fail "dm-crypt failed to load" +modprobe dm-delay > /dev/null 2>&1 +dm_crypt_features + +if [ -n "$DM_SECTOR_SIZE" ]; then + TEST_SECTORS="512 4096" +else + TEST_SECTORS="512" +fi + +modinfo scsi_debug -p | grep -q opt_xferlen_exp && OPT_XFERLEN_EXP="opt_xferlen_exp=6" + +export LANG=C + +[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run + +# REENCRYPTION tests + +# 28 MiBs of zeros (32MiBs - 4MiB LUKS2 header) +HASH1=f8280c81b347b01405277bf9e8bf0685ae8be863ff104797c65b7169f8203fd2 +# 1 MiB of zeros +HASH2=30e14955ebf1352266dc2ff8067e68104607e750abb9d3b36582b8af909fcb58 +# 256 MiBs of zeros +HASH3=a6d72ac7690f53be6ae46ba88506bd97302a093f7108472bd9efc3cefda06484 +# 64 MiBs of zeroes +HASH4=3b6a07d0d404fab4e23b6d34bc6696a6a312dd92821332385e5af7c01c421351 +# 56 MiBs of zeroes +HASH5=8afcb7e7189ce4d112fd245eaa60c3cfcf5a5d5e1d6bf4eb85941d73ef8cfbd5 +# 43 MiBs of zeroes +HASH6=39f7c6d38af574fe2c90ef400dfaba8ef8edccd11bdac998a3f8143a86837331 +# 31 MiBs of zeroes +HASH7=18a393d1a505e22ccf3e29effe3005ea8627e4c36b7cca0e53f58121f49b67e1 +# 60 MiBs of zeroes +HASH8=cf5ac69ca412f9b3b1a8b8de27d368c5c05ed4b1b6aa40e6c38d9cbf23711342 + +prepare dev_size_mb=32 +setup_luks2_env + +echo "[1] Reencryption" +echo -n "[512 sector]" +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c aes-cbc-essiv:sha256 --offset 8192 $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 +check_hash $PWD1 $HASH1 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON 2>&1 | tail -1 | grep -q "not supported" && skip " No reenryption support, test skipped." +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH1 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH1 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH1 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH1 +# simple test --active-name can consume absolute path to mapping +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -c aes-xts-plain64 --init-only $FAST_PBKDF_ARGON || fail +echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail +echo $PWD1 | $CRYPTSETUP reencrypt --active-name /dev/mapper/$DEV_NAME --resilience none -q || fail +XTS_KEY=$($CRYPTSETUP status $DEV_NAME | grep "keysize:" | sed 's/\( keysize: \)\([0-9]\+\)\(.*\)/\2/') +[ "$XTS_KEY" -eq "$DEF_XTS_KEY" ] || fail "xts mode has wrong key size after reencryption ($XTS_KEY != expected $DEF_XTS_KEY)" +echo $PWD1 | $CRYPTSETUP close $DEV_NAME || fail +echo -n "[OK][4096 sector]" +prepare sector_size=4096 dev_size_mb=32 +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c aes-cbc-essiv:sha256 --offset 8192 $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 +check_hash $PWD1 $HASH1 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH1 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH1 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH1 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH1 +if [ -n "$DM_SECTOR_SIZE" ]; then + echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON --sector-size 4096 || fail + check_hash $PWD1 $HASH1 + echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal --sector-size 2048 $FAST_PBKDF_ARGON || fail + check_hash $PWD1 $HASH1 + echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON --sector-size 1024 || fail + check_hash $PWD1 $HASH1 + echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum --sector-size 512 $FAST_PBKDF_ARGON || fail + check_hash $PWD1 $HASH1 +fi +echo -n "[OK][4096/512 sector]" +prepare sector_size=512 physblk_exp=3 dev_size_mb=32 +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c aes-cbc-essiv:sha256 --offset 8192 $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 +check_hash $PWD1 $HASH1 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH1 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH1 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH1 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH1 +echo "[OK]" + +# reencrypt minimal device size (FIXME: change data device size to single encryption sector size) +# temporary small device size is default luks2 hdr size + 1MiB +echo -n "[small device reencryption]" +prepare dev_size_mb=5 +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c aes-cbc-essiv:sha256 --offset 8192 $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 +check_hash $PWD1 $HASH2 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH2 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH2 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH2 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH2 +if [ -n "$DM_SECTOR_SIZE" ]; then + echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON --sector-size 4096 || fail + check_hash $PWD1 $HASH2 + echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal --sector-size 2048 $FAST_PBKDF_ARGON || fail + check_hash $PWD1 $HASH2 + echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON --sector-size 1024 || fail + check_hash $PWD1 $HASH2 + echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum --sector-size 512 $FAST_PBKDF_ARGON || fail + check_hash $PWD1 $HASH2 +fi +echo "[OK]" + +echo "[2] Encryption with data shift" +# well, movin' zeroes :-) +preparebig 64 +wipe_dev $DEV +check_hash_dev $DEV $HASH4 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c aes-cbc-essiv:sha256 -s 128 --reduce-device-size 8M -q $FAST_PBKDF_ARGON || fail +check_hash_head $PWD1 $((56*1024*2)) $HASH5 +wipe_dev $DEV +check_hash_dev $DEV $HASH4 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c twofish-cbc-essiv:sha256 -s 128 --reduce-device-size 21M -q $FAST_PBKDF_ARGON || fail +check_hash_head $PWD1 $((43*1024*2)) $HASH6 +wipe_dev $DEV +# offset 21504 equals 10,5MiBs, equals --reduce-device-size 21M from test above (30M is ignored here, we'll reduce it to 21M in cryptsetup anyway) +echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c twofish-cbc-essiv:sha256 -s 128 --offset 21504 --reduce-device-size 30M -q $FAST_PBKDF_ARGON > /dev/null || fail +check_hash_head $PWD1 $((43*1024*2)) $HASH6 +wipe_dev $DEV +echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --reduce-device-size 33M -q $FAST_PBKDF_ARGON || fail +check_hash_head $PWD1 $((31*1024*2)) $HASH7 +wipe_dev $DEV +echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --reduce-device-size 64M -q $FAST_PBKDF_ARGON > /dev/null 2>&1 && fail +echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --reduce-device-size 8M --init-only -q $FAST_PBKDF_ARGON $DEV || fail +resize_file $DEVBIG -512 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV 2> /dev/null && fail +resize_file $DEVBIG 512 +wipe_dev $DEV +echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c aes-cbc-essiv:sha256 -s 128 --offset 32760 --reduce-device-size 8M -q $FAST_PBKDF_ARGON --init-only >/dev/null 2>&1 && fail +# data offset at 21MiB +echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --header $IMG_HDR --offset 43008 --reduce-device-size 21M -q $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH6 $IMG_HDR +$CRYPTSETUP luksHeaderRestore --header-backup-file $IMG_HDR $DEV -q || fail +check_hash $PWD1 $HASH6 + +# Device activation after encryption initialization +wipe_dev $DEV +echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --init-only -c aes-cbc-essiv:sha256 -s 128 -S11 --reduce-device-size 8M -q $FAST_PBKDF_ARGON $DEV_NAME >/dev/null || fail +$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 || fail +check_hash_dev /dev/mapper/$DEV_NAME $HASH5 +echo $PWD1 | $CRYPTSETUP reencrypt --resume-only $DEV -q || fail +check_hash_dev /dev/mapper/$DEV_NAME $HASH5 + +echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c aes-cbc-essiv:sha256 -s 128 --reduce-device-size 8M -q $FAST_PBKDF_ARGON $DEV_NAME 2>/dev/null && fail +$CRYPTSETUP close $DEV_NAME +check_hash_head $PWD1 $((56*1024*2)) $HASH5 + +# Device activation using key file +wipe_dev $DEV +echo -n $PWD1 > $KEY1 +$CRYPTSETUP reencrypt $DEV --encrypt --init-only -c aes-cbc-essiv:sha256 -s 128 --reduce-device-size 8M --key-file $KEY1 -q $FAST_PBKDF_ARGON $DEV_NAME >/dev/null || fail +$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 || fail +$CRYPTSETUP close $DEV_NAME +echo $PWD1 | $CRYPTSETUP open $DEV --test-passphrase || fail + +echo "[3] Encryption with detached header" +preparebig 256 +wipe_dev $DEV +echo $PWD1 | $CRYPTSETUP reencrypt --encrypt -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail +check_hash $PWD1 $HASH3 $IMG_HDR +wipe_dev $DEV +echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --resilience journal --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail +check_hash $PWD1 $HASH3 $IMG_HDR +wipe_dev $DEV +echo $PWD1 | $CRYPTSETUP reencrypt --encrypt -c twofish-cbc-essiv:sha256 -s 128 --resilience none --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail +check_hash $PWD1 $HASH3 $IMG_HDR +wipe_dev $DEV +echo $PWD1 | $CRYPTSETUP reencrypt --encrypt -c serpent-xts-plain --resilience checksum --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail +check_hash $PWD1 $HASH3 $IMG_HDR + +# Device activation after encryption initialization +wipe_dev $DEV +echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --init-only -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV $DEV_NAME >/dev/null || fail +$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 || fail +check_hash_dev /dev/mapper/$DEV_NAME $HASH3 +echo $PWD1 | $CRYPTSETUP reencrypt --resume-only --header $IMG_HDR --active-name $DEV_NAME -q || fail +check_hash_dev /dev/mapper/$DEV_NAME $HASH3 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c aes-cbc-essiv:sha256 -s 128 --reduce-device-size 8M -q $FAST_PBKDF_ARGON $DEV_NAME 2>/dev/null && fail +$CRYPTSETUP close $DEV_NAME +check_hash $PWD1 $HASH3 $IMG_HDR + +# Device activation using key file +wipe_dev $DEV +echo -n $PWD1 > $KEY1 +$CRYPTSETUP reencrypt $DEV --encrypt --init-only -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR --key-file $KEY1 -q $FAST_PBKDF_ARGON $DEV_NAME >/dev/null || fail +$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 || fail +$CRYPTSETUP close $DEV_NAME +echo $PWD1 | $CRYPTSETUP open --header $IMG_HDR $DEV --test-passphrase || fail + +echo "[4] Reencryption with detached header" +wipe $PWD1 $IMG_HDR +echo $PWD1 | $CRYPTSETUP reencrypt -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail +check_hash $PWD1 $HASH3 $IMG_HDR +echo $PWD1 | $CRYPTSETUP reencrypt --resilience journal --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail +check_hash $PWD1 $HASH3 $IMG_HDR +echo $PWD1 | $CRYPTSETUP reencrypt -c twofish-cbc-essiv:sha256 -s 128 --resilience none --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail +check_hash $PWD1 $HASH3 $IMG_HDR +echo $PWD1 | $CRYPTSETUP reencrypt -c serpent-xts-plain --resilience checksum --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail +check_hash $PWD1 $HASH3 $IMG_HDR +# trivial check for detached header misuse +dd if=/dev/zero of=$IMG bs=4k count=1 >/dev/null 2>&1 +echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail +echo $PWD1 | $CRYPTSETUP open $IMG $DEV_NAME --header $IMG_HDR || fail +echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME2 --header $IMG_HDR || fail +echo $PWD1 | $CRYPTSETUP reencrypt --active-name $DEV_NAME --header $IMG_HDR -q || fail +# key description mismatch in active device +echo $PWD1 | $CRYPTSETUP reencrypt --active-name $DEV_NAME2 --header $IMG_HDR >/dev/null 2>&1 && fail +# also check it can abort initialization in this case +$CRYPTSETUP luksDump $IMG_HDR | grep -q "online-reencrypt" && fail +$CRYPTSETUP close $DEV_NAME || fail +$CRYPTSETUP close $DEV_NAME2 || fail + +echo "[5] Decryption with detached header" +echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 $IMG_HDR +echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --header $IMG_HDR $DEV || fail +check_hash_dev $DEV $HASH3 +echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 $IMG_HDR +echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --resilience journal --header $IMG_HDR $DEV || fail +check_hash_dev $DEV $HASH3 +echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c twofish-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 $IMG_HDR +echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --resilience none --header $IMG_HDR $DEV || fail +check_hash_dev $DEV $HASH3 +echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c serpent-xts-plain --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 $IMG_HDR +echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --resilience checksum --header $IMG_HDR $DEV || fail +check_hash_dev $DEV $HASH3 + +# check deferred remove works as expected after decryption +echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c serpent-xts-plain --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail +open_crypt $PWD1 $IMG_HDR +dmsetup create $DEV_NAME2 --table "0 1 linear /dev/mapper/$DEV_NAME 0" || fail +echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --resilience checksum --header $IMG_HDR --active-name $DEV_NAME || fail +$CRYPTSETUP status $DEV_NAME >/dev/null || fail +dmsetup remove --retry $DEV_NAME2 +$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 && fail + +# check tool can block some funny user ideas +preparebig 64 +echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c serpent-xts-plain -q $FAST_PBKDF_ARGON $DEV || fail +echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV -q 2>/dev/null && fail +echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV --header $DEV -q 2>/dev/null && fail +open_crypt $PWD1 +echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --active-name $DEV_NAME -q 2>/dev/null && fail +echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --active-name $DEV_NAME --header $DEV -q 2>/dev/null && fail +$CRYPTSETUP status $DEV_NAME | grep -q "reencryption: in-progress" && fail +$CRYPTSETUP close $DEV_NAME + +if ! dm_delay_features; then + echo "dm-delay target is missing, skipping recovery tests." + remove_mapping + exit 0 +fi + +echo "[6] Reencryption recovery" +# (check opt-io size optimization in reencryption code does not affect recovery) +# device with opt-io size 32k +prepare_linear_dev 32 opt_blks=64 $OPT_XFERLEN_EXP +OFFSET=8192 + +echo "sector size 512->512" + +get_error_offsets 32 $OFFSET +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 +check_hash $PWD1 $HASH1 + +echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" +reencrypt_recover 512 checksum $HASH1 +reencrypt_recover 512 journal $HASH1 + +if [ -n "$DM_SECTOR_SIZE" ]; then + echo "sector size 512->4096" + + get_error_offsets 32 $OFFSET 4096 + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 + check_hash $PWD1 $HASH1 + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + reencrypt_recover 4096 checksum $HASH1 + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 + check_hash $PWD1 $HASH1 + reencrypt_recover 4096 journal $HASH1 + + echo "sector size 4096->4096" + + get_error_offsets 32 $OFFSET 4096 + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 --sector-size 4096 -c aes-cbc-essiv:sha256 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 + check_hash $PWD1 $HASH1 + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + reencrypt_recover 4096 checksum $HASH1 + reencrypt_recover 4096 journal $HASH1 +fi + +echo "[7] Reencryption recovery (online i/o error)" + +echo "sector size 512->512" + +get_error_offsets 32 $OFFSET +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 +check_hash $PWD1 $HASH1 + +echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" +reencrypt_recover_online 512 checksum $HASH1 +reencrypt_recover_online 512 journal $HASH1 + +if [ -n "$DM_SECTOR_SIZE" ]; then + echo "sector size 512->4096" + + get_error_offsets 32 $OFFSET 4096 + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 + check_hash $PWD1 $HASH1 + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + reencrypt_recover_online 4096 checksum $HASH1 + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 + check_hash $PWD1 $HASH1 + reencrypt_recover_online 4096 journal $HASH1 + + echo "sector size 4096->4096" + + get_error_offsets 32 $OFFSET 4096 + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 --sector-size 4096 -c aes-cbc-essiv:sha256 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 + check_hash $PWD1 $HASH1 + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + reencrypt_recover_online 4096 checksum $HASH1 + reencrypt_recover_online 4096 journal $HASH1 +fi + +echo "[8] Reencryption with detached header recovery" +prepare_linear_dev 31 opt_blks=64 $OPT_XFERLEN_EXP + +echo "sector size 512->512" + +get_error_offsets 31 0 +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 $IMG_HDR +check_hash $PWD1 $HASH7 $IMG_HDR + +echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" +reencrypt_recover 512 checksum $HASH7 $IMG_HDR +reencrypt_recover 512 journal $HASH7 $IMG_HDR + +if [ -n "$DM_SECTOR_SIZE" ]; then + echo "sector size 512->4096" + + get_error_offsets 31 0 4096 + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 $IMG_HDR + check_hash $PWD1 $HASH7 $IMG_HDR + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + reencrypt_recover 4096 checksum $HASH7 $IMG_HDR + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 $IMG_HDR + check_hash $PWD1 $HASH7 $IMG_HDR + reencrypt_recover 4096 journal $HASH7 $IMG_HDR + + echo "sector size 4096->4096" + + get_error_offsets 31 0 4096 + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 4096 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 $IMG_HDR + check_hash $PWD1 $HASH7 $IMG_HDR + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + reencrypt_recover 4096 checksum $HASH7 $IMG_HDR + reencrypt_recover 4096 journal $HASH7 $IMG_HDR +fi + +echo "[9] Reencryption with detached header recovery (online i/o error)" + +echo "sector size 512->512" + +get_error_offsets 31 0 +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 $IMG_HDR +check_hash $PWD1 $HASH7 $IMG_HDR + +echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" +reencrypt_recover_online 512 checksum $HASH7 $IMG_HDR +reencrypt_recover_online 512 journal $HASH7 $IMG_HDR + +if [ -n "$DM_SECTOR_SIZE" ]; then + echo "sector size 512->4096" + + get_error_offsets 31 0 4096 + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 $IMG_HDR + check_hash $PWD1 $HASH7 $IMG_HDR + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + reencrypt_recover_online 4096 checksum $HASH7 $IMG_HDR + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 $IMG_HDR + check_hash $PWD1 $HASH7 $IMG_HDR + reencrypt_recover_online 4096 journal $HASH7 $IMG_HDR + + echo "sector size 4096->4096" + + get_error_offsets 31 0 4096 + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 4096 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 $IMG_HDR + check_hash $PWD1 $HASH7 $IMG_HDR + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + reencrypt_recover_online 4096 checksum $HASH7 $IMG_HDR + reencrypt_recover_online 4096 journal $HASH7 $IMG_HDR +fi + +echo "[10] Encryption recovery" +prepare_linear_dev 64 +OFFSET=$((2*1024*2)) + +echo "sector size 512" + +get_error_offsets 64 $OFFSET 512 $((62*1024*2)) + +echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" +encrypt_recover 512 4M $HASH8 $((60*1024*2)) $HASH4 + +if [ -n "$DM_SECTOR_SIZE" ]; then + echo "sector size 4096" + + get_error_offsets 64 $OFFSET 4096 $((62*1024*2)) + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + encrypt_recover 4096 4M $HASH8 $((60*1024*2)) $HASH4 +fi + +echo "[11] Encryption recovery (online i/o error)" + +echo "sector size 512" + +get_error_offsets 64 $OFFSET 512 $((62*1024*2)) + +echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" +encrypt_recover_online 512 4M $HASH8 $((60*1024*2)) $HASH4 + +if [ -n "$DM_SECTOR_SIZE" ]; then + echo "sector size 4096" + + get_error_offsets 64 $OFFSET 4096 $((62*1024*2)) + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + encrypt_recover_online 4096 4M $HASH8 $((60*1024*2)) $HASH4 +fi + +echo "[12] Encryption with detached header recovery" +prepare_linear_dev 31 opt_blks=64 $OPT_XFERLEN_EXP + +get_error_offsets 31 0 + +echo "sector size 512" + +echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" +encrypt_recover_detached 512 checksum $HASH7 $IMG_HDR +encrypt_recover_detached 512 journal $HASH7 $IMG_HDR + +if [ -n "$DM_SECTOR_SIZE" ]; then + get_error_offsets 31 0 4096 + + echo "sector size 4096" + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + encrypt_recover_detached 4096 checksum $HASH7 $IMG_HDR + encrypt_recover_detached 4096 journal $HASH7 $IMG_HDR +fi + +echo "[13] Encryption with detached header recovery (online i/o error)" + +get_error_offsets 31 0 + +echo "sector size 512" + +echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" +encrypt_recover_detached_online 512 checksum $HASH7 $IMG_HDR +encrypt_recover_detached_online 512 journal $HASH7 $IMG_HDR + +if [ -n "$DM_SECTOR_SIZE" ]; then + get_error_offsets 31 0 4096 + + echo "sector size 4096" + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + encrypt_recover_detached_online 4096 checksum $HASH7 $IMG_HDR + encrypt_recover_detached_online 4096 journal $HASH7 $IMG_HDR +fi + +echo "[14] Decryption with detached header recovery" + +echo "sector size 512" + +# TODO: What should decryption do when it finishes decryption during recovery (with open) +get_error_offsets 31 2049 + +echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" +decrypt_recover_detached 512 journal $HASH7 $IMG_HDR +decrypt_recover_detached 512 checksum $HASH7 $IMG_HDR + +if [ -n "$DM_SECTOR_SIZE" ]; then + echo "sector size 4096" + + # TODO: What should decryption do when it finishes decryption during recovery (with open) + get_error_offsets 31 2048 4096 + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + decrypt_recover_detached 4096 checksum $HASH7 $IMG_HDR + decrypt_recover_detached 4096 journal $HASH7 $IMG_HDR +fi + +echo "[15] Decryption with detached header recovery (online i/o error)" + +echo "sector size 512" + +# TODO: What should decryption do when it finishes decryption during recovery (with open) +get_error_offsets 31 2049 + +echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" +decrypt_recover_detached_online 512 journal $HASH7 $IMG_HDR +decrypt_recover_detached_online 512 checksum $HASH7 $IMG_HDR + +if [ -n "$DM_SECTOR_SIZE" ]; then + echo "sector size 4096" + + # TODO: What should decryption do when it finishes decryption during recovery (with open) + get_error_offsets 31 2048 4096 + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + decrypt_recover_detached_online 4096 checksum $HASH7 $IMG_HDR + decrypt_recover_detached_online 4096 journal $HASH7 $IMG_HDR +fi + +echo "[16] Offline reencryption with fixed device size." +preparebig 68 +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --offset 16384 $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 +check_hash $PWD1 $HASH8 + +for test_ss in $TEST_SECTORS; do +printf "sector size %4s: " $test_ss +for test_res in checksum journal none; do + echo -n "[$test_res]" + reencrypt_offline_fixed_size $test_ss 2048 $HASH2 $test_res $((60*1024*2)) $HASH8 + reencrypt_offline_fixed_size $test_ss $((28*1024*2)) $HASH1 $test_res $((60*1024*2)) $HASH8 + reencrypt_offline_fixed_size $test_ss $((31*1024*2)) $HASH7 $test_res $((60*1024*2)) $HASH8 + echo -n "[OK]" +done +echo "" +done + +echo "[17] Online reencryption with fixed device size." +for test_ss in $TEST_SECTORS; do +printf "sector size %4s: " $test_ss +for test_res in checksum journal none; do + echo -n "[$test_res]" + reencrypt_online_fixed_size $test_ss 2048 $HASH2 $test_res $((60*1024*2)) $HASH8 + reencrypt_online_fixed_size $test_ss $((28*1024*2)) $HASH1 $test_res $((60*1024*2)) $HASH8 + reencrypt_online_fixed_size $test_ss $((31*1024*2)) $HASH7 $test_res $((60*1024*2)) $HASH8 + echo -n "[OK]" +done +echo "" +done + +echo "[18] Offline reencryption with fixed device size (detached header)." +preparebig 60 +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 $IMG_HDR +check_hash $PWD1 $HASH8 $IMG_HDR + +for test_ss in $TEST_SECTORS; do +printf "sector size %4s: " $test_ss +for test_res in checksum journal none; do + echo -n "[$test_res]" + reencrypt_offline_fixed_size $test_ss 2048 $HASH2 $test_res $((60*1024*2)) $HASH8 $IMG_HDR + reencrypt_offline_fixed_size $test_ss $((28*1024*2)) $HASH1 $test_res $((60*1024*2)) $HASH8 $IMG_HDR + reencrypt_offline_fixed_size $test_ss $((31*1024*2)) $HASH7 $test_res $((60*1024*2)) $HASH8 $IMG_HDR + echo -n "[OK]" +done +echo "" +done + +echo "[19] Online reencryption with fixed device size (detached header)." +for test_ss in $TEST_SECTORS; do +printf "sector size %4s: " $test_ss +for test_res in checksum journal none; do + echo -n "[$test_res]" + reencrypt_online_fixed_size $test_ss 2048 $HASH2 $test_res $((60*1024*2)) $HASH8 $IMG_HDR + reencrypt_online_fixed_size $test_ss $((28*1024*2)) $HASH1 $test_res $((60*1024*2)) $HASH8 $IMG_HDR + reencrypt_online_fixed_size $test_ss $((31*1024*2)) $HASH7 $test_res $((60*1024*2)) $HASH8 $IMG_HDR + echo -n "[OK]" +done +echo "" +done + +echo "[20] Offline encryption with fixed device size (detached header)." +for test_ss in $TEST_SECTORS; do +printf "sector size %4s: " $test_ss +for test_res in checksum journal none; do + echo -n "[$test_res]" + encrypt_offline_fixed_size $test_ss 2048 $HASH2 $test_res $((60*1024*2)) $HASH8 $IMG_HDR + encrypt_offline_fixed_size $test_ss $((28*1024*2)) $HASH1 $test_res $((60*1024*2)) $HASH8 $IMG_HDR + encrypt_offline_fixed_size $test_ss $((31*1024*2)) $HASH7 $test_res $((60*1024*2)) $HASH8 $IMG_HDR + echo -n "[OK]" +done +echo "" +done + +echo "[21] Offline decryption with fixed device size (detached header)." +prepare_linear_dev 60 +for test_ss in $TEST_SECTORS; do +printf "sector size %4s: " $test_ss +for test_res in checksum journal none; do + echo -n "[$test_res]" + decrypt_offline_fixed_size $test_ss 2048 $HASH2 $test_res $((60*1024*2)) $HASH8 $IMG_HDR + decrypt_offline_fixed_size $test_ss $((28*1024*2)) $HASH1 $test_res $((60*1024*2)) $HASH8 $IMG_HDR + decrypt_offline_fixed_size $test_ss $((31*1024*2)) $HASH7 $test_res $((60*1024*2)) $HASH8 $IMG_HDR + echo -n "[OK]" +done +echo "" +done + +echo "[22] Multi-keyslot device reencryption" +prepare dev_size_mb=17 +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --offset 32768 $FAST_PBKDF_ARGON $DEV || fail +echo -e "$PWD1\n$PWD2" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV || fail +echo -e "$PWD1\n$PWD3" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 +check_hash $PWD1 $HASH2 + +echo -e "$PWD1\n$PWD2\n$PWD3" | $CRYPTSETUP reencrypt $DEV -q || fail +check_hash $PWD1 $HASH2 +check_hash $PWD2 $HASH2 +check_hash $PWD3 $HASH2 + +# check at least pbkdf type is preserved +$CRYPTSETUP luksDump $DEV | grep -e "3: luks2" -A5 | grep -q "argon2" || fail +$CRYPTSETUP luksDump $DEV | grep -e "4: luks2" -A5 | grep -q "pbkdf2" || fail +$CRYPTSETUP luksDump $DEV | grep -e "5: luks2" -A5 | grep -q "argon2" || fail + +echo $PWD1 | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV $KEY1 || fail + +# with more keyslots, specific has to be selected +$CRYPTSETUP reencrypt $DEV -d $KEY1 -q 2>/dev/null && fail +$CRYPTSETUP reencrypt $DEV -d $KEY1 -q -S0 || fail +open_crypt +check_hash_dev /dev/mapper/$DEV_NAME $HASH2 +$CRYPTSETUP close $DEV_NAME + +# there should be single keyslot now +$CRYPTSETUP reencrypt $DEV -d $KEY1 -q || fail +echo $PWD1 | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV -S1 -d $KEY1 || fail + +echo $PWD3 | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV -S2 --unbound --key-size 32 || fail +echo $PWD3 | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV -S22 --unbound --key-size 32 || fail +echo $PWD3 | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV -S23 --unbound --key-size 32 || fail + +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -S1 -q || fail +$CRYPTSETUP open --test-passphrase -d $KEY1 $DEV 2>/dev/null && fail +echo $PWD3 | $CRYPTSETUP open --test-passphrase -S2 $DEV || fail +echo $PWD3 | $CRYPTSETUP open --test-passphrase -S22 $DEV || fail +check_hash $PWD1 $HASH2 + +# fill 31 keyslots +COUNT=27 +while [ $COUNT -gt 0 ]; do + echo -e "$PWD1\n$PWD1" | $CRYPTSETUP luksAddKey $DEV -q $FAST_PBKDF_ARGON || fail + COUNT=$((COUNT-1)) +done + +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -S0 -q 2>/dev/null && fail +echo $PWD1 | $CRYPTSETUP luksKillSlot $DEV 30 || fail +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -S0 || fail + +COUNT=14 +while [ $COUNT -gt 0 ]; do + echo -e "$PWD1\n$PWD1" | $CRYPTSETUP luksAddKey $DEV -q $FAST_PBKDF_ARGON || fail + COUNT=$((COUNT-1)) +done + +echo -e "$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1" | $CRYPTSETUP reencrypt $DEV -q 2>/dev/null && fail +echo $PWD1 | $CRYPTSETUP luksKillSlot $DEV 1 || fail +# one wrong passphrase +echo -e "$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD2" | $CRYPTSETUP reencrypt $DEV -q 2>/dev/null && fail +echo $PWD1 | $CRYPTSETUP reencrypt $DEV --resume-only -q 2>/dev/null && fail +echo -e "$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1" | $CRYPTSETUP reencrypt $DEV -q || fail + +echo "[23] Reencryption with specified new volume key" +prepare dev_size_mb=32 +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 256 -c aes-cbc-essiv:sha256 --offset 8192 $FAST_PBKDF_ARGON $DEV || fail +echo -e "$PWD1\n$PWD3" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 +check_hash $PWD1 $HASH1 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -S0 $FAST_PBKDF_ARGON --master-key-file $VKEY1 -s 128 || fail +check_hash $PWD1 $HASH1 +$CRYPTSETUP luksErase -q $DEV || fail +echo $PWD1 | $CRYPTSETUP luksAddKey -q $FAST_PBKDF_ARGON --master-key-file $VKEY1 -s 128 $DEV || fail +check_hash $PWD1 $HASH1 + +echo "[24] Reencryption with initial cipher_null" +# aka custom encryption +prepare dev_size_mb=32 +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c cipher_null-ecb --offset 8192 $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 +check_hash $PWD1 $HASH1 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -c aes-xts-plain64 -q $FAST_PBKDF_ARGON || fail +check_hash $PWD1 $HASH1 + +# online +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c cipher_null-ecb --offset 8192 $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 +echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -c aes-xts-plain64 -q $FAST_PBKDF_ARGON || fail +check_hash_dev /dev/mapper/$DEV_NAME $HASH1 +if [ $HAVE_KEYRING -gt 0 ]; then + $CRYPTSETUP status $DEV_NAME | grep -q "key location: keyring" || fail +fi +$CRYPTSETUP close $DEV_NAME + +# simulate LUKS2 device with cipher_null in both keyslot and segment (it can be created only by up conversion from LUKS1) +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 -s 128 -c cipher_null-ecb --offset 8192 $FAST_PBKDF2 $DEV || fail +$CRYPTSETUP convert -q --type luks2 $DEV || fail +wipe $PWD1 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON >/dev/null || fail +check_hash $PWD1 $HASH1 +# both keyslot and segment cipher must not be null after reencryption with default params +$CRYPTSETUP luksDump $DEV | grep -q "cipher_null" && fail + +# multistep reencryption with initial cipher_null +preparebig 64 +echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 $DEV -c null --offset 16384 -q $FAST_PBKDF_ARGON || fail +echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME +wipe_dev /dev/mapper/$DEV_NAME +echo $PWD1 | $CRYPTSETUP reencrypt $DEV --hotzone-size 1M --resilience none -q $FAST_PBKDF_ARGON >/dev/null || fail +$CRYPTSETUP close $DEV_NAME +check_hash $PWD1 $HASH5 + +echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 $DEV -c null --offset 16384 -q $FAST_PBKDF_ARGON || fail +wipe $PWD1 +echo $PWD1 | $CRYPTSETUP reencrypt $DEV --hotzone-size 1M --resilience none -q $FAST_PBKDF_ARGON >/dev/null || fail +check_hash $PWD1 $HASH5 + +echo "[25] Reencryption recovery with cipher_null" +# (check opt-io size optimization in reencryption code does not affect recovery) +# device with opt-io size 32k +prepare_linear_dev 32 opt_blks=64 $OPT_XFERLEN_EXP +OFFSET=8192 + +echo "sector size 512->512" + +get_error_offsets 32 $OFFSET +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 +check_hash $PWD1 $HASH1 + +echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" +reencrypt_recover 512 checksum $HASH1 +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 +reencrypt_recover 512 journal $HASH1 + +if [ -n "$DM_SECTOR_SIZE" ]; then + echo "sector size 512->4096" + + get_error_offsets 32 $OFFSET 4096 + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 + check_hash $PWD1 $HASH1 + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + reencrypt_recover 4096 checksum $HASH1 + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 + check_hash $PWD1 $HASH1 + reencrypt_recover 4096 journal $HASH1 + + echo "sector size 4096->4096" + + get_error_offsets 32 $OFFSET 4096 + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 4096 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 + check_hash $PWD1 $HASH1 + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + reencrypt_recover 4096 checksum $HASH1 + reencrypt_recover 4096 journal $HASH1 +fi + +echo "[26] Reencryption recovery with cipher_null (online i/o error)" + +echo "sector size 512->512" + +get_error_offsets 32 $OFFSET +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 -c null --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 +check_hash $PWD1 $HASH1 + +echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" +reencrypt_recover_online 512 checksum $HASH1 +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 -c null --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail +wipe $PWD1 +reencrypt_recover_online 512 journal $HASH1 + +if [ -n "$DM_SECTOR_SIZE" ]; then + echo "sector size 512->4096" + + get_error_offsets 32 $OFFSET 4096 + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 + check_hash $PWD1 $HASH1 + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + reencrypt_recover_online 4096 checksum $HASH1 + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 + check_hash $PWD1 $HASH1 + reencrypt_recover_online 4096 journal $HASH1 + + echo "sector size 4096->4096" + + get_error_offsets 32 $OFFSET 4096 + echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 4096 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail + wipe $PWD1 + check_hash $PWD1 $HASH1 + + echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]" + reencrypt_recover_online 4096 checksum $HASH1 + reencrypt_recover_online 4096 journal $HASH1 +fi + +remove_mapping +exit 0 diff --git a/tests/luks2-validation-test b/tests/luks2-validation-test new file mode 100755 index 0000000..04183fb --- /dev/null +++ b/tests/luks2-validation-test @@ -0,0 +1,235 @@ +#!/bin/bash + +#turn on debug mode by following env. variable _DEBUG=1 + +PS4='$LINENO:' +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/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 backtrace:" + while caller $frame; do ((frame++)); done + 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" +RUN luks2-segment-wrong-backup-key-0.img "F" "Failed to detect gap in backup segments" +RUN luks2-segment-wrong-backup-key-1.img "F" "Failed to detect gap in backup segments" + +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 Binary files differnew file mode 100644 index 0000000..eaaa73c --- /dev/null +++ b/tests/luks2_header_requirements.xz diff --git a/tests/luks2_header_requirements_free.xz b/tests/luks2_header_requirements_free.xz Binary files differnew file mode 100644 index 0000000..7617ee6 --- /dev/null +++ b/tests/luks2_header_requirements_free.xz diff --git a/tests/luks2_keyslot_unassigned.img.xz b/tests/luks2_keyslot_unassigned.img.xz Binary files differnew file mode 100644 index 0000000..726cdd7 --- /dev/null +++ b/tests/luks2_keyslot_unassigned.img.xz diff --git a/tests/luks2_mda_images.tar.xz b/tests/luks2_mda_images.tar.xz Binary files differnew file mode 100644 index 0000000..a27f5c7 --- /dev/null +++ b/tests/luks2_mda_images.tar.xz diff --git a/tests/luks2_valid_hdr.img.xz b/tests/luks2_valid_hdr.img.xz Binary files differnew file mode 100644 index 0000000..4e03a20 --- /dev/null +++ b/tests/luks2_valid_hdr.img.xz diff --git a/tests/mode-test b/tests/mode-test new file mode 100755 index 0000000..d16482f --- /dev/null +++ b/tests/mode-test @@ -0,0 +1,171 @@ +#!/bin/bash +# +# Test mode compatibility, check input + kernel and cryptsetup cipher status +# +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/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 --retry $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 backtrace:" + while caller $frame; do ((frame++)); done + 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 || fail + dmcrypt_check "$DEV_NAME"_tstdev $OUT + fi + + echo $PASSWORD | $CRYPTSETUP luksFormat --type luks2 --pbkdf pbkdf2 -i 1 -c $1 -s 256 --offset 8192 /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 || fail + 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..0fb58b3 --- /dev/null +++ b/tests/password-hash-test @@ -0,0 +1,190 @@ +#!/bin/bash + +# check hash processing in create command + +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup +DEV_NAME=dmc_test +KEY_FILE=keyfile + +DEV2=$DEV_NAME"_x" + +dmremove() { # device + udevadm settle >/dev/null 2>&1 + dmsetup remove --retry $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 backtrace:" + while caller $frame; do ((frame++)); done + 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..6dc85bd --- /dev/null +++ b/tests/reencryption-compat-test @@ -0,0 +1,397 @@ +#!/bin/bash + +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup +REENC=$CRYPTSETUP_PATH/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) + +function del_scsi_device() +{ + rmmod scsi_debug 2>/dev/null + sleep 2 +} + +function remove_mapping() +{ + [ -b /dev/mapper/$DEV_NAME2 ] && dmsetup remove --retry $DEV_NAME2 + [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove --retry $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 backtrace:" + while caller $frame; do ((frame++)); done + 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 + + if [ "${REENC:0:1}" != "/" ] ; then + MNT_REENC=$START_DIR/$REENC + else + MNT_REENC=$REENC + fi + + echo $PWD2 | $MNT_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." + +# 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 --retry $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 --retry $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 $HASH5 +$CRYPTSETUP --type luks1 luksDump $LOOPDEV1 > /dev/null || fail +prepare 8192 +OFFSET=4096 +echo fake | $REENC $LOOPDEV1 -d $KEY1 --new --type luks1 --reduce-device-size "$OFFSET"S -q $FAST_PBKDF || fail +$CRYPTSETUP open --test-passphrase $LOOPDEV1 -d $KEY1 || fail +wipe_dev $LOOPDEV1 + +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 underlying 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 || fail +check_hash_dev $LOOPDEV1 $HASH4 + +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 -S5 $FAST_PBKDF $LOOPDEV1 || fail +wipe $PWD1 +check_hash $PWD1 $HASH1 +echo $PWD1 | $REENC $LOOPDEV1 -q --decrypt || fail +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..812788a --- /dev/null +++ b/tests/reencryption-compat-test2 @@ -0,0 +1,473 @@ +#!/bin/bash + +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup +REENC=$CRYPTSETUP_PATH/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 --retry $DEV_NAME2 + [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove --retry $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 backtrace:" + while caller $frame; do ((frame++)); done + 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 + + if [ "${REENC:0:1}" != "/" ] ; then + MNT_REENC=$START_DIR/$REENC + else + MNT_REENC=$REENC + fi + echo $PWD2 | $MNT_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 underlying 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 || fail +check_hash_dev $IMG $HASH4 + +echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -S5 $FAST_PBKDF_ARGON $IMG --offset 8192 || fail +wipe $PWD1 +check_hash $PWD1 $HASH5 +echo $PWD1 | $REENC $IMG -q --decrypt || fail +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..e706427 --- /dev/null +++ b/tests/tcrypt-compat-test @@ -0,0 +1,172 @@ +#!/bin/bash + +# check tcrypt images parsing + +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup +TST_DIR=tcrypt-images +MAP=tctst +PASSWORD="aaaaaaaaaaaa" +PASSWORD_HIDDEN="bbbbbbbbbbbb" +PASSWORD_72C="aaaaaaaaaaaabbbbbbbbbbbbccccccccccccddddddddddddeeeeeeeeeeeeffffffffffff" +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 backtrace:" + while caller $frame; do ((frame++)); done + 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_* $TST_DIR/sys_[tv]c_*) ; do + echo -n " $file" + PIM_OPT="" + [[ $file =~ vcpim.* ]] && PIM_OPT="--veracrypt-pim $PIM" + SYS_OPT="" + [[ $file =~ sys_.* ]] && SYS_OPT="--tcrypt-system" + echo $PASSWORD | $CRYPTSETUP tcryptDump --veracrypt $SYS_OPT $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" + PWD=$PASSWORD + [[ $file =~ vck_1_nopw.* ]] && PWD="" + [[ $file =~ vck_1_pw72.* ]] && PWD=$PASSWORD_72C + echo $PWD | $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_* $TST_DIR/sys_[tv]c_*) ; do + echo -n " $file" + PIM_OPT="" + [[ $file =~ vcpim.* ]] && PIM_OPT="--veracrypt-pim $PIM" + SYS_OPT="" + [[ $file =~ sys_.* ]] && SYS_OPT="--tcrypt-system" + out=$(echo $PASSWORD | $CRYPTSETUP tcryptOpen --veracrypt $SYS_OPT $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 Binary files differnew file mode 100644 index 0000000..1841870 --- /dev/null +++ b/tests/tcrypt-images.tar.xz diff --git a/tests/test_utils.c b/tests/test_utils.c new file mode 100644 index 0000000..9f070e9 --- /dev/null +++ b/tests/test_utils.c @@ -0,0 +1,637 @@ +/* + * cryptsetup library API test utilities + * + * Copyright (C) 2009-2021 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2021 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 KERNEL_KEYRING +# include <linux/keyctl.h> +# include <sys/syscall.h> +#endif +#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_ok_return(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); + } +} + +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_ge_equal(int line, const char *func, int64_t x, int64_t y) +{ + printf("FAIL line %d [%s]: expected greater or 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, ¶ms); + if (!target_type || strcmp(target_type, "crypt") != 0) + goto out; + + (void)strsep(¶ms, " "); /* rcipher */ + key = strsep(¶ms, " "); + + 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", msg); + else + fprintf(stdout, "%s", msg); + } + + if (level <= CRYPT_LOG_DEBUG) + return; + + len = strlen(global_log); + + if (len + strlen(msg) > sizeof(global_log)) { + printf("Log buffer is too small, fix the test.\n"); + return; + } + + strncat(global_log, msg, sizeof(global_log) - len); + 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 keyring_check(void) +{ +#ifdef KERNEL_KEYRING + return syscall(__NR_request_key, "logon", "dummy", NULL, 0) == -1l && errno != ENOSYS; +#else + return 0; +#endif +} + +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) && keyring_check()) + 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..8120842 --- /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-2021 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; + + ret = (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..fcd59a8 --- /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 +%H:%M:%S:%N)_${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..888efcc --- /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 +%H:%M:%S:%N)_${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 Binary files differnew file mode 100644 index 0000000..4b443ae --- /dev/null +++ b/tests/valid_header_file.xz diff --git a/tests/verity-compat-test b/tests/verity-compat-test new file mode 100755 index 0000000..7f381e7 --- /dev/null +++ b/tests/verity-compat-test @@ -0,0 +1,484 @@ +#!/bin/bash + +[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".." +VERITYSETUP=$CRYPTSETUP_PATH/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 +# If we need deterministic image creation +DEV_SALT=9e7457222290f1bac0d42ad2de2d602a87bb871c22ab70ca040bad450578a436 +DEV_UUID=a60c98d2-ae9b-4865-bfcb-b4e3ace11033 + +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 backtrace:" + while caller $frame; do ((frame++)); done + [ -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_fail() +{ + echo -n "Root hash check " + ARR=(`$VERITYSETUP format $IMG $IMG_HASH --fec-device $FEC_DEV --fec-roots 2 -h sha256`) + ROOT_HASH=${ARR[28]} + ROOT_HASH_BAD=abcdef0000000000000000000000000000000000000000000000000000000000 + + $VERITYSETUP verify $IMG $IMG_HASH $ROOT_HASH || fail + $VERITYSETUP verify $IMG $IMG_HASH $ROOT_HASH_BAD >/dev/null 2>&1 && fail + $VERITYSETUP verify $IMG $IMG_HASH $ROOT_HASH_BAD --fec-device $FEC_DEV --fec-roots 2 >/dev/null 2>&1 && fail + + $VERITYSETUP open $IMG $DEV_NAME $IMG_HASH $ROOT_HASH || fail + check_exists + dmsetup status $DEV_NAME | grep "verity V" >/dev/null || fail + $VERITYSETUP close $DEV_NAME >/dev/null 2>&1 || fail + + $VERITYSETUP open $IMG $DEV_NAME $IMG_HASH $ROOT_HASH_BAD >/dev/null 2>&1 || fail + check_exists + dmsetup status $DEV_NAME | grep "verity C" >/dev/null || fail + $VERITYSETUP close $DEV_NAME >/dev/null 2>&1 || fail + + echo "[OK]" +} + +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 + + if [ "$HASH_ORIG" != "$HASH_REPAIRED" ]; then + echo -n "[kernel correction failed]" + $VERITYSETUP verify $1 $2 $ROOT_HASH --fec-device=$3 $PARAMS >/dev/null 2>&1 && fail "Userspace verify should fail" + echo -n "[userspace verify failed]" + RET=1 + else + echo -n "[repaired in kernel]" + $VERITYSETUP verify $1 $2 $ROOT_HASH --fec-device=$3 $PARAMS >/dev/null 2>&1 || fail "Userspace verify failed" + echo "[userspace verify][OK]" + RET=0 + fi + rm $1 $2 $3 $IMG_TMP > /dev/null 2>&1 + return $RET +} + +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 --salt=$DEV_SALT --uuid=$DEV_UUID`) + 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_fail + +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 + if check_version 1 7; then + check_option 512 $HASH $SALT 1 sha256 "--panic-on-corruption" "panic_on_corruption" + 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 $IMG 512 500 50000 2457600 4915200 $(($RANDOM % 23 + 2)) $(($INDEX * 4)) 'n' $SALT) || fail "FEC repair failed" + (check_fec $IMG $IMG $IMG 512 500 50000 2457600 4915200 $(($RANDOM % 23 + 2)) $(($INDEX * 4)) 'y' $SALT) || fail "FEC repair failed" + (check_fec $IMG $IMG $IMG 4096 64 6250 4194304 8388608 $(($RANDOM % 23 + 2)) $(($INDEX * 4)) 'n' $SALT) || fail "FEC repair failed" + (check_fec $IMG $IMG $IMG 4096 64 6250 4194304 8388608 $(($RANDOM % 23 + 2)) $(($INDEX * 4)) 'y' $SALT) || 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 + +remove_mapping +exit 0 |